# YOLOv9
A single-stage convolutional detector: one pass scores a dense grid of boxes and NMS drops the duplicates. LibreYOLO carries three variants of it, one of which has no NMS step.
Tasks: Detection. Install: pip install libreyolo.
Verified against LibreYOLO v1.6.0.

## Install

YOLOv9 needs no extra beyond the base package.

```bash
pip install libreyolo
```

## Predict

Weights download from Hugging Face on first use and are cached locally.

**Python**

```python
from libreyolo import LibreYOLO, SAMPLE_IMAGE

model = LibreYOLO("LibreYOLO9s.pt")
result = model(SAMPLE_IMAGE, save=True)

for box in result.boxes:
    print(box.cls, box.conf, box.xyxy)
```

**CLI**

```bash
libreyolo predict model=LibreYOLO9s.pt save=True \
  source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpg
```

**Without NMS**

```python
from libreyolo import LibreYOLO, SAMPLE_IMAGE

# Same call, different checkpoint. The end-to-end head returns its own
# top-scoring predictions, so no NMS runs and iou is ignored.
model = LibreYOLO("LibreYOLO9E2Es.pt")
result = model(SAMPLE_IMAGE, conf=0.25, max_det=300)

print(len(result.boxes))
```

The returned `Results` object is the one every family returns, so swapping in a
different detector is a one line change. On the base and stride-4 models, `conf`
sets the confidence threshold and `iou` the NMS threshold. The end-to-end model
runs no NMS and ignores `iou`, so `conf` and `max_det` are what shape its
output. See [prediction](/docs/predict) for sources, streaming and result
handling.

## Variants

Three variants share a backbone. All three detect only, and they take the same
arguments.

The base model predicts on three feature scales and clears duplicate boxes with
NMS.

The end-to-end model keeps that head and adds a one-to-one matching branch
beside it. Inference reads the one-to-one branch alone and takes its
top-scoring predictions, so no NMS runs. Choose it when the runtime you deploy
to has no NMS operator.

The stride-4 model surfaces one level further up the backbone, extends the neck
down to it and predicts on four scales instead of three. The extra scale is for
objects that cover few pixels; the one published checkpoint for it is trained
on aerial imagery. Base detection checkpoints transfer into it: the backbone
and neck load unchanged, the three pretrained head towers shift up one slot,
and the stride-4 tower starts from random initialization.

| Checkpoint | Input (px) | mAP 50-95 | Params (M) |
| --- | --- | --- | --- |
| `LibreYOLO9c` | 640 | 56.4 | 25.5 |
| `LibreYOLO9m` | 640 | 55.3 | 20.12 |
| `LibreYOLO9s` | 640 | 55.9 | 7.2 |
| `LibreYOLO9t` | 640 | 41.8 | 2.04 |

COCO val2017, 500 images. Published on https://www.visionanalysis.org/

Interactive chart: https://www.visionanalysis.org/embed/scatter?highlight=yolov9c%2Cyolov9m%2Cyolov9s%2Cyolov9t&title=YOLOv9%20on%20COCO&subtitle=Accuracy%20against%20latency%2C%20every%20benchmarked%20model

## Train

**Python**

```python
from libreyolo import LibreYOLO

model = LibreYOLO("LibreYOLO9s.pt")
model.train(data="my-dataset.yaml", epochs=100, imgsz=640, batch=16)
```

**CLI**

```bash
libreyolo train model=LibreYOLO9s.pt data=my-dataset.yaml \
  epochs=100 imgsz=640 batch=16
```

**Small objects**

```python
from libreyolo import LibreYOLO9P2

# The stride-4 variant has no COCO checkpoint of its own, so name a
# base detection one: its backbone and neck load unchanged and the
# stride-4 head tower starts from random initialization.
model = LibreYOLO9P2(None, size="s")
model.train(data="my-dataset.yaml", epochs=100, pretrained="LibreYOLO9s.pt")
```

`pretrained` decides what the run starts from. Pass `True` to load the
published checkpoint for the same model and size, or a name or path for
anything else. Tensors whose shape does not match are skipped rather than
refused, and the run logs how many loaded, so a checkpoint trained on a
different class count is still a usable starting point.

The stride-4 model has no published COCO checkpoint of its own, so `True`
resolves there to a file that does not exist and the download fails. Name a
base detection checkpoint instead.

See [training](/docs/train) for datasets, augmentation, multi-GPU and loggers.

New stock detection fine-tunes enable a training-only PGI branch with `aux_weight=0.25`. `max_labels=300`; SGD momentum warms from 0.8 to 0.937 over three epochs. Old single-head checkpoints resume with that graph. Prediction and export use the main head. `letterbox_pad=None` inherits the checkpoint stamp: unmarked weights use `topleft`, while new official conversions record `center`.

YOLO9 and YOLOX mosaic prefer annotated partners with at most 20 draws; YOLO9 MixUp uses the same policy. See [event histograms](/docs/train/event-histograms) for non-RGB input profiles.

## Validate

`val()` returns a dictionary of `metrics/` keys covering precision, recall,
mAP 50 and mAP 50-95, measured against any dataset in the format you trained on.

**Python**

```python
from libreyolo import LibreYOLO

model = LibreYOLO("LibreYOLO9s.pt")
metrics = model.val(data="my-dataset.yaml")

print(metrics["metrics/mAP50-95"])
print(metrics["metrics/mAP50"])
```

**CLI**

```bash
libreyolo val model=LibreYOLO9s.pt data=my-dataset.yaml
```

**Against COCO**

```bash
# The bundled COCO yaml carries an embedded download script, so it
# needs explicit permission unless the dataset is already local.
libreyolo val model=LibreYOLO9c.pt data=coco.yaml imgsz=640 \
  allow_download_scripts=True
```

## Export

| Task | ONNX | TorchScript | ExecuTorch | TensorRT | OpenVINO | Paddle | MNN | RKNN | ncnn | TFLite | CoreML | Core AI |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Detection | yes | yes | yes | yes | yes | yes | yes |  | yes |  |  | yes |

A "yes" means the export is supported. An empty cell means the exporter refuses that combination.

A tick holds for all three variants: where they differ, the matrix carries the
weakest of the three.

An exported artifact loads back through `LibreYOLO()` on its file suffix, so a
`.onnx` or `.engine` file behaves like a checkpoint and returns the same
`Results`. Running the graph in a bare runtime, with no LibreYOLO installed, is
also supported, but then preprocessing and postprocessing are yours to write.

For the base detection model, the postprocessing half of that can move into the
graph. `nms=True` on an ONNX export puts suppression inside the model, and the
first output becomes a fixed `(1, max_det, 6)` tensor whose rows are
`x1, y1, x2, y2, score, class`, zero-padded past the detection count. That graph
is batch 1 and carries no dynamic axes. The end-to-end and stride-4 models do
not accept the flag.

Each format installs a different extra and takes a few arguments of its own.
Both are on that format's page.

**Python**

```python
from libreyolo import LibreYOLO

model = LibreYOLO("LibreYOLO9s.pt")
model.export(format="onnx", imgsz=640)
```

**CLI**

```bash
libreyolo export model=LibreYOLO9s.pt format=onnx imgsz=640
```

**With NMS in the graph**

```bash
libreyolo export model=LibreYOLO9s.pt format=onnx nms=True \
  conf=0.25 iou=0.45 max_det=300
```

**Use the exported file**

```python
from libreyolo import LibreYOLO, SAMPLE_IMAGE

# The factory routes on the file suffix, so an exported artifact loads
# like any checkpoint and returns the same Results object.
model = LibreYOLO("LibreYOLO9s.onnx")
result = model(SAMPLE_IMAGE)

print(result.boxes.xyxy)
```

[TFLite INT8](/docs/export/tflite) uses `int8=True` with calibration data.

## Checkpoints

Every published weight file for this family.

| File | Input (px) | Task | Weights license |
| --- | --- | --- | --- |
| `LibreYOLO9t.pt` | 640 | Detection | mit |
| `LibreYOLO9s.pt` | 640 | Detection | mit |
| `LibreYOLO9m.pt` | 640 | Detection | mit |
| `LibreYOLO9c.pt` | 640 | Detection | mit |
| `LibreYOLO9E2Et.pt` | 640 | Detection | mit |
| `LibreYOLO9E2Es.pt` | 640 | Detection | mit |
| `LibreYOLO9E2Em.pt` | 640 | Detection | mit |
| `LibreYOLO9E2Ec.pt` | 640 | Detection | mit |
| `LibreYOLO9P2s-visdrone.pt` |  | Detection | cc-by-nc-sa-3.0 |

## Licensing

Check the license on the Hugging Face repository of the specific weights you download. That repository is authoritative and licenses are not always uniform across a family. This is a description of the licenses involved, not legal advice.

- Original work: YOLOv9, MultimediaTechLab
- Upstream license: MIT
- Upstream source: https://github.com/MultimediaTechLab/YOLO
- LibreYOLO code: MIT
- Weights: MIT, republished at https://huggingface.co/LibreYOLO
- Interpretation: MIT is a permissive license, so these weights can be used in commercial and closed-source products. The one standing obligation is to keep the license text and the copyright notice, Kin-Yiu Wong and Hao-Tang Tsui, with any copy you redistribute. It places no condition on your own application code, and a model you train yourself on your own data is yours. Two things are worth knowing beyond that. The port follows the authors' MIT re-release of YOLOv9, not the GPL-3.0 repository that carries the same model, so the permissive terms come from the source LibreYOLO actually derives from. And one checkpoint in this family is not MIT: the stride-4 model trained on VisDrone2019-DET inherits that dataset's CC BY-NC-SA 3.0 terms, which rule out commercial use and require share-alike on anything derived from it.

The stride-4 aerial checkpoint predicts the VisDrone classes. Use the publisher-declared license recorded on its weight repository.

## Citation

```bibtex
@inproceedings{wang2024yolov9,
      title={{YOLOv9}: Learning What You Want to Learn Using Programmable Gradient Information},
      author={Wang, Chien-Yao and Yeh, I-Hau and Liao, Hong-Yuan Mark},
      year={2024},
      booktitle={Proceedings of the European Conference on Computer Vision (ECCV)},
}
```

Copied from https://github.com/MultimediaTechLab/YOLO#citations


## YOLOv9 architecture

- YOLO9-T: [SVG](/diagrams/models/yolov9/t.svg), [interactive diagram](/diagrams/models/yolov9/t.html)
- YOLO9-S: [SVG](/diagrams/models/yolov9/s.svg), [interactive diagram](/diagrams/models/yolov9/s.html)
- YOLO9-M: [SVG](/diagrams/models/yolov9/m.svg), [interactive diagram](/diagrams/models/yolov9/m.html)
- YOLO9-C: [SVG](/diagrams/models/yolov9/c.svg), [interactive diagram](/diagrams/models/yolov9/c.html)
- YOLO9 T/S family: [SVG](/diagrams/models/yolov9/family-ts.svg), [interactive diagram](/diagrams/models/yolov9/family-ts.html)
- YOLO9 E2E-T: [SVG](/diagrams/models/yolov9/e2e-t.svg), [interactive diagram](/diagrams/models/yolov9/e2e-t.html)
- YOLO9 E2E-S: [SVG](/diagrams/models/yolov9/e2e-s.svg), [interactive diagram](/diagrams/models/yolov9/e2e-s.html)
- YOLO9 E2E-M: [SVG](/diagrams/models/yolov9/e2e-m.svg), [interactive diagram](/diagrams/models/yolov9/e2e-m.html)
- YOLO9 E2E-C: [SVG](/diagrams/models/yolov9/e2e-c.svg), [interactive diagram](/diagrams/models/yolov9/e2e-c.html)
- YOLO9 E2E T/S family: [SVG](/diagrams/models/yolov9/e2e-family-ts.svg), [interactive diagram](/diagrams/models/yolov9/e2e-family-ts.html)
- YOLO9 P2-T: [SVG](/diagrams/models/yolov9/p2-t.svg), [interactive diagram](/diagrams/models/yolov9/p2-t.html)
- YOLO9 P2-S: [SVG](/diagrams/models/yolov9/p2-s.svg), [interactive diagram](/diagrams/models/yolov9/p2-s.html)
- YOLO9 P2 T/S family: [SVG](/diagrams/models/yolov9/p2-family-ts.svg), [interactive diagram](/diagrams/models/yolov9/p2-family-ts.html)
