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
| """Local inference for Anime_Image2Prompt. | |
| The model and processor are loaded only from the directory containing this | |
| file. No base model, adapter, or network download is required. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import sys | |
| from pathlib import Path | |
| from typing import Any | |
| import torch | |
| from PIL import Image, ImageOps | |
| from transformers import AutoModelForMultimodalLM, AutoProcessor | |
| MODEL_DIR = Path(__file__).resolve().parent | |
| INFERENCE_CONFIG = MODEL_DIR / "inference_config.json" | |
| TAG_FIELDS = ("general_tags", "character_tags", "copyright_tags") | |
| REQUIRED_MODEL_FILES = ( | |
| "config.json", | |
| "model.safetensors", | |
| "processor_config.json", | |
| "tokenizer.json", | |
| "tokenizer_config.json", | |
| "chat_template.jinja", | |
| "inference_config.json", | |
| ) | |
| def _validate_model_directory(model_dir: Path) -> None: | |
| missing = [name for name in REQUIRED_MODEL_FILES if not (model_dir / name).is_file()] | |
| if missing: | |
| raise FileNotFoundError( | |
| f"Incomplete Anime_Image2Prompt package; missing: {', '.join(missing)}" | |
| ) | |
| def _select_runtime(device: str) -> tuple[str, torch.dtype]: | |
| cuda_available = torch.cuda.is_available() | |
| if device == "cuda" and not cuda_available: | |
| raise RuntimeError( | |
| "CUDA was requested but is unavailable. Install a CUDA-enabled PyTorch " | |
| "build and verify the NVIDIA driver, or use --device cpu." | |
| ) | |
| use_cuda = device == "cuda" or (device == "auto" and cuda_available) | |
| if not use_cuda: | |
| return "cpu", torch.float32 | |
| supports_bf16 = getattr(torch.cuda, "is_bf16_supported", lambda: False)() | |
| return "cuda", torch.bfloat16 if supports_bf16 else torch.float16 | |
| def _parse_model_json(text: str) -> dict[str, list[str]]: | |
| start = text.find("{") | |
| if start < 0: | |
| raise ValueError(f"The model did not return JSON. Raw output: {text!r}") | |
| try: | |
| payload, _ = json.JSONDecoder().raw_decode(text[start:]) | |
| except json.JSONDecodeError as error: | |
| raise ValueError(f"The model returned invalid JSON. Raw output: {text!r}") from error | |
| if not isinstance(payload, dict) or set(payload) != set(TAG_FIELDS): | |
| actual = tuple(payload.keys()) if isinstance(payload, dict) else type(payload).__name__ | |
| raise ValueError(f"Unexpected model output fields: {actual!r}") | |
| normalized: dict[str, list[str]] = {} | |
| for field in TAG_FIELDS: | |
| value = payload[field] | |
| if not isinstance(value, list) or len(value) > 1: | |
| raise ValueError(f"{field} must be [] or a one-string list: {value!r}") | |
| if value: | |
| if not isinstance(value[0], str): | |
| raise ValueError(f"{field} must contain a string: {value!r}") | |
| tags = value[0].split(",") | |
| if any(not tag for tag in tags): | |
| raise ValueError(f"{field} contains an empty tag: {value!r}") | |
| if len(tags) != len(set(tags)): | |
| raise ValueError(f"{field} contains duplicate tags: {value!r}") | |
| normalized[field] = value | |
| return normalized | |
| def _move_inputs(inputs: dict[str, Any], device: torch.device) -> dict[str, Any]: | |
| return { | |
| key: value.to(device) if hasattr(value, "to") else value | |
| for key, value in inputs.items() | |
| } | |
| class AnimeImage2Prompt: | |
| """Reusable local inference session for Anime_Image2Prompt.""" | |
| def __init__(self, device: str = "auto", low_vram: bool = False) -> None: | |
| _validate_model_directory(MODEL_DIR) | |
| runtime, dtype = _select_runtime(device) | |
| self.runtime = runtime | |
| self.dtype = dtype | |
| self.config = json.loads(INFERENCE_CONFIG.read_text(encoding="utf-8")) | |
| if runtime == "cuda": | |
| device_map: str | dict[str, str] = "auto" | |
| else: | |
| device_map = {"": "cpu"} | |
| self.processor = AutoProcessor.from_pretrained( | |
| MODEL_DIR, | |
| local_files_only=True, | |
| ) | |
| if low_vram: | |
| pixels = self.config["image_pixels"] | |
| self.processor.image_processor.size = { | |
| "shortest_edge": pixels["min"], | |
| "longest_edge": pixels["low_vram_max"], | |
| } | |
| self.model = AutoModelForMultimodalLM.from_pretrained( | |
| MODEL_DIR, | |
| dtype=dtype, | |
| device_map=device_map, | |
| attn_implementation="sdpa", | |
| local_files_only=True, | |
| ).eval() | |
| def device(self) -> torch.device: | |
| return self.model.device | |
| def predict( | |
| self, | |
| image: str | Path | Image.Image, | |
| max_new_tokens: int = 1024, | |
| ) -> dict[str, list[str]]: | |
| """Generate structured prompt tags for one image.""" | |
| if isinstance(image, Image.Image): | |
| prepared_image = ImageOps.exif_transpose(image).convert("RGB") | |
| else: | |
| image_path = Path(image).expanduser().resolve() | |
| if not image_path.is_file(): | |
| raise FileNotFoundError(f"Input image does not exist: {image_path}") | |
| with Image.open(image_path) as source: | |
| prepared_image = ImageOps.exif_transpose(source).convert("RGB") | |
| prepared_image.load() | |
| messages = [ | |
| { | |
| "role": "system", | |
| "content": [{"type": "text", "text": self.config["system_prompt"]}], | |
| }, | |
| { | |
| "role": "user", | |
| "content": [ | |
| {"type": "image", "image": prepared_image}, | |
| {"type": "text", "text": self.config["user_prompt"]}, | |
| ], | |
| }, | |
| ] | |
| inputs = self.processor.apply_chat_template( | |
| messages, | |
| tokenize=True, | |
| add_generation_prompt=True, | |
| enable_thinking=False, | |
| return_dict=True, | |
| return_tensors="pt", | |
| ) | |
| inputs = _move_inputs(inputs, self.device) | |
| with torch.inference_mode(): | |
| output_ids = self.model.generate( | |
| **inputs, | |
| max_new_tokens=max_new_tokens, | |
| do_sample=False, | |
| eos_token_id=self.config["assistant_end_token_id"], | |
| pad_token_id=self.processor.tokenizer.pad_token_id, | |
| ) | |
| prompt_length = inputs["input_ids"].shape[1] | |
| generated_ids = output_ids[:, prompt_length:] | |
| raw_text = self.processor.batch_decode( | |
| generated_ids, | |
| skip_special_tokens=True, | |
| clean_up_tokenization_spaces=False, | |
| )[0] | |
| return _parse_model_json(raw_text) | |
| def to_prompt(payload: dict[str, list[str]]) -> str: | |
| """Flatten structured output to a comma-and-space-separated prompt.""" | |
| tags: list[str] = [] | |
| for field in TAG_FIELDS: | |
| values = payload.get(field, []) | |
| if values: | |
| tags.extend(values[0].split(",")) | |
| return ", ".join(tags) | |
| def _parse_args() -> argparse.Namespace: | |
| parser = argparse.ArgumentParser( | |
| description="Generate Danbooru-style prompt tags with Anime_Image2Prompt." | |
| ) | |
| parser.add_argument("image", type=Path, help="path to one input image") | |
| parser.add_argument( | |
| "--output", | |
| type=Path, | |
| help="optional output file; the result is always printed to stdout", | |
| ) | |
| parser.add_argument( | |
| "--format", | |
| choices=("json", "prompt"), | |
| default="json", | |
| help="output format (default: json)", | |
| ) | |
| parser.add_argument( | |
| "--device", | |
| choices=("auto", "cuda", "cpu"), | |
| default="auto", | |
| help="inference device (default: auto)", | |
| ) | |
| parser.add_argument( | |
| "--low-vram", | |
| action="store_true", | |
| help="reduce image tokens to lower peak VRAM usage", | |
| ) | |
| parser.add_argument( | |
| "--max-new-tokens", | |
| type=int, | |
| default=1024, | |
| help="maximum generated tokens (default: 1024)", | |
| ) | |
| return parser.parse_args() | |
| def main() -> int: | |
| args = _parse_args() | |
| if args.max_new_tokens < 1: | |
| raise ValueError("--max-new-tokens must be greater than zero") | |
| runtime, dtype = _select_runtime(args.device) | |
| if runtime == "cuda": | |
| hardware = torch.cuda.get_device_name(0) | |
| print(f"Loading Anime_Image2Prompt on {hardware} ({dtype})...", file=sys.stderr) | |
| else: | |
| print("Loading Anime_Image2Prompt on CPU (this may be slow)...", file=sys.stderr) | |
| tagger = AnimeImage2Prompt(device=args.device, low_vram=args.low_vram) | |
| print("Generating prompt...", file=sys.stderr) | |
| result = tagger.predict(args.image, max_new_tokens=args.max_new_tokens) | |
| if args.format == "json": | |
| rendered = json.dumps(result, ensure_ascii=False, indent=2) + "\n" | |
| else: | |
| rendered = tagger.to_prompt(result) + "\n" | |
| if args.output: | |
| output_path = args.output.expanduser().resolve() | |
| output_path.parent.mkdir(parents=True, exist_ok=True) | |
| output_path.write_text(rendered, encoding="utf-8", newline="\n") | |
| print(f"Saved: {output_path}", file=sys.stderr) | |
| print(rendered, end="") | |
| return 0 | |
| if __name__ == "__main__": | |
| try: | |
| raise SystemExit(main()) | |
| except (FileNotFoundError, RuntimeError, ValueError) as error: | |
| print(f"Error: {error}", file=sys.stderr) | |
| raise SystemExit(1) from error | |