Views
No views yet
| Property | Value |
|---|---|
| Category | Object Detection + Tracking + Line Crossing |
| Base Model | YOLO26 (Ultralytics) |
| Source Framework | PyTorch (Ultralytics) |
| Supported Precisions | FP32, FP16, INT8 (mixed-precision) |
| Inference Engine | OpenVINO |
| Hardware | CPU, GPU, NPU |
| Detected Class(es) | car (2), motorcycle (3), bus (5), truck (7) |
yolo26n, yolo26s, yolo26m, yolo26l, yolo26x.
Smaller variants (yolo26n, yolo26s) are recommended for high-FPS edge
deployment; larger variants improve recall for distant vehicles.1python3 -m venv .venv --system-site-packages
2source .venv/bin/activateNote: The--system-site-packagesflag is required so the virtual environment can access the system-installed OpenVINO and DLStreamer Python packages.
1chmod +x export_and_quantize.sh
2./export_and_quantize.sh1./export_and_quantize.sh yolo26n FP32 # full-precision
2./export_and_quantize.sh yolo26n INT8 # quantized
3./export_and_quantize.sh yolo26s # larger variant, default FP16openvino, ultralytics; adds nncf for INT8).test.jpg) and the smart-parking sample video (smart_parking_720p_30fps.mp4).yolo26n_openvino_model/ -- FP32 or FP16 OpenVINO IR model directory.yolo26n_vehicle_entry_exit_int8.xml / .bin -- INT8 quantized model (only when INT8 is selected).| Precision | CPU | GPU | NPU |
|---|---|---|---|
| FP32 | Yes | Yes | No |
| FP16 | Yes | Yes | Yes |
| INT8 | Yes | Yes | Yes |
car class,
applies simple centroid tracking with track IDs, and logs an entry or exit
event -- with the wall-clock timestamp inside the video -- when a tracked car's
centroid crosses a horizontal virtual line placed at 60% of the frame height.
The saved output video shows only the car detection bounding boxes (no counter
overlay or line).
Change the device string to run on CPU, GPU, or NPU.1import cv2
2import numpy as np
3import openvino as ov
4
5VEHICLE_CLASS_IDS = {2: "car"}
6CONF_THRESHOLD = 0.4
7INPUT_SIZE = 640
8LINE_RATIO = 0.6
9MAX_DIST = 80
10
11core = ov.Core()
12model = core.read_model("yolo26n_openvino_model/yolo26n.xml")
13
14# Change device to "GPU" or "NPU" to run on integrated GPU or NPU.
15compiled = core.compile_model(model, "CPU")
16
17cap = cv2.VideoCapture("smart_parking_720p_30fps.mp4")
18fps = cap.get(cv2.CAP_PROP_FPS) or 30.0
19width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
20height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
21line_y = int(height * LINE_RATIO)
22
23
24def fmt_time(seconds: float) -> str:
25 """Format elapsed video time as MM:SS.mmm."""
26 minutes, secs = divmod(seconds, 60)
27 return f"{int(minutes):02d}:{secs:06.3f}"
28
29
30writer = cv2.VideoWriter(
31 "output_openvino.mp4", cv2.VideoWriter_fourcc(*"mp4v"), fps, (width, height))
32
33tracks: dict[int, tuple[int, int]] = {}
34entry_time: dict[int, float] = {}
35next_id = 0
36entered = 0
37exited = 0
38frame_idx = 0
39
40while True:
41 ok, frame = cap.read()
42 if not ok:
43 break
44 frame_idx += 1
45 h0, w0 = frame.shape[:2]
46 sx, sy = w0 / INPUT_SIZE, h0 / INPUT_SIZE
47
48 blob = cv2.resize(frame, (INPUT_SIZE, INPUT_SIZE))
49 blob = cv2.cvtColor(blob, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0
50 blob = blob.transpose(2, 0, 1)[np.newaxis, ...]
51
52 output = compiled([blob])[compiled.output(0)][0]
53 mask = (output[:, 4] >= CONF_THRESHOLD) & np.isin(
54 output[:, 5].astype(int), list(VEHICLE_CLASS_IDS.keys()))
55 dets = output[mask]
56
57 centroids = []
58 for det in dets:
59 cx = int(((det[0] + det[2]) / 2) * sx)
60 cy = int(((det[1] + det[3]) / 2) * sy)
61 centroids.append((cx, cy))
62
63 new_tracks: dict[int, tuple[int, int]] = {}
64 used = set()
65 for tid, (px, py) in tracks.items():
66 best_d = MAX_DIST
67 best_j = -1
68 for j, (cx, cy) in enumerate(centroids):
69 if j in used:
70 continue
71 d = abs(cx - px) + abs(cy - py)
72 if d < best_d:
73 best_d = d
74 best_j = j
75 if best_j >= 0:
76 cx, cy = centroids[best_j]
77 used.add(best_j)
78 t = frame_idx / fps
79 if py < line_y <= cy:
80 exited += 1
81 enter_t = entry_time.pop(tid, None)
82 if enter_t is not None:
83 print(
84 f"EXIT track={tid:<3} entry={fmt_time(enter_t)} "
85 f"exit={fmt_time(t)}", flush=True)
86 else:
87 print(f"EXIT track={tid:<3} exit={fmt_time(t)}", flush=True)
88 elif py >= line_y > cy:
89 entered += 1
90 entry_time[tid] = t
91 print(f"ENTRY track={tid:<3} entry={fmt_time(t)}", flush=True)
92 new_tracks[tid] = (cx, cy)
93 for j, (cx, cy) in enumerate(centroids):
94 if j not in used:
95 new_tracks[next_id] = (cx, cy)
96 next_id += 1
97 tracks = new_tracks
98
99 for det in dets:
100 x1 = int(det[0] * sx)
101 y1 = int(det[1] * sy)
102 x2 = int(det[2] * sx)
103 y2 = int(det[3] * sy)
104 cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
105 cv2.putText(frame, "car", (x1, max(y1 - 6, 0)),
106 cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
107 writer.write(frame)
108
109cap.release()
110writer.release()
111print(f"Total: entered={entered} exited={exited}", flush=True)"CPU" -- default, works on all Intel platforms."GPU" -- Intel integrated or discrete GPU."NPU" -- Intel NPU (validate with benchmark_app -d NPU).MM:SS.mmm within the video):1ENTRY track=3 entry=00:02.400
2ENTRY track=7 entry=00:05.133
3EXIT track=3 entry=00:02.400 exit=00:09.867
4ENTRY track=12 entry=00:11.267
5EXIT track=7 entry=00:05.133 exit=00:14.700
6EXIT track=12 entry=00:11.267 exit=00:18.933
7Total: entered=3 exited=3
gvatrack
(BoT-SORT) for stable vehicle IDs, keeping only the car class.
A buffer probe reads the tracking metadata and fires entry/exit events
-- logging the entry and exit timestamps taken from each buffer's
presentation time -- when a tracked car crosses the virtual line.
The annotated result is saved to output_dlstreamer.mp4.Notes on running this sample:
Use the FP16 IR (yolo26n_openvino_model/yolo26n.xml). Class names are read automatically from the model's embeddedmetadata.yamlby DLStreamer 2026.0+ -- no externallabels-fileis required. Detections are read with thegstgvaVideoFrameAPI (region.object_id()carries thegvatrackID). ExportPYTHONPATHso the DLStreamer Python module is importable:bash1source /opt/intel/openvino_2026/setupvars.sh 2source /opt/intel/dlstreamer/scripts/setup_dls_env.sh 3export PYTHONPATH=/opt/intel/dlstreamer/python:\ 4/opt/intel/dlstreamer/gstreamer/lib/python3/dist-packages:${PYTHONPATH:-}
1import gi
2
3gi.require_version("Gst", "1.0")
4from gi.repository import Gst
5from gstgva import VideoFrame
6
7Gst.init([])
8
9INPUT_VIDEO = "smart_parking_720p_30fps.mp4"
10VEHICLE_LABELS = {"car"}
11LINE_RATIO = 0.6
12
13# For CPU: change device=GPU to device=CPU.
14# For NPU: change device=GPU to device=NPU (batch-size=1, nireq=4 recommended).
15pipeline_str = (
16 f"filesrc location={INPUT_VIDEO} ! decodebin3 ! "
17 "videoconvert ! "
18 "gvadetect model=yolo26n_openvino_model/yolo26n.xml "
19 "device=GPU "
20 "threshold=0.4 ! queue ! "
21 "gvatrack tracking-type=short-term-imageless ! queue ! "
22 "identity name=probe ! "
23 "gvawatermark displ-cfg=show-roi=car ! "
24 "videoconvert ! video/x-raw,format=I420 ! "
25 "openh264enc ! h264parse ! "
26 "mp4mux ! filesink location=output_dlstreamer.mp4"
27)
28pipeline = Gst.parse_launch(pipeline_str)
29
30prev_positions: dict[int, int] = {}
31entry_time: dict[int, float] = {}
32entered = 0
33exited = 0
34frame_height = 0
35
36
37def fmt_time(seconds: float) -> str:
38 """Format elapsed video time as MM:SS.mmm."""
39 minutes, secs = divmod(seconds, 60)
40 return f"{int(minutes):02d}:{secs:06.3f}"
41
42
43def on_buffer(pad, info):
44 global entered, exited, frame_height
45 buf = info.get_buffer()
46 caps = pad.get_current_caps()
47 if caps and frame_height == 0:
48 frame_height = caps.get_structure(0).get_value("height") or 720
49 line_y = int(frame_height * LINE_RATIO)
50
51 t = buf.pts / Gst.SECOND if buf.pts != Gst.CLOCK_TIME_NONE else 0.0
52 frame = VideoFrame(buf, caps=caps)
53 current: dict[int, int] = {}
54 for region in frame.regions():
55 if region.label() not in VEHICLE_LABELS:
56 continue
57 rect = region.rect()
58 cy = int(rect.y + rect.h / 2)
59 tid = region.object_id()
60 current[tid] = cy
61 if tid in prev_positions:
62 py = prev_positions[tid]
63 if py < line_y <= cy:
64 exited += 1
65 enter_t = entry_time.pop(tid, None)
66 if enter_t is not None:
67 print(
68 f"EXIT track={tid:<3} entry={fmt_time(enter_t)} "
69 f"exit={fmt_time(t)}", flush=True)
70 else:
71 print(f"EXIT track={tid:<3} exit={fmt_time(t)}", flush=True)
72 elif py >= line_y > cy:
73 entered += 1
74 entry_time[tid] = t
75 print(f"ENTRY track={tid:<3} entry={fmt_time(t)}", flush=True)
76 prev_positions.clear()
77 prev_positions.update(current)
78 return Gst.PadProbeReturn.OK
79
80
81probe = pipeline.get_by_name("probe")
82probe.get_static_pad("src").add_probe(Gst.PadProbeType.BUFFER, on_buffer)
83
84pipeline.set_state(Gst.State.PLAYING)
85bus = pipeline.get_bus()
86bus.timed_pop_filtered(
87 Gst.CLOCK_TIME_NONE,
88 Gst.MessageType.EOS | Gst.MessageType.ERROR,
89)
90pipeline.set_state(Gst.State.NULL)
91print(f"Total: entered={entered} exited={exited}", flush=True)device=GPU -- default in the sample code.device=CPU -- change device=GPU to device=CPU.device=NPU -- change device=GPU to device=NPU; use batch-size=1 and nireq=4 for best NPU utilization.MM:SS.mmm within the video):1ENTRY track=1 entry=00:01.900
2ENTRY track=4 entry=00:04.633
3EXIT track=1 entry=00:01.900 exit=00:08.767
4ENTRY track=9 entry=00:10.500
5EXIT track=4 entry=00:04.633 exit=00:13.400
6EXIT track=9 entry=00:10.500 exit=00:17.833
7Total: entered=3 exited=3