vagheshpatel commited on
Commit
f51e1ae
·
verified ·
1 Parent(s): fd890d0

Sync motion-detection 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,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ license_link: LICENSE
4
+ library_name: opencv
5
+ tags:
6
+ - opencv
7
+ - intel
8
+ - motion-detection
9
+ - background-subtraction
10
+ - edge-ai
11
+ - metro
12
+ - dlstreamer
13
+ language:
14
+ - en
15
+ ---
16
+
17
+ # Motion Detection
18
+
19
+ | Property | Value |
20
+ |---|---|
21
+ | **Category** | Motion Analytics (classical computer vision) |
22
+ | **Base Model** | Not applicable -- uses classical background subtraction |
23
+ | **Source Framework** | OpenCV |
24
+ | **Supported Precisions** | Not applicable |
25
+ | **Inference Engine** | OpenCV (CPU) / GStreamer decode via DLStreamer |
26
+ | **Hardware** | CPU, GPU (OpenCV UMat optional) |
27
+ | **Detected Class(es)** | Generic foreground motion regions |
28
+
29
+ ---
30
+
31
+ ## Overview
32
+
33
+ Motion Detection is a Metro Analytics use case that flags moving regions in a video stream without requiring a deep-learning model.
34
+ It uses the OpenCV MOG2 adaptive background subtractor to separate moving foreground pixels from a learned background, then groups them into bounding boxes.
35
+
36
+ A neural detector such as YOLO26 is the best choice when you need to know *what* is moving (person, vehicle, etc.).
37
+ For raw "something changed in the frame" triggering, classical background subtraction is the most efficient and reliable choice, so this use case intentionally avoids a model.
38
+
39
+ Typical Metro deployments include:
40
+
41
+ - **Idle-camera Triggering** -- wake heavier analytics only when motion is present.
42
+ - **Perimeter and After-hours Monitoring** -- alert on any movement in a restricted area.
43
+ - **Bandwidth Reduction** -- record or stream only frames that contain motion.
44
+ - **Pre-filter for Detection** -- gate an expensive YOLO26 pipeline behind a cheap motion check.
45
+
46
+ ---
47
+
48
+ ## Prerequisites
49
+
50
+ - Python 3.11+
51
+ - [Install OpenVINO](https://docs.openvino.ai/2026/get-started/install-openvino.html) (latest version)
52
+ - [Install Intel DLStreamer](https://docs.openedgeplatform.intel.com/2026.0/edge-ai-libraries/dlstreamer/get_started/install/install_guide_ubuntu.html) (latest version)
53
+
54
+ Create and activate a Python virtual environment before running the scripts:
55
+
56
+ ```bash
57
+ python3 -m venv .venv --system-site-packages
58
+ source .venv/bin/activate
59
+ ```
60
+
61
+ > **Note:** The `--system-site-packages` flag is required so the virtual
62
+ > environment can access the system-installed OpenVINO and DLStreamer Python
63
+ > packages.
64
+
65
+ ---
66
+
67
+ ## Getting Started
68
+
69
+ ### Download the Sample Video
70
+
71
+ This use case does not export or quantize a model.
72
+ Run the provided script to download the sample test video:
73
+
74
+ ```bash
75
+ chmod +x export_and_quantize.sh
76
+ ./export_and_quantize.sh
77
+ ```
78
+
79
+ The script downloads `test_video.mp4` into the current directory.
80
+
81
+ ### OpenCV Sample
82
+
83
+ The sample below reads `test_video.mp4`, applies MOG2 background subtraction,
84
+ removes shadows and noise, groups foreground pixels into bounding boxes, and
85
+ writes the annotated result to `output_opencv.mp4`.
86
+ It prints one line per frame with the number of motion regions found.
87
+
88
+ ```python
89
+ import cv2
90
+ import numpy as np
91
+
92
+ INPUT_VIDEO = "test_video.mp4"
93
+ MIN_AREA = 500 # ignore motion blobs smaller than this many pixels
94
+
95
+ cap = cv2.VideoCapture(INPUT_VIDEO)
96
+ fps = cap.get(cv2.CAP_PROP_FPS) or 30.0
97
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
98
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
99
+
100
+ bg = cv2.createBackgroundSubtractorMOG2(
101
+ history=200, varThreshold=25, detectShadows=True)
102
+ writer = cv2.VideoWriter(
103
+ "output_opencv.mp4", cv2.VideoWriter_fourcc(*"mp4v"), fps, (width, height))
104
+ kernel = np.ones((3, 3), np.uint8)
105
+
106
+ frame_idx = 0
107
+ motion_frames = 0
108
+ while True:
109
+ ok, frame = cap.read()
110
+ if not ok:
111
+ break
112
+ frame_idx += 1
113
+
114
+ fg = bg.apply(frame)
115
+ # MOG2 marks shadows as 127; keep only strong foreground (255).
116
+ fg = cv2.threshold(fg, 200, 255, cv2.THRESH_BINARY)[1]
117
+ fg = cv2.morphologyEx(fg, cv2.MORPH_OPEN, kernel)
118
+
119
+ contours, _ = cv2.findContours(
120
+ fg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
121
+ regions = [c for c in contours if cv2.contourArea(c) >= MIN_AREA]
122
+ if regions:
123
+ motion_frames += 1
124
+ for c in regions:
125
+ x, y, w, h = cv2.boundingRect(c)
126
+ cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)
127
+ status_text = "Motion Detected" if regions else "No Motion"
128
+ status_color = (0, 0, 255) if regions else (0, 255, 0)
129
+ cv2.putText(frame, status_text, (10, 30),
130
+ cv2.FONT_HERSHEY_SIMPLEX, 0.9, status_color, 2)
131
+ cv2.putText(frame, f"Motion regions: {len(regions)}", (10, 60),
132
+ cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
133
+ print(f"Frame {frame_idx}: motion regions={len(regions)}", flush=True)
134
+ writer.write(frame)
135
+
136
+ cap.release()
137
+ writer.release()
138
+ print(f"Motion detected in {motion_frames} frames", flush=True)
139
+ ```
140
+
141
+ **Device targets:**
142
+
143
+ - `"CPU"` -- default for OpenCV background subtraction.
144
+ - `"GPU"` -- enable OpenCV transparent API by wrapping frames in `cv2.UMat(frame)` on systems with an OpenCL-capable Intel GPU.
145
+ - `"NPU"` -- not applicable; background subtraction is not a neural workload.
146
+
147
+ #### Expected Output
148
+
149
+ ![OpenVINO expected output](expected_output_openvino.gif)
150
+
151
+ ### DLStreamer Sample
152
+
153
+ The sample below uses the DLStreamer GStreamer decode stack
154
+ (`decodebin3 ! videoconvert`) to pull frames into Python via `appsink`,
155
+ applies the same MOG2 background subtraction, and encodes the annotated
156
+ result to `output_dlstreamer.mp4`.
157
+ Using `appsink` keeps the pipeline headless-safe and avoids VA-API
158
+ zero-copy elements that fail over SSH.
159
+
160
+ ```python
161
+ import gi
162
+
163
+ gi.require_version("Gst", "1.0")
164
+ from gi.repository import Gst
165
+
166
+ import numpy as np
167
+
168
+ Gst.init([])
169
+
170
+ # Import cv2 after Gst.init to avoid GStreamer re-initialization conflicts.
171
+ import cv2
172
+
173
+ INPUT_VIDEO = "test_video.mp4"
174
+ MIN_AREA = 500
175
+
176
+ # Decode with the DLStreamer/GStreamer stack and hand BGR frames to OpenCV.
177
+ pipeline_str = (
178
+ f"filesrc location={INPUT_VIDEO} ! decodebin3 ! videoconvert ! "
179
+ "video/x-raw,format=BGR ! "
180
+ "appsink name=sink emit-signals=false sync=false"
181
+ )
182
+ pipeline = Gst.parse_launch(pipeline_str)
183
+ sink = pipeline.get_by_name("sink")
184
+ pipeline.set_state(Gst.State.PLAYING)
185
+
186
+ bg = cv2.createBackgroundSubtractorMOG2(
187
+ history=200, varThreshold=25, detectShadows=True)
188
+ kernel = np.ones((3, 3), np.uint8)
189
+ writer = None
190
+ frame_idx = 0
191
+ motion_frames = 0
192
+
193
+ while True:
194
+ sample = sink.emit("pull-sample")
195
+ if sample is None:
196
+ break
197
+ buf = sample.get_buffer()
198
+ caps = sample.get_caps().get_structure(0)
199
+ width = caps.get_value("width")
200
+ height = caps.get_value("height")
201
+
202
+ ok, mapinfo = buf.map(Gst.MapFlags.READ)
203
+ if not ok:
204
+ continue
205
+ frame = np.ndarray((height, width, 3), dtype=np.uint8,
206
+ buffer=mapinfo.data).copy()
207
+ buf.unmap(mapinfo)
208
+ frame_idx += 1
209
+
210
+ fg = bg.apply(frame)
211
+ fg = cv2.threshold(fg, 200, 255, cv2.THRESH_BINARY)[1]
212
+ fg = cv2.morphologyEx(fg, cv2.MORPH_OPEN, kernel)
213
+ contours, _ = cv2.findContours(
214
+ fg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
215
+ regions = [c for c in contours if cv2.contourArea(c) >= MIN_AREA]
216
+ if regions:
217
+ motion_frames += 1
218
+ for c in regions:
219
+ x, y, w, h = cv2.boundingRect(c)
220
+ cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)
221
+ status_text = "Motion Detected" if regions else "No Motion"
222
+ status_color = (0, 0, 255) if regions else (0, 255, 0)
223
+ cv2.putText(frame, status_text, (10, 30),
224
+ cv2.FONT_HERSHEY_SIMPLEX, 0.9, status_color, 2)
225
+
226
+ if writer is None:
227
+ writer = cv2.VideoWriter(
228
+ "output_dlstreamer.mp4", cv2.VideoWriter_fourcc(*"mp4v"),
229
+ 30.0, (width, height))
230
+ writer.write(frame)
231
+ print(f"Frame {frame_idx}: motion regions={len(regions)}", flush=True)
232
+
233
+ pipeline.set_state(Gst.State.NULL)
234
+ if writer:
235
+ writer.release()
236
+ print(f"Motion detected in {motion_frames} frames", flush=True)
237
+ ```
238
+
239
+ The decode stack runs on the CPU; to offload decode to an Intel GPU, install
240
+ the DLStreamer VA-API plugins and prepend `vaapidecodebin` in environments that
241
+ support it (not recommended on headless or SSH systems).
242
+
243
+ #### Expected Output
244
+
245
+ ![DLStreamer expected output](expected_output_dlstreamer.gif)
246
+
247
+ ---
248
+
249
+ ## License
250
+
251
+ Licensed under the MIT License. See [LICENSE](LICENSE) for details.
252
+
253
+ ## References
254
+
255
+ - [OpenCV Background Subtraction Tutorial](https://docs.opencv.org/4.x/d1/dc5/tutorial_background_subtraction.html)
256
+ - [OpenCV MOG2 Background Subtractor](https://docs.opencv.org/4.x/d7/d7b/classcv_1_1BackgroundSubtractorMOG2.html)
257
+ - [Intel DLStreamer](https://docs.openedgeplatform.intel.com/2026.0/edge-ai-libraries/dlstreamer/index.html)
258
+ - [OpenVINO Documentation](https://docs.openvino.ai/)
expected_output_dlstreamer.gif ADDED

Git LFS Details

  • SHA256: 88791ff2bf4b0d7c26c5eea20193d91a771b22387c784bf3c8503091586c9000
  • Pointer size: 133 Bytes
  • Size of remote file: 16.2 MB
expected_output_openvino.gif ADDED

Git LFS Details

  • SHA256: 52bf44a94e4a91b0b386914ca18d6458c3f3e066942745bb8283ca87f3563244
  • Pointer size: 133 Bytes
  • Size of remote file: 12.2 MB
export_and_quantize.sh ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # SPDX-License-Identifier: MIT
3
+ # Copyright (C) Intel Corporation
4
+ #
5
+ # Download the sample video for the motion-detection use case.
6
+ # This use case uses classical computer vision (OpenCV background
7
+ # subtraction) and the GStreamer decode stack in DLStreamer; no model
8
+ # export or quantization is required.
9
+ # Usage: ./export_and_quantize.sh
10
+
11
+ set -euo pipefail
12
+
13
+ SAMPLE_VIDEO_URL="https://github.com/intel-iot-devkit/sample-videos/raw/master/people-detection.mp4"
14
+
15
+ # Ask for approval before downloading models and sample files
16
+ echo ""
17
+ echo "This script will download:"
18
+ echo " - Model weights and/or sample files"
19
+ echo ""
20
+ read -p "Continue with downloads? (yes/no): " APPROVAL
21
+ if [[ "${APPROVAL}" != "yes" ]]; then
22
+ echo "Download cancelled by user."
23
+ exit 0
24
+ fi
25
+ echo ""
26
+ echo "--- Downloading sample test video ---"
27
+ if [[ ! -f test_video.mp4 ]]; then
28
+ wget -q -O test_video.mp4 "${SAMPLE_VIDEO_URL}"
29
+ echo "Downloaded: test_video.mp4"
30
+ else
31
+ echo "Already present: test_video.mp4"
32
+ fi
33
+
34
+ echo "--- Done ---"
35
+ echo "Sample : $(pwd)/test_video.mp4"
36
+ echo "Note : This use case requires no model; run the README samples directly."