Nodes/Ultra LoRA Reference Images/_UltraExactSaveImage
ComfyUI Node

_UltraExactSaveImage

The saver that writes a PNG and its caption or refuses to write anything

By Yamoon-jjang·Created 14 days ago·Updated 14 days ago· 3
_UltraExactSaveImage
  • images
    ◄filename_stem01_front_neutral►
    ◄caption►
    ◄date_subfolder►
    ◄dataset_plan►

    A LoRA trainer doesn't want pretty pictures, it wants pairs: 0001.png next to 0001.txt holding the caption. SaveImage gives you the PNG and nothing else. _UltraExactSaveImage gives you both, atomically, and throws an error rather than save a file it wasn't scheduled to save.

    That strictness is the point. It's the last node in each branch of the Ultra LoRA Reference Images graph, and it's the reason you can queue a 40-image run, walk away, and still tell afterwards whether all 40 landed.

    Where it lands

    ComfyUI/output/lora_dataset/<run folder>/, and the run folder looks like 20261019_142233_847211_24_face - timestamp down to microseconds, then the batch count, then the mode. The microseconds aren't vanity: two queues expanded at the same moment would otherwise collide on a folder name.

    Inside, every image gets a numbered, self-describing stem from the parent's plan - 01_face_closeup_front_neutral_up.png in face mode, 01_upper_body_slight_left_standing_outfit2.png in balance - because a training set you can audit by filename is worth ten you can't.

    The mechanism, briefly

    For each image in the batch it writes the PNG (with the usual ComfyUI prompt and workflow text chunks, unless you launched with --disable-metadata), then writes the caption to a same-name .txt. Both writes go through a temp-file rename, so a crash mid-write doesn't leave you with half a PNG and a trainer that chokes on it. That sidecar convention - same basename, .txt alongside, one caption per image - is what kohya, ai-toolkit and OneTrainer all read; the reasoning is in docs/knowledge/image-io-metadata.md.

    Then it does the clever bit, which is why this node exists instead of a plain SaveImage. When it starts, the folder gets a _INCOMPLETE.txt marker plus a hidden .ultra_lora_reference_progress/ directory. Each variant drops a small JSON record there once its files are final. After the last one, the node checks every stem in its expected-file plan, and only if all are accounted for does it delete the progress dir and the marker.

    Which means: folder has _INCOMPLETE.txt → that run died partway. You don't have to count files. That's genuinely nicer than what most batch savers give you.

    Inputs - the two you might touch, and the three you shouldn't

    • images - the IMAGE batch to save. One image, one PNG; a four-image batch becomes stem.png, stem_02.png, stem_03.png, stem_04.png, all sharing the caption.
    • filename_stem - the name without extension. Defaults to 01_front_neutral so you can see the shape of it.

    The other three are wired from the parent graph and deliberately hostile to hand-editing:

    • caption - the text that becomes the .txt. Empty caption means no .txt at all, silently. If your dataset folder is suspiciously image-only, that's why.
    • date_subfolder - validated against a strict format. Anything that isn't a real run folder gets a hard error, not a fallback.
    • dataset_plan - the JSON list of stems this run is supposed to produce. filename_stem must appear in it.

    Outputs: none. RETURN_TYPES is empty and it's an output node. It previews the saved image in the UI and terminates. There is nothing to wire downstream, so don't sit there looking for the output dot.

    Installing it

    It's inside the pack; nothing separate. Manager → search Ultra LoRA Reference Images, or:

    cd ComfyUI/custom_nodes
    git clone https://github.com/Yamoon-jjang/comfyui-Ultra-LoRA-Reference-Images
    

    Restart ComfyUI. No pip installs - the pack's requirements.txt is comments only. You'll need the Qwen Image Edit models separately (diffusion model, text encoder, VAE) to actually generate anything, but the saver itself only needs a writable output/.

    Where people get burned

    Every branch saves into the same run folder, which is how you get a coherent dataset rather than forty folders of one image. Queue twice and you get two folders - the parent node never caches, so re-queueing always means a new run and a new directory. Nothing is overwritten; nothing is resumed.

    Hand-wired, it errors. Set filename_stem yourself without a matching dataset_plan and you get "not in the internal dataset save plan". Same for a made-up date_subfolder. This node is an implementation detail of the graph-expansion trick - for arbitrary batch saving with captions, use a dataset-prep pack instead.

    Interrupted runs leave debris. Kill ComfyUI mid-run and the folder keeps _INCOMPLETE.txt and a progress dir with some JSON in it. The images that finished are real and complete; delete the marker and the hidden folder once you've eyeballed the set, or just use the finished ones and skip the stragglers.

    A caption is not a caption until you check it. The text is generated by the parent node along with the image, and the two can disagree when the model ignores an instruction - a "sad expression" caption on a giggling face teaches the wrong thing. Open twenty of them before you train. Caption what should vary, leave identity undescribed; docs/knowledge/lora-training.md is blunt about how much of your final LoRA quality is decided right here rather than in the trainer.

    Category_internal

    Inputs (5)

    NameTypeDefaultDescription
    imagesIMAGE—
    filename_stemSTRING01_front_neutral—
    captionSTRING—
    date_subfolderSTRING—
    dataset_planSTRING—

    Outputs (0)

    No outputs