vagheshpatel commited on
Commit
ae0ee67
·
verified ·
1 Parent(s): 6937001

Sync object-classification from metro-analytics-catalog

Browse files
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ expected_output_dlstreamer.gif filter=lfs diff=lfs merge=lfs -text
37
+ expected_output_openvino.gif filter=lfs diff=lfs merge=lfs -text
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) Intel Corporation.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE
README.md ADDED
@@ -0,0 +1,388 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ license_link: LICENSE
4
+ library_name: openvino
5
+ pipeline_tag: object-detection
6
+ tags:
7
+ - openvino
8
+ - intel
9
+ - yolo
10
+ - yolo26
11
+ - object-classification
12
+ - classification
13
+ - smart-city
14
+ - situational-awareness
15
+ - traffic
16
+ - edge-ai
17
+ - metro
18
+ - dlstreamer
19
+ language:
20
+ - en
21
+ ---
22
+
23
+ # Object Classification
24
+
25
+ | Property | Value |
26
+ |---|---|
27
+ | **Category** | Object Classification (Traffic Categorization: People / Vehicles) |
28
+ | **Base Model** | [YOLO26](https://docs.ultralytics.com/models/yolo26/) (Ultralytics) |
29
+ | **Source Framework** | PyTorch (Ultralytics) |
30
+ | **Supported Precisions** | FP32, FP16, INT8 (mixed-precision) |
31
+ | **Inference Engine** | OpenVINO |
32
+ | **Hardware** | CPU, GPU, NPU |
33
+ | **Detected Class(es)** | `person` and vehicle classes grouped into `People` and `Vehicles` |
34
+
35
+ ---
36
+
37
+ ## Overview
38
+
39
+ Object Classification is a Metro Analytics use case that detects objects with [YOLO26](https://docs.ultralytics.com/models/yolo26/) and then categorizes each detection into higher-level city-operations groups.
40
+ It is built on the state-of-the-art YOLO26 real-time detector, quantized to INT8 for efficient inference on Intel hardware.
41
+ Where the general object-detection use case reports every one of the 80 COCO classes individually, this use case rolls the traffic-relevant classes up into two semantic categories, `People` and `Vehicles`, so operators get an at-a-glance picture of a scene.
42
+
43
+ The traffic categories are:
44
+
45
+ - **People** -- the COCO `person` class.
46
+ - **Vehicles** -- the COCO `bicycle`, `car`, `motorcycle`, `bus`, `train`, and `truck` classes.
47
+
48
+ Objects outside these categories are ignored to keep the output focused on traffic situational awareness.
49
+
50
+ Typical Metro deployments include:
51
+
52
+ - **Situational Awareness** -- summarize each camera feed as live People and Vehicles counts.
53
+ - **Automated City Operations** -- feed category counts into signal timing, congestion, and dispatch logic.
54
+ - **Intersection and Roundabout Monitoring** -- track the mix of pedestrians and vehicles at busy junctions.
55
+ - **Trend Analytics** -- aggregate category counts over time to understand traffic patterns.
56
+
57
+ Available variants: `yolo26n`, `yolo26s`, `yolo26m`, `yolo26l`, `yolo26x`.
58
+ Smaller variants (`yolo26n`, `yolo26s`) are recommended for high-FPS edge deployment; larger variants improve recall for small objects.
59
+
60
+ ---
61
+
62
+ ## Prerequisites
63
+
64
+ - Python 3.11+
65
+ - [Install OpenVINO](https://docs.openvino.ai/2026/get-started/install-openvino.html) (latest version)
66
+ - [Install Intel DLStreamer](https://docs.openedgeplatform.intel.com/2026.0/edge-ai-libraries/dlstreamer/get_started/install/install_guide_ubuntu.html) (latest version)
67
+
68
+ Create and activate a Python virtual environment before running the scripts:
69
+
70
+ ```bash
71
+ python3 -m venv .venv --system-site-packages
72
+ source .venv/bin/activate
73
+ ```
74
+
75
+ > **Note:** The `--system-site-packages` flag is required so the virtual
76
+ > environment can access the system-installed OpenVINO and DLStreamer Python
77
+ > packages.
78
+
79
+ ---
80
+
81
+ ## Getting Started
82
+
83
+ ### Download and Quantize Model
84
+
85
+ Run the provided script to download, export to OpenVINO IR, and optionally quantize:
86
+
87
+ ```bash
88
+ chmod +x export_and_quantize.sh
89
+ ./export_and_quantize.sh
90
+ ```
91
+
92
+ This exports the default **yolo26n** model in **FP16** precision.
93
+
94
+ #### Optional: Select a Different Variant or Precision
95
+
96
+ ```bash
97
+ ./export_and_quantize.sh yolo26n FP32 # full-precision
98
+ ./export_and_quantize.sh yolo26n INT8 # quantized
99
+ ./export_and_quantize.sh yolo26s # larger variant, default FP16
100
+ ```
101
+
102
+ Replace `yolo26n` with any variant (`yolo26s`, `yolo26m`, `yolo26l`, `yolo26x`).
103
+ The second argument selects the precision (`FP32`, `FP16`, `INT8`); the default is **FP16**.
104
+
105
+ The script performs the following steps:
106
+
107
+ 1. Installs dependencies (`openvino`, `ultralytics`; adds `nncf` for INT8).
108
+ 2. Downloads a sample traffic video (`test_video.mp4`) of an urban roundabout at low resolution (640x360).
109
+ 3. Downloads the PyTorch weights and exports to OpenVINO IR.
110
+ 4. *(INT8 only)* Quantizes the model using NNCF post-training quantization.
111
+
112
+ Output files:
113
+
114
+ - `yolo26n_openvino_model/` -- FP32 or FP16 OpenVINO IR model directory.
115
+ - `yolo26n_objcls_int8.xml` / `yolo26n_objcls_int8.bin` -- INT8 quantized model *(only when `INT8` is selected)*.
116
+
117
+ #### Precision / Device Compatibility
118
+
119
+ | Precision | CPU | GPU | NPU |
120
+ |---|---|---|---|
121
+ | FP32 | Yes | Yes | No |
122
+ | FP16 | Yes | Yes | Yes |
123
+ | INT8 | Yes | Yes | Yes |
124
+
125
+ > **Note:** The INT8 calibration uses a frame from the bundled sample video.
126
+ > For production accuracy, replace it with a representative set of frames from
127
+ > the target deployment site.
128
+
129
+ ### OpenVINO Sample
130
+
131
+ The sample below runs YOLO26 inference on the sample traffic video, maps each
132
+ detection into the `People` or `Vehicles` category, draws boxes colored per
133
+ category, overlays live category counts, and writes the annotated result to
134
+ `output_openvino.mp4`.
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
+ import cv2
140
+ import numpy as np
141
+ import openvino as ov
142
+
143
+ CONF_THRESHOLD = 0.4
144
+ INPUT_SIZE = 640
145
+
146
+ # Map the traffic-relevant COCO class ids into higher-level city categories.
147
+ # People and Vehicles are the two categories tracked for situational awareness.
148
+ CATEGORY_BY_CLASS_ID = {
149
+ 0: "People", # person
150
+ 1: "Vehicles", # bicycle
151
+ 2: "Vehicles", # car
152
+ 3: "Vehicles", # motorcycle
153
+ 5: "Vehicles", # bus
154
+ 6: "Vehicles", # train
155
+ 7: "Vehicles", # truck
156
+ }
157
+ # BGR overlay colors for each category.
158
+ CATEGORY_COLORS = {
159
+ "People": (0, 200, 0),
160
+ "Vehicles": (255, 128, 0),
161
+ }
162
+
163
+ core = ov.Core()
164
+ model = core.read_model("yolo26n_openvino_model/yolo26n.xml")
165
+
166
+ # YOLO26 embeds the 80 COCO class names in rt_info. Ultralytics separates
167
+ # multi-word names with underscores (e.g. "traffic_light"), so restore spaces.
168
+ COCO_NAMES = [
169
+ name.replace("_", " ")
170
+ for name in model.get_rt_info()["model_info"]["labels"].value.split()
171
+ ]
172
+
173
+ # Change device to "GPU" or "NPU" to run on integrated GPU or NPU.
174
+ compiled = core.compile_model(model, "CPU")
175
+ output_port = compiled.output(0)
176
+
177
+ cap = cv2.VideoCapture("test_video.mp4")
178
+ fps = cap.get(cv2.CAP_PROP_FPS) or 30.0
179
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
180
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
181
+ writer = cv2.VideoWriter(
182
+ "output_openvino.mp4", cv2.VideoWriter_fourcc(*"mp4v"), fps, (width, height)
183
+ )
184
+
185
+ totals = {"People": 0, "Vehicles": 0}
186
+ frame_idx = 0
187
+ while True:
188
+ ok, frame = cap.read()
189
+ if not ok:
190
+ break
191
+ frame_idx += 1
192
+
193
+ blob = cv2.resize(frame, (INPUT_SIZE, INPUT_SIZE))
194
+ blob = cv2.cvtColor(blob, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0
195
+ blob = blob.transpose(2, 0, 1)[np.newaxis, ...] # NCHW
196
+
197
+ # YOLO26 end-to-end output: [1, 300, 6] = [x1, y1, x2, y2, confidence, class_id].
198
+ output = compiled([blob])[output_port][0]
199
+
200
+ sx, sy = width / INPUT_SIZE, height / INPUT_SIZE
201
+ counts = {"People": 0, "Vehicles": 0}
202
+ for x1, y1, x2, y2, conf, class_id in output:
203
+ if conf < CONF_THRESHOLD:
204
+ continue
205
+ category = CATEGORY_BY_CLASS_ID.get(int(class_id))
206
+ if category is None:
207
+ continue # not a traffic-relevant object
208
+ counts[category] += 1
209
+ totals[category] += 1
210
+ color = CATEGORY_COLORS[category]
211
+ px1, py1 = int(x1 * sx), int(y1 * sy)
212
+ px2, py2 = int(x2 * sx), int(y2 * sy)
213
+ label = f"{category}: {COCO_NAMES[int(class_id)]} {conf:.2f}"
214
+ cv2.rectangle(frame, (px1, py1), (px2, py2), color, 2)
215
+ cv2.putText(frame, label, (px1, py1 - 5),
216
+ cv2.FONT_HERSHEY_SIMPLEX, 2.0, color, 2)
217
+
218
+ # Overlay the per-category counts for this frame.
219
+ banner = f"People: {counts['People']} Vehicles: {counts['Vehicles']}"
220
+ cv2.rectangle(frame, (0, 0), (width, 60), (0, 0, 0), -1)
221
+ cv2.putText(frame, banner, (15, 45),
222
+ cv2.FONT_HERSHEY_SIMPLEX, 2.0, (255, 255, 255), 2)
223
+
224
+ if frame_idx % 30 == 0:
225
+ print(f"frame {frame_idx}: {banner}", flush=True)
226
+
227
+ writer.write(frame)
228
+
229
+ cap.release()
230
+ writer.release()
231
+ print(f"Summary: People={totals['People']} Vehicles={totals['Vehicles']}")
232
+ print("Saved: output_openvino.mp4")
233
+ ```
234
+
235
+ **Device targets:**
236
+
237
+ - `"CPU"` -- default, works on all Intel platforms.
238
+ - `"GPU"` -- Intel integrated or discrete GPU.
239
+ - `"NPU"` -- Intel NPU (validate with `benchmark_app -d NPU`).
240
+
241
+ ### Try It on a Sample Video
242
+
243
+ The `export_and_quantize.sh` script downloads `test_video.mp4` automatically.
244
+ Re-run the OpenVINO sample above.
245
+ The script reads `test_video.mp4`, prints the running People and Vehicles counts to the console, and writes the annotated video to `output_openvino.mp4`.
246
+
247
+ Expected console output (representative):
248
+
249
+ ```text
250
+ frame 30: People: 4 Vehicles: 6
251
+ frame 60: People: 3 Vehicles: 7
252
+ frame 90: People: 5 Vehicles: 5
253
+ Summary: People=372 Vehicles=548
254
+ Saved: output_openvino.mp4
255
+ ```
256
+
257
+ #### Expected Output
258
+
259
+ ![OpenVINO expected output](expected_output_openvino.gif)
260
+
261
+ ### DLStreamer Sample
262
+
263
+ The pipeline below runs the FP16 YOLO26 detector on the sample video via
264
+ `gvadetect`, overlays bounding boxes with `gvawatermark` for the traffic-relevant
265
+ classes only (non-traffic detections such as `handbag` are filtered out via
266
+ `show-roi`), saves the annotated result to `output_dlstreamer.mp4`, and prints the
267
+ `People` and `Vehicles` category counts per frame from the detection metadata.
268
+
269
+ > **Notes on running this sample:**
270
+ >
271
+ > - Use the FP16 IR (`yolo26n_openvino_model/yolo26n.xml`). Class names are
272
+ > read automatically from the model's embedded `metadata.yaml` by
273
+ > DLStreamer 2026.0+ -- no external `labels-file` is required.
274
+ > - Export `PYTHONPATH` so the DLStreamer Python module is importable:
275
+ >
276
+ > ```bash
277
+ > source /opt/intel/openvino_2026/setupvars.sh
278
+ > source /opt/intel/dlstreamer/scripts/setup_dls_env.sh
279
+ > export PYTHONPATH=/opt/intel/dlstreamer/python:\
280
+ > /opt/intel/dlstreamer/gstreamer/lib/python3/dist-packages:${PYTHONPATH:-}
281
+ > ```
282
+
283
+ ```python
284
+ import gi
285
+
286
+ gi.require_version("Gst", "1.0")
287
+ gi.require_version("GstAnalytics", "1.0")
288
+ from gi.repository import Gst, GLib, GstAnalytics
289
+
290
+ Gst.init([])
291
+
292
+ INPUT_VIDEO = "test_video.mp4"
293
+
294
+ # Traffic-relevant COCO labels grouped into higher-level city categories.
295
+ CATEGORY_BY_LABEL = {
296
+ "person": "People",
297
+ "bicycle": "Vehicles",
298
+ "car": "Vehicles",
299
+ "motorcycle": "Vehicles",
300
+ "bus": "Vehicles",
301
+ "train": "Vehicles",
302
+ "truck": "Vehicles",
303
+ }
304
+
305
+ # For CPU: change device=GPU to device=CPU.
306
+ # For NPU: change device=GPU to device=NPU (batch-size=1, nireq=4 recommended).
307
+ # gvawatermark displ-cfg:
308
+ # show-roi=... draws only the traffic-relevant classes (person + vehicles),
309
+ # so non-traffic detections such as handbag/backpack are not boxed.
310
+ # font-scale=1.5 enlarges the label text for better visualization.
311
+ pipeline_str = (
312
+ f"filesrc location={INPUT_VIDEO} ! decodebin3 ! "
313
+ "videoconvert ! "
314
+ "gvadetect model=yolo26n_openvino_model/yolo26n.xml "
315
+ "device=GPU "
316
+ "threshold=0.4 ! queue ! "
317
+ "gvawatermark "
318
+ "displ-cfg=show-roi=person:bicycle:car:motorcycle:bus:train:truck,font-scale=2.5 ! "
319
+ "videoconvert ! video/x-raw,format=I420 ! "
320
+ "openh264enc ! h264parse ! "
321
+ "mp4mux ! filesink name=sink location=output_dlstreamer.mp4"
322
+ )
323
+ pipeline = Gst.parse_launch(pipeline_str)
324
+
325
+ totals = {"People": 0, "Vehicles": 0}
326
+
327
+
328
+ def on_buffer(pad, info):
329
+ buf = info.get_buffer()
330
+ rmeta = GstAnalytics.buffer_get_analytics_relation_meta(buf)
331
+ if rmeta is None:
332
+ return Gst.PadProbeReturn.OK
333
+ counts = {"People": 0, "Vehicles": 0}
334
+ idx = 1
335
+ while True:
336
+ ok, od = rmeta.get_od_mtd(idx)
337
+ if not ok:
338
+ break
339
+ label = GLib.quark_to_string(od.get_obj_type())
340
+ category = CATEGORY_BY_LABEL.get(label)
341
+ if category is not None:
342
+ counts[category] += 1
343
+ totals[category] += 1
344
+ idx += 1
345
+ if counts["People"] or counts["Vehicles"]:
346
+ print(f"frame: People={counts['People']} Vehicles={counts['Vehicles']}",
347
+ flush=True)
348
+ return Gst.PadProbeReturn.OK
349
+
350
+
351
+ sink = pipeline.get_by_name("sink")
352
+ sink_pad = sink.get_static_pad("sink")
353
+ sink_pad.add_probe(Gst.PadProbeType.BUFFER, on_buffer)
354
+
355
+ pipeline.set_state(Gst.State.PLAYING)
356
+ bus = pipeline.get_bus()
357
+ bus.timed_pop_filtered(
358
+ Gst.CLOCK_TIME_NONE,
359
+ Gst.MessageType.EOS | Gst.MessageType.ERROR,
360
+ )
361
+ pipeline.set_state(Gst.State.NULL)
362
+ print(f"Summary: People={totals['People']} Vehicles={totals['Vehicles']}")
363
+ ```
364
+
365
+ #### Expected Output
366
+
367
+ ![DLStreamer expected output](expected_output_dlstreamer.gif)
368
+
369
+ **Device targets:**
370
+
371
+ - `device=GPU` -- default in the sample code.
372
+ - `device=CPU` -- change `device=GPU` to `device=CPU`.
373
+ - `device=NPU` -- change `device=GPU` to `device=NPU`; use `batch-size=1` and `nireq=4` for best NPU utilization.
374
+
375
+ ---
376
+
377
+ ## License
378
+
379
+ Licensed under the MIT License. See [LICENSE](LICENSE) for details.
380
+
381
+ ## References
382
+
383
+ - [YOLO26 Documentation](https://docs.ultralytics.com/models/yolo26/)
384
+ - [OpenVINO YOLO26 Notebook](https://github.com/openvinotoolkit/openvino_notebooks/blob/latest/notebooks/yolov26-optimization/yolov26-object-detection.ipynb)
385
+ - [Sample video: Urban roundabout with cars and pedestrian (Pexels)](https://www.pexels.com/video/urban-roundabout-with-cars-and-pedestrian-30119018/)
386
+ - [OpenVINO Documentation](https://docs.openvino.ai/)
387
+ - [NNCF Post-Training Quantization](https://docs.openvino.ai/latest/nncf_ptq_introduction.html)
388
+ - [Intel DLStreamer](https://docs.openedgeplatform.intel.com/2026.0/edge-ai-libraries/dlstreamer/index.html)
expected_output_dlstreamer.gif ADDED

Git LFS Details

  • SHA256: 5a4e686ebce4ea17a79c139bc2dfd6329da6a8fcbbce89772850a0ed98f205ad
  • Pointer size: 133 Bytes
  • Size of remote file: 20.7 MB
expected_output_openvino.gif ADDED

Git LFS Details

  • SHA256: d928ee003607a1c8ac86c5dafb410e5f0900d3af76c30c6a95451eac80e91c9e
  • Pointer size: 133 Bytes
  • Size of remote file: 28.8 MB
export_and_quantize.sh ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # SPDX-License-Identifier: MIT
3
+ # Copyright (C) Intel Corporation
4
+ #
5
+ # Export a YOLO26 detector to OpenVINO IR for the object-classification use case.
6
+ # The accompanying samples group detected objects into higher-level city
7
+ # categories (People, Vehicles) for traffic situational awareness.
8
+ # Usage: ./export_and_quantize.sh [MODEL_VARIANT] [PRECISION]
9
+ # Example: ./export_and_quantize.sh yolo26n FP16
10
+ #
11
+ # Supported precisions:
12
+ # FP32 -- Full-precision floating-point weights
13
+ # FP16 -- Half-precision floating-point weights (default)
14
+ # INT8 -- Quantized 8-bit integer weights (requires NNCF)
15
+ #
16
+ # Precision / device compatibility:
17
+ # | Precision | CPU | GPU | NPU |
18
+ # |-----------|-----|-----|-----|
19
+ # | FP32 | Yes | Yes | No |
20
+ # | FP16 | Yes | Yes | Yes |
21
+ # | INT8 | Yes | Yes | Yes |
22
+
23
+ set -euo pipefail
24
+
25
+ MODEL_NAME="${1:-yolo26n}"
26
+ PRECISION="${2:-FP16}"
27
+ PRECISION="$(echo "${PRECISION}" | tr '[:lower:]' '[:upper:]')"
28
+
29
+ if [[ "${PRECISION}" != "FP32" && "${PRECISION}" != "FP16" && "${PRECISION}" != "INT8" ]]; then
30
+ echo "ERROR: unsupported precision '${PRECISION}'. Choose FP32, FP16, or INT8." >&2
31
+ exit 1
32
+ fi
33
+
34
+ echo "--- Installing dependencies ---"
35
+ if [[ "${PRECISION}" == "INT8" ]]; then
36
+ pip install -qU openvino nncf ultralytics
37
+ else
38
+ pip install -qU openvino ultralytics
39
+ fi
40
+
41
+ # Ask for approval before downloading models and sample files
42
+ echo ""
43
+ echo "This script will download:"
44
+ echo " - Model weights and a sample traffic video"
45
+ echo ""
46
+ read -p "Continue with downloads? (yes/no): " APPROVAL
47
+ if [[ "${APPROVAL}" != "yes" ]]; then
48
+ echo "Download cancelled by user."
49
+ exit 0
50
+ fi
51
+
52
+ echo ""
53
+ echo "--- Downloading sample test video (urban roundabout) ---"
54
+ if [[ ! -f test_video.mp4 ]]; then
55
+ wget -q -O test_video.mp4 \
56
+ "https://www.pexels.com/download/video/30119018/?fps=59.94&h=720&w=1280"
57
+ echo "Downloaded: test_video.mp4"
58
+ else
59
+ echo "Already present: test_video.mp4"
60
+ fi
61
+
62
+ if [[ "${PRECISION}" == "FP32" ]]; then
63
+ HALF_FLAG="False"
64
+ EXPORT_LABEL="FP32"
65
+ else
66
+ HALF_FLAG="True"
67
+ EXPORT_LABEL="FP16"
68
+ fi
69
+
70
+ echo "--- Exporting ${MODEL_NAME} to OpenVINO IR (${EXPORT_LABEL}) ---"
71
+ python3 -c "
72
+ from ultralytics import YOLO
73
+
74
+ model = YOLO('${MODEL_NAME}.pt')
75
+ model.export(format='openvino', half=${HALF_FLAG}, dynamic=False, imgsz=640)
76
+ print('Export complete: ${MODEL_NAME}_openvino_model/')
77
+ "
78
+
79
+ if [[ "${PRECISION}" == "INT8" ]]; then
80
+ echo "--- Quantizing to INT8 with NNCF ---"
81
+ python3 -c "
82
+ import nncf
83
+ import openvino as ov
84
+ import numpy as np
85
+ import cv2
86
+
87
+ core = ov.Core()
88
+ model = core.read_model('${MODEL_NAME}_openvino_model/${MODEL_NAME}.xml')
89
+
90
+ # Calibrate on a representative frame from the sample traffic video.
91
+ cap = cv2.VideoCapture('test_video.mp4')
92
+ ok, frame = cap.read()
93
+ cap.release()
94
+ if not ok:
95
+ raise SystemExit('Could not read a calibration frame from test_video.mp4')
96
+ img = cv2.resize(frame, (640, 640))
97
+ img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0
98
+ img = img.transpose(2, 0, 1)[np.newaxis, ...] # NCHW
99
+
100
+ def transform_fn(data_item):
101
+ return img
102
+
103
+ calibration_dataset = nncf.Dataset(list(range(300)), transform_fn)
104
+
105
+ quantized = nncf.quantize(
106
+ model,
107
+ calibration_dataset,
108
+ preset=nncf.QuantizationPreset.MIXED,
109
+ subset_size=300,
110
+ )
111
+
112
+ ov.save_model(quantized, '${MODEL_NAME}_objcls_int8.xml')
113
+ print('Quantization complete: ${MODEL_NAME}_objcls_int8.xml')
114
+ "
115
+ fi
116
+ echo "--- Done ---"