Thresholds and filtering
Four arguments decide which predictions survive: conf, iou, max_det and classes. Only two of them apply to every family, because a set predictor decodes a fixed query set and never runs NMS.
The four arguments
| Argument | Default | Applies to |
|---|---|---|
conf | 0.25 | Every family |
iou | 0.45 | Families that run non-maximum suppression |
max_det | 300 | Every family |
classes | None | Every family |
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt") result = model( SAMPLE_IMAGE, conf=0.25, # keep predictions at or above this score iou=0.45, # NMS overlap threshold, where NMS runs max_det=300, # cap per image classes=None, # or a list of class ids)print(len(result.boxes))from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt") for conf in (0.1, 0.25, 0.5, 0.75): result = model(SAMPLE_IMAGE, conf=conf) print(conf, len(result.boxes))libreyolo predict model=LibreYOLO9s.pt conf=0.4 iou=0.5 max_det=100 \ source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpgTwo of these are universal and two are not, which is the single most useful thing to know before tuning anything.
Validation uses different defaults on purpose: val() runs at conf=0.001 and
iou=0.6, because average precision is computed over a full precision-recall
curve and a 0.25 cutoff would truncate it.
conf
conf is the score below which a prediction is discarded. It applies to every
family, including the ones that never run NMS, and it is the first control to
reach for when there are too many or too few detections.
The default of 0.25 suits looking at pictures. Feeding a downstream system
usually wants it higher; measuring accuracy wants it far lower.
iou
iou is the overlap above which non-maximum suppression removes the
lower-scoring of two boxes of the same class. It only means something if the
family runs suppression at all.
A set predictor decodes a fixed number of queries and takes the top scoring
ones. Duplicates are suppressed inside the architecture during training, not by
a postprocessing step, so there is no threshold to turn. These families accept
iou for API parity and ignore it:
CenterNet, DEIM, DETR, Deformable DETR, D-FINE, DINO-DETR, EdgeCrafter, Faster R-CNN, LW-DETR, Mask R-CNN, RF-DETR, RT-DETR, and the end-to-end YOLOv9 head. Variants built on those decoders inherit the behavior.
from libreyolo import LibreYOLO, SAMPLE_IMAGE # RF-DETR decodes a fixed query set, so iou changes nothing here.model = LibreYOLO("LibreRFDETRs.pt") loose = model(SAMPLE_IMAGE, iou=0.9)tight = model(SAMPLE_IMAGE, iou=0.1) # Same count either way. conf and max_det are the controls that work.print(len(loose.boxes), len(tight.boxes))Most of them say so in their postprocessing docstrings, but no warning is
raised at runtime, so a sweep over iou on RF-DETR produces a flat line rather
than an error. Faster R-CNN and Mask R-CNN are a slightly different case: both
already ran NMS inside the model, at a fixed upstream threshold that iou has
no supported way to change.
These families do use it: YOLOv1 through YOLOv4, YOLOv7, YOLOv9, YOLOX, YOLO-NAS, RTMDet, PicoDet, EfficientDet, FCOS, RetinaNet and SSD.
Two prediction-time options make iou matter even for a set predictor, because
both merge boxes after the model has finished:
tiling=Truereconciles overlapping tiles with per-class NMS atiouaugment=Truemerges flipped views with per-class NMS atiou
Both are covered in Inference performance.
Open-vocabulary detectors have their own rule. A family whose processor runs
NMS declares its own default threshold and honors iou, which is the case for
OMDet-Turbo. Families that suppress nothing, Grounding DINO, OWLv2 and OV-DEIM,
emit a warning when iou is passed. That warning is the only one of its kind in
the library.
max_det
max_det caps how many predictions come back for one image. It applies
everywhere, but through different mechanisms: an NMS family truncates after
suppression, a set predictor uses it as the size of its top-k selection.
Some families clamp below whatever you ask for, because their upstream
reference configuration does. SSD caps at 200, RTMDet instance segmentation at
100, and FCOS at its own per-image detection limit. Raising max_det past
those has no effect.
The one place max_det is applied centrally rather than per family is tiled
inference, where the merged list is truncated after tiles are reconciled.
Class filtering
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt") # Class ids index model.names. On COCO, 0 is person.result = model(SAMPLE_IMAGE, classes=[0]) print({result.names[int(c)] for c in result.boxes.cls.tolist()})from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt")result = model(SAMPLE_IMAGE) wanted = {"person", "backpack"}ids = [i for i, name in result.names.items() if name in wanted]print(ids) filtered = model(SAMPLE_IMAGE, classes=ids)print(len(filtered.boxes))classes takes a list of class ids and keeps only predictions whose class is in
it. Ids index result.names, and the surest way to get one is to read names
off a result rather than assuming a dataset ordering.
Filtering happens centrally, after each family's postprocessing, in the single funnel every prediction path goes through. That has two consequences worth knowing. It works on every family, including the ones with no NMS. And it also filters the payloads aligned with the boxes, so masks, keypoints and oriented boxes are cut down alongside them rather than left mismatched.
On the command line, classes accepts a bare integer, a list, or a
comma-separated string:
libreyolo predict model=LibreYOLO9s.pt classes=0 source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpglibreyolo predict model=LibreYOLO9s.pt classes="[0,2,5]" source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpgFiltering is not free accuracy. A model still spends its budget predicting
classes you then discard, and max_det is applied by the family before the
filter, so an image crowded with unwanted classes can hit the cap before your
class is reached. Lower conf or raise max_det if that happens.
agnostic_nms
agnostic_nms is accepted and does nothing. Passing it raises a warning saying
it is a no-op for command line compatibility, and the argument is discarded.
There is no class-agnostic suppression mode. Every NMS call in the library is
class-aware, so two overlapping boxes of different classes both survive, at any
iou. Where that is a problem, filter with classes first, or suppress across
classes yourself on result.boxes.
What predict rejects
Two arguments raise instead of warning: visualize and embed both raise
NotImplementedError. For embeddings, load the model with task="embed" and
call predict or embed normally.
Anything unrecognized raises TypeError naming the supported options, so a
typo fails immediately rather than being silently ignored.
These are accepted, warned about and discarded: agnostic_nms, boxes, dnn,
half, line_width, retina_masks, show_conf, show_labels and verbose.