vagheshpatel commited on
Commit
807b82c
·
verified ·
1 Parent(s): ac9dbb5

Sync motion-tracking from metro-analytics-catalog

Browse files
Files changed (2) hide show
  1. README.md +48 -19
  2. 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.3.0, Python 3.11+
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 yolo26n # default: FP16
 
 
 
 
 
 
 
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:** For production accuracy, replace the random calibration tensors in
94
- > `export_and_quantize.sh` with a representative sample of frames from the
95
- > target deployment site.
96
 
97
  ### OpenVINO Sample
98
 
99
- The sample below uses the Ultralytics `model.track()` API with the FP16
100
- OpenVINO model directory to detect and track objects in a video, assigning
101
- persistent track IDs via the built-in BoT-SORT tracker.
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
- > **Note:** Ultralytics requires the OpenVINO model directory (e.g.,
107
- > `yolo26n_openvino_model/`) rather than a bare `.xml` file.
108
- > The INT8 model (`yolo26n_tracking_int8.xml`) can be used directly
109
- > with the OpenVINO Python API but not with Ultralytics `YOLO()`.
 
 
 
 
 
110
 
111
  ```python
112
  import subprocess
@@ -116,9 +128,9 @@ import cv2
116
  import numpy as np
117
  from ultralytics import YOLO
118
 
119
- # Load the FP16 OpenVINO model directory for tracking.
120
- # Change device to "gpu:0" or "npu:0" for GPU/NPU.
121
- model = YOLO("yolo26n_openvino_model", task="detect")
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 decodebin.
229
  # For NPU: change device=CPU to device=NPU (batch-size=1, nireq=4 recommended).
230
  pipeline_str = (
231
- "filesrc location=test_video.mp4 ! decodebin ! videoconvert ! "
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 `decodebin` for zero-copy color conversion.
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 np.random.rand(1, 3, 640, 640).astype(np.float32)
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