Skip to content

Commit d88ef3d

Browse files
Refinements
1 parent 09082ee commit d88ef3d

File tree

10 files changed

+164
-123
lines changed

10 files changed

+164
-123
lines changed

backends/openvino/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ executorch
4343
Before you begin, ensure you have openvino installed and configured on your system:
4444

4545
```bash
46-
git clone https://github.com/openvinotoolkit/openvino.git
47-
cd openvino && git checkout releases/2025/1
46+
git clone https://github.com/daniil-lyakhov/openvino.git
47+
cd openvino && git checkout dl/executorch/yolo12
4848
git submodule update --init --recursive
4949
sudo ./install_build_dependencies.sh
5050
mkdir build && cd build

backends/openvino/scripts/openvino_build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ main() {
3333
-DEXECUTORCH_BUILD_EXTENSION_TENSOR=ON \
3434
-DEXECUTORCH_BUILD_OPENVINO_EXECUTOR_RUNNER=ON \
3535
-DPYTHON_EXECUTABLE=python \
36+
-DEXECUTORCH_LOG_LEVEL=Debug \
3637
-B"${build_dir}"
37-
#-DEXECUTORCH_LOG_LEVEL=Debug \
3838

3939

4040
# Build the project

examples/models/yolo12/CMakeLists.txt

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,6 @@ list(APPEND _common_include_directories
6161
${XNNPACK_ROOT}/third-party/pthreadpool/include
6262
)
6363

64-
list(APPEND link_libraries extension_threadpool cpuinfo)
65-
list(APPEND _common_include_directories
66-
${XNNPACK_ROOT}/third-party/cpuinfo/include
67-
)
68-
69-
message(STATUS ${link_libraries})
70-
7164
set(PROJECT_SOURCES
7265
main.cpp
7366
inference.h

examples/models/yolo12/README.md

Lines changed: 57 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,90 @@
1-
# YOLOv8/YOLOv5 C++ Inference with OpenCV DNN
1+
# YOLO12 Detection C++ Inference with ExecuTorch
22

3-
This example demonstrates how to perform inference using Ultralytics YOLO12 models in C++ leveraging the Executorch backends:
4-
- OpenVINO
5-
- XNNPACK.
3+
<p align="center">
4+
<br>
5+
<img src="./yolo12s_demo.gif" width=300>
6+
<br>
7+
</p>
68

7-
## 🛠️ Usage
9+
This example demonstrates how to perform inference of Ultralytics YOLO12 family detection models in C++ leveraging the Executorch backends:
10+
- [OpenVINO](../../../backends/openvino/README.md)
11+
- [XNNPACK](../../../backends/xnnpack/README.md)
812

9-
Follow these steps to set up and run the C++ inference example:
1013

11-
```bash
12-
# 1. Clone the Ultralytics repository
13-
git clone https://github.com/ultralytics/ultralytics
14-
cd ultralytics
14+
# Instructions
1515

16-
# 2. Install Ultralytics Python package (needed for exporting models)
17-
pip install .
16+
### Step 1: Install ExecuTorch
1817

19-
# 3. Navigate to the C++ example directory
20-
cd examples/YOLOv8-CPP-Inference
18+
To install ExecuTorch, follow this [guide](https://pytorch.org/executorch/stable/getting-started-setup.html).
2119

22-
# 4. Export Models: Add yolov8*.onnx and/or yolov5*.onnx models (see export instructions below)
23-
# Place the exported ONNX models in the current directory (YOLOv8-CPP-Inference).
20+
### Step 2: Install the backend of your choice
2421

25-
# 5. Update Source Code: Edit main.cpp and set the 'projectBasePath' variable
26-
# to the absolute path of the 'YOLOv8-CPP-Inference' directory on your system.
27-
# Example: std::string projectBasePath = "/path/to/your/ultralytics/examples/YOLOv8-CPP-Inference";
22+
- [OpenVINO backend installation guide](../../../backends/openvino/README.md#build-instructions)
23+
- [XNNPACK backend installation guilde](https://pytorch.org/executorch/stable/tutorial-xnnpack-delegate-lowering.html#running-the-xnnpack-model-with-cmake)
2824

29-
# 6. Configure OpenCV DNN Backend (Optional - CUDA):
30-
# - The default CMakeLists.txt attempts to use CUDA for GPU acceleration with OpenCV DNN.
31-
# - If your OpenCV build doesn't support CUDA/cuDNN, or you want CPU inference,
32-
# remove the CUDA-related lines from CMakeLists.txt.
25+
### Step 3: Install the demo requirements
3326

34-
# 7. Build the project
35-
mkdir build
36-
cd build
37-
cmake ..
38-
make
3927

40-
# 8. Run the inference executable
41-
./Yolov8CPPInference
28+
Python demo requirements:
29+
```bash
30+
python -m pip install -r examples/models/yolo12/requirements.txt
4231
```
4332

44-
## ✨ Exporting YOLOv8 and YOLOv5 Models
33+
Demo infenrece dependency - OpenCV library:
34+
https://opencv.org/get-started/
4535

46-
You need to export your trained PyTorch models to the [ONNX](https://onnx.ai/) format to use them with OpenCV DNN.
4736

48-
**Exporting Ultralytics YOLOv8 Models:**
37+
### Step 4: Export the Yolo12 model to the ExecuTorch
4938

50-
Use the Ultralytics CLI to export. Ensure you specify the desired `imgsz` and `opset`. For compatibility with this example, `opset=12` is recommended.
5139

40+
OpenVINO:
5241
```bash
53-
yolo export model=yolov8s.pt imgsz=640,480 format=onnx opset=12 # Example: 640x480 resolution
42+
python export_and_quantize.py --model_name yolo12s --input_dims=[1920,1080] --backend openvino --device CPU
5443
```
5544

56-
**Exporting YOLOv5 Models:**
45+
XNNPACK:
46+
```bash
47+
python export_and_quantize.py --model_name yolo12s --input_dims=[1920,1080] --backend xnnpack
48+
```
5749

58-
Use the `export.py` script from the YOLOv5 repository structure (included within the cloned `ultralytics` repo).
50+
> **_NOTE:_** Quantization is comming soon!
5951
52+
To get a full parameters description please use the following command:
6053
```bash
61-
# Assuming you are in the 'ultralytics' base directory after cloning
62-
python export.py --weights yolov5s.pt --imgsz 640 480 --include onnx --opset 12 # Example: 640x480 resolution
54+
python export_and_quantize.py
6355
```
56+
### Step 5: Build the demo project
6457

65-
Place the generated `.onnx` files (e.g., `yolov8s.onnx`, `yolov5s.onnx`) into the `ultralytics/examples/YOLOv8-CPP-Inference/` directory.
58+
OpenVINO:
6659

67-
**Example Output:**
68-
69-
_yolov8s.onnx:_
60+
```bash
61+
cd examples/models/yolo12
62+
mkdir build && cd build
63+
cmake -DCMAKE_BUILD_TYPE=Release -DUSE_OPENVINO_BACKEND=ON ..
64+
make -j$(nproc)
65+
```
7066

71-
![YOLOv8 ONNX Output](https://user-images.githubusercontent.com/40023722/217356132-a4cecf2e-2729-4acb-b80a-6559022d7707.png)
67+
XNNPACK:
7268

73-
_yolov5s.onnx:_
69+
```bash
70+
cd examples/models/yolo12
71+
mkdir build && cd build
72+
cmake -DCMAKE_BUILD_TYPE=Release -DUSE_XNNPACK_BACKEND=ON ..
73+
make -j$(nproc)
74+
```
7475

75-
![YOLOv5 ONNX Output](https://user-images.githubusercontent.com/40023722/217357005-07464492-d1da-42e3-98a7-fc753f87d5e6.png)
76+
### Step 6: Run the demo
7677

77-
## 📝 Notes
78+
```bash
79+
./build/Yolo12DetectionDemo -model_path /path/to/exported/model -input_path /path/to/video/file -output_path /path/to/output/annotated/video
80+
```
7881

79-
- This repository utilizes the [OpenCV DNN API](https://docs.opencv.org/4.x/d6/d0f/group__dnn.html) to run [ONNX](https://onnx.ai/) exported models of YOLOv5 and Ultralytics YOLOv8.
80-
- While not explicitly tested, it might theoretically work for other YOLO architectures like YOLOv6 and YOLOv7 if their ONNX export formats are compatible.
81-
- The example models are exported with a rectangular resolution (640x480), but the code should handle models exported with different resolutions. Consider using techniques like [letterboxing](https://docs.ultralytics.com/modes/predict/#letterbox) if your input images have different aspect ratios than the model's training resolution, especially for square `imgsz` exports.
82-
- The `main` branch version includes a simple GUI wrapper using [Qt](https://www.qt.io/). However, the core logic resides in the `Inference` class (`inference.h`, `inference.cpp`).
83-
- A key part of the `Inference` class demonstrates how to handle the output differences between YOLOv5 and YOLOv8 models, effectively transposing YOLOv8's output format to match the structure expected from YOLOv5 for consistent post-processing.
82+
To get a full parameters description please use the following command:
83+
```
84+
./build/Yolo12DetectionDemo --help
85+
```
8486

85-
## 🤝 Contributing
8687

87-
Contributions are welcome! If you find any issues or have suggestions for improvement, please feel free to open an issue or submit a pull request. See our [Contributing Guide](https://docs.ultralytics.com/help/contributing/) for more details.
88+
# Credits:
89+
Ultralytics examples: https://github.com/ultralytics/ultralytics/tree/main/examples
90+
Sample video: https://www.pexels.com/@shanu-1040189/

examples/models/yolo12/build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
rm -r build
22
mkdir build && cd build
3-
cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_XNNPACK_BACKEND=ON -DUSE_OPENVINO_BACKEND=ON ..
3+
cmake -DCMAKE_BUILD_TYPE=Release -DUSE_XNNPACK_BACKEND=ON -DUSE_OPENVINO_BACKEND=ON ..
44
make -j 30

examples/models/yolo12/export_and_quantize.py

Lines changed: 50 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
import cv2
1515
import executorch
16-
import nncf.torch
1716
import numpy as np
1817
import torch
1918
from executorch.backends.openvino.partitioner import OpenvinoPartitioner
@@ -32,7 +31,6 @@
3231
to_edge_transform_and_lower,
3332
)
3433
from executorch.exir.backend.backend_details import CompileSpec
35-
from nncf.experimental.torch.fx import quantize_pt2e
3634
from torch.ao.quantization.quantize_pt2e import convert_pt2e, prepare_pt2e
3735
from torch.export.exported_program import ExportedProgram
3836
from torch.fx.passes.graph_drawer import FxGraphDrawer
@@ -82,45 +80,48 @@ def lower_to_openvino(
8280
subset_size: int,
8381
quantize: bool,
8482
) -> ExecutorchProgramManager:
85-
if quantize:
86-
target_input_dims = tuple(example_args[0].shape[2:])
83+
import nncf.torch
84+
from nncf.experimental.torch.fx import quantize_pt2e
8785

88-
def ext_transform_fn(sample):
89-
sample = transform_fn(sample)
90-
return pad_to_target(sample, target_input_dims)
86+
with nncf.torch.disable_patching():
87+
if quantize:
88+
target_input_dims = tuple(example_args[0].shape[2:])
9189

92-
quantizer = OpenVINOQuantizer(mode=QuantizationMode.INT8_TRANSFORMER)
93-
quantizer.set_ignored_scope(
94-
types=["mul", "sub", "sigmoid", "__getitem__"],
95-
subgraphs=[nncf.Subgraph(inputs=["cat_18"], outputs=["output"])]
96-
)
97-
quantized_model = quantize_pt2e(
98-
aten_dialect.module(),
99-
quantizer,
100-
nncf.Dataset(calibration_dataset, ext_transform_fn),
101-
subset_size=subset_size,
102-
smooth_quant=True,
103-
fold_quantize=False
104-
)
90+
def ext_transform_fn(sample):
91+
sample = transform_fn(sample)
92+
return pad_to_target(sample, target_input_dims)
10593

106-
visualize_fx_model(quantized_model, "tmp_quantized_model.svg")
107-
aten_dialect = torch.export.export(quantized_model, example_args)
108-
# Convert to edge dialect and lower the module to the backend with a custom partitioner
109-
compile_spec = [CompileSpec("device", device.encode())]
110-
lowered_module: EdgeProgramManager = to_edge_transform_and_lower(
111-
aten_dialect,
112-
partitioner=[
113-
OpenvinoPartitioner(compile_spec),
114-
],
115-
compile_config=EdgeCompileConfig(
116-
_skip_dim_order=True,
117-
),
118-
)
94+
quantizer = OpenVINOQuantizer(mode=QuantizationMode.INT8_TRANSFORMER)
95+
quantizer.set_ignored_scope(
96+
types=["mul", "sub", "sigmoid", "__getitem__"],
97+
)
98+
quantized_model = quantize_pt2e(
99+
aten_dialect.module(),
100+
quantizer,
101+
nncf.Dataset(calibration_dataset, ext_transform_fn),
102+
subset_size=subset_size,
103+
smooth_quant=True,
104+
fold_quantize=False,
105+
)
119106

120-
# Apply backend-specific passes
121-
return lowered_module.to_executorch(
122-
config=executorch.exir.ExecutorchBackendConfig()
123-
)
107+
visualize_fx_model(quantized_model, "tmp_quantized_model.svg")
108+
aten_dialect = torch.export.export(quantized_model, example_args)
109+
# Convert to edge dialect and lower the module to the backend with a custom partitioner
110+
compile_spec = [CompileSpec("device", device.encode())]
111+
lowered_module: EdgeProgramManager = to_edge_transform_and_lower(
112+
aten_dialect,
113+
partitioner=[
114+
OpenvinoPartitioner(compile_spec),
115+
],
116+
compile_config=EdgeCompileConfig(
117+
_skip_dim_order=True,
118+
),
119+
)
120+
121+
# Apply backend-specific passes
122+
return lowered_module.to_executorch(
123+
config=executorch.exir.ExecutorchBackendConfig()
124+
)
124125

125126

126127
def lower_to_xnnpack(
@@ -217,6 +218,7 @@ def main(
217218
model = YOLO(model_name)
218219

219220
if quantize:
221+
raise NotImplementedError("Quantization is comming soon!")
220222
if video_path is None:
221223
raise RuntimeError(
222224
"Could not quantize model without the video for the calibration."
@@ -273,7 +275,8 @@ def transform_fn(frame):
273275
"--model_name",
274276
type=str,
275277
default="yolo12s",
276-
help="Ultralytics yolo model name.",
278+
choices=["yolo12n", "yolo12s", "yolo12m", "yolo12l", "yolo12x"],
279+
help="Ultralytics yolo12 model name.",
277280
)
278281
parser.add_argument(
279282
"--input_dims",
@@ -312,14 +315,12 @@ def transform_fn(frame):
312315
args = parser.parse_args()
313316

314317
# Run the main function with parsed arguments
315-
# Disable nncf patching as export of the patched model is not supported.
316-
with nncf.torch.disable_patching():
317-
main(
318-
model_name=args.model_name,
319-
input_dims=args.input_dims,
320-
quantize=args.quantize,
321-
video_path=args.video_path,
322-
subset_size=args.subset_size,
323-
backend=args.backend,
324-
device=args.device,
325-
)
318+
main(
319+
model_name=args.model_name,
320+
input_dims=args.input_dims,
321+
quantize=args.quantize,
322+
video_path=args.video_path,
323+
subset_size=args.subset_size,
324+
backend=args.backend,
325+
device=args.device,
326+
)

examples/models/yolo12/inference.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,10 @@ std::vector<Detection> infer_yolo_once(
7777
ScalarType::Float);
7878
const auto result = module.forward(t_input);
7979

80-
ET_CHECK_MSG(result.ok(), "Could not infer the model with an error");
80+
ET_CHECK_MSG(
81+
result.ok(),
82+
"Execution of method forward failed with status 0x%" PRIx32,
83+
(uint32_t)result.error());
8184

8285
const auto t = result->at(0).toTensor(); // Using only the 0 output
8386
// yolov8 has an output of shape (batchSize, 84, 8400) (Num classes +

0 commit comments

Comments
 (0)