Image Patch Editor
Keep the pixels Qwen and Klein didn't touch — ImgPatchNode auto-masks your edits
- original_image
- edited_image
- merged_image
- final_mask
- heatmap
- raw_mask
- debug_overlay
The Image Patch Editor does one thing, and it's the thing every whole-frame editing model is secretly bad at: leaving the rest of the image alone. Feed it an original and an edited version of the same image, and it figures out which pixels actually changed, builds a mask by itself, and composites the edited content back onto the untouched original. No mask painting, no bbox dragging. That's the entire pitch, and it's a good one.
If you've fought Qwen-Image-Edit or Flux 2 Klein long enough, you know the problem: they take the whole image in and emit a whole new image out, so the stuff you didn't ask to change comes back close but not identical. Do it five times in a row and your character's face has subtly melted. The standard 2026 fix is crop-edit-stitch - render only the changed region, composite it back, leave every other pixel bit-identical. This node is that pattern minus the manual mask step: it detects the changed region for you. Reach for it anywhere you'd otherwise hand-draw a mask and composite with ImageCompositeMasked.
How it works
Three passes, all CPU, all classic computer vision. First it can auto-align the edited image to the original using ORB feature matching with a RANSAC fit - the README's claim is real, and worth knowing the limits of: the transform only handles uniform scale and translation, never rotation or shear. Align a cropped-and-upscaled region and it's perfect; rotate the edit and it silently falls back to a plain resize.
Then it computes a difference heatmap with one of three algorithms: difference (plain pixel-wise absolute difference), l2_norm (Euclidean color distance - more sensitive to hue shifts), or ssim (perceptual structural similarity, slowest but most human-like). That heatmap gets thresholded into a binary raw_mask, and then a pipeline of cleanup: min_area_size drops small specks, mask_expansion dilates or erodes it, blur_radius feathers the edges. Finally the edited image is composited onto the original with Image.composite using that mask.
The inputs that matter
Only a few actually need touching from the defaults:
tolerance- the one to tune, and it's counterintuitive: higher detects more changes, lower only catches dramatic ones. Start at 0.3 and crawl up until the mask covers what changed and nothing else.min_area_size- the noise gate. Raise it when the heatmap is picking up JPEG grain and compression artifacts as "changes."detection_alg-ssimfor quality,differenceorl2_normwhen you want speed.region_size/stride- the sliding window. Keepstrideat 1 for full overlap (slowest, most accurate); bump it towardregion_sizefor a fast approximation.edit_max_alpha- cap how strongly the edited pixels come through; useful for fusing two model outputs without hard cutovers.
The outputs you'll actually wire up: merged_image goes straight into a Save Image node, final_mask is a proper MASK you can feed anywhere else, and debug_overlay - original with the detected changes highlighted in red - is your sanity check for that first run. heatmap and raw_mask are for tuning and curiosity.
Installation
No models, no API keys, no VRAM - it's pure image math. The heavy dependency is scikit-image, which the code genuinely uses (ORB, RANSAC, SSIM).
cd ComfyUI/custom_nodes
git clone https://github.com/Premik/ComfyUI-ImgPatchEditor
cd ComfyUI-ImgPatchEditor
pip install -r requirements.txt
Restart ComfyUI and look for Image Patch Editor under the ImgPatch category. ComfyUI Manager also finds it by searching "Image Patch Editor" or "ImgPatchEditor". (Source note: the README also mentions a pillow-avif-plugin dependency, but it's commented out in requirements.txt - you only need scikit-image and numpy, and ComfyUI already ships Pillow.)
Gotchas
The node requires original_image to be a single image - it raises a clear error if you feed it a batch. edited_image, though, accepts a whole batch, and it processes them chained: each edited frame patches the output of the previous one. That's the intended iterative-refinement workflow, and also a footgun if you expected independent outputs. And one more trap: if the sizes mismatch and allow_rescale is off, you get a hard ValueError - turn allow_rescale on to let the ORB alignment sort it out.
It's a young, single-node pack with zero community footprint yet, so treat the defaults as a starting point and trust debug_overlay, not your eyes, when tuning. For the "edit a little, keep the rest pristine" crowd this is quietly the most useful three lines of a workflow you'll add this month.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| original_image | IMAGE | — | |
| edited_image | IMAGE | — | |
| detection_alg | COMBO | 3 options: difference, l2_norm, ssim | |
| region_size | INT | 81–128 | Size of the blocks used for difference detection. |
| stride | INT | 11–64 | Step size for the sliding window. 1 means full overlap (slowest, most accurate), equal to region_size means no overlap (fastest). |
| min_area_size | INT | 40–128 | Minimum size of the changed area to keep. |
| tolerance | FLOAT | 0.500–5 | Sensitivity of difference detection. Higher values will detect more changes. |
| blur_radius | FLOAT | 3.00–100 | Radius of the gaussian blur applied to the mask. |
| mask_expansion | INT | 0-128–128 | Expand or shrink the mask. Positive values expand, negative values shrink. |
| edit_max_alpha | FLOAT | 1.000–1 | Maximum alpha for the edited image overlay. |
| allow_rescale | BOOLEAN | true | If true, rescales the edited image to match original size. |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| merged_image | IMAGE | The original image with the patched areas from the edited image. |
| final_mask | MASK | The final mask used for merging. White indicates changes (taken from edited image), black indicates unchanged areas. |
| heatmap | IMAGE | Visualization of differences. Brighter areas indicate larger differences between original and edited images. |
| raw_mask | MASK | Initial binary mask before post-processing. White indicates detected changes, black indicates no change. |
| debug_overlay | IMAGE | Original image with a red overlay highlighting the detected changes. |