> This is the documentation for LibreYOLO 1.6.0. Current version: https://www.libreyolo.com/docs/predict/thresholds

# 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.
Verified against LibreYOLO v1.6.0.

## 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 |

**The four arguments**

```python
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))
```

**Sweeping conf**

```python
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))
```

**CLI**

```bash
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.jpg
```

Two 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.

Detection validation exposes `metrics/best_conf` and per-class thresholds from F1 at IoU 0.50. Use them as measured starting points for deployment thresholds; see [validation](/docs/v1.6.0/train/validation). Segmentation does not expose these keys.

## 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.

**iou on a family that runs no NMS**

```python
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=True` reconciles overlapping tiles with per-class NMS at `iou`
- `augment=True` merges flipped views with per-class NMS at `iou`

Both are covered in [Inference performance](/docs/v1.6.0/predict/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

**Filtering to specific classes**

```python
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()})
```

**Finding the id for a name**

```python
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:

```bash
libreyolo predict model=LibreYOLO9s.pt classes=0 source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpg
libreyolo predict model=LibreYOLO9s.pt classes="[0,2,5]" source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpg
```

Filtering 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`.
