vagheshpatel commited on
Commit
0d4136a
·
verified ·
1 Parent(s): 826f77b

Update native RC1 samples and supported device documentation

Browse files
Files changed (5) hide show
  1. README.md +68 -18
  2. config.json +11 -3
  3. dlstreamer_sample.py +71 -0
  4. export_and_quantize.sh +119 -11
  5. openvino_sample.py +64 -0
README.md CHANGED
@@ -8,6 +8,7 @@ tags:
8
  - intel
9
  - yolo
10
  - yolo26
 
11
  - object-counting
12
  - coco
13
  - edge-ai
@@ -22,8 +23,8 @@ language:
22
  | Property | Value |
23
  |---|---|
24
  | **Category** | Object Detection + Counting (80-class COCO) |
25
- | **Base Model** | [YOLO26](https://docs.ultralytics.com/models/yolo26/) (Ultralytics) |
26
- | **Source Framework** | PyTorch (Ultralytics) |
27
  | **Supported Precisions** | FP32, FP16, INT8 (mixed-precision) |
28
  | **Inference Engine** | OpenVINO |
29
  | **Hardware** | CPU, GPU, NPU |
@@ -50,19 +51,38 @@ Typical Metro deployments include:
50
  - **Inventory Counting** -- count bags, bottles, or other items in a zone.
51
  - **Throughput Metrics** -- aggregate per-frame counts into time series.
52
 
53
- Available variants: `yolo26n`, `yolo26s`, `yolo26m`, `yolo26l`, `yolo26x`.
54
  Smaller variants (`yolo26n`, `yolo26s`) are recommended for high-FPS edge deployment; larger variants improve recall for small or distant objects.
55
 
56
  For line-crossing counts (directional entry/exit), see the
57
  [vehicle-entry-exit-logging](../vehicle-entry-exit-logging/) use case.
58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  ---
60
 
61
  ## Prerequisites
62
 
 
 
 
 
63
  - Python 3.11+
64
  - [Install OpenVINO](https://docs.openvino.ai/2026/get-started/install-openvino.html) (latest version)
65
- - [Install Intel DLStreamer](https://docs.openedgeplatform.intel.com/2026.0/edge-ai-libraries/dlstreamer/get_started/install/install_guide_ubuntu.html) (latest version)
66
 
67
  Create and activate a Python virtual environment before running the scripts:
68
 
@@ -90,20 +110,22 @@ chmod +x export_and_quantize.sh
90
 
91
  This exports the default **yolo26n** model in **FP16** precision.
92
 
93
- #### Optional: Select a Different Variant or Precision
94
 
95
  ```bash
96
- ./export_and_quantize.sh yolo26n FP32 # full-precision
97
- ./export_and_quantize.sh yolo26n INT8 # quantized
98
- ./export_and_quantize.sh yolo26s # larger variant, default FP16
 
 
99
  ```
100
 
101
- Replace `yolo26n` with any variant (`yolo26s`, `yolo26m`, `yolo26l`, `yolo26x`).
102
  The second argument selects the precision (`FP32`, `FP16`, `INT8`); the default is **FP16**.
103
 
104
  The script performs the following steps:
105
 
106
- 1. Installs dependencies (`openvino`, `ultralytics`; adds `nncf` for INT8).
107
  2. Downloads a sample test image (`test.jpg`) and a sample test video
108
  (`test_video.mp4`, the
109
  [`person-bicycle-car-detection.mp4`](https://github.com/intel-iot-devkit/sample-videos/blob/master/person-bicycle-car-detection.mp4)
@@ -111,7 +133,7 @@ The script performs the following steps:
111
  3. Downloads the PyTorch weights and exports to OpenVINO IR.
112
  4. *(INT8 only)* Quantizes the model using NNCF post-training quantization.
113
 
114
- Output files:
115
 
116
  - `yolo26n_openvino_model/` -- FP32 or FP16 OpenVINO IR model directory.
117
  - `yolo26n_objcount_int8.xml` / `yolo26n_objcount_int8.bin` -- INT8 quantized model *(only when `INT8` is selected)*.
@@ -135,7 +157,16 @@ per-class count and a total count for a single image.
135
  YOLO26 is end-to-end (NMS-free), so no manual non-maximum suppression is needed.
136
  Change the `device` string to run on CPU, GPU, or NPU.
137
 
 
 
 
138
  ```python
 
 
 
 
 
 
139
  from collections import Counter
140
 
141
  import cv2
@@ -143,10 +174,12 @@ import numpy as np
143
  import openvino as ov
144
 
145
  CONF_THRESHOLD = 0.4
146
- INPUT_SIZE = 640
 
 
147
 
148
  core = ov.Core()
149
- model = core.read_model("yolo26n_openvino_model/yolo26n.xml")
150
 
151
  # YOLO26 embeds the 80 COCO class names in rt_info -- read them instead of
152
  # hardcoding the list. Ultralytics separates multi-word names with
@@ -166,7 +199,8 @@ blob = cv2.resize(image, (INPUT_SIZE, INPUT_SIZE))
166
  blob = cv2.cvtColor(blob, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0
167
  blob = blob.transpose(2, 0, 1)[np.newaxis, ...] # NCHW
168
 
169
- # YOLO26 end-to-end output: [1, 300, 6] = [x1, y1, x2, y2, confidence, class_id]
 
170
  output = compiled([blob])[compiled.output(0)][0]
171
  dets = output[output[:, 4] >= CONF_THRESHOLD]
172
 
@@ -192,6 +226,7 @@ cv2.putText(image, summary[:60], (10, 30),
192
  cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
193
  cv2.imwrite("output_openvino.jpg", image)
194
  ```
 
195
 
196
  **Device targets:**
197
 
@@ -231,7 +266,7 @@ three classes counted below.
231
 
232
  > **Notes on running this sample:**
233
  >
234
- > - Use the FP16 IR (`yolo26n_openvino_model/yolo26n.xml`). Class names are
235
  > read automatically from the model's embedded `metadata.yaml` by
236
  > DLStreamer 2026.0+ -- no external `labels-file` is required.
237
  > - Export `PYTHONPATH` so the DLStreamer Python module is importable:
@@ -243,7 +278,16 @@ three classes counted below.
243
  > /opt/intel/dlstreamer/gstreamer/lib/python3/dist-packages:${PYTHONPATH:-}
244
  > ```
245
 
 
 
 
246
  ```python
 
 
 
 
 
 
247
  from collections import Counter
248
 
249
  import gi
@@ -260,12 +304,15 @@ INPUT_VIDEO = "test_video.mp4"
260
  # a cyclist, and cars.
261
  CLASSES_OF_INTEREST = {"person", "bicycle", "car"}
262
 
 
 
 
263
  # For CPU: change device=GPU to device=CPU.
264
  # For NPU: change device=GPU to device=NPU (batch-size=1, nireq=4 recommended).
265
  pipeline_str = (
266
  f"filesrc location={INPUT_VIDEO} ! decodebin3 ! "
267
  "videoconvert ! "
268
- "gvadetect model=yolo26n_openvino_model/yolo26n.xml "
269
  "device=GPU "
270
  "threshold=0.4 ! queue ! "
271
  "gvawatermark ! videoconvert ! video/x-raw,format=I420 ! "
@@ -307,6 +354,7 @@ bus.timed_pop_filtered(
307
  )
308
  pipeline.set_state(Gst.State.NULL)
309
  ```
 
310
 
311
  **Device targets:**
312
 
@@ -327,8 +375,10 @@ Licensed under the MIT License. See [LICENSE](LICENSE) for details.
327
  ## References
328
 
329
  - [YOLO26 Documentation](https://docs.ultralytics.com/models/yolo26/)
 
 
330
  - [Ultralytics Object Counting Guide](https://docs.ultralytics.com/guides/object-counting/)
331
- - [Intel DLStreamer gvadetect](https://docs.openedgeplatform.intel.com/2026.0/edge-ai-libraries/dlstreamer/elements/gvadetect.html)
332
  - [OpenVINO Documentation](https://docs.openvino.ai/)
333
  - [NNCF Post-Training Quantization](https://docs.openvino.ai/latest/nncf_ptq_introduction.html)
334
- - [Intel DLStreamer](https://docs.openedgeplatform.intel.com/2026.0/edge-ai-libraries/dlstreamer/index.html)
 
8
  - intel
9
  - yolo
10
  - yolo26
11
+ - detr
12
  - object-counting
13
  - coco
14
  - edge-ai
 
23
  | Property | Value |
24
  |---|---|
25
  | **Category** | Object Detection + Counting (80-class COCO) |
26
+ | **Base Model** | [YOLO26](https://docs.ultralytics.com/models/yolo26/) (Ultralytics, default) or [DETR](https://huggingface.co/facebook/detr-resnet-50) (Facebook, alternative) |
27
+ | **Source Framework** | PyTorch (Ultralytics, Hugging Face Transformers) |
28
  | **Supported Precisions** | FP32, FP16, INT8 (mixed-precision) |
29
  | **Inference Engine** | OpenVINO |
30
  | **Hardware** | CPU, GPU, NPU |
 
51
  - **Inventory Counting** -- count bags, bottles, or other items in a zone.
52
  - **Throughput Metrics** -- aggregate per-frame counts into time series.
53
 
54
+ Available variants: `yolo26n`, `yolo26s`, `yolo26m`, `yolo26l`.
55
  Smaller variants (`yolo26n`, `yolo26s`) are recommended for high-FPS edge deployment; larger variants improve recall for small or distant objects.
56
 
57
  For line-crossing counts (directional entry/exit), see the
58
  [vehicle-entry-exit-logging](../vehicle-entry-exit-logging/) use case.
59
 
60
+ ### Model Selection
61
+
62
+ [DETR](https://huggingface.co/facebook/detr-resnet-50) (DEtection TRansformer) is available as an alternative to YOLO26.
63
+ DETR is a transformer-based, NMS-free detector trained on COCO and released under the Apache-2.0 license.
64
+ The export script embeds DETR normalization, box decoding, and the class remap into the OpenVINO IR, so the DETR model produces the same output layout and class ids as YOLO26.
65
+ The same OpenVINO and DLStreamer samples run either model; select one with the `MODEL` constant at the top of each sample.
66
+
67
+ | Model | Variants | Input Size | Output | License |
68
+ |---|---|---|---|---|
69
+ | YOLO26 (default) | `yolo26n`, `yolo26s`, `yolo26m`, `yolo26l` | 640 x 640 | `[1, 300, 6]` | AGPL-3.0 |
70
+ | DETR | `detr-resnet-50` | 800 x 800 | `[1, 100, 6]` | Apache-2.0 |
71
+
72
+ Choose YOLO26 for maximum throughput on edge devices.
73
+ Choose DETR when a permissively licensed transformer detector is preferred; it is heavier and runs at a lower frame rate.
74
+
75
  ---
76
 
77
  ## Prerequisites
78
 
79
+ Target stack: Intel DLStreamer **2026.3.0 RC1** with the matching OpenVINO GenAI bundle **2026.5.0.0.dev20261030** (OpenVINO Runtime **2026.5.0.dev20261030**).
80
+ Use the [native RC1 setup](../README.md#native-rc1-runtime) before running these samples; this is a development stack, not a stable release.
81
+ See the [device validation matrix](../DEVICE_SUPPORT_MATRIX.md) for measured CPU, GPU, and NPU results.
82
+
83
  - Python 3.11+
84
  - [Install OpenVINO](https://docs.openvino.ai/2026/get-started/install-openvino.html) (latest version)
85
+ - [Install Intel DLStreamer](https://docs.openedgeplatform.intel.com/dev/edge-ai-libraries/dlstreamer/get_started/install/install_guide_ubuntu.html) (latest version)
86
 
87
  Create and activate a Python virtual environment before running the scripts:
88
 
 
110
 
111
  This exports the default **yolo26n** model in **FP16** precision.
112
 
113
+ #### Optional: Select a Different Model, Variant, or Precision
114
 
115
  ```bash
116
+ ./export_and_quantize.sh yolo26n FP32 # full-precision
117
+ ./export_and_quantize.sh yolo26n INT8 # quantized
118
+ ./export_and_quantize.sh yolo26s # larger variant, default FP16
119
+ ./export_and_quantize.sh detr-resnet-50 # DETR alternative, default FP16
120
+ ./export_and_quantize.sh detr-resnet-50 INT8 # DETR quantized
121
  ```
122
 
123
+ The first argument selects the model: any YOLO26 variant (`yolo26n`, `yolo26s`, `yolo26m`, `yolo26l`) or DETR (`detr-resnet-50`).
124
  The second argument selects the precision (`FP32`, `FP16`, `INT8`); the default is **FP16**.
125
 
126
  The script performs the following steps:
127
 
128
+ 1. Installs dependencies (`openvino`, `ultralytics` for YOLO26 or `transformers`, `torch` for DETR; adds `nncf` for INT8).
129
  2. Downloads a sample test image (`test.jpg`) and a sample test video
130
  (`test_video.mp4`, the
131
  [`person-bicycle-car-detection.mp4`](https://github.com/intel-iot-devkit/sample-videos/blob/master/person-bicycle-car-detection.mp4)
 
133
  3. Downloads the PyTorch weights and exports to OpenVINO IR.
134
  4. *(INT8 only)* Quantizes the model using NNCF post-training quantization.
135
 
136
+ Output files (replace `yolo26n` with the selected model, for example `detr-resnet-50`):
137
 
138
  - `yolo26n_openvino_model/` -- FP32 or FP16 OpenVINO IR model directory.
139
  - `yolo26n_objcount_int8.xml` / `yolo26n_objcount_int8.bin` -- INT8 quantized model *(only when `INT8` is selected)*.
 
157
  YOLO26 is end-to-end (NMS-free), so no manual non-maximum suppression is needed.
158
  Change the `device` string to run on CPU, GPU, or NPU.
159
 
160
+ The full sample is in [openvino_sample.py](openvino_sample.py); run it with `python3 openvino_sample.py` or copy the code below.
161
+
162
+ <!-- sample: openvino_sample.py -->
163
  ```python
164
+ # SPDX-License-Identifier: MIT
165
+ # Copyright (C) Intel Corporation
166
+ #
167
+ # Object Counting: OpenVINO Runtime sample.
168
+ # See README.md for prerequisites and inputs. Run: python3 openvino_sample.py
169
+
170
  from collections import Counter
171
 
172
  import cv2
 
174
  import openvino as ov
175
 
176
  CONF_THRESHOLD = 0.4
177
+ # Select the model: "yolo26n" (default) or "detr-resnet-50".
178
+ MODEL = "yolo26n"
179
+ INPUT_SIZE = 800 if MODEL.startswith("detr") else 640
180
 
181
  core = ov.Core()
182
+ model = core.read_model(f"{MODEL}_openvino_model/{MODEL}.xml")
183
 
184
  # YOLO26 embeds the 80 COCO class names in rt_info -- read them instead of
185
  # hardcoding the list. Ultralytics separates multi-word names with
 
199
  blob = cv2.cvtColor(blob, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0
200
  blob = blob.transpose(2, 0, 1)[np.newaxis, ...] # NCHW
201
 
202
+ # End-to-end output: [1, N, 6] = [x1, y1, x2, y2, confidence, class_id]
203
+ # (N = 300 for YOLO26, 100 for DETR)
204
  output = compiled([blob])[compiled.output(0)][0]
205
  dets = output[output[:, 4] >= CONF_THRESHOLD]
206
 
 
226
  cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
227
  cv2.imwrite("output_openvino.jpg", image)
228
  ```
229
+ <!-- /sample -->
230
 
231
  **Device targets:**
232
 
 
266
 
267
  > **Notes on running this sample:**
268
  >
269
+ > - Use the FP16 IR (`yolo26n_openvino_model/yolo26n.xml` or `detr-resnet-50_openvino_model/detr-resnet-50.xml`). Class names are
270
  > read automatically from the model's embedded `metadata.yaml` by
271
  > DLStreamer 2026.0+ -- no external `labels-file` is required.
272
  > - Export `PYTHONPATH` so the DLStreamer Python module is importable:
 
278
  > /opt/intel/dlstreamer/gstreamer/lib/python3/dist-packages:${PYTHONPATH:-}
279
  > ```
280
 
281
+ The full sample is in [dlstreamer_sample.py](dlstreamer_sample.py); run it with `python3 dlstreamer_sample.py` or copy the code below.
282
+
283
+ <!-- sample: dlstreamer_sample.py -->
284
  ```python
285
+ # SPDX-License-Identifier: MIT
286
+ # Copyright (C) Intel Corporation
287
+ #
288
+ # Object Counting: DLStreamer pipeline sample.
289
+ # See README.md for prerequisites and inputs. Run: python3 dlstreamer_sample.py
290
+
291
  from collections import Counter
292
 
293
  import gi
 
304
  # a cyclist, and cars.
305
  CLASSES_OF_INTEREST = {"person", "bicycle", "car"}
306
 
307
+ # Select the model: "yolo26n" (default) or "detr-resnet-50".
308
+ MODEL = "yolo26n"
309
+
310
  # For CPU: change device=GPU to device=CPU.
311
  # For NPU: change device=GPU to device=NPU (batch-size=1, nireq=4 recommended).
312
  pipeline_str = (
313
  f"filesrc location={INPUT_VIDEO} ! decodebin3 ! "
314
  "videoconvert ! "
315
+ f"gvadetect model={MODEL}_openvino_model/{MODEL}.xml "
316
  "device=GPU "
317
  "threshold=0.4 ! queue ! "
318
  "gvawatermark ! videoconvert ! video/x-raw,format=I420 ! "
 
354
  )
355
  pipeline.set_state(Gst.State.NULL)
356
  ```
357
+ <!-- /sample -->
358
 
359
  **Device targets:**
360
 
 
375
  ## References
376
 
377
  - [YOLO26 Documentation](https://docs.ultralytics.com/models/yolo26/)
378
+ - [DETR Model Card (facebook/detr-resnet-50)](https://huggingface.co/facebook/detr-resnet-50)
379
+ - [End-to-End Object Detection with Transformers (DETR paper)](https://arxiv.org/abs/2005.12872)
380
  - [Ultralytics Object Counting Guide](https://docs.ultralytics.com/guides/object-counting/)
381
+ - [Intel DLStreamer gvadetect](https://docs.openedgeplatform.intel.com/dev/edge-ai-libraries/dlstreamer/elements/gvadetect.html)
382
  - [OpenVINO Documentation](https://docs.openvino.ai/)
383
  - [NNCF Post-Training Quantization](https://docs.openvino.ai/latest/nncf_ptq_introduction.html)
384
+ - [Intel DLStreamer](https://docs.openedgeplatform.intel.com/dev/edge-ai-libraries/dlstreamer/index.html)
config.json CHANGED
@@ -3,8 +3,8 @@
3
  "library_name": "openvino",
4
  "pipeline_tag": "object-detection",
5
  "category": "Object Detection + Counting (80-class COCO)",
6
- "base_model": "YOLO26 (Ultralytics)",
7
- "source_framework": "PyTorch (Ultralytics)",
8
  "supported_precisions": [
9
  "FP32",
10
  "FP16",
@@ -18,5 +18,13 @@
18
  ],
19
  "detected_classes": "All 80 COCO classes (counted per class)",
20
  "hf_repo_id": "Intel/object-counting",
21
- "description": "Object counting use case using YOLO26 INT8 to detect and report per-class counts across all 80 COCO classes"
 
 
 
 
 
 
 
 
22
  }
 
3
  "library_name": "openvino",
4
  "pipeline_tag": "object-detection",
5
  "category": "Object Detection + Counting (80-class COCO)",
6
+ "base_model": "YOLO26 (Ultralytics, default) or DETR facebook/detr-resnet-50 (alternative)",
7
+ "source_framework": "PyTorch (Ultralytics, Hugging Face Transformers)",
8
  "supported_precisions": [
9
  "FP32",
10
  "FP16",
 
18
  ],
19
  "detected_classes": "All 80 COCO classes (counted per class)",
20
  "hf_repo_id": "Intel/object-counting",
21
+ "description": "Object counting use case using YOLO26 (default) or DETR to detect and report per-class counts across all 80 COCO classes",
22
+ "selectable_models": [
23
+ "yolo26n",
24
+ "yolo26s",
25
+ "yolo26m",
26
+ "yolo26l",
27
+ "detr-resnet-50"
28
+ ],
29
+ "default_model": "yolo26n"
30
  }
dlstreamer_sample.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (C) Intel Corporation
3
+ #
4
+ # Object Counting: DLStreamer pipeline sample.
5
+ # See README.md for prerequisites and inputs. Run: python3 dlstreamer_sample.py
6
+
7
+ from collections import Counter
8
+
9
+ import gi
10
+
11
+ gi.require_version("Gst", "1.0")
12
+ gi.require_version("GstAnalytics", "1.0")
13
+ from gi.repository import Gst, GLib, GstAnalytics
14
+
15
+ Gst.init([])
16
+
17
+ INPUT_VIDEO = "test_video.mp4"
18
+
19
+ # Only these classes are counted; the sample video contains pedestrians,
20
+ # a cyclist, and cars.
21
+ CLASSES_OF_INTEREST = {"person", "bicycle", "car"}
22
+
23
+ # Select the model: "yolo26n" (default) or "detr-resnet-50".
24
+ MODEL = "yolo26n"
25
+
26
+ # For CPU: change device=GPU to device=CPU.
27
+ # For NPU: change device=GPU to device=NPU (batch-size=1, nireq=4 recommended).
28
+ pipeline_str = (
29
+ f"filesrc location={INPUT_VIDEO} ! decodebin3 ! "
30
+ "videoconvert ! "
31
+ f"gvadetect model={MODEL}_openvino_model/{MODEL}.xml "
32
+ "device=GPU "
33
+ "threshold=0.4 ! queue ! "
34
+ "gvawatermark ! videoconvert ! video/x-raw,format=I420 ! "
35
+ "openh264enc ! h264parse ! "
36
+ "mp4mux ! filesink name=sink location=output_dlstreamer.mp4"
37
+ )
38
+ pipeline = Gst.parse_launch(pipeline_str)
39
+
40
+
41
+ def on_buffer(pad, info):
42
+ buf = info.get_buffer()
43
+ rmeta = GstAnalytics.buffer_get_analytics_relation_meta(buf)
44
+ if rmeta is None:
45
+ return Gst.PadProbeReturn.OK
46
+ counts = Counter()
47
+ idx = 1
48
+ while True:
49
+ ok, od = rmeta.get_od_mtd(idx)
50
+ if not ok:
51
+ break
52
+ label = GLib.quark_to_string(od.get_obj_type())
53
+ if label in CLASSES_OF_INTEREST:
54
+ counts[label] += 1
55
+ idx += 1
56
+ if counts:
57
+ summary = ", ".join(f"{n} {name}" for name, n in counts.items())
58
+ print(f"Object counts: {summary}", flush=True)
59
+ return Gst.PadProbeReturn.OK
60
+
61
+
62
+ sink = pipeline.get_by_name("sink")
63
+ sink.get_static_pad("sink").add_probe(Gst.PadProbeType.BUFFER, on_buffer)
64
+
65
+ pipeline.set_state(Gst.State.PLAYING)
66
+ bus = pipeline.get_bus()
67
+ bus.timed_pop_filtered(
68
+ Gst.CLOCK_TIME_NONE,
69
+ Gst.MessageType.EOS | Gst.MessageType.ERROR,
70
+ )
71
+ pipeline.set_state(Gst.State.NULL)
export_and_quantize.sh CHANGED
@@ -2,9 +2,19 @@
2
  # SPDX-License-Identifier: MIT
3
  # Copyright (C) Intel Corporation
4
  #
5
- # Export a YOLO26 detector to OpenVINO IR for the object-counting use case.
6
  # Usage: ./export_and_quantize.sh [MODEL_VARIANT] [PRECISION]
7
  # Example: ./export_and_quantize.sh yolo26n FP16
 
 
 
 
 
 
 
 
 
 
8
  #
9
  # Supported precisions:
10
  # FP32 -- Full-precision floating-point weights
@@ -29,12 +39,20 @@ if [[ "${PRECISION}" != "FP32" && "${PRECISION}" != "FP16" && "${PRECISION}" !=
29
  exit 1
30
  fi
31
 
32
- echo "--- Installing dependencies ---"
33
- if [[ "${PRECISION}" == "INT8" ]]; then
34
- pip install -qU openvino nncf ultralytics
35
- else
36
- pip install -qU openvino ultralytics
37
- fi
 
 
 
 
 
 
 
 
38
 
39
  # Ask for approval before downloading models and sample files
40
  echo ""
@@ -59,6 +77,17 @@ else
59
  echo "WARNING: HuggingFace tracking ping failed (offline?); continuing." >&2
60
  fi
61
  echo ""
 
 
 
 
 
 
 
 
 
 
 
62
  echo "--- Downloading sample test image ---"
63
  if [[ ! -f test.jpg ]]; then
64
  wget -q -O test.jpg https://ultralytics.com/images/bus.jpg
@@ -84,14 +113,92 @@ else
84
  EXPORT_LABEL="FP16"
85
  fi
86
 
87
- echo "--- Exporting ${MODEL_NAME} to OpenVINO IR (${EXPORT_LABEL}) ---"
88
- python3 -c "
 
89
  from ultralytics import YOLO
90
 
91
  model = YOLO('${MODEL_NAME}.pt')
92
- model.export(format='openvino', half=${HALF_FLAG}, dynamic=False, imgsz=640)
93
  print('Export complete: ${MODEL_NAME}_openvino_model/')
94
  "
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
  if [[ "${PRECISION}" == "INT8" ]]; then
97
  echo "--- Quantizing to INT8 with NNCF ---"
@@ -106,7 +213,7 @@ model = core.read_model('${MODEL_NAME}_openvino_model/${MODEL_NAME}.xml')
106
 
107
  # Use the downloaded test image for calibration instead of random noise.
108
  img = cv2.imread('test.jpg')
109
- img = cv2.resize(img, (640, 640))
110
  img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0
111
  img = img.transpose(2, 0, 1)[np.newaxis, ...] # NCHW
112
 
@@ -120,6 +227,7 @@ quantized = nncf.quantize(
120
  calibration_dataset,
121
  preset=nncf.QuantizationPreset.MIXED,
122
  subset_size=300,
 
123
  )
124
 
125
  ov.save_model(quantized, '${MODEL_NAME}_objcount_int8.xml')
 
2
  # SPDX-License-Identifier: MIT
3
  # Copyright (C) Intel Corporation
4
  #
5
+ # Export a YOLO26 or DETR detector to OpenVINO IR for the object-counting use case.
6
  # Usage: ./export_and_quantize.sh [MODEL_VARIANT] [PRECISION]
7
  # Example: ./export_and_quantize.sh yolo26n FP16
8
+ # Example: ./export_and_quantize.sh detr-resnet-50 FP16
9
+ #
10
+ # Supported model variants:
11
+ # yolo26n, yolo26s, yolo26m, yolo26l -- Ultralytics YOLO26 (default)
12
+ # detr-resnet-50 -- Facebook DETR (Hugging Face)
13
+ #
14
+ # DETR is exported end-to-end: ImageNet normalization, softmax,
15
+ # box decoding, and the COCO-91 to COCO-80 class remap are embedded in the
16
+ # graph, so the IR produces the same [1, N, 6] output layout and class ids as
17
+ # YOLO26 and runs with the same OpenVINO and DLStreamer samples.
18
  #
19
  # Supported precisions:
20
  # FP32 -- Full-precision floating-point weights
 
39
  exit 1
40
  fi
41
 
42
+ case "${MODEL_NAME}" in
43
+ yolo26n|yolo26s|yolo26m|yolo26l)
44
+ MODEL_FAMILY="yolo"
45
+ INPUT_SIZE=640
46
+ ;;
47
+ detr-resnet-50)
48
+ MODEL_FAMILY="detr"
49
+ INPUT_SIZE=800
50
+ ;;
51
+ *)
52
+ echo "ERROR: unsupported model '${MODEL_NAME}'. Choose yolo26[n|s|m|l] or detr-resnet-50." >&2
53
+ exit 1
54
+ ;;
55
+ esac
56
 
57
  # Ask for approval before downloading models and sample files
58
  echo ""
 
77
  echo "WARNING: HuggingFace tracking ping failed (offline?); continuing." >&2
78
  fi
79
  echo ""
80
+ echo "--- Installing dependencies ---"
81
+ if [[ "${MODEL_FAMILY}" == "detr" ]]; then
82
+ DEPS=(openvino transformers torch pyyaml opencv-python)
83
+ else
84
+ DEPS=(openvino ultralytics)
85
+ fi
86
+ if [[ "${PRECISION}" == "INT8" ]]; then
87
+ DEPS+=(nncf)
88
+ fi
89
+ pip install -qU "${DEPS[@]}"
90
+
91
  echo "--- Downloading sample test image ---"
92
  if [[ ! -f test.jpg ]]; then
93
  wget -q -O test.jpg https://ultralytics.com/images/bus.jpg
 
113
  EXPORT_LABEL="FP16"
114
  fi
115
 
116
+ if [[ "${MODEL_FAMILY}" == "yolo" ]]; then
117
+ echo "--- Exporting ${MODEL_NAME} to OpenVINO IR (${EXPORT_LABEL}) ---"
118
+ python3 -c "
119
  from ultralytics import YOLO
120
 
121
  model = YOLO('${MODEL_NAME}.pt')
122
+ model.export(format='openvino', half=${HALF_FLAG}, dynamic=False, imgsz=${INPUT_SIZE}, nms=False)
123
  print('Export complete: ${MODEL_NAME}_openvino_model/')
124
  "
125
+ else
126
+ echo "--- Exporting facebook/${MODEL_NAME} to OpenVINO IR (${EXPORT_LABEL}) ---"
127
+ python3 - "${MODEL_NAME}" "${HALF_FLAG}" "${INPUT_SIZE}" <<'PYEXPORT'
128
+ import sys
129
+ from pathlib import Path
130
+
131
+ import openvino as ov
132
+ import torch
133
+ import yaml
134
+ from transformers import DetrForObjectDetection
135
+
136
+ model_name, half_flag, size = sys.argv[1], sys.argv[2] == "True", int(sys.argv[3])
137
+ model_id = f"facebook/{model_name}"
138
+
139
+ # The "no_timm" revision uses the transformers ResNet backbone (no timm dependency).
140
+ detr = DetrForObjectDetection.from_pretrained(model_id, revision="no_timm").eval()
141
+ id2label = detr.config.id2label
142
+ # DETR predicts 91 COCO category ids (with "N/A" gaps) plus a no-object class.
143
+ # Keep the 80 real categories so class ids match YOLO26 (person = 0).
144
+ coco80 = [k for k in sorted(id2label) if id2label[k] != "N/A"]
145
+ assert len(coco80) == 80, f"unexpected label map with {len(coco80)} classes"
146
+
147
+
148
+ class DetrEndToEnd(torch.nn.Module):
149
+ """Input: RGB [0, 1] NCHW. Output: [1, 100, 6] = [x1, y1, x2, y2, conf, class_id] in input pixels."""
150
+
151
+ def __init__(self, model: torch.nn.Module) -> None:
152
+ super().__init__()
153
+ self.model = model
154
+ self.register_buffer("mean", torch.tensor([0.485, 0.456, 0.406]).view(1, 3, 1, 1))
155
+ self.register_buffer("std", torch.tensor([0.229, 0.224, 0.225]).view(1, 3, 1, 1))
156
+ self.register_buffer("coco80", torch.tensor(coco80))
157
+
158
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
159
+ out = self.model(pixel_values=(x - self.mean) / self.std, return_dict=True)
160
+ probs = out.logits.softmax(-1).index_select(-1, self.coco80)
161
+ conf, cls = probs.max(-1)
162
+ cx, cy, w, h = (out.pred_boxes * size).unbind(-1)
163
+ return torch.stack([cx - w / 2, cy - h / 2, cx + w / 2, cy + h / 2, conf, cls.float()], -1)
164
+
165
+
166
+ ov_model = ov.convert_model(
167
+ DetrEndToEnd(detr), example_input=torch.zeros(1, 3, size, size), input=[1, 3, size, size]
168
+ )
169
+ ov_model.outputs[0].get_tensor().set_names({"output0"})
170
+
171
+ names = {i: id2label[k] for i, k in enumerate(coco80)}
172
+ # DLStreamer reads preprocessing and the post-processor type from model_info.
173
+ model_info = {
174
+ "model_type": "YOLO",
175
+ "labels": " ".join(n.replace(" ", "_") for n in names.values()),
176
+ "resize_type": "standard",
177
+ "reverse_input_channels": "YES",
178
+ "scale_values": "255",
179
+ "iou_threshold": "0.7",
180
+ }
181
+ for key, value in model_info.items():
182
+ ov_model.set_rt_info(value, ["model_info", key])
183
+
184
+ out_dir = Path(f"{model_name}_openvino_model")
185
+ out_dir.mkdir(exist_ok=True)
186
+ ov.save_model(ov_model, out_dir / f"{model_name}.xml", compress_to_fp16=half_flag)
187
+ metadata = {
188
+ "description": f"{model_id} exported end-to-end with YOLO26-compatible output",
189
+ "license": "Apache-2.0",
190
+ "task": "detect",
191
+ "batch": 1,
192
+ "imgsz": [size, size],
193
+ "names": names,
194
+ "channels": 3,
195
+ "end2end": True,
196
+ }
197
+ with open(out_dir / "metadata.yaml", "w", encoding="utf-8") as f:
198
+ yaml.safe_dump(metadata, f, sort_keys=False)
199
+ print(f"Export complete: {out_dir}/")
200
+ PYEXPORT
201
+ fi
202
 
203
  if [[ "${PRECISION}" == "INT8" ]]; then
204
  echo "--- Quantizing to INT8 with NNCF ---"
 
213
 
214
  # Use the downloaded test image for calibration instead of random noise.
215
  img = cv2.imread('test.jpg')
216
+ img = cv2.resize(img, (${INPUT_SIZE}, ${INPUT_SIZE}))
217
  img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0
218
  img = img.transpose(2, 0, 1)[np.newaxis, ...] # NCHW
219
 
 
227
  calibration_dataset,
228
  preset=nncf.QuantizationPreset.MIXED,
229
  subset_size=300,
230
+ model_type=nncf.ModelType.TRANSFORMER if '${MODEL_FAMILY}' == 'detr' else None,
231
  )
232
 
233
  ov.save_model(quantized, '${MODEL_NAME}_objcount_int8.xml')
openvino_sample.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: MIT
2
+ # Copyright (C) Intel Corporation
3
+ #
4
+ # Object Counting: OpenVINO Runtime sample.
5
+ # See README.md for prerequisites and inputs. Run: python3 openvino_sample.py
6
+
7
+ from collections import Counter
8
+
9
+ import cv2
10
+ import numpy as np
11
+ import openvino as ov
12
+
13
+ CONF_THRESHOLD = 0.4
14
+ # Select the model: "yolo26n" (default) or "detr-resnet-50".
15
+ MODEL = "yolo26n"
16
+ INPUT_SIZE = 800 if MODEL.startswith("detr") else 640
17
+
18
+ core = ov.Core()
19
+ model = core.read_model(f"{MODEL}_openvino_model/{MODEL}.xml")
20
+
21
+ # YOLO26 embeds the 80 COCO class names in rt_info -- read them instead of
22
+ # hardcoding the list. Ultralytics separates multi-word names with
23
+ # underscores (e.g. "traffic_light"), so restore spaces for display.
24
+ COCO_NAMES = [
25
+ name.replace("_", " ")
26
+ for name in model.get_rt_info()["model_info"]["labels"].value.split()
27
+ ]
28
+
29
+ # Change device to "GPU" or "NPU" to run on integrated GPU or NPU.
30
+ compiled = core.compile_model(model, "CPU")
31
+
32
+ image = cv2.imread("test.jpg")
33
+ h0, w0 = image.shape[:2]
34
+
35
+ blob = cv2.resize(image, (INPUT_SIZE, INPUT_SIZE))
36
+ blob = cv2.cvtColor(blob, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0
37
+ blob = blob.transpose(2, 0, 1)[np.newaxis, ...] # NCHW
38
+
39
+ # End-to-end output: [1, N, 6] = [x1, y1, x2, y2, confidence, class_id]
40
+ # (N = 300 for YOLO26, 100 for DETR)
41
+ output = compiled([blob])[compiled.output(0)][0]
42
+ dets = output[output[:, 4] >= CONF_THRESHOLD]
43
+
44
+ sx, sy = w0 / INPUT_SIZE, h0 / INPUT_SIZE
45
+ counts = Counter(COCO_NAMES[int(d[5])] for d in dets)
46
+
47
+ print(f"Total objects: {len(dets)}")
48
+ print("Object counts:")
49
+ for name, n in sorted(counts.items(), key=lambda kv: (-kv[1], kv[0])):
50
+ print(f" {name}: {n}")
51
+
52
+ colors = np.random.RandomState(42).randint(0, 255, (80, 3)).tolist()
53
+ for det in dets:
54
+ x1, y1, x2, y2 = (int(det[0] * sx), int(det[1] * sy),
55
+ int(det[2] * sx), int(det[3] * sy))
56
+ cid = int(det[5])
57
+ cv2.rectangle(image, (x1, y1), (x2, y2), colors[cid], 2)
58
+ cv2.putText(image, COCO_NAMES[cid], (x1, y1 - 5),
59
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, colors[cid], 2)
60
+
61
+ summary = ", ".join(f"{n} {name}" for name, n in counts.items())
62
+ cv2.putText(image, summary[:60], (10, 30),
63
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
64
+ cv2.imwrite("output_openvino.jpg", image)