Sync motion-tracking from metro-analytics-catalog
Browse files- README.md +48 -19
- export_and_quantize.sh +16 -1
README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
# Motion Tracking
|
| 2 |
|
| 3 |
-
> **Validated with:** OpenVINO 2026.1.0, NNCF 3.0.0, DLStreamer 2026.0, Ultralytics 8.
|
| 4 |
|
| 5 |
| Property | Value |
|
| 6 |
|---|---|
|
|
@@ -62,9 +62,17 @@ Run the provided script to download, export to OpenVINO IR, and optionally quant
|
|
| 62 |
|
| 63 |
```bash
|
| 64 |
chmod +x export_and_quantize.sh
|
| 65 |
-
./export_and_quantize.sh
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
./export_and_quantize.sh yolo26n FP32 # full-precision
|
| 67 |
./export_and_quantize.sh yolo26n INT8 # quantized
|
|
|
|
| 68 |
```
|
| 69 |
|
| 70 |
Replace `yolo26n` with any variant (`yolo26s`, `yolo26m`, `yolo26l`, `yolo26x`).
|
|
@@ -90,23 +98,27 @@ Output files:
|
|
| 90 |
| FP16 | Yes | Yes | Yes |
|
| 91 |
| INT8 | Yes | Yes | Yes |
|
| 92 |
|
| 93 |
-
> **Note:**
|
| 94 |
-
>
|
| 95 |
-
> target deployment site.
|
| 96 |
|
| 97 |
### OpenVINO Sample
|
| 98 |
|
| 99 |
-
The sample below uses the Ultralytics `model.track()` API with the
|
| 100 |
-
|
| 101 |
-
|
| 102 |
Each annotated frame -- with bounding boxes, track IDs, and per-track
|
| 103 |
trajectory polylines -- is written to `output.mp4`.
|
| 104 |
-
Change the `device` string to run on CPU, GPU, or NPU.
|
| 105 |
|
| 106 |
-
> **
|
| 107 |
-
>
|
| 108 |
-
>
|
| 109 |
-
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
|
| 111 |
```python
|
| 112 |
import subprocess
|
|
@@ -116,9 +128,9 @@ import cv2
|
|
| 116 |
import numpy as np
|
| 117 |
from ultralytics import YOLO
|
| 118 |
|
| 119 |
-
#
|
| 120 |
-
#
|
| 121 |
-
model = YOLO("
|
| 122 |
|
| 123 |
video_path = "test_video.mp4"
|
| 124 |
cap = cv2.VideoCapture(video_path)
|
|
@@ -188,6 +200,23 @@ print("Wrote output.mp4", flush=True)
|
|
| 188 |
- For GPU: set `device="gpu:0"` in the `model.track()` call.
|
| 189 |
- For NPU: set `device="npu:0"` (validate availability with `benchmark_app -d NPU`).
|
| 190 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 191 |
### DLStreamer Sample
|
| 192 |
|
| 193 |
The pipeline below runs the YOLO26 FP16 detector via `gvadetect` on
|
|
@@ -225,10 +254,10 @@ from gstgva import VideoFrame
|
|
| 225 |
|
| 226 |
Gst.init(None)
|
| 227 |
|
| 228 |
-
# For GPU: change device=CPU to device=GPU and add vapostproc after
|
| 229 |
# For NPU: change device=CPU to device=NPU (batch-size=1, nireq=4 recommended).
|
| 230 |
pipeline_str = (
|
| 231 |
-
"filesrc location=test_video.mp4 !
|
| 232 |
"video/x-raw,format=BGR ! "
|
| 233 |
"gvadetect model=yolo26n_openvino_model/yolo26n.xml "
|
| 234 |
"device=CPU threshold=0.4 ! queue ! "
|
|
@@ -314,7 +343,7 @@ print("Wrote output.mp4", flush=True)
|
|
| 314 |
**Device targets:**
|
| 315 |
|
| 316 |
- `device=CPU` -- default in the sample code.
|
| 317 |
-
- `device=GPU` -- add `vapostproc` after `
|
| 318 |
- `device=NPU` -- use `batch-size=1` and `nireq=4` for best NPU utilization.
|
| 319 |
|
| 320 |
---
|
|
|
|
| 1 |
# Motion Tracking
|
| 2 |
|
| 3 |
+
> **Validated with:** OpenVINO 2026.1.0, NNCF 3.0.0, DLStreamer 2026.0, Ultralytics 8.4.46, Python 3.11+
|
| 4 |
|
| 5 |
| Property | Value |
|
| 6 |
|---|---|
|
|
|
|
| 62 |
|
| 63 |
```bash
|
| 64 |
chmod +x export_and_quantize.sh
|
| 65 |
+
./export_and_quantize.sh
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
This exports the default **yolo26n** model in **FP16** precision.
|
| 69 |
+
|
| 70 |
+
#### Optional: Select a Different Variant or Precision
|
| 71 |
+
|
| 72 |
+
```bash
|
| 73 |
./export_and_quantize.sh yolo26n FP32 # full-precision
|
| 74 |
./export_and_quantize.sh yolo26n INT8 # quantized
|
| 75 |
+
./export_and_quantize.sh yolo26s # larger variant, default FP16
|
| 76 |
```
|
| 77 |
|
| 78 |
Replace `yolo26n` with any variant (`yolo26s`, `yolo26m`, `yolo26l`, `yolo26x`).
|
|
|
|
| 98 |
| FP16 | Yes | Yes | Yes |
|
| 99 |
| INT8 | Yes | Yes | Yes |
|
| 100 |
|
| 101 |
+
> **Note:** The INT8 calibration uses frames from the bundled sample video.
|
| 102 |
+
> For production accuracy, replace it with a representative set of frames from
|
| 103 |
+
> the target deployment site.
|
| 104 |
|
| 105 |
### OpenVINO Sample
|
| 106 |
|
| 107 |
+
The sample below uses the Ultralytics `model.track()` API with the PyTorch
|
| 108 |
+
weights to detect and track objects in a video, assigning persistent track IDs
|
| 109 |
+
via the built-in BoT-SORT tracker.
|
| 110 |
Each annotated frame -- with bounding boxes, track IDs, and per-track
|
| 111 |
trajectory polylines -- is written to `output.mp4`.
|
|
|
|
| 112 |
|
| 113 |
+
> **Important:** The `model.track()` API requires PyTorch weights (`.pt`).
|
| 114 |
+
> Using the OpenVINO model directory with `model.track()` produces zero
|
| 115 |
+
> detections in Ultralytics 8.4.x due to an incompatibility in the tracker
|
| 116 |
+
> integration. Use `model.predict()` for single-frame inference with the
|
| 117 |
+
> OpenVINO backend, or use the DLStreamer sample below for OpenVINO-accelerated
|
| 118 |
+
> tracking.
|
| 119 |
+
>
|
| 120 |
+
> The INT8 model (`yolo26n_tracking_int8.xml`) can be used directly with the
|
| 121 |
+
> OpenVINO Python API but not with the Ultralytics `YOLO()` wrapper.
|
| 122 |
|
| 123 |
```python
|
| 124 |
import subprocess
|
|
|
|
| 128 |
import numpy as np
|
| 129 |
from ultralytics import YOLO
|
| 130 |
|
| 131 |
+
# Use PyTorch weights for tracking -- model.track() requires the .pt backend.
|
| 132 |
+
# The OpenVINO model directory works with model.predict() but not model.track().
|
| 133 |
+
model = YOLO("yolo26n.pt", task="detect")
|
| 134 |
|
| 135 |
video_path = "test_video.mp4"
|
| 136 |
cap = cv2.VideoCapture(video_path)
|
|
|
|
| 200 |
- For GPU: set `device="gpu:0"` in the `model.track()` call.
|
| 201 |
- For NPU: set `device="npu:0"` (validate availability with `benchmark_app -d NPU`).
|
| 202 |
|
| 203 |
+
### Try It on a Sample Video
|
| 204 |
+
|
| 205 |
+
The `export_and_quantize.sh` script downloads `test_video.mp4` automatically.
|
| 206 |
+
Run the OpenVINO sample above.
|
| 207 |
+
The script processes each frame, prints per-track positions to the console,
|
| 208 |
+
and writes the annotated video to `output.mp4`.
|
| 209 |
+
|
| 210 |
+
Expected console output (representative):
|
| 211 |
+
|
| 212 |
+
```text
|
| 213 |
+
Track 1: class=0 center=(320,240)
|
| 214 |
+
Track 2: class=0 center=(450,300)
|
| 215 |
+
```
|
| 216 |
+
|
| 217 |
+
`output.mp4` shows bounding boxes with track IDs and colored trajectory
|
| 218 |
+
polylines for each tracked object.
|
| 219 |
+
|
| 220 |
### DLStreamer Sample
|
| 221 |
|
| 222 |
The pipeline below runs the YOLO26 FP16 detector via `gvadetect` on
|
|
|
|
| 254 |
|
| 255 |
Gst.init(None)
|
| 256 |
|
| 257 |
+
# For GPU: change device=CPU to device=GPU and add vapostproc after decodebin3.
|
| 258 |
# For NPU: change device=CPU to device=NPU (batch-size=1, nireq=4 recommended).
|
| 259 |
pipeline_str = (
|
| 260 |
+
"filesrc location=test_video.mp4 ! decodebin3 ! videoconvert ! "
|
| 261 |
"video/x-raw,format=BGR ! "
|
| 262 |
"gvadetect model=yolo26n_openvino_model/yolo26n.xml "
|
| 263 |
"device=CPU threshold=0.4 ! queue ! "
|
|
|
|
| 343 |
**Device targets:**
|
| 344 |
|
| 345 |
- `device=CPU` -- default in the sample code.
|
| 346 |
+
- `device=GPU` -- add `vapostproc` after `decodebin3` for zero-copy color conversion.
|
| 347 |
- `device=NPU` -- use `batch-size=1` and `nireq=4` for best NPU utilization.
|
| 348 |
|
| 349 |
---
|
export_and_quantize.sh
CHANGED
|
@@ -70,12 +70,27 @@ if [[ "${PRECISION}" == "INT8" ]]; then
|
|
| 70 |
import nncf
|
| 71 |
import openvino as ov
|
| 72 |
import numpy as np
|
|
|
|
| 73 |
|
| 74 |
core = ov.Core()
|
| 75 |
model = core.read_model('${MODEL_NAME}_openvino_model/${MODEL_NAME}.xml')
|
| 76 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
def transform_fn(data_item):
|
| 78 |
-
return
|
| 79 |
|
| 80 |
calibration_dataset = nncf.Dataset(list(range(300)), transform_fn)
|
| 81 |
|
|
|
|
| 70 |
import nncf
|
| 71 |
import openvino as ov
|
| 72 |
import numpy as np
|
| 73 |
+
import cv2
|
| 74 |
|
| 75 |
core = ov.Core()
|
| 76 |
model = core.read_model('${MODEL_NAME}_openvino_model/${MODEL_NAME}.xml')
|
| 77 |
|
| 78 |
+
# Extract frames from the sample video for calibration.
|
| 79 |
+
cap = cv2.VideoCapture('test_video.mp4')
|
| 80 |
+
frames = []
|
| 81 |
+
while len(frames) < 300:
|
| 82 |
+
ret, frame = cap.read()
|
| 83 |
+
if not ret:
|
| 84 |
+
cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
|
| 85 |
+
continue
|
| 86 |
+
img = cv2.resize(frame, (640, 640))
|
| 87 |
+
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0
|
| 88 |
+
img = img.transpose(2, 0, 1)[np.newaxis, ...]
|
| 89 |
+
frames.append(img)
|
| 90 |
+
cap.release()
|
| 91 |
+
|
| 92 |
def transform_fn(data_item):
|
| 93 |
+
return frames[data_item % len(frames)]
|
| 94 |
|
| 95 |
calibration_dataset = nncf.Dataset(list(range(300)), transform_fn)
|
| 96 |
|