DOGMA MaskOwnership v56.7
Who owns pixel 512 when two categories both want it
- mask_1
- mask_2
- mask_3
- mask_4
- mask_5
- mask_1
- mask_2
- mask_3
- mask_4
- mask_5
- ownership_report
Ask an open-vocabulary segmenter for "vehicles" and for "road" and they will both hand you the same tarmac under the same car. Paste both back and you've rendered the same pixels twice with two different repairs, in whatever order the graph happened to run. This node settles the argument before it happens.
The problem it solves
Multi-category restoration is arbitration, not detection. SAM 3's text-prompted segmentation is good and it is not mutually exclusive - the KB's note is that it masks what you point it at, and the thing you point at often overlaps the thing the next slot is pointed at. A car's wheels are road. A person's feet are pavement. A building's base is ground.
The usual fix in pipelines is ordering: do category A, then category B on what's left. That works, but only if everyone agrees on the order, and it silently discards pixels when the order gets it wrong. DOGMA's answer is to make the order explicit, name it, and refuse to render a category that got eaten.
The priority ladder
Five slots in, five masks out. Each slot carries a category string, and the category decides priority:
| Priority | Category | |---|---| | 0 | people | | 1 | vehicles | | 2 | everything else (objects) | | 3 | structures - buildings, houses, cathedral, bridge, tower, facade | | 4 | surfaces - road, ground, pavement, floor, water, grass, vegetation, sky |
Lower number wins. People beat cars, cars beat generic objects, objects beat architecture, architecture beats ground and sky. Each category keeps only the pixels not already claimed by something above it.
That ladder is the interesting part, because it encodes a photographic judgement rather than a technical one. Foreground subjects get fidelity; the ground gets whatever's left. Sky sits at the bottom, which is consistent with the rest of the DOGMA line - the older v22 settings node protects sky entirely, stitch strength zero.
What else it does
Three bits of hygiene, all reported:
- Fragment removal. After a category loses pixels to its betters, any remaining connected component smaller than 9 pixels is dropped. A three-pixel sliver of tarmac between two tires is noise, not a surface.
- Explicit loss reporting. The
ownership_reportgives a line per category: instances kept, and how many pixels were removed as overlapping. Pixel counts, not vibes. - Loud failure. If a live category ends up completely covered by the others, it raises:
DOGMA: <category> was fully covered by other categories. Check conflicting detections before diffusion.That's a real detection conflict - two categories claiming the same thing - and it's better to stop than to render a category with no pixels and call it a pass. Inactive categories (noneand friends) are exempt, because an empty slot being empty is not news.
Everything is done on boolean masks on CPU at 0.5 threshold, and the masks must all match the analysis image's dimensions or it raises.
Inputs and outputs
- mask_1..mask_5 and category_1..category_5 - all ten are required. That's the thing to know: there is no "just wire two of them" mode. Unused slots need a mask and an inactive category string (
none), which is why the DOGMA graphs wire dummy/empty masks into the spare slots. - Outputs mask_1..mask_5 (the arbitrated, exclusive ownership masks, matching their input slots) and ownership_report.
Mask output N corresponds to input slot N and its category - the slots are positional, not sorted, so your downstream per-category chains stay wired the same way they were before arbitration.
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 current workflow - this is a v56.7 node and only the newer graphs wire all five slots correctly. No model downloads. Note the module imports scipy.ndimage, so the scipy>=1.10 in requirements.txt is not optional even though the README claims the pack is dependency-free.
Gotchas
The five-slot requirement is the friction. If you only care about two categories, you still build five wires, and the empty ones need to be genuinely empty (a torch.zeros mask is fine) with an inactive category string. Category strings that aren't in the canonical active vocabulary are fine, but empty strings aren't treated as inactive by this node's check - the masks just come out as whatever survives.
Canonicalisation is limited to four groups. The module maps plurals and synonyms only for buildings, vehicles, people and road. "Pedestrians" becomes people and gets top priority; "cyclists" stays a generic object at priority 2 and will lose overlaps to people and cars. If a category's priority surprises you, check the canonicaliser's word lists - that's the whole rulebook.
A conflict here is a conflict between two audited masks. This runs after the audit, so both sides of the overlap passed their own verdict - which is exactly the case worth stopping on, and the error is telling you your categories aren't as distinct as the planning step assumed.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| mask_1 | MASK | — | |
| category_1 | STRING | — | |
| mask_2 | MASK | — | |
| category_2 | STRING | — | |
| mask_3 | MASK | — | |
| category_3 | STRING | — | |
| mask_4 | MASK | — | |
| category_4 | STRING | — | |
| mask_5 | MASK | — | |
| category_5 | STRING | — |
Outputs (6)
| Name | Type | Description |
|---|---|---|
| mask_1 | MASK | — |
| mask_2 | MASK | — |
| mask_3 | MASK | — |
| mask_4 | MASK | — |
| mask_5 | MASK | — |
| ownership_report | STRING | — |