Luck Annotator
Luck Annotator for region-guided editing
- image
- image_out
- mark_data
If you've ever stared at a Qwen VL or Flux 2 Klein prompt that "sort of" followed your instructions, you know the real problem: you told it what to do but never where. Luck Annotator (registered as ImageAnnotator, shown in ComfyUI as "Luck Annotator") is the "where". It's an interactive canvas that sits inside the node and lets you draw numbered points, rectangles, and polygons on your image, then hands that annotated image - plus the JSON behind it - to whatever vision-language model you're driving. Think of it as a spatial highlighter for image-editing models that are smart enough to read drawings but not your mind.
It's squarely aimed at the post-2025 editing crowd: Flux 2 Klein, Qwen 2.5/3 VL, Nano Banana, and friends that accept visual guidance. The 2026 story in the inpainting world is that instruction editors took over from masks for most jobs - but those models still can't keep their hands off the rest of your image, and region guidance is how you stop them from drifting. Mark region 2 with a box, prompt "add a hat there," and the model has an actual target instead of a prayer.
How it works
There's no magic and no API call - the whole thing is a frontend editor plus a Pillow render. When you add the node and wire an image into it, the frontend (web/js/image_annotator.js) embeds a zoom/pan canvas directly in the node, with a toolbar for Select, Hand, Point (P), Rect (M/R), and Polygon (L), plus up to 50 steps of undo. Every stroke you make is serialized to a JSON blob and written into the node's hidden mark_data widget.
On execution, the backend parses that JSON and re-draws the marks onto the image with Pillow's ImageDraw - colored strokes, adjustable fill transparency, point size, and a numbered badge on every mark. The overlay is baked in at your input image's native resolution. That's the key architectural fact: this node outputs rendered pixels and JSON, not a ComfyUI mask. It's a visual prompt for a VLM, not an inpainting mask.
The inputs and outputs that matter
There are exactly two inputs. image (required) is your source. mark_data (optional, multiline string) is the JSON of existing marks - this is how annotations survive. Save the JSON somewhere (or keep it from the output), paste it back in, and the same marks re-render on a fresh image. That round-trip is the node's real trick: your annotations are data, not just pixels.
Outputs:
image_out(IMAGE) - the source with all visible marks composited on top. Wire this into your QwenVL node or Klein reference input.mark_data(STRING) - the JSON annotation data, handy for a ShowText node or for logging.
Install
No heavy dependencies: requirements.txt is literally just Pillow - torch and numpy come from ComfyUI itself. Easiest path is ComfyUI Manager (search "Luck Annotator" or "comfyui-image-annotator"), or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/luckdvr/comfyui-image-annotator
Then restart ComfyUI. That's it - no model files, no API keys, no first-run downloads.
Where people get burned
A few real gotchas, straight from the code:
- Only the first frame of a batch gets annotated. The backend pulls
image[0]and ignores the rest. Feed it a single image, or you'll wonder where your other frames went. - The node always re-executes.
IS_CHANGEDreturnsNaN, so ComfyUI reruns it every queue - that's deliberate (so edits always render fresh) but it means no caching. - Labels depend on your system fonts. On a Linux box without DejaVu or Liberation fonts, the numbered badges fall back to Pillow's tiny bitmap font. Most installs are fine; headless minimal containers occasionally aren't.
- Don't confuse it with a mask tool. If your workflow wants a proper binary mask for classic inpainting, reach for a segmentation/auto-mask node instead - this one paints guidance, it doesn't carve out regions.
It's a niche tool with a small footprint, but if you're doing region-guided edits with a vision-language model, it's the missing pointer finger between your intent and the model's attention.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| mark_dataopt | STRING | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image_out | IMAGE | — |
| mark_data | STRING | — |