[Example] YOLO-Series(v5-11) ONNXRuntime Rust (#17311)
Co-authored-by: UltralyticsAssistant <web@ultralytics.com> Co-authored-by: Glenn Jocher <glenn.jocher@ultralytics.com>pull/17318/head
parent
d28caa9a58
commit
f95dc37311
8 changed files with 362 additions and 29 deletions
@ -0,0 +1,12 @@ |
|||||||
|
[package] |
||||||
|
name = "YOLO-ONNXRuntime-Rust" |
||||||
|
version = "0.1.0" |
||||||
|
edition = "2021" |
||||||
|
authors = ["Jamjamjon <xxyydzml@outlook.com>"] |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
anyhow = "1.0.92" |
||||||
|
clap = "4.5.20" |
||||||
|
tracing = "0.1.40" |
||||||
|
tracing-subscriber = "0.3.18" |
||||||
|
usls = { version = "0.0.19", features = ["auto"] } |
@ -0,0 +1,94 @@ |
|||||||
|
# YOLO-Series ONNXRuntime Rust Demo for Core YOLO Tasks |
||||||
|
|
||||||
|
This repository provides a Rust demo for key YOLO-Series tasks such as `Classification`, `Segmentation`, `Detection`, `Pose Detection`, and `OBB` using ONNXRuntime. It supports various YOLO models (v5 - 11) across multiple vision tasks. |
||||||
|
|
||||||
|
## Introduction |
||||||
|
|
||||||
|
- This example leverages the latest versions of both ONNXRuntime and YOLO models. |
||||||
|
- We utilize the [usls](https://github.com/jamjamjon/usls/tree/main) crate to streamline YOLO model inference, providing efficient data loading, visualization, and optimized inference performance. |
||||||
|
|
||||||
|
## Features |
||||||
|
|
||||||
|
- **Extensive Model Compatibility**: Supports `YOLOv5`, `YOLOv6`, `YOLOv7`, `YOLOv8`, `YOLOv9`, `YOLOv10`, `YOLO11`, `YOLO-world`, `RTDETR`, and others, covering a wide range of YOLO versions. |
||||||
|
- **Versatile Task Coverage**: Includes `Classification`, `Segmentation`, `Detection`, `Pose`, and `OBB`. |
||||||
|
- **Precision Flexibility**: Works with `FP16` and `FP32` ONNX models. |
||||||
|
- **Execution Providers**: Accelerated support for `CPU`, `CUDA`, `CoreML`, and `TensorRT`. |
||||||
|
- **Dynamic Input Shapes**: Dynamically adjusts to variable `batch`, `width`, and `height` dimensions for flexible model input. |
||||||
|
- **Flexible Data Loading**: The `DataLoader` handles images, folders, videos, and video streams. |
||||||
|
- **Real-Time Display and Video Export**: `Viewer` provides real-time frame visualization and video export functions, similar to OpenCV’s `imshow()` and `imwrite()`. |
||||||
|
- **Enhanced Annotation and Visualization**: The `Annotator` facilitates comprehensive result rendering, with support for bounding boxes (HBB), oriented bounding boxes (OBB), polygons, masks, keypoints, and text labels. |
||||||
|
|
||||||
|
## Setup Instructions |
||||||
|
|
||||||
|
### 1. ONNXRuntime Linking |
||||||
|
|
||||||
|
<details> |
||||||
|
<summary>You have two options to link the ONNXRuntime library:</summary> |
||||||
|
|
||||||
|
- **Option 1: Manual Linking** |
||||||
|
|
||||||
|
- For detailed setup, consult the [ONNX Runtime linking documentation](https://ort.pyke.io/setup/linking). |
||||||
|
- **Linux or macOS**: |
||||||
|
1. Download the ONNX Runtime package from the [Releases page](https://github.com/microsoft/onnxruntime/releases). |
||||||
|
2. Set up the library path by exporting the `ORT_DYLIB_PATH` environment variable: |
||||||
|
```shell |
||||||
|
export ORT_DYLIB_PATH=/path/to/onnxruntime/lib/libonnxruntime.so.1.19.0 |
||||||
|
``` |
||||||
|
|
||||||
|
- **Option 2: Automatic Download** |
||||||
|
- Use the `--features auto` flag to handle downloading automatically: |
||||||
|
```shell |
||||||
|
cargo run -r --example yolo --features auto |
||||||
|
``` |
||||||
|
|
||||||
|
</details> |
||||||
|
|
||||||
|
### 2. \[Optional\] Install CUDA, CuDNN, and TensorRT |
||||||
|
|
||||||
|
- The CUDA execution provider requires CUDA version `12.x`. |
||||||
|
- The TensorRT execution provider requires both CUDA `12.x` and TensorRT `10.x`. |
||||||
|
|
||||||
|
### 3. \[Optional\] Install ffmpeg |
||||||
|
|
||||||
|
To view video frames and save video inferences, install `rust-ffmpeg`. For instructions, see: |
||||||
|
[https://github.com/zmwangx/rust-ffmpeg/wiki/Notes-on-building#dependencies](https://github.com/zmwangx/rust-ffmpeg/wiki/Notes-on-building#dependencies) |
||||||
|
|
||||||
|
## Get Started |
||||||
|
|
||||||
|
```Shell |
||||||
|
# customized |
||||||
|
cargo run -r -- --task detect --ver v8 --nc 6 --model xxx.onnx # YOLOv8 |
||||||
|
|
||||||
|
# Classify |
||||||
|
cargo run -r -- --task classify --ver v5 --scale s --width 224 --height 224 --nc 1000 # YOLOv5 |
||||||
|
cargo run -r -- --task classify --ver v8 --scale n --width 224 --height 224 --nc 1000 # YOLOv8 |
||||||
|
cargo run -r -- --task classify --ver v11 --scale n --width 224 --height 224 --nc 1000 # YOLOv11 |
||||||
|
|
||||||
|
# Detect |
||||||
|
cargo run -r -- --task detect --ver v5 --scale n # YOLOv5 |
||||||
|
cargo run -r -- --task detect --ver v6 --scale n # YOLOv6 |
||||||
|
cargo run -r -- --task detect --ver v7 --scale t # YOLOv7 |
||||||
|
cargo run -r -- --task detect --ver v8 --scale n # YOLOv8 |
||||||
|
cargo run -r -- --task detect --ver v9 --scale t # YOLOv9 |
||||||
|
cargo run -r -- --task detect --ver v10 --scale n # YOLOv10 |
||||||
|
cargo run -r -- --task detect --ver v11 --scale n # YOLOv11 |
||||||
|
cargo run -r -- --task detect --ver rtdetr --scale l # RTDETR |
||||||
|
|
||||||
|
# Pose |
||||||
|
cargo run -r -- --task pose --ver v8 --scale n # YOLOv8-Pose |
||||||
|
cargo run -r -- --task pose --ver v11 --scale n # YOLOv11-Pose |
||||||
|
|
||||||
|
# Segment |
||||||
|
cargo run -r -- --task segment --ver v5 --scale n # YOLOv5-Segment |
||||||
|
cargo run -r -- --task segment --ver v8 --scale n # YOLOv8-Segment |
||||||
|
cargo run -r -- --task segment --ver v11 --scale n # YOLOv8-Segment |
||||||
|
cargo run -r -- --task segment --ver v8 --model yolo/FastSAM-s-dyn-f16.onnx # FastSAM |
||||||
|
|
||||||
|
# OBB |
||||||
|
cargo run -r -- --ver v8 --task obb --scale n --width 1024 --height 1024 --source images/dota.png # YOLOv8-Obb |
||||||
|
cargo run -r -- --ver v11 --task obb --scale n --width 1024 --height 1024 --source images/dota.png # YOLOv11-Obb |
||||||
|
``` |
||||||
|
|
||||||
|
**`cargo run -- --help` for more options** |
||||||
|
|
||||||
|
For more details, please refer to [usls-yolo](https://github.com/jamjamjon/usls/tree/main/examples/yolo). |
@ -0,0 +1,236 @@ |
|||||||
|
use anyhow::Result; |
||||||
|
use clap::Parser; |
||||||
|
|
||||||
|
use usls::{ |
||||||
|
models::YOLO, Annotator, DataLoader, Device, Options, Viewer, Vision, YOLOScale, YOLOTask, |
||||||
|
YOLOVersion, COCO_SKELETONS_16, |
||||||
|
}; |
||||||
|
|
||||||
|
#[derive(Parser, Clone)] |
||||||
|
#[command(author, version, about, long_about = None)] |
||||||
|
pub struct Args { |
||||||
|
/// Path to the ONNX model
|
||||||
|
#[arg(long)] |
||||||
|
pub model: Option<String>, |
||||||
|
|
||||||
|
/// Input source path
|
||||||
|
#[arg(long, default_value_t = String::from("../../ultralytics/assets/bus.jpg"))] |
||||||
|
pub source: String, |
||||||
|
|
||||||
|
/// YOLO Task
|
||||||
|
#[arg(long, value_enum, default_value_t = YOLOTask::Detect)] |
||||||
|
pub task: YOLOTask, |
||||||
|
|
||||||
|
/// YOLO Version
|
||||||
|
#[arg(long, value_enum, default_value_t = YOLOVersion::V8)] |
||||||
|
pub ver: YOLOVersion, |
||||||
|
|
||||||
|
/// YOLO Scale
|
||||||
|
#[arg(long, value_enum, default_value_t = YOLOScale::N)] |
||||||
|
pub scale: YOLOScale, |
||||||
|
|
||||||
|
/// Batch size
|
||||||
|
#[arg(long, default_value_t = 1)] |
||||||
|
pub batch_size: usize, |
||||||
|
|
||||||
|
/// Minimum input width
|
||||||
|
#[arg(long, default_value_t = 224)] |
||||||
|
pub width_min: isize, |
||||||
|
|
||||||
|
/// Input width
|
||||||
|
#[arg(long, default_value_t = 640)] |
||||||
|
pub width: isize, |
||||||
|
|
||||||
|
/// Maximum input width
|
||||||
|
#[arg(long, default_value_t = 1024)] |
||||||
|
pub width_max: isize, |
||||||
|
|
||||||
|
/// Minimum input height
|
||||||
|
#[arg(long, default_value_t = 224)] |
||||||
|
pub height_min: isize, |
||||||
|
|
||||||
|
/// Input height
|
||||||
|
#[arg(long, default_value_t = 640)] |
||||||
|
pub height: isize, |
||||||
|
|
||||||
|
/// Maximum input height
|
||||||
|
#[arg(long, default_value_t = 1024)] |
||||||
|
pub height_max: isize, |
||||||
|
|
||||||
|
/// Number of classes
|
||||||
|
#[arg(long, default_value_t = 80)] |
||||||
|
pub nc: usize, |
||||||
|
|
||||||
|
/// Class confidence
|
||||||
|
#[arg(long)] |
||||||
|
pub confs: Vec<f32>, |
||||||
|
|
||||||
|
/// Enable TensorRT support
|
||||||
|
#[arg(long)] |
||||||
|
pub trt: bool, |
||||||
|
|
||||||
|
/// Enable CUDA support
|
||||||
|
#[arg(long)] |
||||||
|
pub cuda: bool, |
||||||
|
|
||||||
|
/// Enable CoreML support
|
||||||
|
#[arg(long)] |
||||||
|
pub coreml: bool, |
||||||
|
|
||||||
|
/// Use TensorRT half precision
|
||||||
|
#[arg(long)] |
||||||
|
pub half: bool, |
||||||
|
|
||||||
|
/// Device ID to use
|
||||||
|
#[arg(long, default_value_t = 0)] |
||||||
|
pub device_id: usize, |
||||||
|
|
||||||
|
/// Enable performance profiling
|
||||||
|
#[arg(long)] |
||||||
|
pub profile: bool, |
||||||
|
|
||||||
|
/// Disable contour drawing, for saving time
|
||||||
|
#[arg(long)] |
||||||
|
pub no_contours: bool, |
||||||
|
|
||||||
|
/// Show result
|
||||||
|
#[arg(long)] |
||||||
|
pub view: bool, |
||||||
|
|
||||||
|
/// Do not save output
|
||||||
|
#[arg(long)] |
||||||
|
pub nosave: bool, |
||||||
|
} |
||||||
|
|
||||||
|
fn main() -> Result<()> { |
||||||
|
let args = Args::parse(); |
||||||
|
|
||||||
|
// logger
|
||||||
|
if args.profile { |
||||||
|
tracing_subscriber::fmt() |
||||||
|
.with_max_level(tracing::Level::INFO) |
||||||
|
.init(); |
||||||
|
} |
||||||
|
|
||||||
|
// model path
|
||||||
|
let path = match &args.model { |
||||||
|
None => format!( |
||||||
|
"yolo/{}-{}-{}.onnx", |
||||||
|
args.ver.name(), |
||||||
|
args.scale.name(), |
||||||
|
args.task.name() |
||||||
|
), |
||||||
|
Some(x) => x.to_string(), |
||||||
|
}; |
||||||
|
|
||||||
|
// saveout
|
||||||
|
let saveout = match &args.model { |
||||||
|
None => format!( |
||||||
|
"{}-{}-{}", |
||||||
|
args.ver.name(), |
||||||
|
args.scale.name(), |
||||||
|
args.task.name() |
||||||
|
), |
||||||
|
Some(x) => { |
||||||
|
let p = std::path::PathBuf::from(&x); |
||||||
|
p.file_stem().unwrap().to_str().unwrap().to_string() |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// device
|
||||||
|
let device = if args.cuda { |
||||||
|
Device::Cuda(args.device_id) |
||||||
|
} else if args.trt { |
||||||
|
Device::Trt(args.device_id) |
||||||
|
} else if args.coreml { |
||||||
|
Device::CoreML(args.device_id) |
||||||
|
} else { |
||||||
|
Device::Cpu(args.device_id) |
||||||
|
}; |
||||||
|
|
||||||
|
// build options
|
||||||
|
let options = Options::new() |
||||||
|
.with_model(&path)? |
||||||
|
.with_yolo_version(args.ver) |
||||||
|
.with_yolo_task(args.task) |
||||||
|
.with_device(device) |
||||||
|
.with_trt_fp16(args.half) |
||||||
|
.with_ixx(0, 0, (1, args.batch_size as _, 4).into()) |
||||||
|
.with_ixx(0, 2, (args.height_min, args.height, args.height_max).into()) |
||||||
|
.with_ixx(0, 3, (args.width_min, args.width, args.width_max).into()) |
||||||
|
.with_confs(if args.confs.is_empty() { |
||||||
|
&[0.2, 0.15] |
||||||
|
} else { |
||||||
|
&args.confs |
||||||
|
}) |
||||||
|
.with_nc(args.nc) |
||||||
|
.with_find_contours(!args.no_contours) // find contours or not
|
||||||
|
// .with_names(&COCO_CLASS_NAMES_80) // detection class names
|
||||||
|
// .with_names2(&COCO_KEYPOINTS_17) // keypoints class names
|
||||||
|
// .exclude_classes(&[0])
|
||||||
|
// .retain_classes(&[0, 5])
|
||||||
|
.with_profile(args.profile); |
||||||
|
|
||||||
|
// build model
|
||||||
|
let mut model = YOLO::new(options)?; |
||||||
|
|
||||||
|
// build dataloader
|
||||||
|
let dl = DataLoader::new(&args.source)? |
||||||
|
.with_batch(model.batch() as _) |
||||||
|
.build()?; |
||||||
|
|
||||||
|
// build annotator
|
||||||
|
let annotator = Annotator::default() |
||||||
|
.with_skeletons(&COCO_SKELETONS_16) |
||||||
|
.without_masks(true) // no masks plotting when doing segment task
|
||||||
|
.with_bboxes_thickness(3) |
||||||
|
.with_keypoints_name(false) // enable keypoints names
|
||||||
|
.with_saveout_subs(&["YOLO"]) |
||||||
|
.with_saveout(&saveout); |
||||||
|
|
||||||
|
// build viewer
|
||||||
|
let mut viewer = if args.view { |
||||||
|
Some(Viewer::new().with_delay(5).with_scale(1.).resizable(true)) |
||||||
|
} else { |
||||||
|
None |
||||||
|
}; |
||||||
|
|
||||||
|
// run & annotate
|
||||||
|
for (xs, _paths) in dl { |
||||||
|
let ys = model.forward(&xs, args.profile)?; |
||||||
|
let images_plotted = annotator.plot(&xs, &ys, !args.nosave)?; |
||||||
|
|
||||||
|
// show image
|
||||||
|
match &mut viewer { |
||||||
|
Some(viewer) => viewer.imshow(&images_plotted)?, |
||||||
|
None => continue, |
||||||
|
} |
||||||
|
|
||||||
|
// check out window and key event
|
||||||
|
match &mut viewer { |
||||||
|
Some(viewer) => { |
||||||
|
if !viewer.is_open() || viewer.is_key_pressed(usls::Key::Escape) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
None => continue, |
||||||
|
} |
||||||
|
|
||||||
|
// write video
|
||||||
|
if !args.nosave { |
||||||
|
match &mut viewer { |
||||||
|
Some(viewer) => viewer.write_batch(&images_plotted)?, |
||||||
|
None => continue, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// finish video write
|
||||||
|
if !args.nosave { |
||||||
|
if let Some(viewer) = &mut viewer { |
||||||
|
viewer.finish_write()?; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
Loading…
Reference in new issue