RF-DETR
A detection transformer that predicts a fixed set of objects instead of a dense grid, so it needs no NMS at inference. LibreYOLO supports it for four tasks.
- Tasks
- detection, instance segmentation, pose, oriented boxes
- Sizes
- n, s, m, l for detection and oriented boxes; n through xx for segmentation; x for pose
- Install
pip install "libreyolo[rfdetr]"- Support tier
- Flagship, since v1.0.0. Features are designed and fully GPU-validated here first.
- Licenses
- Code MIT, weights Apache-2.0. Commercial use
Install
RF-DETR needs its own extra, which pulls in transformers for the backbone.
pip install "libreyolo[rfdetr]"Without it, loading an RF-DETR checkpoint or model name, from Python or the CLI, stops with an error that names this install line.
Predict
Weights download from Hugging Face on first use and are cached locally.
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreRFDETRs.pt")result = model(SAMPLE_IMAGE, save=True) for box in result.boxes: print(box.cls, box.conf, box.xyxy)libreyolo predict model=LibreRFDETRs.pt source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpg save=Truefrom libreyolo import LibreYOLO model = LibreYOLO("LibreRFDETRs.pt") # Any source the library accepts: file, folder, URL, webcam index,# RTSP stream, or a .streams listfor result in model.predict("clip.mp4", stream=True, save=True): 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. conf and max_det filter the query
selection; there is no NMS step to tune. See
prediction for sources, streaming and result handling.
Detection, segmentation and oriented-box paths use floating-point OpenCV bilinear resizing without antialiasing; pose keeps its antialiased resize. Rectangular imgsz=(height, width) must satisfy the task patch/window grid. The checkpoint inventory includes the UI detector. Event histograms use the recorded input profile.
Variants
Four detection sizes, n to l, and four tasks that share one architecture:
segmentation, pose and oriented boxes reuse the detection decoder with a
different head, so they take the same arguments. Segmentation adds x and
xx, and pose comes only in x. The sizes carry similar parameter counts and differ mainly
in input resolution.
| Checkpoint | Input (px) | mAP 50-95 | Params (M) |
|---|---|---|---|
| LibreRFDETRn | 384 | 51.4 | 30.47 |
| LibreRFDETRs | 512 | 55.2 | 32.11 |
| LibreRFDETRm | 576 | 57.4 | 33.69 |
| LibreRFDETRl | 704 | 58.6 | 33.93 |
COCO val2017, 500 images. Measured by the LibreYOLO benchmark harness and published on Vision Analysis, where latency across hardware and runtimes is compared and the full run records live.
Train
Training starts from a published checkpoint, for all four tasks.
pretrained=False instead reinitializes the whole network, backbone included,
and trains from scratch.
from libreyolo import LibreYOLO model = LibreYOLO("LibreRFDETRs.pt")model.train(data="my-dataset.yaml", epochs=50, imgsz=512, batch=8, lr0=1e-4)libreyolo train model=LibreRFDETRs.pt data=my-dataset.yaml \ epochs=50 imgsz=512 batch=8 lr0=1e-4from libreyolo import LibreYOLO model = LibreYOLO("LibreRFDETRs.pt")model.train(data="my-dataset.yaml", epochs=50, lora=True)libreyolo train model=LibreRFDETRs.pt data=my-dataset.yaml \ epochs=50 device=0,1 batch=-1Two arguments matter more here than on a CNN detector. Keep lr0 at or below
1e-4, since transformer detectors diverge at learning rates a YOLO model
tolerates. Leave imgsz at the checkpoint's native resolution unless you have a
reason to change it. The input must divide evenly by the backbone patch size
times the window count; LibreYOLO checks this before the run starts and names
the nearest valid sizes.
Segment fine-tuning starts from a segment checkpoint, because RF-DETR has no
detect-to-segment transfer. libreyolo train model=LibreRFDETRn-seg.pt task=segment
loads that checkpoint, and a detection checkpoint with task=segment stops
with an error.
See training for datasets, augmentation, multi-GPU and loggers.
Fresh runs default to output_dir=None, resolving to an incremented runs/train/rfdetr_exp with exist_ok=False. A resumed run keeps writing into its checkpoint's run directory. Multi-class pose datasets use kpt_names keyed by class index or name; an empty list marks a box-only class. Predictions pad keypoints to kpt_shape; keypoint-mAP fitness does not score box-only classes.
Validate
val() returns a dictionary of metrics/ keys covering mAP 50, mAP 50-95,
mAP 75 and COCO average recall, measured against any dataset in the format you trained on.
from libreyolo import LibreYOLO model = LibreYOLO("LibreRFDETRs.pt") # val() returns a plain dict, not an objectmetrics = model.val(data="my-dataset.yaml", imgsz=512) print(metrics["metrics/mAP50-95"])print(metrics["metrics/mAP50"])print(metrics["metrics/mAP75"], metrics["metrics/AR100"])libreyolo val model=LibreRFDETRs.pt data=my-dataset.yaml imgsz=512# The bundled COCO yaml carries an embedded download script, so it# needs explicit permission unless the dataset is already local.libreyolo val model=LibreRFDETRn.pt data=coco.yaml imgsz=384 \ allow_download_scripts=TrueExport
| Task | ONNX | TorchScript | ExecuTorch | TensorRT | OpenVINO | Paddle | MNN | RKNN | ncnn | TFLite | CoreML | Core AI |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Detection | Detection to ONNX: supported | Detection to TorchScript: supported | Detection to ExecuTorch: supported | Detection to TensorRT: supported. Runtime parity coverage lives in tests/e2e/test_tensorrt.py. | Detection to OpenVINO: supported. Runtime parity coverage lives in tests/e2e/test_openvino.py. | Detection to Paddle: not supported | Detection to MNN: supported | Detection to RKNN: not supported | Detection to ncnn: not supported | Detection to TFLite: not supported | Detection to CoreML: supported. Conversion is available, but runtime parity requires a macOS runner. | Detection to Core AI: supported |
| Instance segmentation | Instance segmentation to ONNX: supported | Instance segmentation to TorchScript: supported | Instance segmentation to ExecuTorch: supported | Instance segmentation to TensorRT: supported. A published Apache-2.0 trained segmentation checkpoint exports and reloads, but public top-k class membership changes. | Instance segmentation to OpenVINO: supported. After Hungarian query alignment, the converted-runtime element match rate is 69.0%, below the validation bar. | Instance segmentation to Paddle: not supported | Instance segmentation to MNN: not supported | Instance segmentation to RKNN: not supported | Instance segmentation to ncnn: not supported | Instance segmentation to TFLite: not supported | Instance segmentation to CoreML: not supported | Instance segmentation to Core AI: not supported |
| Pose | Pose to ONNX: supported | Pose to TorchScript: supported | Pose to ExecuTorch: supported | Pose to TensorRT: supported. A published Apache-2.0 trained pose checkpoint exports and reloads, but matched public boxes fall to 0.704 IoU with 41.4-pixel coordinate drift. | Pose to OpenVINO: supported. After Hungarian query alignment, the converted-runtime element match rate is 72.75%, below the validation bar. | Pose to Paddle: not supported | Pose to MNN: not supported | Pose to RKNN: not supported | Pose to ncnn: not supported | Pose to TFLite: not supported | Pose to CoreML: not supported | Pose to Core AI: not supported |
| Oriented boxes | Oriented boxes to ONNX: supported | Oriented boxes to TorchScript: supported | Oriented boxes to ExecuTorch: supported | Oriented boxes to TensorRT: supported. A deterministic synthetic OBB fixture exports and reloads, but public top-k class membership changes. | Oriented boxes to OpenVINO: supported. After Hungarian query alignment, the converted-runtime element match rate is 91.25%, below the validation bar. | Oriented boxes to Paddle: not supported | Oriented boxes to MNN: not supported | Oriented boxes to RKNN: not supported | Oriented boxes to ncnn: not supported | Oriented boxes to TFLite: not supported | Oriented boxes to CoreML: not supported | Oriented boxes to Core AI: not supported |
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.
from libreyolo import LibreYOLO model = LibreYOLO("LibreRFDETRs.pt")model.export(format="onnx", imgsz=512)model.export(format="tensorrt", imgsz=512, half=True) # Arguments accepted for every format:## format "onnx" | "torchscript" | "executorch" | "tensorrt"# | "openvino" | "paddle" | "mnn" | "rknn" | "ncnn"# | "tflite" | "coreml" | "coreai".# "engine" is an alias for tensorrt, "litert" for tflite.# imgsz int, or (height, width). Defaults to the checkpoint's# native resolution.# batch int, default 1.# half bool, export in FP16. Default False.# int8 bool, export in INT8. Default False. Needs `data`.# data path to a dataset YAML, used to calibrate int8.# fraction float, share of that calibration set to use. Default 1.0.# dynamic bool, dynamic axes. Default True.# simplify bool, run ONNX graph simplification. Default True.# opset int, ONNX opset. Chosen per family when not given.# device str, device to trace on. Defaults to the model's device.# output_path str, defaults to a name derived from the checkpoint.# verbose bool, default False.# allow_download_scripts bool, default False. Permits embedded# Python in a dataset YAML that has to be downloaded.## A few formats take extra arguments of their own, such as an RKNN# target platform. Those are documented on each format's page.libreyolo export model=LibreRFDETRs.pt format=onnx imgsz=512libreyolo export model=LibreRFDETRs.pt format=tensorrt imgsz=512 half=Truefrom 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("LibreRFDETRs.onnx")result = model(SAMPLE_IMAGE) print(result.boxes.xyxy)import numpy as npimport onnxruntime as ort # Running the graph directly means doing your own preprocessing and# postprocessing. Inspect the signature before wiring anything up.session = ort.InferenceSession("weights/LibreRFDETRs.onnx")name = session.get_inputs()[0].nameoutputs = session.run(None, {name: np.zeros((1, 3, 512, 512), dtype=np.float32)}) for meta, array in zip(session.get_outputs(), outputs): print(meta.name, array.shape)ONNX inputs and outputs
| Tensor | input float32 [batch, 3, height, width] |
|---|---|
| Color | RGB |
| Scaling | pixel / 255 |
| Normalization | mean [0.485,0.456,0.406], std [0.229,0.224,0.225] |
| Resize | custom, bilinear |
The input tensor is named input, not images.
Square input only. Export rejects a non-square imgsz. The default size comes from the checkpoint and is stored in the imgsz metadata key: 384 for LibreRFDETRn, 312 for LibreRFDETRn-seg, 576 for LibreRFDETRx-pose.
A custom imgsz must be a multiple of patch size x windows: 32 for detection and OBB, 12 for segmentation n, 24 for the other segmentation sizes and for pose.
Stretch the whole image to width x height. There is no letterbox and no padding, so the aspect ratio is not kept.
Detection, segmentation and OBB: convert the 8-bit RGB image to float32 and divide by 255 first, then resize the float image with cv2.resize INTER_LINEAR (half-pixel centers, no antialiasing). Resizing the 8-bit image before dividing is close but moves scores by up to about 0.01.
Pose resizes differently: divide by 255 the same way, then resize the float image with antialiased bilinear, as torch.nn.functional.interpolate does with mode bilinear, align_corners False and antialias True. This is the filter of Pillow's BILINEAR, which widens by the shrink factor, but in float with no rounding to 8 bits. Per axis, let s = in_size / out_size and f = max(s, 1). Output pixel i has center c = (i + 0.5) * s. It reads source pixels j from int(c - f + 0.5) to int(c + f + 0.5) - 1, clipped to the image, each with weight max(0, 1 - |j + 0.5 - c| / f). Divide the weights by their sum.
The pose model is sensitive to this. On the test image, cv2.resize INTER_LINEAR or INTER_AREA adds 3 to 9 detections, and Pillow on the 8-bit image adds one and moves keypoints by more than 4 px.
Then subtract the mean and divide by the std per channel.
nms=True is not available for this family: export raises NotImplementedError.
Detection, default export
| Output | Shape | Meaning |
|---|---|---|
dets | [batch, queries, 4] | Boxes as cx, cy, w, h, normalized 0 to 1 of the input. 300 queries. |
labels | [batch, queries, columns] | Raw class logits per query, sigmoid not applied. The COCO checkpoints have 91 columns, where column k is COCO category id k. For other checkpoints column j is class j. |
Postprocessing
- Apply sigmoid to labels.
- Flatten the scores of one image to queries x columns and take the top k over all of them, with k equal to queries (300). For flat index i, the query is i // columns and the column is i % columns. One query can yield several classes.
- Keep the entries whose score is strictly above the confidence threshold (0.25 by default). There is no NMS.
- When labels has 91 columns and nc is 80 (the COCO checkpoints), the column is a COCO category id. Class i is the i-th id, counting from 0, in this list: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 67, 70, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 84, 85, 86, 87, 88, 89, 90. Drop entries whose id is not in the list. With any other column count the column is the class.
- Convert the query's box to x1 = cx - w / 2, y1 = cy - h / 2, x2 = cx + w / 2, y2 = cy + h / 2.
- Map boxes back to the original image: multiply x by the original width and y by the original height. The input was stretched, so no pad offset applies. LibreYOLO does not clip these boxes.
Instance segmentation, default export
| Output | Shape | Meaning |
|---|---|---|
dets | [batch, queries, 4] | Boxes as cx, cy, w, h, normalized 0 to 1 of the input. 100 queries for sizes n and s, 200 for m and l, 300 for x and xx. |
labels | [batch, queries, columns] | Raw class logits per query, sigmoid not applied. 91 columns for the COCO checkpoints, where column k is COCO category id k. |
masks | [batch, queries, mask_h, mask_w] | Raw mask logits per query over the whole input, at a quarter of the input size: 78 x 78 at 312 x 312. |
Postprocessing
- Select, threshold, map classes and decode boxes exactly as for detection, with k equal to queries.
- Take the mask of each kept query and resize the logits straight to the original image size with bilinear, half-pixel centers and no antialiasing (cv2.resize INTER_LINEAR on the float logits gives the same).
- A pixel is foreground when its resized logit is above 0.
Pose, default export
| Output | Shape | Meaning |
|---|---|---|
dets | [batch, queries, 4] | Boxes as cx, cy, w, h, normalized 0 to 1 of the input. 100 queries. |
labels | [batch, queries, columns] | Raw class logits per query, sigmoid not applied, with the keypoint class term already added inside the graph. Column 0 is an empty slot and column j + 1 is class j, so the person checkpoint has 2 columns. |
keypoints | [batch, queries, columns * num_keypoints, 8] | One block of num_keypoints rows per label column, 2 x 17 = 34 rows here. Each row is x, y normalized 0 to 1 of the input, findable logit, visible logit, log_l11, l21, log_l22 and a class logit term. The num_keypoints_per_class metadata key ([0, 17]) says how many rows of each block are real keypoints. |
Postprocessing
- Apply sigmoid to labels. Flatten to queries x columns and take the top k over all of them, with k equal to queries (100). For flat index i, the query is i // columns and the column is i % columns.
- For each entry take its column's keypoint block: rows column * num_keypoints onward, of which the first num_keypoints_per_class[column] are used.
- Rescore each entry whose column has keypoints. Per used keypoint, with a = log_l11, b = l21, d = log_l22 and p = sigmoid(findable logit): sigma = exp(-2a) + exp(-2d) + b^2 * exp(-2a - 2d). Then trace = log(sum of p * sigma / sum of p) over the used keypoints, and score = score * exp(-0.2 * trace). Scores can go above 1.
- Drop entries whose column is 0. The class is column - 1.
- Keep the entries whose new score is strictly above the confidence threshold (0.25 by default). There is no NMS.
- Decode and map the box as for detection. LibreYOLO does not clip these boxes.
- Keypoint x is x * original width and keypoint y is y * original height. Keypoint confidence is sigmoid(findable logit). LibreYOLO does not use the visible logit.
Oriented boxes, default export
| Output | Shape | Meaning |
|---|---|---|
dets | [batch, queries, 4] | Rotated boxes as cx, cy, w, h, normalized 0 to 1 of the input. 300 queries. |
labels | [batch, queries, columns] | Raw class logits per query, sigmoid not applied. Column j is class j. The 6-class checkpoint has 7 columns: the last one is not a class and LibreYOLO does not drop it. |
angles | [batch, queries, 1] | Rotation in radians from -pi / 2 to pi / 2, tanh already applied, measured on the square input. The width side points along (cos(angle), sin(angle)) with y pointing down. |
Postprocessing
- Select and threshold as for detection, with k equal to queries (300). The column is the class.
- The stretch scales x and y by different amounts, so map each box back through its corners. In normalized input units, with c = cos(angle) and s = sin(angle), the corners are (cx, cy) - u - v, + u - v, + u + v and - u + v, with u = (c * w / 2, s * w / 2) and v = (-s * h / 2, c * h / 2).
- Multiply corner x by the original width and corner y by the original height.
- Fit cv2.minAreaRect to the 4 corners and convert its angle to radians. Raise w and h to at least 1e-4. If h > w, swap them and add pi / 2 to the angle. Then wrap the angle to [-pi / 2, pi / 2) with angle = (angle + pi / 2) mod pi - pi / 2.
- The result is cx, cy, w, h, angle in original-image pixels, with w the long side.
To check your own implementation, run the same file through LibreYOLO: LibreYOLO("model.onnx").predict("image.jpg") returns the reference results. Verified with LibreYOLO 1.7.0.
Checkpoints
Every published weight file for this family.
| File | Input (px) | Weights license |
|---|---|---|
| Detection | ||
| LibreRFDETRl.pt | 704 | apache-2.0 |
| LibreRFDETRm.pt | 576 | apache-2.0 |
| LibreRFDETRn.pt | 384 | apache-2.0 |
| LibreRFDETRs.pt | 512 | apache-2.0 |
| LibreRFDETRm-ui.pt | mit | |
| Instance segmentation | ||
| LibreRFDETRn-seg.pt | 312 | apache-2.0 |
| LibreRFDETRs-seg.pt | 384 | apache-2.0 |
| LibreRFDETRm-seg.pt | 432 | apache-2.0 |
| LibreRFDETRl-seg.pt | 504 | apache-2.0 |
| LibreRFDETRx-seg.pt | 624 | apache-2.0 |
| LibreRFDETRxx-seg.pt | 768 | apache-2.0 |
| Pose | ||
| LibreRFDETRx-pose.pt | 576 | apache-2.0 |
| Oriented boxes | ||
| LibreRFDETRn-obb.pt | 384 | cc-by-4.0 |
| LibreRFDETRs-obb.pt | 512 | cc-by-4.0 |
| LibreRFDETRm-obb.pt | 576 | cc-by-4.0 |
| LibreRFDETRl-obb.pt | 704 | cc-by-4.0 |
Every file above exists in the LibreYOLO org today and downloads on first use.
Licensing
Check the license on the Hugging Face repository of the specific weights you download. Every checkpoint in the LibreYOLO org carries one, and they are not always the same across a family. That repository is the authoritative source; the summary below describes what applied when this page was last verified.
This is a description of the licenses involved, not legal advice. If the answer matters commercially, read the licenses yourself and take your own counsel.
- Original work
- RF-DETR, Roboflow
- Upstream license
- Apache-2.0
- Upstream source
- github.com/roboflow/rf-detr
- LibreYOLO code
- MIT
- Weights
- Apache-2.0, republished at huggingface.co/LibreYOLO
- Interpretation
- Apache-2.0 is a permissive license, so these weights can be used in commercial and closed-source products. It asks you to keep its license text and attribution notices with any copy of the weights you redistribute, and it grants a patent license. It places no obligation on your own application code, and weights you train yourself on your own data are yours.
Citation
@inproceedings{robinson2026rfdetr,
title = {RF-DETR: Real-Time Detection Transformer},
author = {Robinson, Isaac and Robicheaux, Peter and Popov, Matvei and Ramanan, Deva and Peri, Neehar},
booktitle = {International Conference on Learning Representations (ICLR)},
year = {2026},
url = {https://arxiv.org/abs/2511.09554}
}Copied from the authors' citation block at github.com/roboflow/rf-detr#citation.