SpotEdit Mask Generator
The mask that stops Qwen Image Edit from redrawing everything
- original_latents
- modified_latents
- vae
- spotedit_mask
- mask_image
- mask
If you've edited with Qwen Image Edit, you've seen the annoyance: you ask it to change a jacket color and the face comes back slightly different anyway. The model re-emits the whole frame, so nothing pins the pixels you didn't mention. SpotEdit's answer is to bolt a mask back on around the edit - and this node is the half that draws it. It compares the original image's latent against a 4-step "coarse" pre-sample, works out which tokens actually changed, and hands you a mask. That mask is what tells the second sampling pass where Qwen is allowed to work. And, just as importantly, where it isn't.
This is the SpotEdit Mask Generator from the SpotEdit category in the llldmxy99/ComfyUI-SpotEdit-Dev pack, an obscure single-commit "Dev" snapshot that implements the SpotEdit paper (arXiv:2512.22323) for Qwen Image Edit. It's a two-node pack, and this is the one you tune by eye.
How it works
The mechanism is simple to describe: it diffs two latents token-by-token. Feed it the original image's latent and the latent from a partial pre-run, and for every latent token it computes how far apart the two are, using one of three judge_methods:
- LPIPS - a perceptual comparison that runs the latents through the VAE decoder's early layers. Best quality, but it requires the
vaeinput wired up. - L4 - mean |x−y|⁴ per token. Cheap, no VAE, surprisingly effective.
- cosine - cosine similarity between token vectors. The zero-dependency fallback.
Anything above threshold counts as "changed" (edit region); everything else is "background" and gets marked for reuse. The default threshold is 0.15 - raise it to make the edit region bigger, lower it to shrink it. Then dilation_radius (default 1) expands the mask a little so the boundary has breathing room, and the whole thing is patched onto Qwen's token grid (patch size 2) automatically. You never touch that part.
The inputs and outputs that matter
The required inputs are exactly two latents plus the dials:
original_latents- VAE Encode of your source image.modified_latents- the coarse latent from the pre-run KSampler (steps 0–4,return_with_leftover_noiseon). This is the whole trick: that 4-step pass is enough to show where the edit is heading without committing to it.thresholdandjudge_method- the two knobs you'll actually fiddle with.vae(optional) - only needed for LPIPS; leave it out and the node falls back to cosine.
Outputs, three of them:
spotedit_mask- theSPOTEDIT_MASKobject. Goes straight intoSpotEdit Apply.mask_image- a white-on-black preview where white = edit region. Wire this to aPreview Imagenode and tunethresholduntil the white covers exactly what you want changed. This preview is why the two-pass design is worth it: you finally see the mask before committing.mask- a standardMASKoutput, in case you want to reuse it in a regular inpainting workflow.
Installing it
Same as any custom node, but note this pack targets Qwen Image Edit only - it hooks ComfyUI's Qwen Image transformer, so it's useless without a Qwen Image Edit checkpoint loaded.
cd ComfyUI/custom_nodes
git clone https://github.com/llldmxy99/ComfyUI-SpotEdit-Dev
cd ComfyUI-SpotEdit-Dev
pip install -r requirements.txt # just einops
Restart ComfyUI. Or use ComfyUI Manager and search "ComfyUI-SpotEdit-Dev" - same result.
Common issues
The README's own warning is the main one: LPIPS mode is finicky about your VAE. If it errors, you get an automatic fallback to cosine (with a yellow console warning), or you can just switch judge_method yourself or adjust the threshold. Also don't be alarmed that the preview looks chunky - it's nearest-neighbor upscaled 8× from latent resolution, so the hard edges are normal; the real mask is finer. And temper expectations: this is a developer snapshot with a Chinese README and one commit. If it misbehaves, cosine mode is your safest setting, and previewing the mask before you ever hit the second KSampler is how you debug it.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| original_latents | LATENT | — | |
| modified_latents | LATENT | — | |
| threshold | FLOAT | 0.150–1 | — |
| judge_method | COMBO | 3 options: LPIPS, L4, cosine | |
| dilation_radius | INT | 10–10 | — |
| vaeopt | VAE | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| spotedit_mask | SPOTEDIT_MASK | — |
| mask_image | IMAGE | — |
| mask | MASK | — |