Prompt Studio Edit Mask
Putting Back the Pixels Qwen Rewrote Without Asking
- generated
- source
- inpaint_mask
- IMAGE
Ask an edit model for one small change and it hands you back the whole frame - close, but not identical. Ask for five changes in a row and you've quietly reshot the entire photograph. This node is the fix, and it's the least glamorous one: composite the edit back over the original and keep everything you didn't paint.
What it is
PS_EditMask goes at the very end of an editing workflow, between your decoder and your Save/Preview node. It takes the generated image, the original source (usually from Prompt Studio Image Source), optionally a mask, and returns one IMAGE where the pixels outside the mask are the original source pixels at source resolution.
The KB's read on why this is necessary is blunt: the editor re-emits the whole frame, "so unmasked regions come back close but not identical, and the error compounds across a chain of edits" - see qwen-image-edit's panel and the inpainting guide, which lists bit-identical unmasked pixels as the one thing masks still uniquely give you in 2026. Qwen-Image-Edit took the jobs masks used to do; it did not take that guarantee. Prompt Studio's answer is to bolt the guarantee back on after the fact, rather than make the model behave.
How it works
There are two compositing paths in the source, and which one fires depends on what you connect:
inpaint_maskconnected - this is the normal path in the pose-preservation workflows. The generated image is bilinearly resized to the source's dimensions and blended assource + (generated - source) * mask, with the mask clamped to 0–1. Because it's a weighted blend, a soft mask edge gives you a soft seam instead of a visible cut. It also means the source must be RGB, and the mask must match the source exactly, or you get an explicit error (Background preservation requires an RGB source).mask_jsonset - the Studio's saved painted-mask record, which carries the mask image plus the identity and SHA-256 digest of the source it was painted on. This routes through the pack's reference-region compositor, and it will refuse a mask that belongs to a different source. Normally you leave this widget empty; it's a record, not a filename, and typing a PNG name into it just fails.- Neither - the
generatedinput passes through unchanged. No mask, no compositing, no surprise crop.
Wiring it
Three wires, and the bundled Qwen Edit workflows show the shape:
generated← your VAEDecode outputsource← the Prompt Studio Image Source node that holds the image being editedinpaint_mask← Structure Input'sinpaint_maskoutput, so the composite uses the exact same reviewed mask the model used for native inpainting
Then send this node's IMAGE output to Save or Preview. The pack's shipped save nodes are configured for PNG with lossless mode deliberately - if you save a composited edit as a compressed WebP, you've thrown away the bit-exact pixels you came here for.
One per workflow. This node only composites; PS_ApplyQwenStructure is what supplies the conditioning.
Install
Nothing extra beyond the pack itself:
cd ComfyUI/custom_nodes
git clone https://github.com/tiko13/ComfyUI_PromptStudio
Restart ComfyUI. The pack adds no runtime pip requirements - the runtime is ComfyUI plus PIL/torch, which you already have. There's no model download associated with this node; if you're using the inpaint path you've already downloaded the Qwen ControlNet Union patch for Apply Qwen Structure.
Common issues
- "The mask's source changed. Review a new background mask." You edited or replaced the source image after painting the mask. That check exists because a stale mask composites garbage into the wrong places; reopen the mask editor and repaint.
- "Inpaint mask dimensions must match the source." The mask is at a different resolution than the source. Studio's mask editor works at source resolution, so this usually means a manual MASK was resized somewhere upstream.
- "Inpaint image batches do not match." The source batch is neither 1 nor equal to the generated batch. Feed a single source image.
- The composite seam is visible as a hard line. You're using a binary mask. Feeding the same soft-edged mask from Estimate Pose Mask (or blurring a manual one) fixes it, since the blend is weighted, not a threshold.
- Nothing composites and no error appears.
inpaint_maskis unconnected andmask_jsonis empty, so the node is doing exactly what it should: passing the generated image straight through.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| generated | IMAGE | Decoded result of the edit. Connect the decoder or final image-processing output here, then connect this node's output to Save/Preview Image. | |
| source | IMAGE | Original image being edited, from Studio Image Source. With an active mask, pixels outside the painted area are taken from this image at source resolution. | |
| mask_jsonopt | STRING | Filled by Studio with the painted mask image and its source identity/digest. Normally leave empty; no mask passes generated through unchanged. This is a saved Studio mask record, not a plain image filename. | |
| inpaint_maskopt | MASK | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | Edited image with the original source preserved outside the active mask. Without a mask, the generated input is returned unchanged. |