Image-Text-to-Text
Transformers
Safetensors
qwen3_5
anime
image-to-prompt
image-tagging
danbooru
qwen3.5
vision-language
conversational
Instructions to use damoncao/Anime_Image2Prompt with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use damoncao/Anime_Image2Prompt with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="damoncao/Anime_Image2Prompt") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# Load model directly from transformers import AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("damoncao/Anime_Image2Prompt") model = AutoModelForMultimodalLM.from_pretrained("damoncao/Anime_Image2Prompt", device_map="auto") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] inputs = processor.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=40) print(processor.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use damoncao/Anime_Image2Prompt with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "damoncao/Anime_Image2Prompt" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "damoncao/Anime_Image2Prompt", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/damoncao/Anime_Image2Prompt
- SGLang
How to use damoncao/Anime_Image2Prompt with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "damoncao/Anime_Image2Prompt" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "damoncao/Anime_Image2Prompt", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "damoncao/Anime_Image2Prompt" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "damoncao/Anime_Image2Prompt", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use damoncao/Anime_Image2Prompt with Docker Model Runner:
docker model run hf.co/damoncao/Anime_Image2Prompt
Upload folder using huggingface_hub
Browse files- .gitattributes +5 -0
- LICENSE +202 -0
- README.md +314 -0
- SHA256SUMS +17 -0
- chat_template.jinja +154 -0
- config.json +103 -0
- generation_config.json +7 -0
- images/01-regen.png +3 -0
- images/01.png +3 -0
- images/02-regen.png +3 -0
- images/02.png +3 -0
- inference.py +274 -0
- inference_config.json +18 -0
- model.safetensors +3 -0
- processor_config.json +60 -0
- requirements.txt +5 -0
- tokenizer.json +3 -0
- tokenizer_config.json +35 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,8 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
tokenizer.json filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
images/01-regen.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
images/01.png filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
images/02-regen.png filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
images/02.png filter=lfs diff=lfs merge=lfs -text
|
LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Apache License
|
| 3 |
+
Version 2.0, January 2004
|
| 4 |
+
http://www.apache.org/licenses/
|
| 5 |
+
|
| 6 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 7 |
+
|
| 8 |
+
1. Definitions.
|
| 9 |
+
|
| 10 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 11 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 12 |
+
|
| 13 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 14 |
+
the copyright owner that is granting the License.
|
| 15 |
+
|
| 16 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 17 |
+
other entities that control, are controlled by, or are under common
|
| 18 |
+
control with that entity. For the purposes of this definition,
|
| 19 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 20 |
+
direction or management of such entity, whether by contract or
|
| 21 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 22 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 23 |
+
|
| 24 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 25 |
+
exercising permissions granted by this License.
|
| 26 |
+
|
| 27 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 28 |
+
including but not limited to software source code, documentation
|
| 29 |
+
source, and configuration files.
|
| 30 |
+
|
| 31 |
+
"Object" form shall mean any form resulting from mechanical
|
| 32 |
+
transformation or translation of a Source form, including but
|
| 33 |
+
not limited to compiled object code, generated documentation,
|
| 34 |
+
and conversions to other media types.
|
| 35 |
+
|
| 36 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 37 |
+
Object form, made available under the License, as indicated by a
|
| 38 |
+
copyright notice that is included in or attached to the work
|
| 39 |
+
(an example is provided in the Appendix below).
|
| 40 |
+
|
| 41 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 42 |
+
form, that is based on (or derived from) the Work and for which the
|
| 43 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 44 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 45 |
+
of this License, Derivative Works shall not include works that remain
|
| 46 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 47 |
+
the Work and Derivative Works thereof.
|
| 48 |
+
|
| 49 |
+
"Contribution" shall mean any work of authorship, including
|
| 50 |
+
the original version of the Work and any modifications or additions
|
| 51 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 52 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 53 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 54 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 55 |
+
means any form of electronic, verbal, or written communication sent
|
| 56 |
+
to the Licensor or its representatives, including but not limited to
|
| 57 |
+
communication on electronic mailing lists, source code control systems,
|
| 58 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 59 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 60 |
+
excluding communication that is conspicuously marked or otherwise
|
| 61 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 62 |
+
|
| 63 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 64 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 65 |
+
subsequently incorporated within the Work.
|
| 66 |
+
|
| 67 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 68 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 69 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 70 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 71 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 72 |
+
Work and such Derivative Works in Source or Object form.
|
| 73 |
+
|
| 74 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 75 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 76 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 77 |
+
(except as stated in this section) patent license to make, have made,
|
| 78 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 79 |
+
where such license applies only to those patent claims licensable
|
| 80 |
+
by such Contributor that are necessarily infringed by their
|
| 81 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 82 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 83 |
+
institute patent litigation against any entity (including a
|
| 84 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 85 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 86 |
+
or contributory patent infringement, then any patent licenses
|
| 87 |
+
granted to You under this License for that Work shall terminate
|
| 88 |
+
as of the date such litigation is filed.
|
| 89 |
+
|
| 90 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 91 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 92 |
+
modifications, and in Source or Object form, provided that You
|
| 93 |
+
meet the following conditions:
|
| 94 |
+
|
| 95 |
+
(a) You must give any other recipients of the Work or
|
| 96 |
+
Derivative Works a copy of this License; and
|
| 97 |
+
|
| 98 |
+
(b) You must cause any modified files to carry prominent notices
|
| 99 |
+
stating that You changed the files; and
|
| 100 |
+
|
| 101 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 102 |
+
that You distribute, all copyright, patent, trademark, and
|
| 103 |
+
attribution notices from the Source form of the Work,
|
| 104 |
+
excluding those notices that do not pertain to any part of
|
| 105 |
+
the Derivative Works; and
|
| 106 |
+
|
| 107 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 108 |
+
distribution, then any Derivative Works that You distribute must
|
| 109 |
+
include a readable copy of the attribution notices contained
|
| 110 |
+
within such NOTICE file, excluding those notices that do not
|
| 111 |
+
pertain to any part of the Derivative Works, in at least one
|
| 112 |
+
of the following places: within a NOTICE text file distributed
|
| 113 |
+
as part of the Derivative Works; within the Source form or
|
| 114 |
+
documentation, if provided along with the Derivative Works; or,
|
| 115 |
+
within a display generated by the Derivative Works, if and
|
| 116 |
+
wherever such third-party notices normally appear. The contents
|
| 117 |
+
of the NOTICE file are for informational purposes only and
|
| 118 |
+
do not modify the License. You may add Your own attribution
|
| 119 |
+
notices within Derivative Works that You distribute, alongside
|
| 120 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 121 |
+
that such additional attribution notices cannot be construed
|
| 122 |
+
as modifying the License.
|
| 123 |
+
|
| 124 |
+
You may add Your own copyright statement to Your modifications and
|
| 125 |
+
may provide additional or different license terms and conditions
|
| 126 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 127 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 128 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 129 |
+
the conditions stated in this License.
|
| 130 |
+
|
| 131 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 132 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 133 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 134 |
+
this License, without any additional terms or conditions.
|
| 135 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 136 |
+
the terms of any separate license agreement you may have executed
|
| 137 |
+
with Licensor regarding such Contributions.
|
| 138 |
+
|
| 139 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 140 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 141 |
+
except as required for reasonable and customary use in describing the
|
| 142 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 143 |
+
|
| 144 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 145 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 146 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 147 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 148 |
+
implied, including, without limitation, any warranties or conditions
|
| 149 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 150 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 151 |
+
appropriateness of using or redistributing the Work and assume any
|
| 152 |
+
risks associated with Your exercise of permissions under this License.
|
| 153 |
+
|
| 154 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 155 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 156 |
+
unless required by applicable law (such as deliberate and grossly
|
| 157 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 158 |
+
liable to You for damages, including any direct, indirect, special,
|
| 159 |
+
incidental, or consequential damages of any character arising as a
|
| 160 |
+
result of this License or out of the use or inability to use the
|
| 161 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 162 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 163 |
+
other commercial damages or losses), even if such Contributor
|
| 164 |
+
has been advised of the possibility of such damages.
|
| 165 |
+
|
| 166 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 167 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 168 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 169 |
+
or other liability obligations and/or rights consistent with this
|
| 170 |
+
License. However, in accepting such obligations, You may act only
|
| 171 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 172 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 173 |
+
defend, and hold each Contributor harmless for any liability
|
| 174 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 175 |
+
of your accepting any such warranty or additional liability.
|
| 176 |
+
|
| 177 |
+
END OF TERMS AND CONDITIONS
|
| 178 |
+
|
| 179 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 180 |
+
|
| 181 |
+
To apply the Apache License to your work, attach the following
|
| 182 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 183 |
+
replaced with your own identifying information. (Don't include
|
| 184 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 185 |
+
comment syntax for the file format. We also recommend that a
|
| 186 |
+
file or class name and description of purpose be included on the
|
| 187 |
+
same "printed page" as the copyright notice for easier
|
| 188 |
+
identification within third-party archives.
|
| 189 |
+
|
| 190 |
+
Copyright 2026 Alibaba Cloud
|
| 191 |
+
|
| 192 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 193 |
+
you may not use this file except in compliance with the License.
|
| 194 |
+
You may obtain a copy of the License at
|
| 195 |
+
|
| 196 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 197 |
+
|
| 198 |
+
Unless required by applicable law or agreed to in writing, software
|
| 199 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 200 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 201 |
+
See the License for the specific language governing permissions and
|
| 202 |
+
limitations under the License.
|
README.md
CHANGED
|
@@ -1,3 +1,317 @@
|
|
| 1 |
---
|
| 2 |
license: apache-2.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
license: apache-2.0
|
| 3 |
+
library_name: transformers
|
| 4 |
+
pipeline_tag: image-text-to-text
|
| 5 |
+
tags:
|
| 6 |
+
- anime
|
| 7 |
+
- image-to-prompt
|
| 8 |
+
- image-tagging
|
| 9 |
+
- danbooru
|
| 10 |
+
- qwen3.5
|
| 11 |
+
- vision-language
|
| 12 |
---
|
| 13 |
+
|
| 14 |
+
# Anime_Image2Prompt
|
| 15 |
+
|
| 16 |
+
[English](#english) | [中文说明](#中文说明)
|
| 17 |
+
|
| 18 |
+
## English
|
| 19 |
+
|
| 20 |
+
### Model overview
|
| 21 |
+
|
| 22 |
+
**Anime_Image2Prompt** is a Qwen3.5-2B-based vision-language model that converts a single anime, illustration, or anime-style image into Danbooru-style prompt tags. It separates general visual attributes, character identities, and copyrights/franchises, making it useful for image organization, dataset annotation, and prompt extraction in generative-image workflows.
|
| 23 |
+
|
| 24 |
+
The repository contains all weights required for inference; no base model or separate adapter is needed. The model is stored in BF16, occupies approximately 4.12 GiB, and has 2,213,241,664 parameters. The included program loads local files from this directory by default.
|
| 25 |
+
|
| 26 |
+
### Original-to-regeneration comparison
|
| 27 |
+
|
| 28 |
+
The following two pairs demonstrate the complete workflow. `01.png` and `02.png` are the original images. Each original was processed by **Anime_Image2Prompt**, and the extracted prompt was passed without manual rewriting directly to [WAI-illustrious-SDXL v15.0](https://civitai.red/models/827184/wai-illustrious-sdxl?modelVersionId=2167369). The resulting images are `01-regen.png` and `02-regen.png`, respectively.
|
| 29 |
+
|
| 30 |
+
<table>
|
| 31 |
+
<tr>
|
| 32 |
+
<th width="50%">01 · Original</th>
|
| 33 |
+
<th width="50%">01 · Prompt regeneration</th>
|
| 34 |
+
</tr>
|
| 35 |
+
<tr>
|
| 36 |
+
<td><img src="images/01.png" alt="Example 01 original" width="100%"></td>
|
| 37 |
+
<td><img src="images/01-regen.png" alt="Example 01 prompt regeneration" width="100%"></td>
|
| 38 |
+
</tr>
|
| 39 |
+
<tr>
|
| 40 |
+
<th width="50%">02 · Original</th>
|
| 41 |
+
<th width="50%">02 · Prompt regeneration</th>
|
| 42 |
+
</tr>
|
| 43 |
+
<tr>
|
| 44 |
+
<td><img src="images/02.png" alt="Example 02 original" width="100%"></td>
|
| 45 |
+
<td><img src="images/02-regen.png" alt="Example 02 prompt regeneration" width="100%"></td>
|
| 46 |
+
</tr>
|
| 47 |
+
</table>
|
| 48 |
+
|
| 49 |
+
These examples illustrate how the model captures major semantics such as character identity, clothing, scene, and composition. Prompt regeneration is not image-to-image or pixel-level reconstruction; the final image also depends on the generation model, random seed, and sampling settings.
|
| 50 |
+
|
| 51 |
+
### Output format
|
| 52 |
+
|
| 53 |
+
The model's native output is a fixed JSON object:
|
| 54 |
+
|
| 55 |
+
```json
|
| 56 |
+
{
|
| 57 |
+
"general_tags": ["1girl,long hair,blue eyes"],
|
| 58 |
+
"character_tags": ["example character"],
|
| 59 |
+
"copyright_tags": ["example copyright"]
|
| 60 |
+
}
|
| 61 |
+
```
|
| 62 |
+
|
| 63 |
+
- `general_tags`: counts, appearance, clothing, actions, composition, and scene attributes.
|
| 64 |
+
- `character_tags`: character names recognized by the model.
|
| 65 |
+
- `copyright_tags`: recognized works or franchises.
|
| 66 |
+
- Each non-empty category contains one comma-separated string. An unknown category is returned as an empty array, `[]`.
|
| 67 |
+
|
| 68 |
+
The included program can also combine all categories into a flat prompt:
|
| 69 |
+
|
| 70 |
+
```text
|
| 71 |
+
1girl, long hair, blue eyes, example character, example copyright
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
### Requirements
|
| 75 |
+
|
| 76 |
+
- Python 3.10 or newer; Python 3.11 is recommended.
|
| 77 |
+
- An NVIDIA GPU with at least 8 GB of VRAM is recommended. More VRAM provides additional headroom.
|
| 78 |
+
- CPU inference is supported but slow; at least 16 GB of system RAM is recommended.
|
| 79 |
+
- The weight file is larger than 4 GB. Use a large-file-capable filesystem such as NTFS, exFAT, APFS, or ext4 instead of FAT32.
|
| 80 |
+
|
| 81 |
+
### Installation
|
| 82 |
+
|
| 83 |
+
Create a virtual environment inside the model directory:
|
| 84 |
+
|
| 85 |
+
```bash
|
| 86 |
+
python -m venv .venv
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
Windows PowerShell:
|
| 90 |
+
|
| 91 |
+
```powershell
|
| 92 |
+
.\.venv\Scripts\Activate.ps1
|
| 93 |
+
python -m pip install --upgrade pip
|
| 94 |
+
python -m pip install -r requirements.txt
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
Linux / macOS:
|
| 98 |
+
|
| 99 |
+
```bash
|
| 100 |
+
source .venv/bin/activate
|
| 101 |
+
python -m pip install --upgrade pip
|
| 102 |
+
python -m pip install -r requirements.txt
|
| 103 |
+
```
|
| 104 |
+
|
| 105 |
+
If you need a specific CUDA build, install the PyTorch build matching your GPU driver from the [official PyTorch selector](https://pytorch.org/get-started/locally/) before installing the remaining requirements.
|
| 106 |
+
|
| 107 |
+
### Quick start
|
| 108 |
+
|
| 109 |
+
The script resides beside the model files, so no model path is required:
|
| 110 |
+
|
| 111 |
+
```bash
|
| 112 |
+
python inference.py /path/to/image.png
|
| 113 |
+
```
|
| 114 |
+
|
| 115 |
+
The default output is structured JSON. Save it to a file with:
|
| 116 |
+
|
| 117 |
+
```bash
|
| 118 |
+
python inference.py /path/to/image.png --output result.json
|
| 119 |
+
```
|
| 120 |
+
|
| 121 |
+
Print a flat, copy-ready prompt instead:
|
| 122 |
+
|
| 123 |
+
```bash
|
| 124 |
+
python inference.py /path/to/image.png --format prompt
|
| 125 |
+
```
|
| 126 |
+
|
| 127 |
+
Useful options:
|
| 128 |
+
|
| 129 |
+
```bash
|
| 130 |
+
# Force CPU inference
|
| 131 |
+
python inference.py image.png --device cpu
|
| 132 |
+
|
| 133 |
+
# Reduce image tokens and peak VRAM usage
|
| 134 |
+
python inference.py image.png --low-vram
|
| 135 |
+
|
| 136 |
+
# Show every option
|
| 137 |
+
python inference.py --help
|
| 138 |
+
```
|
| 139 |
+
|
| 140 |
+
The program applies EXIF orientation and RGB conversion automatically and accepts common image formats supported by Pillow. Model loading uses `local_files_only=True`, so it does not download additional weights.
|
| 141 |
+
|
| 142 |
+
### Python API
|
| 143 |
+
|
| 144 |
+
```python
|
| 145 |
+
from inference import AnimeImage2Prompt
|
| 146 |
+
|
| 147 |
+
tagger = AnimeImage2Prompt()
|
| 148 |
+
result = tagger.predict("image.png")
|
| 149 |
+
|
| 150 |
+
print(result)
|
| 151 |
+
print(tagger.to_prompt(result))
|
| 152 |
+
```
|
| 153 |
+
|
| 154 |
+
Reuse the same `AnimeImage2Prompt` instance for multiple images to avoid reloading the model.
|
| 155 |
+
|
| 156 |
+
### Limitations
|
| 157 |
+
|
| 158 |
+
- The model is intended primarily for anime and illustration images. Performance may degrade on photographs or images far outside its training distribution.
|
| 159 |
+
- Tags are not guaranteed to be exhaustive or correct. Character and franchise recognition can be especially uncertain.
|
| 160 |
+
- The model may return tags describing sensitive content. Apply content filtering appropriate to your use case and follow applicable laws and platform policies.
|
| 161 |
+
- Do not use the model as the sole basis for safety moderation, identity decisions, or other high-impact decisions.
|
| 162 |
+
|
| 163 |
+
### License
|
| 164 |
+
|
| 165 |
+
This repository is released under the Apache License 2.0. See [`LICENSE`](LICENSE) for details.
|
| 166 |
+
|
| 167 |
+
---
|
| 168 |
+
|
| 169 |
+
## 中文说明
|
| 170 |
+
|
| 171 |
+
### 模型简介
|
| 172 |
+
|
| 173 |
+
**Anime_Image2Prompt** 是一个基于 Qwen3.5-2B 的动漫图像理解模型,可将单张动漫、插画或二次元风格图片转换为 Danbooru 风格的提示词。模型会分别识别画面通用特征、角色名称和作品来源,适合用于图片归档、数据集标注和生成式图像工作流中的提示词提取。
|
| 174 |
+
|
| 175 |
+
模型权重已完整包含在本仓库中,无需额外下载基础模型或适配器。权重以 BF16 保存,约 4.12 GiB,共 2,213,241,664 个参数。推理程序默认只读取当前目录中的本地文件。
|
| 176 |
+
|
| 177 |
+
### 原图与提示词重绘对比
|
| 178 |
+
|
| 179 |
+
下面展示两组端到端效果对比。`01.png` 和 `02.png` 是原图;处理流程为:将原图输入 **Anime_Image2Prompt**,把模型提取出的提示词不经人工改写,直接交给 [WAI-illustrious-SDXL v15.0](https://civitai.red/models/827184/wai-illustrious-sdxl?modelVersionId=2167369) 绘制,对应结果分别为 `01-regen.png` 和 `02-regen.png`。
|
| 180 |
+
|
| 181 |
+
<table>
|
| 182 |
+
<tr>
|
| 183 |
+
<th width="50%">01 · 原图</th>
|
| 184 |
+
<th width="50%">01 · 提示词重绘</th>
|
| 185 |
+
</tr>
|
| 186 |
+
<tr>
|
| 187 |
+
<td><img src="images/01.png" alt="示例 01 原图" width="100%"></td>
|
| 188 |
+
<td><img src="images/01-regen.png" alt="示例 01 提示词重绘结果" width="100%"></td>
|
| 189 |
+
</tr>
|
| 190 |
+
<tr>
|
| 191 |
+
<th width="50%">02 · 原图</th>
|
| 192 |
+
<th width="50%">02 · 提示词重绘</th>
|
| 193 |
+
</tr>
|
| 194 |
+
<tr>
|
| 195 |
+
<td><img src="images/02.png" alt="示例 02 原图" width="100%"></td>
|
| 196 |
+
<td><img src="images/02-regen.png" alt="示例 02 提示词重绘结果" width="100%"></td>
|
| 197 |
+
</tr>
|
| 198 |
+
</table>
|
| 199 |
+
|
| 200 |
+
这些示例用于观察模型对角色、服饰、场景和构图等主要语义的提取能力。提示词重绘并非图生图或像素级复刻,具体画面仍会受到绘图模型、随机种子和采样参数影响。
|
| 201 |
+
|
| 202 |
+
### 输出格式
|
| 203 |
+
|
| 204 |
+
模型原生输出固定为 JSON:
|
| 205 |
+
|
| 206 |
+
```json
|
| 207 |
+
{
|
| 208 |
+
"general_tags": ["1girl,long hair,blue eyes"],
|
| 209 |
+
"character_tags": ["example character"],
|
| 210 |
+
"copyright_tags": ["example copyright"]
|
| 211 |
+
}
|
| 212 |
+
```
|
| 213 |
+
|
| 214 |
+
- `general_tags`:人物数量、外观、服饰、动作、构图和场景等通用标签。
|
| 215 |
+
- `character_tags`:模型能够识别的角色名称。
|
| 216 |
+
- `copyright_tags`:模型能够识别的作品或系列名称。
|
| 217 |
+
- 每个非空类别包含一个以英文逗号连接的字符串;无法识别时返回空数组 `[]`。
|
| 218 |
+
|
| 219 |
+
随附程序也可以把三个类别合并为常用的扁平提示词:
|
| 220 |
+
|
| 221 |
+
```text
|
| 222 |
+
1girl, long hair, blue eyes, example character, example copyright
|
| 223 |
+
```
|
| 224 |
+
|
| 225 |
+
### 环境要求
|
| 226 |
+
|
| 227 |
+
- Python 3.10 或更高版本,推荐 Python 3.11。
|
| 228 |
+
- 推荐使用具有 8 GB 或更多显存的 NVIDIA GPU;显存越大,运行越稳定。
|
| 229 |
+
- CPU 可以运行,但速度较慢,建议至少准备 16 GB 系统内存。
|
| 230 |
+
- 模型文件大于 4 GB,请使用 NTFS、exFAT、APFS、ext4 等支持大文件的文件系统,不要使用 FAT32。
|
| 231 |
+
|
| 232 |
+
### 安装
|
| 233 |
+
|
| 234 |
+
在模型目录中创建虚拟环境并安装依赖:
|
| 235 |
+
|
| 236 |
+
```bash
|
| 237 |
+
python -m venv .venv
|
| 238 |
+
```
|
| 239 |
+
|
| 240 |
+
Windows PowerShell:
|
| 241 |
+
|
| 242 |
+
```powershell
|
| 243 |
+
.\.venv\Scripts\Activate.ps1
|
| 244 |
+
python -m pip install --upgrade pip
|
| 245 |
+
python -m pip install -r requirements.txt
|
| 246 |
+
```
|
| 247 |
+
|
| 248 |
+
Linux / macOS:
|
| 249 |
+
|
| 250 |
+
```bash
|
| 251 |
+
source .venv/bin/activate
|
| 252 |
+
python -m pip install --upgrade pip
|
| 253 |
+
python -m pip install -r requirements.txt
|
| 254 |
+
```
|
| 255 |
+
|
| 256 |
+
如果需要特定 CUDA 版本,请先按照 [PyTorch 官方安装说明](https://pytorch.org/get-started/locally/) 安装与显卡驱动匹配的 PyTorch,再执行依赖安装命令。
|
| 257 |
+
|
| 258 |
+
### 快速使用
|
| 259 |
+
|
| 260 |
+
推理脚本与模型放在同一目录,因此无需填写模型路径:
|
| 261 |
+
|
| 262 |
+
```bash
|
| 263 |
+
python inference.py /path/to/image.png
|
| 264 |
+
```
|
| 265 |
+
|
| 266 |
+
默认在终端输出结构化 JSON。保存结果:
|
| 267 |
+
|
| 268 |
+
```bash
|
| 269 |
+
python inference.py /path/to/image.png --output result.json
|
| 270 |
+
```
|
| 271 |
+
|
| 272 |
+
直接输出可复制的扁平提示词:
|
| 273 |
+
|
| 274 |
+
```bash
|
| 275 |
+
python inference.py /path/to/image.png --format prompt
|
| 276 |
+
```
|
| 277 |
+
|
| 278 |
+
常用选项:
|
| 279 |
+
|
| 280 |
+
```bash
|
| 281 |
+
# 强制使用 CPU
|
| 282 |
+
python inference.py image.png --device cpu
|
| 283 |
+
|
| 284 |
+
# 降低图像 token 数以减少峰值显存占用
|
| 285 |
+
python inference.py image.png --low-vram
|
| 286 |
+
|
| 287 |
+
# 查看全部参数
|
| 288 |
+
python inference.py --help
|
| 289 |
+
```
|
| 290 |
+
|
| 291 |
+
程序会自动处理图片的 EXIF 方向并转换为 RGB,支持 Pillow 可读取的常见图片格式。加载模型时使用 `local_files_only=True`,不会联网下载其他权重。
|
| 292 |
+
|
| 293 |
+
### Python 调用
|
| 294 |
+
|
| 295 |
+
```python
|
| 296 |
+
from inference import AnimeImage2Prompt
|
| 297 |
+
|
| 298 |
+
tagger = AnimeImage2Prompt()
|
| 299 |
+
result = tagger.predict("image.png")
|
| 300 |
+
|
| 301 |
+
print(result)
|
| 302 |
+
print(tagger.to_prompt(result))
|
| 303 |
+
```
|
| 304 |
+
|
| 305 |
+
同一个 `AnimeImage2Prompt` 实例可连续处理多张图片,模型只会加载一次。
|
| 306 |
+
|
| 307 |
+
### 使用限制
|
| 308 |
+
|
| 309 |
+
- 本模型主要面向动漫和插画图片,真实照片或与训练分布差异较大的图片可能表现较差。
|
| 310 |
+
- 标签并不保证完整或完全准确,角色与作品来源尤其可能发生误识别。
|
| 311 |
+
- 模型可能输出敏感内容标签。使用者应根据应用场景进行内容过滤,并遵守当地法律及平台规则。
|
| 312 |
+
- 本模型不应作为安全审核、身份判断或其他高风险决策的唯一依据。
|
| 313 |
+
|
| 314 |
+
### 许可证
|
| 315 |
+
|
| 316 |
+
本仓库依据 Apache License 2.0 发布,详情见 [`LICENSE`](LICENSE)。
|
| 317 |
+
|
SHA256SUMS
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
34448b82c17d60fec9b65b1f093c115ddbaadc04beb1b0140b6bfed2e012a930 .gitattributes
|
| 2 |
+
273d8e0e683b885071fb17e08d71e5f2a5ddfb5309756181681de4f5a1822d80 chat_template.jinja
|
| 3 |
+
137bf1846d31c6a192563742fe779d4cdc70ace03f89c8ab75853bb1bd980011 config.json
|
| 4 |
+
774d3ef576350368f17c9d5840e58b45b6f96e1027d614c7a086e38a07bb1897 generation_config.json
|
| 5 |
+
0ec7a0f616ab7807fffdf79f8aad21099d745721902530019636ba4738387bb2 inference.py
|
| 6 |
+
52496bd91e81ff8f5dfdb2edc8b9985563af531a7db3c5250473033a36f1c2da inference_config.json
|
| 7 |
+
07226f7a7f661ef0cb3ce5fbef78f5cc8ee6952a430db07a4661c1c0a2bd57e7 images/01-regen.png
|
| 8 |
+
2a6f2ddca81021930896da8c21d776419e2d2a0b5f60557fdb1991a91a3821e7 images/01.png
|
| 9 |
+
abbeaaaf80623948a0980bc6f9ad062fef49babb2c803535735efc3d7425aa5a images/02-regen.png
|
| 10 |
+
98819176c7d08ba1981ee039af36e8769ae46073a7f7e15186dd2b506773a0d4 images/02.png
|
| 11 |
+
bbedc3fda3305820b977265f01b8619d87570a6739de3a5582c3464840f1e57a LICENSE
|
| 12 |
+
8e7b265c171b46ee7e3dd4809b3b1cfc933c40cbdbe5c976f499e654daa0be3b model.safetensors
|
| 13 |
+
04920fe4d2d3491ed36e324231199cf5c1576fa41a34e0eff432bbe1e86ce5d8 processor_config.json
|
| 14 |
+
598544c992ff9d934b44c7b01addf3dc1887e5b52f1cdf3882670970f538cc12 README.md
|
| 15 |
+
1b8b6fbefca746644cb6a0e0fc5d541d54689f21b1e26a4ce0d11155353a31c8 requirements.txt
|
| 16 |
+
06b9509352d2af50381ab2247e083b80d32d5c0aba91c272ca9ff729b6a0e523 tokenizer.json
|
| 17 |
+
2a364f7d378ea19bdeedb7ab23b3f57d661a8aca282532a6d8e9e38d2252d740 tokenizer_config.json
|
chat_template.jinja
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{%- set image_count = namespace(value=0) %}
|
| 2 |
+
{%- set video_count = namespace(value=0) %}
|
| 3 |
+
{%- macro render_content(content, do_vision_count, is_system_content=false) %}
|
| 4 |
+
{%- if content is string %}
|
| 5 |
+
{{- content }}
|
| 6 |
+
{%- elif content is iterable and content is not mapping %}
|
| 7 |
+
{%- for item in content %}
|
| 8 |
+
{%- if 'image' in item or 'image_url' in item or item.type == 'image' %}
|
| 9 |
+
{%- if is_system_content %}
|
| 10 |
+
{{- raise_exception('System message cannot contain images.') }}
|
| 11 |
+
{%- endif %}
|
| 12 |
+
{%- if do_vision_count %}
|
| 13 |
+
{%- set image_count.value = image_count.value + 1 %}
|
| 14 |
+
{%- endif %}
|
| 15 |
+
{%- if add_vision_id %}
|
| 16 |
+
{{- 'Picture ' ~ image_count.value ~ ': ' }}
|
| 17 |
+
{%- endif %}
|
| 18 |
+
{{- '<|vision_start|><|image_pad|><|vision_end|>' }}
|
| 19 |
+
{%- elif 'video' in item or item.type == 'video' %}
|
| 20 |
+
{%- if is_system_content %}
|
| 21 |
+
{{- raise_exception('System message cannot contain videos.') }}
|
| 22 |
+
{%- endif %}
|
| 23 |
+
{%- if do_vision_count %}
|
| 24 |
+
{%- set video_count.value = video_count.value + 1 %}
|
| 25 |
+
{%- endif %}
|
| 26 |
+
{%- if add_vision_id %}
|
| 27 |
+
{{- 'Video ' ~ video_count.value ~ ': ' }}
|
| 28 |
+
{%- endif %}
|
| 29 |
+
{{- '<|vision_start|><|video_pad|><|vision_end|>' }}
|
| 30 |
+
{%- elif 'text' in item %}
|
| 31 |
+
{{- item.text }}
|
| 32 |
+
{%- else %}
|
| 33 |
+
{{- raise_exception('Unexpected item type in content.') }}
|
| 34 |
+
{%- endif %}
|
| 35 |
+
{%- endfor %}
|
| 36 |
+
{%- elif content is none or content is undefined %}
|
| 37 |
+
{{- '' }}
|
| 38 |
+
{%- else %}
|
| 39 |
+
{{- raise_exception('Unexpected content type.') }}
|
| 40 |
+
{%- endif %}
|
| 41 |
+
{%- endmacro %}
|
| 42 |
+
{%- if not messages %}
|
| 43 |
+
{{- raise_exception('No messages provided.') }}
|
| 44 |
+
{%- endif %}
|
| 45 |
+
{%- if tools and tools is iterable and tools is not mapping %}
|
| 46 |
+
{{- '<|im_start|>system\n' }}
|
| 47 |
+
{{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
|
| 48 |
+
{%- for tool in tools %}
|
| 49 |
+
{{- "\n" }}
|
| 50 |
+
{{- tool | tojson }}
|
| 51 |
+
{%- endfor %}
|
| 52 |
+
{{- "\n</tools>" }}
|
| 53 |
+
{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
|
| 54 |
+
{%- if messages[0].role == 'system' %}
|
| 55 |
+
{%- set content = render_content(messages[0].content, false, true)|trim %}
|
| 56 |
+
{%- if content %}
|
| 57 |
+
{{- '\n\n' + content }}
|
| 58 |
+
{%- endif %}
|
| 59 |
+
{%- endif %}
|
| 60 |
+
{{- '<|im_end|>\n' }}
|
| 61 |
+
{%- else %}
|
| 62 |
+
{%- if messages[0].role == 'system' %}
|
| 63 |
+
{%- set content = render_content(messages[0].content, false, true)|trim %}
|
| 64 |
+
{{- '<|im_start|>system\n' + content + '<|im_end|>\n' }}
|
| 65 |
+
{%- endif %}
|
| 66 |
+
{%- endif %}
|
| 67 |
+
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
|
| 68 |
+
{%- for message in messages[::-1] %}
|
| 69 |
+
{%- set index = (messages|length - 1) - loop.index0 %}
|
| 70 |
+
{%- if ns.multi_step_tool and message.role == "user" %}
|
| 71 |
+
{%- set content = render_content(message.content, false)|trim %}
|
| 72 |
+
{%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}
|
| 73 |
+
{%- set ns.multi_step_tool = false %}
|
| 74 |
+
{%- set ns.last_query_index = index %}
|
| 75 |
+
{%- endif %}
|
| 76 |
+
{%- endif %}
|
| 77 |
+
{%- endfor %}
|
| 78 |
+
{%- if ns.multi_step_tool %}
|
| 79 |
+
{{- raise_exception('No user query found in messages.') }}
|
| 80 |
+
{%- endif %}
|
| 81 |
+
{%- for message in messages %}
|
| 82 |
+
{%- set content = render_content(message.content, true)|trim %}
|
| 83 |
+
{%- if message.role == "system" %}
|
| 84 |
+
{%- if not loop.first %}
|
| 85 |
+
{{- raise_exception('System message must be at the beginning.') }}
|
| 86 |
+
{%- endif %}
|
| 87 |
+
{%- elif message.role == "user" %}
|
| 88 |
+
{{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
|
| 89 |
+
{%- elif message.role == "assistant" %}
|
| 90 |
+
{%- set reasoning_content = '' %}
|
| 91 |
+
{%- if message.reasoning_content is string %}
|
| 92 |
+
{%- set reasoning_content = message.reasoning_content %}
|
| 93 |
+
{%- else %}
|
| 94 |
+
{%- if '</think>' in content %}
|
| 95 |
+
{%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
| 96 |
+
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
|
| 97 |
+
{%- endif %}
|
| 98 |
+
{%- endif %}
|
| 99 |
+
{%- set reasoning_content = reasoning_content|trim %}
|
| 100 |
+
{%- if loop.index0 > ns.last_query_index %}
|
| 101 |
+
{{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}
|
| 102 |
+
{%- else %}
|
| 103 |
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
| 104 |
+
{%- endif %}
|
| 105 |
+
{%- if message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %}
|
| 106 |
+
{%- for tool_call in message.tool_calls %}
|
| 107 |
+
{%- if tool_call.function is defined %}
|
| 108 |
+
{%- set tool_call = tool_call.function %}
|
| 109 |
+
{%- endif %}
|
| 110 |
+
{%- if loop.first %}
|
| 111 |
+
{%- if content|trim %}
|
| 112 |
+
{{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 113 |
+
{%- else %}
|
| 114 |
+
{{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 115 |
+
{%- endif %}
|
| 116 |
+
{%- else %}
|
| 117 |
+
{{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
| 118 |
+
{%- endif %}
|
| 119 |
+
{%- if tool_call.arguments is defined %}
|
| 120 |
+
{%- for args_name, args_value in tool_call.arguments|items %}
|
| 121 |
+
{{- '<parameter=' + args_name + '>\n' }}
|
| 122 |
+
{%- set args_value = args_value | tojson | safe if args_value is mapping or (args_value is sequence and args_value is not string) else args_value | string %}
|
| 123 |
+
{{- args_value }}
|
| 124 |
+
{{- '\n</parameter>\n' }}
|
| 125 |
+
{%- endfor %}
|
| 126 |
+
{%- endif %}
|
| 127 |
+
{{- '</function>\n</tool_call>' }}
|
| 128 |
+
{%- endfor %}
|
| 129 |
+
{%- endif %}
|
| 130 |
+
{{- '<|im_end|>\n' }}
|
| 131 |
+
{%- elif message.role == "tool" %}
|
| 132 |
+
{%- if loop.previtem and loop.previtem.role != "tool" %}
|
| 133 |
+
{{- '<|im_start|>user' }}
|
| 134 |
+
{%- endif %}
|
| 135 |
+
{{- '\n<tool_response>\n' }}
|
| 136 |
+
{{- content }}
|
| 137 |
+
{{- '\n</tool_response>' }}
|
| 138 |
+
{%- if not loop.last and loop.nextitem.role != "tool" %}
|
| 139 |
+
{{- '<|im_end|>\n' }}
|
| 140 |
+
{%- elif loop.last %}
|
| 141 |
+
{{- '<|im_end|>\n' }}
|
| 142 |
+
{%- endif %}
|
| 143 |
+
{%- else %}
|
| 144 |
+
{{- raise_exception('Unexpected message role.') }}
|
| 145 |
+
{%- endif %}
|
| 146 |
+
{%- endfor %}
|
| 147 |
+
{%- if add_generation_prompt %}
|
| 148 |
+
{{- '<|im_start|>assistant\n' }}
|
| 149 |
+
{%- if enable_thinking is defined and enable_thinking is true %}
|
| 150 |
+
{{- '<think>\n' }}
|
| 151 |
+
{%- else %}
|
| 152 |
+
{{- '<think>\n\n</think>\n\n' }}
|
| 153 |
+
{%- endif %}
|
| 154 |
+
{%- endif %}
|
config.json
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model_name": "Anime_Image2Prompt",
|
| 3 |
+
"architectures": [
|
| 4 |
+
"Qwen3_5ForConditionalGeneration"
|
| 5 |
+
],
|
| 6 |
+
"dtype": "bfloat16",
|
| 7 |
+
"image_token_id": 248056,
|
| 8 |
+
"model_type": "qwen3_5",
|
| 9 |
+
"text_config": {
|
| 10 |
+
"attention_bias": false,
|
| 11 |
+
"attention_dropout": 0.0,
|
| 12 |
+
"attn_output_gate": true,
|
| 13 |
+
"bos_token_id": null,
|
| 14 |
+
"dtype": "bfloat16",
|
| 15 |
+
"eos_token_id": 248044,
|
| 16 |
+
"full_attention_interval": 4,
|
| 17 |
+
"head_dim": 256,
|
| 18 |
+
"hidden_act": "silu",
|
| 19 |
+
"hidden_size": 2048,
|
| 20 |
+
"initializer_range": 0.02,
|
| 21 |
+
"intermediate_size": 6144,
|
| 22 |
+
"layer_types": [
|
| 23 |
+
"linear_attention",
|
| 24 |
+
"linear_attention",
|
| 25 |
+
"linear_attention",
|
| 26 |
+
"full_attention",
|
| 27 |
+
"linear_attention",
|
| 28 |
+
"linear_attention",
|
| 29 |
+
"linear_attention",
|
| 30 |
+
"full_attention",
|
| 31 |
+
"linear_attention",
|
| 32 |
+
"linear_attention",
|
| 33 |
+
"linear_attention",
|
| 34 |
+
"full_attention",
|
| 35 |
+
"linear_attention",
|
| 36 |
+
"linear_attention",
|
| 37 |
+
"linear_attention",
|
| 38 |
+
"full_attention",
|
| 39 |
+
"linear_attention",
|
| 40 |
+
"linear_attention",
|
| 41 |
+
"linear_attention",
|
| 42 |
+
"full_attention",
|
| 43 |
+
"linear_attention",
|
| 44 |
+
"linear_attention",
|
| 45 |
+
"linear_attention",
|
| 46 |
+
"full_attention"
|
| 47 |
+
],
|
| 48 |
+
"linear_conv_kernel_dim": 4,
|
| 49 |
+
"linear_key_head_dim": 128,
|
| 50 |
+
"linear_num_key_heads": 16,
|
| 51 |
+
"linear_num_value_heads": 16,
|
| 52 |
+
"linear_value_head_dim": 128,
|
| 53 |
+
"mamba_ssm_dtype": "float32",
|
| 54 |
+
"max_position_embeddings": 262144,
|
| 55 |
+
"mlp_only_layers": [],
|
| 56 |
+
"model_type": "qwen3_5_text",
|
| 57 |
+
"mtp_num_hidden_layers": 1,
|
| 58 |
+
"mtp_use_dedicated_embeddings": false,
|
| 59 |
+
"num_attention_heads": 8,
|
| 60 |
+
"num_hidden_layers": 24,
|
| 61 |
+
"num_key_value_heads": 2,
|
| 62 |
+
"pad_token_id": null,
|
| 63 |
+
"partial_rotary_factor": 0.25,
|
| 64 |
+
"rms_norm_eps": 1e-06,
|
| 65 |
+
"rope_parameters": {
|
| 66 |
+
"mrope_interleaved": true,
|
| 67 |
+
"mrope_section": [
|
| 68 |
+
11,
|
| 69 |
+
11,
|
| 70 |
+
10
|
| 71 |
+
],
|
| 72 |
+
"partial_rotary_factor": 0.25,
|
| 73 |
+
"rope_theta": 10000000,
|
| 74 |
+
"rope_type": "default"
|
| 75 |
+
},
|
| 76 |
+
"tie_word_embeddings": true,
|
| 77 |
+
"use_cache": true,
|
| 78 |
+
"vocab_size": 248320
|
| 79 |
+
},
|
| 80 |
+
"tie_word_embeddings": true,
|
| 81 |
+
"transformers_version": "5.14.1",
|
| 82 |
+
"use_cache": true,
|
| 83 |
+
"video_token_id": 248057,
|
| 84 |
+
"vision_config": {
|
| 85 |
+
"deepstack_visual_indexes": [],
|
| 86 |
+
"depth": 24,
|
| 87 |
+
"dtype": "bfloat16",
|
| 88 |
+
"hidden_act": "gelu_pytorch_tanh",
|
| 89 |
+
"hidden_size": 1024,
|
| 90 |
+
"in_channels": 3,
|
| 91 |
+
"initializer_range": 0.02,
|
| 92 |
+
"intermediate_size": 4096,
|
| 93 |
+
"model_type": "qwen3_5_vision",
|
| 94 |
+
"num_heads": 16,
|
| 95 |
+
"num_position_embeddings": 2304,
|
| 96 |
+
"out_hidden_size": 2048,
|
| 97 |
+
"patch_size": 16,
|
| 98 |
+
"spatial_merge_size": 2,
|
| 99 |
+
"temporal_patch_size": 2
|
| 100 |
+
},
|
| 101 |
+
"vision_end_token_id": 248054,
|
| 102 |
+
"vision_start_token_id": 248053
|
| 103 |
+
}
|
generation_config.json
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_from_model_config": true,
|
| 3 |
+
"eos_token_id": 248046,
|
| 4 |
+
"pad_token_id": 248044,
|
| 5 |
+
"transformers_version": "5.14.1",
|
| 6 |
+
"use_cache": true
|
| 7 |
+
}
|
images/01-regen.png
ADDED
|
Git LFS Details
|
images/01.png
ADDED
|
Git LFS Details
|
images/02-regen.png
ADDED
|
Git LFS Details
|
images/02.png
ADDED
|
Git LFS Details
|
inference.py
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Local inference for Anime_Image2Prompt.
|
| 2 |
+
|
| 3 |
+
The model and processor are loaded only from the directory containing this
|
| 4 |
+
file. No base model, adapter, or network download is required.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
from __future__ import annotations
|
| 8 |
+
|
| 9 |
+
import argparse
|
| 10 |
+
import json
|
| 11 |
+
import sys
|
| 12 |
+
from pathlib import Path
|
| 13 |
+
from typing import Any
|
| 14 |
+
|
| 15 |
+
import torch
|
| 16 |
+
from PIL import Image, ImageOps
|
| 17 |
+
from transformers import AutoModelForMultimodalLM, AutoProcessor
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
MODEL_DIR = Path(__file__).resolve().parent
|
| 21 |
+
INFERENCE_CONFIG = MODEL_DIR / "inference_config.json"
|
| 22 |
+
TAG_FIELDS = ("general_tags", "character_tags", "copyright_tags")
|
| 23 |
+
REQUIRED_MODEL_FILES = (
|
| 24 |
+
"config.json",
|
| 25 |
+
"model.safetensors",
|
| 26 |
+
"processor_config.json",
|
| 27 |
+
"tokenizer.json",
|
| 28 |
+
"tokenizer_config.json",
|
| 29 |
+
"chat_template.jinja",
|
| 30 |
+
"inference_config.json",
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def _validate_model_directory(model_dir: Path) -> None:
|
| 35 |
+
missing = [name for name in REQUIRED_MODEL_FILES if not (model_dir / name).is_file()]
|
| 36 |
+
if missing:
|
| 37 |
+
raise FileNotFoundError(
|
| 38 |
+
f"Incomplete Anime_Image2Prompt package; missing: {', '.join(missing)}"
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def _select_runtime(device: str) -> tuple[str, torch.dtype]:
|
| 43 |
+
cuda_available = torch.cuda.is_available()
|
| 44 |
+
if device == "cuda" and not cuda_available:
|
| 45 |
+
raise RuntimeError(
|
| 46 |
+
"CUDA was requested but is unavailable. Install a CUDA-enabled PyTorch "
|
| 47 |
+
"build and verify the NVIDIA driver, or use --device cpu."
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
use_cuda = device == "cuda" or (device == "auto" and cuda_available)
|
| 51 |
+
if not use_cuda:
|
| 52 |
+
return "cpu", torch.float32
|
| 53 |
+
|
| 54 |
+
supports_bf16 = getattr(torch.cuda, "is_bf16_supported", lambda: False)()
|
| 55 |
+
return "cuda", torch.bfloat16 if supports_bf16 else torch.float16
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def _parse_model_json(text: str) -> dict[str, list[str]]:
|
| 59 |
+
start = text.find("{")
|
| 60 |
+
if start < 0:
|
| 61 |
+
raise ValueError(f"The model did not return JSON. Raw output: {text!r}")
|
| 62 |
+
|
| 63 |
+
try:
|
| 64 |
+
payload, _ = json.JSONDecoder().raw_decode(text[start:])
|
| 65 |
+
except json.JSONDecodeError as error:
|
| 66 |
+
raise ValueError(f"The model returned invalid JSON. Raw output: {text!r}") from error
|
| 67 |
+
|
| 68 |
+
if not isinstance(payload, dict) or set(payload) != set(TAG_FIELDS):
|
| 69 |
+
actual = tuple(payload.keys()) if isinstance(payload, dict) else type(payload).__name__
|
| 70 |
+
raise ValueError(f"Unexpected model output fields: {actual!r}")
|
| 71 |
+
|
| 72 |
+
normalized: dict[str, list[str]] = {}
|
| 73 |
+
for field in TAG_FIELDS:
|
| 74 |
+
value = payload[field]
|
| 75 |
+
if not isinstance(value, list) or len(value) > 1:
|
| 76 |
+
raise ValueError(f"{field} must be [] or a one-string list: {value!r}")
|
| 77 |
+
if value:
|
| 78 |
+
if not isinstance(value[0], str):
|
| 79 |
+
raise ValueError(f"{field} must contain a string: {value!r}")
|
| 80 |
+
tags = value[0].split(",")
|
| 81 |
+
if any(not tag for tag in tags):
|
| 82 |
+
raise ValueError(f"{field} contains an empty tag: {value!r}")
|
| 83 |
+
if len(tags) != len(set(tags)):
|
| 84 |
+
raise ValueError(f"{field} contains duplicate tags: {value!r}")
|
| 85 |
+
normalized[field] = value
|
| 86 |
+
return normalized
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def _move_inputs(inputs: dict[str, Any], device: torch.device) -> dict[str, Any]:
|
| 90 |
+
return {
|
| 91 |
+
key: value.to(device) if hasattr(value, "to") else value
|
| 92 |
+
for key, value in inputs.items()
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
class AnimeImage2Prompt:
|
| 97 |
+
"""Reusable local inference session for Anime_Image2Prompt."""
|
| 98 |
+
|
| 99 |
+
def __init__(self, device: str = "auto", low_vram: bool = False) -> None:
|
| 100 |
+
_validate_model_directory(MODEL_DIR)
|
| 101 |
+
runtime, dtype = _select_runtime(device)
|
| 102 |
+
self.runtime = runtime
|
| 103 |
+
self.dtype = dtype
|
| 104 |
+
self.config = json.loads(INFERENCE_CONFIG.read_text(encoding="utf-8"))
|
| 105 |
+
|
| 106 |
+
if runtime == "cuda":
|
| 107 |
+
device_map: str | dict[str, str] = "auto"
|
| 108 |
+
else:
|
| 109 |
+
device_map = {"": "cpu"}
|
| 110 |
+
|
| 111 |
+
self.processor = AutoProcessor.from_pretrained(
|
| 112 |
+
MODEL_DIR,
|
| 113 |
+
local_files_only=True,
|
| 114 |
+
)
|
| 115 |
+
if low_vram:
|
| 116 |
+
pixels = self.config["image_pixels"]
|
| 117 |
+
self.processor.image_processor.size = {
|
| 118 |
+
"shortest_edge": pixels["min"],
|
| 119 |
+
"longest_edge": pixels["low_vram_max"],
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
self.model = AutoModelForMultimodalLM.from_pretrained(
|
| 123 |
+
MODEL_DIR,
|
| 124 |
+
dtype=dtype,
|
| 125 |
+
device_map=device_map,
|
| 126 |
+
attn_implementation="sdpa",
|
| 127 |
+
local_files_only=True,
|
| 128 |
+
).eval()
|
| 129 |
+
|
| 130 |
+
@property
|
| 131 |
+
def device(self) -> torch.device:
|
| 132 |
+
return self.model.device
|
| 133 |
+
|
| 134 |
+
def predict(
|
| 135 |
+
self,
|
| 136 |
+
image: str | Path | Image.Image,
|
| 137 |
+
max_new_tokens: int = 1024,
|
| 138 |
+
) -> dict[str, list[str]]:
|
| 139 |
+
"""Generate structured prompt tags for one image."""
|
| 140 |
+
if isinstance(image, Image.Image):
|
| 141 |
+
prepared_image = ImageOps.exif_transpose(image).convert("RGB")
|
| 142 |
+
else:
|
| 143 |
+
image_path = Path(image).expanduser().resolve()
|
| 144 |
+
if not image_path.is_file():
|
| 145 |
+
raise FileNotFoundError(f"Input image does not exist: {image_path}")
|
| 146 |
+
with Image.open(image_path) as source:
|
| 147 |
+
prepared_image = ImageOps.exif_transpose(source).convert("RGB")
|
| 148 |
+
prepared_image.load()
|
| 149 |
+
|
| 150 |
+
messages = [
|
| 151 |
+
{
|
| 152 |
+
"role": "system",
|
| 153 |
+
"content": [{"type": "text", "text": self.config["system_prompt"]}],
|
| 154 |
+
},
|
| 155 |
+
{
|
| 156 |
+
"role": "user",
|
| 157 |
+
"content": [
|
| 158 |
+
{"type": "image", "image": prepared_image},
|
| 159 |
+
{"type": "text", "text": self.config["user_prompt"]},
|
| 160 |
+
],
|
| 161 |
+
},
|
| 162 |
+
]
|
| 163 |
+
inputs = self.processor.apply_chat_template(
|
| 164 |
+
messages,
|
| 165 |
+
tokenize=True,
|
| 166 |
+
add_generation_prompt=True,
|
| 167 |
+
enable_thinking=False,
|
| 168 |
+
return_dict=True,
|
| 169 |
+
return_tensors="pt",
|
| 170 |
+
)
|
| 171 |
+
inputs = _move_inputs(inputs, self.device)
|
| 172 |
+
|
| 173 |
+
with torch.inference_mode():
|
| 174 |
+
output_ids = self.model.generate(
|
| 175 |
+
**inputs,
|
| 176 |
+
max_new_tokens=max_new_tokens,
|
| 177 |
+
do_sample=False,
|
| 178 |
+
eos_token_id=self.config["assistant_end_token_id"],
|
| 179 |
+
pad_token_id=self.processor.tokenizer.pad_token_id,
|
| 180 |
+
)
|
| 181 |
+
|
| 182 |
+
prompt_length = inputs["input_ids"].shape[1]
|
| 183 |
+
generated_ids = output_ids[:, prompt_length:]
|
| 184 |
+
raw_text = self.processor.batch_decode(
|
| 185 |
+
generated_ids,
|
| 186 |
+
skip_special_tokens=True,
|
| 187 |
+
clean_up_tokenization_spaces=False,
|
| 188 |
+
)[0]
|
| 189 |
+
return _parse_model_json(raw_text)
|
| 190 |
+
|
| 191 |
+
@staticmethod
|
| 192 |
+
def to_prompt(payload: dict[str, list[str]]) -> str:
|
| 193 |
+
"""Flatten structured output to a comma-and-space-separated prompt."""
|
| 194 |
+
tags: list[str] = []
|
| 195 |
+
for field in TAG_FIELDS:
|
| 196 |
+
values = payload.get(field, [])
|
| 197 |
+
if values:
|
| 198 |
+
tags.extend(values[0].split(","))
|
| 199 |
+
return ", ".join(tags)
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
def _parse_args() -> argparse.Namespace:
|
| 203 |
+
parser = argparse.ArgumentParser(
|
| 204 |
+
description="Generate Danbooru-style prompt tags with Anime_Image2Prompt."
|
| 205 |
+
)
|
| 206 |
+
parser.add_argument("image", type=Path, help="path to one input image")
|
| 207 |
+
parser.add_argument(
|
| 208 |
+
"--output",
|
| 209 |
+
type=Path,
|
| 210 |
+
help="optional output file; the result is always printed to stdout",
|
| 211 |
+
)
|
| 212 |
+
parser.add_argument(
|
| 213 |
+
"--format",
|
| 214 |
+
choices=("json", "prompt"),
|
| 215 |
+
default="json",
|
| 216 |
+
help="output format (default: json)",
|
| 217 |
+
)
|
| 218 |
+
parser.add_argument(
|
| 219 |
+
"--device",
|
| 220 |
+
choices=("auto", "cuda", "cpu"),
|
| 221 |
+
default="auto",
|
| 222 |
+
help="inference device (default: auto)",
|
| 223 |
+
)
|
| 224 |
+
parser.add_argument(
|
| 225 |
+
"--low-vram",
|
| 226 |
+
action="store_true",
|
| 227 |
+
help="reduce image tokens to lower peak VRAM usage",
|
| 228 |
+
)
|
| 229 |
+
parser.add_argument(
|
| 230 |
+
"--max-new-tokens",
|
| 231 |
+
type=int,
|
| 232 |
+
default=1024,
|
| 233 |
+
help="maximum generated tokens (default: 1024)",
|
| 234 |
+
)
|
| 235 |
+
return parser.parse_args()
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
def main() -> int:
|
| 239 |
+
args = _parse_args()
|
| 240 |
+
if args.max_new_tokens < 1:
|
| 241 |
+
raise ValueError("--max-new-tokens must be greater than zero")
|
| 242 |
+
|
| 243 |
+
runtime, dtype = _select_runtime(args.device)
|
| 244 |
+
if runtime == "cuda":
|
| 245 |
+
hardware = torch.cuda.get_device_name(0)
|
| 246 |
+
print(f"Loading Anime_Image2Prompt on {hardware} ({dtype})...", file=sys.stderr)
|
| 247 |
+
else:
|
| 248 |
+
print("Loading Anime_Image2Prompt on CPU (this may be slow)...", file=sys.stderr)
|
| 249 |
+
|
| 250 |
+
tagger = AnimeImage2Prompt(device=args.device, low_vram=args.low_vram)
|
| 251 |
+
print("Generating prompt...", file=sys.stderr)
|
| 252 |
+
result = tagger.predict(args.image, max_new_tokens=args.max_new_tokens)
|
| 253 |
+
|
| 254 |
+
if args.format == "json":
|
| 255 |
+
rendered = json.dumps(result, ensure_ascii=False, indent=2) + "\n"
|
| 256 |
+
else:
|
| 257 |
+
rendered = tagger.to_prompt(result) + "\n"
|
| 258 |
+
|
| 259 |
+
if args.output:
|
| 260 |
+
output_path = args.output.expanduser().resolve()
|
| 261 |
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
| 262 |
+
output_path.write_text(rendered, encoding="utf-8", newline="\n")
|
| 263 |
+
print(f"Saved: {output_path}", file=sys.stderr)
|
| 264 |
+
|
| 265 |
+
print(rendered, end="")
|
| 266 |
+
return 0
|
| 267 |
+
|
| 268 |
+
|
| 269 |
+
if __name__ == "__main__":
|
| 270 |
+
try:
|
| 271 |
+
raise SystemExit(main())
|
| 272 |
+
except (FileNotFoundError, RuntimeError, ValueError) as error:
|
| 273 |
+
print(f"Error: {error}", file=sys.stderr)
|
| 274 |
+
raise SystemExit(1) from error
|
inference_config.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model_name": "Anime_Image2Prompt",
|
| 3 |
+
"task": "anime_image_to_prompt",
|
| 4 |
+
"system_prompt": "You are an image tagging model. Analyze the image and return only one valid JSON object. Do not add Markdown, explanations, comments, or hidden reasoning. Preserve tag spelling. For each non-empty category, use a JSON array containing exactly one string and join all unique tags inside that string with commas and no spaces after commas. Use an empty array for an empty category. Never repeat a tag. Close every JSON array and the JSON object, then stop immediately.",
|
| 5 |
+
"user_prompt": "Identify the Danbooru tags visible or attributable to this image. Return exactly these three keys: general_tags, character_tags, copyright_tags. Put all tags for each non-empty category in one comma-separated string inside a single-element JSON array. Use an empty array when a category cannot be identified.",
|
| 6 |
+
"tag_fields": [
|
| 7 |
+
"general_tags",
|
| 8 |
+
"character_tags",
|
| 9 |
+
"copyright_tags"
|
| 10 |
+
],
|
| 11 |
+
"assistant_end_token": "<|im_end|>",
|
| 12 |
+
"assistant_end_token_id": 248046,
|
| 13 |
+
"image_pixels": {
|
| 14 |
+
"min": 200704,
|
| 15 |
+
"max": 401408,
|
| 16 |
+
"low_vram_max": 301056
|
| 17 |
+
}
|
| 18 |
+
}
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8e7b265c171b46ee7e3dd4809b3b1cfc933c40cbdbe5c976f499e654daa0be3b
|
| 3 |
+
size 4426558864
|
processor_config.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"image_processor": {
|
| 3 |
+
"do_convert_rgb": true,
|
| 4 |
+
"do_normalize": true,
|
| 5 |
+
"do_rescale": true,
|
| 6 |
+
"do_resize": true,
|
| 7 |
+
"image_mean": [
|
| 8 |
+
0.5,
|
| 9 |
+
0.5,
|
| 10 |
+
0.5
|
| 11 |
+
],
|
| 12 |
+
"image_processor_type": "Qwen2VLImageProcessor",
|
| 13 |
+
"image_std": [
|
| 14 |
+
0.5,
|
| 15 |
+
0.5,
|
| 16 |
+
0.5
|
| 17 |
+
],
|
| 18 |
+
"merge_size": 2,
|
| 19 |
+
"patch_size": 16,
|
| 20 |
+
"resample": 3,
|
| 21 |
+
"rescale_factor": 0.00392156862745098,
|
| 22 |
+
"size": {
|
| 23 |
+
"longest_edge": 401408,
|
| 24 |
+
"shortest_edge": 200704
|
| 25 |
+
},
|
| 26 |
+
"temporal_patch_size": 2
|
| 27 |
+
},
|
| 28 |
+
"processor_class": "Qwen3VLProcessor",
|
| 29 |
+
"video_processor": {
|
| 30 |
+
"do_convert_rgb": true,
|
| 31 |
+
"do_normalize": true,
|
| 32 |
+
"do_rescale": true,
|
| 33 |
+
"do_resize": true,
|
| 34 |
+
"do_sample_frames": true,
|
| 35 |
+
"fps": 2,
|
| 36 |
+
"image_mean": [
|
| 37 |
+
0.5,
|
| 38 |
+
0.5,
|
| 39 |
+
0.5
|
| 40 |
+
],
|
| 41 |
+
"image_std": [
|
| 42 |
+
0.5,
|
| 43 |
+
0.5,
|
| 44 |
+
0.5
|
| 45 |
+
],
|
| 46 |
+
"max_frames": 768,
|
| 47 |
+
"merge_size": 2,
|
| 48 |
+
"min_frames": 4,
|
| 49 |
+
"patch_size": 16,
|
| 50 |
+
"resample": 3,
|
| 51 |
+
"rescale_factor": 0.00392156862745098,
|
| 52 |
+
"return_metadata": false,
|
| 53 |
+
"size": {
|
| 54 |
+
"longest_edge": 25165824,
|
| 55 |
+
"shortest_edge": 4096
|
| 56 |
+
},
|
| 57 |
+
"temporal_patch_size": 2,
|
| 58 |
+
"video_processor_type": "Qwen3VLVideoProcessor"
|
| 59 |
+
}
|
| 60 |
+
}
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
torch>=2.6.0
|
| 2 |
+
transformers>=5.14.1,<6.0.0
|
| 3 |
+
accelerate>=1.6.0
|
| 4 |
+
pillow>=10.0.0
|
| 5 |
+
safetensors>=0.5.0
|
tokenizer.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:06b9509352d2af50381ab2247e083b80d32d5c0aba91c272ca9ff729b6a0e523
|
| 3 |
+
size 19989325
|
tokenizer_config.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"add_prefix_space": false,
|
| 3 |
+
"audio_bos_token": "<|audio_start|>",
|
| 4 |
+
"audio_eos_token": "<|audio_end|>",
|
| 5 |
+
"audio_token": "<|audio_pad|>",
|
| 6 |
+
"backend": "tokenizers",
|
| 7 |
+
"bos_token": null,
|
| 8 |
+
"clean_up_tokenization_spaces": false,
|
| 9 |
+
"eos_token": "<|im_end|>",
|
| 10 |
+
"errors": "replace",
|
| 11 |
+
"image_token": "<|image_pad|>",
|
| 12 |
+
"is_local": true,
|
| 13 |
+
"local_files_only": false,
|
| 14 |
+
"max_pixels": 401408,
|
| 15 |
+
"min_pixels": 200704,
|
| 16 |
+
"model_max_length": 262144,
|
| 17 |
+
"model_specific_special_tokens": {
|
| 18 |
+
"audio_bos_token": "<|audio_start|>",
|
| 19 |
+
"audio_eos_token": "<|audio_end|>",
|
| 20 |
+
"audio_token": "<|audio_pad|>",
|
| 21 |
+
"image_token": "<|image_pad|>",
|
| 22 |
+
"video_token": "<|video_pad|>",
|
| 23 |
+
"vision_bos_token": "<|vision_start|>",
|
| 24 |
+
"vision_eos_token": "<|vision_end|>"
|
| 25 |
+
},
|
| 26 |
+
"pad_token": "<|endoftext|>",
|
| 27 |
+
"pretokenize_regex": "(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\\r\\n\\p{L}\\p{N}]?[\\p{L}\\p{M}]+|\\p{N}| ?[^\\s\\p{L}\\p{M}\\p{N}]+[\\r\\n]*|\\s*[\\r\\n]+|\\s+(?!\\S)|\\s+",
|
| 28 |
+
"processor_class": "Qwen3VLProcessor",
|
| 29 |
+
"split_special_tokens": false,
|
| 30 |
+
"tokenizer_class": "Qwen2Tokenizer",
|
| 31 |
+
"unk_token": null,
|
| 32 |
+
"video_token": "<|video_pad|>",
|
| 33 |
+
"vision_bos_token": "<|vision_start|>",
|
| 34 |
+
"vision_eos_token": "<|vision_end|>"
|
| 35 |
+
}
|