Segs Select (NH)
Pick the second person from the left, not all of them
- segs
- segs
- mask
- count
SAM 3's party trick is that it finds every instance of what you name. That's also the problem. Ask for person on a group photo and you get five masks in one union - and then you want to relight only one of them, or inpaint only the biggest object in the frame, or drop three doubtful detections that came back at 0.31.
Segs Select is the filter between "what was found" and "what you actually want". It filters by label, score and size, sorts the survivors, and takes a slice.
The three steps, in order
Everything happens in a fixed sequence, and knowing it saves you from surprising results: filters first (labels, min_score, min_area, max_area), then sorting (sort_by), then the slice (start, count). So start 1, count 1 on a score-sorted list is the second-best instance, not the second one found.
labels takes a comma-separated list with * and ? wildcards - *shirt catches t-shirt and dress shirt. Case doesn't matter, singular and plural match each other (arm finds arms), _ counts as a space, and common Vietnamese words translate, so tóc finds hair. Empty keeps everything.
min_score and the two area fields are the quality gate. min_area and max_area are fractions of the image - 0.01 is one percent - and, importantly, area is the share of pixels the mask covers, not the size of its box. A thin diagonal object and a compact one with the same box are judged differently, and rightly so.
sort_by is where the node earns its keep. Eight options: score, area, left_to_right, right_to_left, top_to_bottom, bottom_to_top, center_first, as_found. The positional ones use box centres; center_first orders by distance from the centre of the image.
Recipes
| You want | Set |
|---|---|
| the second person from the left | labels person, sort_by left_to_right, start 1, count 1 |
| the biggest object of anything found | sort_by area, count 1 |
| the three surest detections | sort_by score, count 3 |
| nothing under 1% of the picture | min_area 0.01 |
| the instance nearest the middle | sort_by center_first, count 1 |
The biggest-object recipe with labels empty is genuinely useful: run SAM 3 with person, dog, car, sort by area, take one, and you've masked "the subject" without knowing in advance what the subject is.
Per image, always
Every image of a batch is handled separately, so start 1 skips the first instance of each image, not the first instance overall. On a 20-photo batch that's usually what you want and occasionally exactly what you don't - if you needed the 4th detection across the whole batch, this isn't the node.
Outputs are segs (the kept instances, still sorted), mask (their union, one per image) and count (how many survived, over the whole batch). The sorted order persists downstream, which is the quiet feature: put Segs To Masks after this and its per-instance masks come out in your sort order, so "person 1, person 2, person 3 left to right" is a real thing you can build.
Installing it
ComfyUI Manager, search NH-Nodes, install, restart. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/jetthuangai/NH-Nodes.git
cd NH-Nodes
python -m pip install -r requirements.txt
There's nothing to download for this node - it's arithmetic over instances you already produced. The pack's requirements (numpy, Pillow, scipy, opencv-python, scikit-image, transformers, tqdm, huggingface_hub) cover it, on a recent ComfyUI (tested on 0.41) and Python 3.10+.
Common issues
Labels don't match. Different nodes use different words, and the pack is upfront about it. Body Segment has lips, teeth, tongue; Portrait Segment has one mouth for the inside of the mouth. Segs Select also reads a node's group words the same way Segs Compose does, so skin on Body Segment's segs keeps its face, torso, arms, hands, legs and feet parts. When nothing matches, get the real labels from Segs Preview or Segs To Masks rather than guessing.
Everything disappeared. Check min_area first. SAM 3 on a 6000 px photo will happily return a 0.2% instance, and a leftover min_area from a previous experiment will eat it.
start/count behave oddly on batches. They're per image, as above. Also note both cap at 4096, which nobody hits.
You wanted one mask, not the instances. You can use the mask output directly and skip Segs To Masks entirely - it's the union of what survived, and it's much cheaper than materialising full-size masks per instance.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| segs | NH_SEGS | — | |
| labels | STRING | Labels to keep, separated by commas; * and ? are wildcards ("*shirt"). Common Vietnamese words work too (tóc, túi). Empty keeps every label. | |
| min_score | FLOAT | 0.000–1 | Drop instances with a lower confidence. |
| min_area | FLOAT | 0.00000–1 | Drop instances smaller than this fraction of the image (0.01 = 1%). |
| max_area | FLOAT | 1.00000–1 | Drop instances larger than this fraction of the image. |
| sort_by | COMBO | score | Order of the kept instances: highest score, largest area, by position (box centre), nearest the image centre first, or as found. |
| start | INT | 00–4096 | Skip this many instances of the sorted list (0 starts at the first). |
| count | INT | 00–4096 | Keep this many instances from start on; 0 keeps all of them. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| segs | NH_SEGS | — |
| mask | MASK | — |
| count | INT | — |