DOGMA SoftStitch v56.6
Pasting the repair back without a visible rectangle
- base_image
- patches
- generation_masks
- blend_masks
- stitch
- image
- info
The last step of the crop-render-paste loop, and the one that decides whether anyone can tell you did anything. It takes your re-rendered patches and composites them onto the base image with a distance-transform feather and a deliberate low-frequency compromise - because the two things that make a paste visible are a hard edge and a colour shift.
The two seams
The KB's inpainting notes name both failures: a mask-shaped patch with a visible boundary, and the grey-box problem, where a VAE round-trip comes back with a slightly different exposure or hue than its surroundings. Feathering fixes the first. Nothing fixes the second except being careful about what you take from the patch, which is what the low-frequency term here is for.
How it works
Two masks arrive per crop, and the way they combine is the whole design:
seed = resample(blend_mask, nearest) >= .5
support = resample(generation_mask, nearest) >= .5
seed &= support
blend_mask is the exact approved ownership, generation_mask is the dilated sampling support from DOGMADualMaskV566. The AND means the paste can never be wider than what you verified, even though the sampler was allowed to work in a slightly bigger region. Nearest-neighbour resampling is used on purpose: interpolating a binary mask can invent ownership from fractional edge values.
The alpha comes from a Euclidean distance transform of the seed, computed on a one-pixel-padded copy so that a mask touching all four edges of the crop still has a background border. Distance is then ramped through a smoothstep over a width of clamp(4, 24, 1% of the shorter crop side) - 5px on a 512 crop - which gives you a feathered falloff that reaches exactly zero at the ownership boundary. Outside it, the base image is untouched, bit for bit.
Then the colour part. The delta between patch and region is downscaled to at most 512 on the long side with area sampling, blurred with a Gaussian of sigma 4, and interpolated back up. low_frequency_strength (0–1, default 0.15) decides how much of that broad change to keep:
corrected = region + delta - (1 - strength) * low
At the default, 85% of the broad exposure/colour shift is subtracted out, so you keep the patch's fine detail and its texture, but the base image's lighting gradient survives. Set it to 1.0 and you take the whole delta, colour drift and all - which is the right choice when the patch is genuinely differently lit and the "drift" is real. Set it to 0 and you're keeping source colour everywhere, which is the conservative choice on a platematch-critical shot.
Patches are resized to their stitch slot's recorded width/height with bicubic + antialias before compositing, and crops that were padded to a multiple of 16 get their padding trimmed using the metadata's pad_right / pad_bottom - so the pad the crop step added is undone here and nowhere else.
Inputs and outputs
All list inputs, all required:
- base_image - the source you're restoring onto. Only the first entry is used; everything else is the patch list.
- patches - IMAGE list, one per crop.
- generation_masks - MASK list, the support masks.
- blend_masks - MASK list, the exact ownership masks.
- stitch -
DOGMA_STITCHlist: per-crop metadata withx,y,width,heightand optional padding. Entries flaggednoopare skipped, which is how an inactive slot costs nothing here. - category - STRING wire, carried for logging.
- kind - STRING wire (
STRUCTURE/SURFACE/OBJECT). Required, and in the shipped v56.6 code it doesn't branch the logic - the composite is the same for every kind. - low_frequency_strength - 0 to 1, default 0.15, step 0.05.
Outputs: image and info ("N crops; distance feather reaches zero at boundary; exact source outside approved ownership; low-frequency change retained 0.15").
Install
comfy node install comfyui-dogma-nodes
# or
cd ComfyUI/custom_nodes
git clone https://github.com/axior/ComfyUI-DOGMA-Nodes
pip install -r ComfyUI-DOGMA-Nodes/requirements.txt
Restart, and load a v56.6-or-later workflow. This node needs scipy - the distance transform is scipy.ndimage.distance_transform_edt - so the scipy>=1.10 in requirements.txt is load-bearing even though the README says the pack has no extra dependencies. No models required.
Gotchas
The counts must match exactly. If patches, generation masks, blend masks and stitch entries aren't the same length, it raises before doing anything: DOGMA: crop/mask/stitch counts differ; refusing list misalignment. The most common cause is a slot that produced a patch but no stitch entry, or a mask list that got filtered somewhere upstream without the others following.
Coordinates are validated, so bad metadata fails loudly. A stitch entry whose x/y are negative or whose box runs past the base image raises rather than clamping. If you're hand-building stitch metadata (or adapting an older graph), that check is the thing that will find your off-by-one.
0.15 is a good default and a bad universal. It's tuned for repainting objects into the same shot. For a patch generated from a genuinely different exposure - a plate you matched badly upstream - raise it toward 0.5–0.7, or you'll get patches that look under-lit against a bright source, which reads as a soft rectangle even though the alpha is perfect.
Feather width is derived, not settable. It scales with the shorter crop side, so a large crop gets a proportionally wider soft edge than a small one.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| base_image | IMAGE | — | |
| patches | IMAGE | — | |
| generation_masks | MASK | — | |
| blend_masks | MASK | — | |
| stitch | DOGMA_STITCH | — | |
| category | STRING | — | |
| kind | STRING | — | |
| low_frequency_strength | FLOAT | 0.150–1 | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| info | STRING | — |