Skip to content

Vishnu-RM-2001/deepstream-obb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

12 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Ultralytics YOLO-OBB on NVIDIA DeepStream 9.0

License: MIT DeepStream YOLO OBB

Run Ultralytics YOLO Oriented Bounding Box (OBB) detection β€” YOLOv8-OBB and YOLO11-OBB β€” on NVIDIA DeepStream 9.0, end to end. DeepStream 9.0 already ships the OBB parser, and nvdsosd draws the rotated boxes for you. You bring an ONNX model + a config, and you get rotated detection boxes on video.

dota

Clean multi-class detection on real DOTA imagery (tennis courts, swimming pool, vehicles shown). Each box is oriented (rotated to the object's heading), not axis-aligned. The demo runs on 5 real DOTA tiles spanning ships, planes, vehicles, courts, harbors, and pool.


How it works

Pipeline

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           Input (file / RTSP)           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   β”‚
                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              nvstreammux                β”‚
β”‚        Batches decoded frames           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   β”‚
                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                nvinfer                  β”‚
β”‚   YOLO-OBB ONNX β†’ TensorRT (FP16)       β”‚
β”‚                                         β”‚
β”‚  NvDsInferParseCustomYoloV11OBB:        β”‚
β”‚   β€’ Decodes output tensor [1, 20, N]    β”‚
β”‚   β€’ Applies confidence filter + NMS     β”‚
β”‚   β€’ Sets bbox + rotation_angle (rad)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   β”‚
                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               nvtiler                   β”‚
β”‚   Tiles streams into one canvas         β”‚
β”‚   (single stream in this config)        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   β”‚
                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               nvdsosd                   β”‚
β”‚  Draws oriented (rotated) boxes+labels  β”‚
β”‚  rotation_angle β†’ NvOSD_RectParams      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                   β”‚
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”
          β–Ό                 β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  [sink0] File    β”‚  β”‚  [sink1] Display β”‚
β”‚  H.264 MP4/NVENC β”‚  β”‚  Live X11 window β”‚
β”‚  out_obb.mp4     β”‚  β”‚  (optional)      β”‚
β”‚  (default)       β”‚  β”‚                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

OBB CustomParser and Rendering

DeepStream 9.0 ships two things that make this entirely config-driven:

  1. Prebuilt OBB parser β€” NvDsInferParseCustomYoloV11OBB is compiled into libnvds_infercustomparser.so inside the container. Despite the name, this parser handles both YOLOv8-OBB and YOLO11-OBB output format. The pgie config points at it with parse-bbox-func-name and custom-lib-path; no source code or build step required.

  2. Automatic OBB rendering β€” gstnvinfer copies the parser’s per-object rotation_angle (radians) into NvOSD_RectParams.rotation_angle (degrees), and nvdsosd renders the rotated rectangle natively. No application code is needed.


Repository layout

Deepstream-OBB/
β”œβ”€β”€ README.md
β”œβ”€β”€ configs/
β”‚   β”œβ”€β”€ config_deepstream_app.txt      # the deepstream-app pipeline
β”‚   β”œβ”€β”€ config_pgie_yolo_obb.txt       # the nvinfer (primary GIE) config
β”‚   └── labels_dota.txt                # 15 DOTA class names
β”œβ”€β”€ assets/                 # sample annotated frames (committed, for the README)
β”œβ”€β”€ data/                   # input videos (tracked; update [source0] uri to point at your file)
β”œβ”€β”€ models/                 # ONNX + TensorRT engine (gitignored; generated on first run)
└── output/                 # annotated mp4 results (gitignored)

Prerequisites

DeepStream 9.0 requirements (source)

  • Ubuntu 24.04
  • NVIDIA driver 590.48.01
  • CUDA 13.1
  • TensorRT 10.14.1.48
  • GStreamer 1.24.2

This project uses the Docker image (nvcr.io/nvidia/deepstream:9.0-samples-multiarch) which bundles all of the above β€” only the host driver needs to meet the minimum version.

Host requirements

  • NVIDIA GPU
  • NVIDIA driver β‰₯ 590.48.01
  • Docker + NVIDIA Container Toolkit (--gpus all must work):
    docker run --rm --gpus all nvcr.io/nvidia/deepstream:9.0-samples-multiarch nvidia-smi
  • For model export on the host: python3 with pip install ultralytics.

What is NOT in this repo (gitignored β€” generate locally per the steps below):

  • models/ β€” ONNX and TensorRT engine files
  • output/ β€” pipeline results

Quick start

From the repo root:

# 1) (once) pull the DeepStream image
docker pull nvcr.io/nvidia/deepstream:9.0-samples-multiarch

# 2) export a YOLO-OBB model to ONNX  -> models/<model>.onnx
pip install ultralytics
mkdir -p models && cd models
# YOLOv8 (pick one):
python3 -c "from ultralytics import YOLO; YOLO('yolov8n-obb.pt').export(format='onnx', imgsz=1024, opset=20)"
# or YOLO11:
# python3 -c "from ultralytics import YOLO; YOLO('yolo11n-obb.pt').export(format='onnx', imgsz=1024, opset=20)"
cd ..
mkdir -p output
# Then update onnx-file and model-engine-file in configs/config_pgie_yolo_obb.txt

# 3) run the pipeline  -> output/out_obb.mp4
#    (first run builds the TensorRT engine: a few minutes; cached afterwards)
docker run --rm --gpus all \
  -v "$PWD":/workspace -w /workspace/configs \
  -e LD_LIBRARY_PATH=/opt/nvidia/deepstream/deepstream/lib \
  nvcr.io/nvidia/deepstream:9.0-samples-multiarch \
  deepstream-app -c config_deepstream_app.txt

Open output/out_obb.mp4 β€” a slideshow of 5 real DOTA tiles with clean oriented boxes per scene (ships + harbors, planes, tennis & basketball courts, small/large vehicles, swimming pool).

To run on your own footage, drop a file in data/ and point configs/config_deepstream_app.txt β†’ [source0] uri at it.


Step by step

1. Pull the DeepStream image (once)

docker pull nvcr.io/nvidia/deepstream:9.0-samples-multiarch

Verify GPU access works:

docker run --rm --gpus all nvcr.io/nvidia/deepstream:9.0-samples-multiarch nvidia-smi

2. Export the model

Export a YOLO-OBB model to ONNX on the host. Both YOLOv8-OBB and YOLO11-OBB are supported:

pip install ultralytics
mkdir -p models && cd models

# Option A β€” YOLOv8-OBB (nano; replace with yolov8s/m/l/x-obb for larger variants)
python3 -c "from ultralytics import YOLO; YOLO('yolov8n-obb.pt').export(format='onnx', imgsz=1024, opset=20)"

# Option B β€” YOLO11-OBB (nano; replace with yolo11s/m/l/x-obb for larger variants)
# python3 -c "from ultralytics import YOLO; YOLO('yolo11n-obb.pt').export(format='onnx', imgsz=1024, opset=20)"

cd ..
mkdir -p output

Then update onnx-file and model-engine-file in configs/config_pgie_yolo_obb.txt to match the exported filename.

3. Run

Place your input video in data/ and update [source0] uri in configs/config_deepstream_app.txt to point at it (e.g. file:///workspace/data/your_video.mp4). Also set [streammux] width/height to your video's resolution, then run from the repo root:

docker run --rm --gpus all \
  -v "$PWD":/workspace -w /workspace/configs \
  -e LD_LIBRARY_PATH=/opt/nvidia/deepstream/deepstream/lib \
  nvcr.io/nvidia/deepstream:9.0-samples-multiarch \
  deepstream-app -c config_deepstream_app.txt

The TensorRT engine is written into models/ (mounted), so it is reused on subsequent runs. Output is saved to output/out_obb.mp4.


Configuration reference

Key settings in configs/config_pgie_yolo_obb.txt:

Property Value Why
net-scale-factor 0.0039215697 1/255 β€” YOLO expects 0–1 RGB input
model-color-format 0 RGB
num-detected-classes 15 DOTAv1 classes
network-mode 2 FP16 (use 0 for FP32)
maintain-aspect-ratio 0 frame is stretched to fill the network input
cluster-mode 4 No clustering β€” the parser does its own NMS and sets the angle; DeepStream clustering would drop it
parse-bbox-func-name NvDsInferParseCustomYoloV11OBB the prebuilt OBB parser (works for both YOLOv8-OBB and YOLO11-OBB)
custom-lib-path …/libnvds_infercustomparser.so where that parser lives in the image
[class-attrs-all] pre-cluster-threshold 0.25 global confidence threshold (see note below)

Pipeline / I/O is in configs/config_deepstream_app.txt β€” source, streammux resolution, OSD, and the file/EGL sink.


Use your own input video

Edit configs/config_deepstream_app.txt:

[source0]
uri=file:///workspace/data/your_video.mp4     # put the file in ./data
# or an RTSP camera:
# type=4
# uri=rtsp://user:pass@host:554/stream

[streammux]
width=1920      # set to your video's resolution for best box alignment
height=1080

Put the file in ./data/ (mounted to /workspace/data), then run the docker run command above.

The model is DOTA = aerial / top-down. It detects well on nadir scenes (marinas, ports, parking lots, airports from above) and finds little on street-level or strongly oblique footage. That is the domain, not a bug.

Switching models

The same config and parser work for any Ultralytics YOLO-OBB model. To switch:

  1. Export the model to ONNX:

    cd models
    # YOLOv8 variants:
    python3 -c "from ultralytics import YOLO; YOLO('yolov8s-obb.pt').export(format='onnx', imgsz=1024, opset=20)"
    # YOLO11 variants:
    # python3 -c "from ultralytics import YOLO; YOLO('yolo11s-obb.pt').export(format='onnx', imgsz=1024, opset=20)"
    # Available sizes: n / s / m / l / x
  2. Update configs/config_pgie_yolo_obb.txt:

    onnx-file=/workspace/models/yolov8s-obb.onnx
    model-engine-file=/workspace/models/yolov8s-obb.onnx_b1_gpu0_fp16.engine
    num-detected-classes=15   # adjust if using a non-DOTA model
  3. If using custom classes, replace configs/labels_dota.txt with your own labels (one per line) and set num-detected-classes accordingly. The parser is generic: num_classes = channels βˆ’ 5.


Interactive shell in the container (for debugging)

To poke around inside the container (run gst-inspect-1.0, deepstream-app --help, etc.):

docker run --gpus all -it --rm \
  -v "$PWD":/workspace -w /workspace \
  -e LD_LIBRARY_PATH=/opt/nvidia/deepstream/deepstream/lib \
  nvcr.io/nvidia/deepstream:9.0-samples-multiarch bash

Live display instead of a file (optional)

By default [sink0] (file sink) is active and [sink1] (display) is disabled. To enable live display:

  1. Open configs/config_deepstream_app.txt and set enable=1 under [sink1] (and optionally enable=0 under [sink0] to suppress the file output).
  2. Pass the X server and DRI device into the container:
xhost +local:root
docker run --rm --gpus all \
  -e DISPLAY=$DISPLAY \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
  --device /dev/dri \
  -v "$PWD":/workspace -w /workspace/configs \
  -e LD_LIBRARY_PATH=/opt/nvidia/deepstream/deepstream/lib \
  nvcr.io/nvidia/deepstream:9.0-samples-multiarch \
  deepstream-app -c config_deepstream_app.txt

License

The files in this repository (configs, documentation) are released under the MIT License β€” see LICENSE.

This project depends on third-party components with separate licenses β€” see NOTICE for full details:

Component License Notes
NVIDIA DeepStream SDK 9.0 NVIDIA Proprietary Runs inside the Docker container; not distributed here
Ultralytics YOLO-OBB weights AGPL-3.0 Commercial license available from Ultralytics; ONNX/engine is gitignored
DOTA dataset (demo imagery) DOTA Academic License assets/ images derived from DOTA tiles; academic use only

Commercial use: if you deploy YOLO-OBB weights (YOLOv8-OBB or YOLO11-OBB, or a derived ONNX/TensorRT engine) in a commercial product, you must obtain a commercial license from Ultralytics. The configs and documentation in this repo are MIT and have no such restriction.

References

About

YOLO Oriented Bounding Box (OBB) detection on NVIDIA DeepStream 9.0

Topics

Resources

License

Stars

2 stars

Watchers

0 watching

Forks

Sponsor this project

 

Packages

 
 
 

Contributors