DOGMA Protected Masks v23 — Roadway People Split
Splitting road people from pavement people
- mask_1
- mask_2
- mask_3
- mask_4
- mask_5
- mask_6
- mask_1
- mask_2
- mask_3
- mask_4
- mask_5
- mask_6
- summary
v13 and v21 both subtract broad surfaces from the objects inside them. v23 does something different, and arguably smarter: it uses the masks' spatial relationship to split one category into two.
The problem is pedestrians. A person walking down the middle of the road and a person waiting on the pavement are, to a promptable segmenter, the same object. Ask it for person twice with different thresholds and you get two near-identical masks, which is why the pack's fixed plans carry both a roadway people slot and a people slot. DOGMAProtectedMasksV23 is where those two slots actually diverge.
How it works
Six mask_n inputs and six category_n strings, plus protect_radius (default 24, range 0–128). Every mask is collapsed and normalised to a common resolution, then classified into a family.
The logic, in order:
- Road is support only. The road mask comes out unchanged and is never generated through. Its job is to be a spatial reference, not a paint region.
- Roadway people = people ∩ dilated road. Take the roadway-people mask as the segmenter returned it, dilate the road mask by
protect_radius, and intersect. Whatever survives is a person standing on road. - Ordinary people lose the roadway people. The intersection is dilated again by a smaller radius (roughly
protect_radius / 3) and subtracted from the regularpeoplemask. Thepeopleoutput ismask × (1 − road_person_block), so the pavement pass hands those pixels away and the roadway pass owns them. No overlap, no double-repair. - Everything else - vehicles, street furniture, signage, architecture - is passed through as a union.
Outputs are the same six masks plus summary, newline-separated, with one line per slot: slot 3: roadway people = PERSON ∩ dilated ROAD support, slot 4: ordinary people exclude roadway-person region, slot 1: road union unchanged. Read that when a person mask looks thinner than the segmenter's output - it's supposed to.
Why the split is worth having
Two overlapping person masks means two local passes on the same body, each with its own prompt and its own repair, and the second one wins wherever the masks overlap. On a crowd that's the fastest route to a smeared double-exposure in the middle of the street. Partitioning the pixels by "is this person on asphalt" is a crude classifier - it's a dilated intersection, not a semantic one - but it's cheap, deterministic, and it fails gracefully: a person half on the curb just gets attributed to the roadway set.
Install
ComfyUI Manager → search DOGMA Nodes, or:
cd ComfyUI/custom_nodes
git clone https://github.com/axior/ComfyUI-DOGMA-Nodes
# restart ComfyUI
No dependencies - max-pools and multiplies. Undocumented in the pack README, like every node in the semantic family.
Gotchas
Your category strings must actually say the thing. The family classifier is text matching. "roadway people", "pedestrian in road", "person in road" and "people in road" map to the roadway family; plain "people" and "pedestrians" map to the ordinary one. Get both slots labelled "people" and steps 2 and 3 never fire - you'll see union unchanged for both in summary.
A loose road mask makes a bad split. If the road mask spills onto the sidewalk, half your pedestrians get classified as road users. protect_radius is the dial: raise it and more people count as "on road", lower it and the split gets tighter. 24 is a reasonable start for street scenes; 60+ starts claiming everyone standing near a curb.
This version doesn't protect surfaces from objects. Unlike v13/v21, road, vegetation and sky are not stripped of the cars and people inside them - v23 only partitions people. If you were relying on the surface subtraction, you want v13 or v21.
Inputs (13)
| 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 | — | |
| mask_6 | MASK | — | |
| category_6 | STRING | — | |
| protect_radius | INT | 240–128 | — |
Outputs (7)
| Name | Type | Description |
|---|---|---|
| mask_1 | MASK | — |
| mask_2 | MASK | — |
| mask_3 | MASK | — |
| mask_4 | MASK | — |
| mask_5 | MASK | — |
| mask_6 | MASK | — |
| summary | STRING | — |