Nodes/ComfyUI-EditUtils/EditUtils: QwenImage 2.1 Edit Text Encode lrzjason
ComfyUI Node

EditUtils: QwenImage 2.1 Edit Text Encode lrzjason

Three Reference Images, One Node

By lrzjason·Created 8 months ago·Updated 3 days ago· 234
EditUtils: QwenImage 2.1 Edit Text Encode lrzjason
  • clip
  • vae
  • image1
  • image2
  • image3
  • mask
  • conditioning
  • latent
  • custom_output
  • main_image
  • mask
prompt
ref_longest_edge1024

The whole 2.1 edit path in one box

Most EditUtils graphs are a chain: a model config node, one config preparer per image, and a big encode node that reads the lot. QwenImage21EditTextEncode_EditUtils is the pack's escape hatch from that - clip, VAE, prompt, up to three images, done. It builds the same config entries internally (pad, lanczos, vision on, 32-pixel alignment, first image as the main image) and calls the shared encoder, so you get identical behaviour with none of the wiring.

Reach for it when you're doing the ordinary thing: one source image and a sentence, or a person plus a product. Reach for the preparer chain instead the moment you need a fourth reference, a different resize per image, a mask on something other than image 1, an area resize mode, or ROPE placement. Note the other path too: the simple node hardcodes the 2.1 settings, so QwenImage21ModelConfig_EditUtils does nothing for it.

What it's actually feeding

Qwen-Image-Edit is the model that turned instruction editing into the default open workflow, and Qwen-Image 2.1 changes how the reference gets in. The clip here is a Qwen3-VL text encoder, and the resized reference goes into its vision tower and into the VAE - the same resized tensor for both, which is the part 2.1 simplified. Each reference's latent is then spliced into the text sequence at the encoder's vision slots, which is why there's no Picture 1: prompt prefix to type and no vl_target_size to tune. You write an instruction; the encoder handles the rest.

The latent output is the main image's own padded latent - 64 channels at 16x downscale on 2.1 - and it's your sampling start latent. That's the "edit" in edit model: you're not denoising from noise, you're denoising from the image, with the prompt as the instruction to change it.

Inputs and outputs

clip, vae and prompt are required. image1 through image3 are optional ports but at least one has to be connected, and image1 is always the main image. ref_longest_edge (1024 default) sets the reference target, snapped to 32-pixel multiples. mask applies to image1 only, and only if its dimensions match the image - otherwise it's skipped with a console note and the run continues unmasked.

Outputs, in order: conditioning goes to your positive input on KSampler, carrying the reference latents and rope offsets on the chain; latent goes to latent_image; main_image hands you back the resized/padded version that was actually encoded; mask is the forwarded mask; custom_output is a dict of intermediates - including pad_info, which is what you need to undo the padding after decode. Wire it into QwenEditOutputExtractor_EditUtils if you'd rather have named pins than dig through a dictionary.

The padding round trip

Because references are aligned to the grid, the main image gets padded to a 32-pixel multiple, and the latent you sample from includes that padding. After VAEDecode, run the image through CropWithPadInfo_EditUtils with the pad_info from custom_output to get your actual framing back. Skip this and your output is politely off-centre with a strip of extra pixels on one side - a classic "why is my edit shifted" post.

Install

ComfyUI Manager, search ComfyUI-EditUtils, or:

cd ComfyUI/custom_nodes
git clone https://github.com/lrzjason/ComfyUI-EditUtils

Restart ComfyUI. The repo has no requirements.txt, so nothing extra installs - but it does require a ComfyUI with upstream Qwen-Image 2.1 (qwen_image21) support, or the model won't load. Files from the example workflow: qwen_image_2.1_int8_convrot.safetensors in UNETLoader, qwen3vl_8b_fp8_scaled.safetensors in CLIPLoader with type qwen_image, qwen_image_2.1_vae_bf16.safetensors in VAELoader.

Troubleshooting

Forgetting to connect any image is the beginner trap, because ComfyUI happily leaves optional ports empty and the node only complains at run time: ValueError: At least one image must be provided. If you see it, that's all it means.

Settings-wise, the example workflow is a good floor: euler / simple, 25 steps, CFG 1.0. That CFG isn't a typo or a niche low-guidance setting - on the guidance-distilled, LLM-encoded generation CFG 1 is the default, guidance is baked into the weights, and pushing it to 7 burns the image at double the render time. And prompt it like an instruction, not a tag list: (dress:1.4) isn't weighted here, it's fed to the language model as literal punctuation.

For consistency across repeated edits, the author recommends his own Consistency Edit LoRA (civitai.com/models/1939453, mirrored on HuggingFace as lrzjason/Consistance_Edit_Lora) - same author behind the QwenEditUtils workflows and the Anything2Real LoRAs, so that's dogfooding rather than an ad. It's also the standard fix for the edit-line complaint that unedited regions drift across a chain of edits.

Categoryadvanced/conditioning

Inputs (8)

NameTypeDefaultDescription
clipCLIP
vaeVAE
promptSTRING
image1optIMAGE
image2optIMAGE
image3optIMAGE
ref_longest_edgeoptINT102432–4096Reference target size. Snapped to 32-pixel multiples.
maskoptMASK

Outputs (5)

NameTypeDescription
conditioningCONDITIONING
latentLATENT
custom_outputANY
main_imageIMAGE
maskMASK