DHan-Composite
Paste the edit back and keep every pixel it didn't touch
- generated_image
- source_image
- edit_mask
- image
- composite_mask
- report
- debug_view
You asked for one change and got a whole new photograph
Ask Nano Banana, GPT Image, Qwen Image Edit or Flux Kontext to put a leather jacket on someone and it'll do that happily - along with quietly re-rendering the curtains, the background grain, and the exact exposure of every pixel you liked. Instruction editors are whole-frame models wearing a mask-shaped hat.
The one thing a local workflow can do that no edit model will do for you is leave the rest of the frame bit-identical. Composite the changed region back onto the original and nothing outside it moved. That's the standing advice - "PLEASE composite your image after inpainting!" was a +356 post for a reason - and doing it by hand takes a mask combine, a feather, ImageCompositeMasked, and maybe a color match.
DHan-Composite is that chain in one node: source_image, generated_image and a mask in, one frame out where only the masked region is new. It runs no model and calls nothing over the network - the local half of any hosted-editor workflow.
How the paste actually works
Short and deterministic. The pack declares zero dependencies - no OpenCV, SciPy or scikit-image.
- The generated image is bilinearly resized to the source canvas, and your
edit_maskis thresholded to binary at 0.5 - soft masks get hardened. - In the detection modes it builds a change mask: per-pixel RGB distance between source and generated, against a threshold of
0.025 + 0.20 × change_sensitivity. mask_modepicks yours, the detected one, or their union, thenmask_grow_pxdilates the result.seam_matchaveragessource - generatedover an edge ring inside the hard mask and adds that shift back onto the generated pixels there - one mean per image, so it fixes a uniform color offset, not a drifting gradient.- The alpha ramp is a double box blur of the hard mask at
edge_feather_px, and the final pixel issource * (1 - alpha) + matched * alpha.
The inputs worth touching
edit_mask is required and always wired, even in Detected Changes mode where its contents are ignored. Drop a core SolidMask at 0 into it and move on.
mask_mode-Edit Mask(default, trusts your paint),Edit Mask + Detected Changes(the union), orDetected Changes(no painting).change_sensitivity(0–1, default 0.2) - detection modes only, and the naming reads backwards. Higher is stricter: 0.0 is permissive, 1.0 catches only strong changes. The default threshold works out at 0.065 in RGB, low enough that re-encode noise counts as an edit.mask_grow_px(0–256, default 0) andedge_feather_px(0–256, default 12) - push the region outward when the edit bleeds past your paint, and ramp the blend; 4–12 px is the normal range for a mask seam.seam_match(0–1, default 0.35) - raise it when the patch reads as a different white balance.
Outputs
image is your composite. composite_mask is the binary hard mask (post-grow, pre-feather) - fine for a detailer or MaskToImage, wrong if you wanted a soft edge. report is a text summary for something like PreviewAny, and the fastest way to check which mode actually ran. debug_view is a 2×2 source/generated/mask/result grid when debug is on, and a 1×1 black image when it's off.
Install
Manager, search DHan, or:
cd ComfyUI/custom_nodes
git clone https://github.com/DHan315/Comfyui-DHan-Composite
Restart. That's the whole install - nothing to pip install, no model files. It's one node (DHan-Composite, category DHan/Composite) plus a hidden legacy alias: workflows saved under its old name still load as a deprecated ARCCompositeNode, with identical behavior.
Where people get burned
Detected Changes at the default sensitivity flags everything. At 0.2 you'll often get a full-white mask and a passthrough composite - which looks like the node is broken. Raise change_sensitivity until only your region lights up, or paint the mask and use Edit Mask.
Aspect mismatches get squashed. An editor returning 1024×1024 for your 832×1216 source gives you a horizontally squeezed composite. And uneven batches collapse: if both images arrive batched with different sizes and neither is 1, the node keeps the first frame and repeats it.
A hard line at the edge means less feather and more mask_grow_px. Right shape but wrong light is a color problem: raise seam_match, or use a statistics-based Color Match Image pass if the correction needs to vary across the region.
Node missing after install? With nothing to pip-install, that's usually a path problem - the clone landed in a custom_nodes folder ComfyUI isn't reading. Check the startup log.
Reach for it when
Your edit is almost right - right edit, wrong everything-else - and you want the source preserved. API editors, drifting local editors, a detail pass you want pasted back at original resolution. If the whole image is fair game, skip it.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| generated_image | IMAGE | — | |
| source_image | IMAGE | — | |
| edit_mask | MASK | — | |
| mask_mode | COMBO | Edit Mask | 3 options: Edit Mask, Edit Mask + Detected Changes, Detected Changes |
| change_sensitivity | FLOAT | 0.200–1 | — |
| mask_grow_px | INT | 00–256 | — |
| edge_feather_px | INT | 120–256 | — |
| seam_match | FLOAT | 0.350–1 | — |
| debug | BOOLEAN | false | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| composite_mask | MASK | — |
| report | STRING | — |
| debug_view | IMAGE | — |