DOGMA v39 Adaptive Crops
Crop once, upscale once, keep the object whole — DOGMA v39 Adaptive Crops
- image
- reference_image
- masks
- crops
- reference_crops
- crop_masks
- stitch
- info
Every detailer is the same loop: detect, crop, upscale, re-render, paste back. The interesting failure modes are all in the crop step, because that's where you decide whether an object stays whole, whether the seam lands on a smooth gradient, and whether the model gets enough context to know what it's looking at. DOGMAAdaptiveCropsV39 is the crop step for the v39 generation, and it deliberately behaves differently for countable objects and for big surfaces.
What you feed it, what you get
Required inputs: image, reference_image, masks, category, kind, max_groups (default 8), context_px (default 176), target_long_side (default 2048).
Both images matter and they're not the same thing. image is the damaged source you're cropping from; reference_image gets cropped with the identical geometry so the branch has a clean partner to compare against or to blend from. If they differ in size the node resizes the reference to match, so keep them a matched pair.
Outputs are lists: crops, reference_crops, crop_masks (one mask per crop, same geometry), stitch, and a single info string. stitch is the pack's DOGMA_STITCH payload - a list of placement records (x, y, width, height, plus a noop flag) that the matching stitch node uses to put the edited pixels back exactly where they came from. Wire crops into your Klein masked inpaint, then stitch plus crop_masks and the edited images into DOGMADetailBandStitchV39. Lose the stitch wire and you have no way to composite.
How the two paths differ
With kind=OBJECT the node hands off to the pack's proven v27 whole-object cropper. It deduplicates overlapping SAM detections, keeps each object whole, groups only directly adjacent instances, and never tiles a crop - if a crop exceeds target_long_side the whole thing is downscaled as one image instead. That's a real design opinion: splitting a pedestrian across two tiles is how you get a person with two heads.
With kind=STRUCTURE or SURFACE it ignores individual masks and unions them, then finds occupied 256px cells and connected-components them into a handful of broad macro regions. Giant regions are split into overlapping windows on a stride of ~0.75 × target_long_side, and each window is only kept if the union mask actually has content in it (mean > 0.005). Then mid_strength-style detail injection does the work rather than full regeneration - which is the only sane way to touch a road: you cannot re-render 40 megapixels of asphalt.
context_px is the padding added around each region before cropping. It's the anti-seam knob: too little and the model sees an object with no surroundings and invents them; too much and you're paying for pixels you already had. 176 is a sensible middle for 2048px targets.
The safe no-op is the best feature
category="none", an empty mask tensor, or a mask that's essentially empty all return a safe no-op: a centre 768px square with a zero mask and a noop record, plus an info string saying so. Nothing crashes, nothing downstream gets a fake generated crop, and the stitch places a zero-mask patch that composites to nothing. If you've ever watched a detailer graph die because one category found no objects, you know why this matters.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/axior/ComfyUI-DOGMA-Nodes
Or Manager → DOGMA Nodes, then restart. No pip dependencies at all - requirements.txt is a single comment. The README on GitHub does not document this node (it's stale, covering only the WAN VACE and sampler nodes), so don't go looking for its parameters there. The real cost is the render: you need FLUX.2 Klein 9B (~16-20 GB fp8) for the crops, and 2048px crops at max_groups=8 is eight sampling passes per sector. Budget accordingly.
Common issues
Empty crop list → the branch does nothing. You gave it a mask with nothing above threshold, or a category of none. Check info first; it tells you which.
Crops that are all context and no subject. Raise context_px was the wrong move - it's already too high for small objects. Drop it toward 96-128 for vehicles and people on a 2048 target.
Objects cut in half. That's the OBJECT path being handed masks that arrived as one blob (a merged SAM mask for two adjacent cars). Fix the masks, not the cropper.
Soft, oversmoothed results. You're upscaling too far. If target_long_side is 2048 and the source region is 200px wide, you're asking a model to invent 10x the detail. Keep the crop's source side large (max_source_side in the settings nodes) or lower the target.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| reference_image | IMAGE | — | |
| masks | MASK | — | |
| category | STRING | — | |
| kind | STRING | — | |
| max_groups | INT | 81–16 | — |
| context_px | INT | 1760–640 | — |
| target_long_side | INT | 2048768–3072 | — |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| crops | IMAGE | — |
| reference_crops | IMAGE | — |
| crop_masks | MASK | — |
| stitch | DOGMA_STITCH | — |
| info | STRING | — |