comfyui-headless-image21
Generates game art assets headlessly via the local ComfyUI server (Qwen-Image 2.1): text-to-image, reference-guided edits, and true transparent-background RGBA PNGs. Invoke when the user asks for 素材图/
SKILL.mdskills/comfyui-headless-image21/SKILL.md
GitHubComfyUI Headless Image Generation (Qwen-Image 2.1 game assets)
Drive the locally installed ComfyUI without its UI: start a headless server, submit an
API-format workflow via POST /prompt, poll /history, download results via /view.
All commands below run the bundled script scripts/comfy21.py (stdlib only, no deps).
Two verified workflow templates live in workflows/ for manual API use. The official
Qwen-Image 2.1 prompt-writing guide (model card + ComfyUI docs) is distilled in
references/prompt-guide.md — consult it when writing or fixing prompts.
1. Path discovery — NEVER hardcode install or model paths
This machine's ComfyUI layout must be discovered at runtime, in this order:
COMFYUI_HOMEenv var, if set (folder containingComfyUI/main.pyormain.py).%APPDATA%\Comfy Desktop\installations.json— the Desktop app's install registry; each entry'sinstallPathcontains the checkout and a bundled.venvpython.- Shallow scan of every existing drive root and common program directories for
directories matching
*omfy*(matched folders are scanned one level deeper). No machine-specific paths are assumed anywhere.
Model registration locations (where weights are actually looked up) are never assumed either:
%APPDATA%\Comfy Desktop\settings.json→modelsDirs(Desktop shared model root).%APPDATA%\Comfy Desktop\shared_model_paths.yaml— auto-generated by the Desktop app; registering a model root here is what makes it visible to a Desktop-launched server.<code_root>\extra_model_paths.yaml— ComfyUI core reads this file at every startup (upstream mechanism, gitignored). User-registered roots (e.g. a dedicated Qwen-Image-2.1 folder) typically live here, NOT in the Desktop registry.- When the server is running,
GET /models/<folder>is the ground truth of what is registered.python scripts/comfy21.py modelsprints it.
A headless server does not see the Desktop registries by itself — the script passes
--extra-model-paths-config <shared_model_paths.yaml> and
--input-directory/--output-directory (derived from the registered models dir) at launch.
2. Weight check and download (Qwen-Image 2.1)
Check what exists before generating:
python scripts\comfy21.py find
Required files (either the int8_convrot quantized set or the bf16 set works; int8 is lighter; weights larger than VRAM still run via ComfyUI's dynamic-VRAM streaming, at a speed cost):
| Type | File (pick one per row) | Registered under |
|---|---|---|
| diffusion_models | qwen_image_2.1_int8_convrot.safetensors or qwen_image_2.1_bf16.safetensors |
diffusion_models/ |
| text_encoders | qwen3vl_8b_int8_convrot.safetensors or qwen3vl_8b_bf16.safetensors |
text_encoders/ |
| vae | qwen_image_2.1_vae_bf16.safetensors |
vae/ |
"Registered under" means: inside a directory that one of the registration files above maps
to that type — find it with find, do not guess a drive path.
If weights are missing, download from the original HF repo or the mirror, then place each
file into the registered dir find reported:
:: original
curl -L -o <registered_dir>\qwen_image_2.1_int8_convrot.safetensors ^
https://huggingface.co/Comfy-Org/Qwen-Image-2.1/resolve/main/split_files/diffusion_models/qwen_image_2.1_int8_convrot.safetensors
:: mirror (hf-mirror.com) — same URL with the host swapped, or via huggingface-cli:
set HF_ENDPOINT=https://hf-mirror.com
huggingface-cli download Comfy-Org/Qwen-Image-2.1 split_files/text_encoders/qwen3vl_8b_int8_convrot.safetensors --local-dir .
Repo: https://huggingface.co/Comfy-Org/Qwen-Image-2.1 (mirror: https://hf-mirror.com/Comfy-Org/Qwen-Image-2.1),
files under split_files/diffusion_models|text_encoders|vae/. Restart the server after
adding weights? Not needed for weights placed before launch; a running server lists model
files dynamically, but a restart is the safe fallback.
3. Quickstart
python scripts\comfy21.py find :: discover + check weights (run this first)
python scripts\comfy21.py serve :: start headless server on 127.0.0.1:8188 (idempotent)
python scripts\comfy21.py models :: list registered model files
python scripts\comfy21.py t2i --prompt "a fire sword icon, game item art" --transparent --out assets
python scripts\comfy21.py edit --image .\hero.png --out assets :: default = background removal
python scripts\comfy21.py edit --image .\hero.png --instruction "Make the sword glow blue" --out assets
python scripts\comfy21.py stop
Outputs land in --out (default ./generated). If the Desktop app is already running,
use --port 8189 — two servers cannot share a port.
4. Text-to-image (文生图)
Workflow graph (see workflows/qwen21_t2i_api.json):
UNETLoader → TextEncodeQwenImage21(clip, prompt, negative_prompt, resolution) → EmptyLatentImage → KSampler(euler/simple, cfg 1.0, 25 steps, denoise 1.0) → VAEDecode → SaveImage.
CLIPLoadermust usetype: qwen_image(not lumina2/other).resolutionon the encoder node is a total pixel budget (native 1024, up to 2048); aspect ratio preserved. Keep width/height multiples of 32.cfgstays 1.0 on the official path — negative_prompt is unused at cfg 1. The CLI exposes the knobs anyway and warns when you pass--negativewithout--cfg:--negative "blurry, text" --cfg 2.0activates it (cfg 2 also tracks dense prompts / small text better, per the official tutorial).--sampler/--schedulerdefault to euler/simple like every official template.- Official pipeline uses ~40–50 steps with euler; 25 is the template default and is fine for drafts; raise for final quality.
Batch generation (multiple images at once)
Two verified paths — pick by what varies:
- Same prompt, N variations →
--n Nputs N images in a single run (EmptyLatentImage.batch_size = N; seeds run seed, seed+1, …; the model loads once, so this is the fastest path). Verified:--n 3at 512px/8 steps → 3 files in one submission.python scripts\comfy21.py t2i --prompt "..." --n 8 --transparent --out assets - Different prompts →
--prompts-file: prompts are separated by blank lines (a prompt may span multiple lines — they are joined with spaces), or use a.jsonfile with an array of strings /{"prompts": [...]}(internal newlines preserved).#comment lines are skipped. Every prompt becomes a queued job; ALL jobs are submitted first so the server keeps the GPU busy, then results are collected one by one. Verified: 2-line block + single line → 2 jobs (not 3).python scripts\comfy21.py t2i --prompts-file prompts.txt --transparent --out assets
There is no client-side parallelism — ComfyUI serializes on one GPU; the win is that you never wait on submission, and one batch run amortizes model load across N images.
5. Reference-guided generation (参考生图)
Workflow graph (see workflows/qwen21_edit_api.json): same loaders, plus LoadImage,
and the reference feeds TextEncodeQwenImage21 together with the VAE; the KSampler's
latent_image is the encoder node's third output (["5", 2]) so the canvas follows
the reference size.
Three hard rules (each was a real failure during verification):
- Autogrow images use dotted keys in API JSON:
"images.image_1": ["<load_node>", 0]. A flat"image_1"key raisesTypeError: unexpected keyword argument; a nested"images": {"image_1": ...}dict is silently dropped — the prompt still executes and the model just ignores your reference. If an edit "ignores the image", check this first. - The instruction prompt is an edit directive. Multi-image edits reference slots in text
as
<image1>…<image10>(e.g. "Put the hat from <image2> on the dog in <image1>"). - The reference image must sit in the server's registered input directory — the
script copies it there automatically;
LoadImageonly sees that folder.
6. Transparent-background assets (背景透明)
Two verified methods:
A. Straight from text-to-image (preferred, single step). The model card's official RGBA template (verified: alpha min 0, ~50% transparent pixels):
"This is an RGBA image with transparency. A cute cartoon dragon sticker. The image has alpha channel and the background is transparent."
t2i --transparent wraps your prompt in exactly this template. Also verified on the same
seed: any prompt containing "alpha channel" triggers transparency on its own, while
"transparent background"/"sticker"/Chinese 透明背景 alone produce fully opaque white
backgrounds. Full prompt-writing rules: references/prompt-guide.md.
B. Edit-based cutout for existing art. edit with the default instruction
"Remove the background, and output a PNG image" turns any image into a subject-only
RGBA PNG (verified: ~66% transparent pixels, soft edges).
Always verify the alpha of deliverables (both methods output RGBA, but only a real check proves transparency):
from PIL import Image; import numpy as np
a = np.array(Image.open(p))[:, :, 3]
print(a.min(), (a < 128).mean()) # expect min 0 and a healthy transparent fraction
Mechanism, for debugging: the 2.1 VAE is natively RGBA (64-ch latent = R/G/B/A × 16 ch;
comfy/sd.py marks it "Qwen Image 2.1 VAE ... RGBA"), so SaveImage writes alpha directly —
no matting node involved anywhere. The transformer has no alpha-specific code; whether alpha
is transparent is a trained behavior triggered by the prompt (or carried in by reference
images with alpha, via comfy_extras/nodes_qwen.py: "the vae keeps all four").
7. Pitfalls
| Symptom | Cause / fix |
|---|---|
Value not in list: unet_name ... |
weights not registered — run find; check §1 registration files |
| Edit ignores the reference image | autogrow key must be images.image_1 (dotted), see §5 |
| Headless server can't see Desktop models | launch args must include --extra-model-paths-config shared_model_paths.yaml |
| LoadImage "No such file" | image not in the registered input dir (--input-directory) |
| Port conflict with Desktop app | --port 8189 |
| Opaque alpha despite asking for transparency | prompt lacks the alpha phrases — use --transparent (official template, §6A) |
| Server won't come up | read the log: %TEMP%\comfy21_server.log |
8. Official prompt guide
references/prompt-guide.md distills the official guidance with sources: t2i prompt
order (subject → composition → style), text-in-quotes rendering rules, the official RGBA
template, edit-instruction style (imperative + identity-preservation phrasing), localized
edits and region marking by color, multi-reference slot addressing, cfg/steps interplay,
and the optional refine_prompt enhancement. Read it before crafting or fixing prompts.