|
Download README.md from Intel/motion-detection: direct link, hf CLI and curl.
- Browser
- Download file 8.56 kB
-
https://huggingface.co/Intel/motion-detection/resolve/main/README.md
- Command line
-
hf download hf://Intel/motion-detection/README.md
-
curl -L -o README.md https://huggingface.co/Intel/motion-detection/resolve/main/README.md
8.56 kB
| license: mit | |
| license_link: LICENSE | |
| library_name: opencv | |
| tags: | |
| - opencv | |
| - intel | |
| - motion-detection | |
| - background-subtraction | |
| - edge-ai | |
| - metro | |
| - dlstreamer | |
| language: | |
| - en | |
| # Motion Detection | |
| | Property | Value | | |
| |---|---| | |
| | **Category** | Motion Analytics (classical computer vision) | | |
| | **Base Model** | Not applicable -- uses classical background subtraction | | |
| | **Source Framework** | OpenCV | | |
| | **Supported Precisions** | Not applicable | | |
| | **Inference Engine** | OpenCV (CPU) / GStreamer decode via DLStreamer | | |
| | **Hardware** | CPU, GPU (OpenCV UMat optional) | | |
| | **Detected Class(es)** | Generic foreground motion regions | | |
| --- | |
| ## Overview | |
| Motion Detection is a Metro Analytics use case that flags moving regions in a video stream without requiring a deep-learning model. | |
| It uses the OpenCV MOG2 adaptive background subtractor to separate moving foreground pixels from a learned background, then groups them into bounding boxes. | |
| A neural detector such as YOLO26 is the best choice when you need to know *what* is moving (person, vehicle, etc.). | |
| 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. | |
| Typical Metro deployments include: | |
| - **Idle-camera Triggering** -- wake heavier analytics only when motion is present. | |
| - **Perimeter and After-hours Monitoring** -- alert on any movement in a restricted area. | |
| - **Bandwidth Reduction** -- record or stream only frames that contain motion. | |
| - **Pre-filter for Detection** -- gate an expensive YOLO26 pipeline behind a cheap motion check. | |
| --- | |
| ## Prerequisites | |
| - Python 3.11+ | |
| - [Install OpenVINO](https://docs.openvino.ai/2026/get-started/install-openvino.html) (latest version) | |
| - [Install Intel DLStreamer](https://docs.openedgeplatform.intel.com/2026.0/edge-ai-libraries/dlstreamer/get_started/install/install_guide_ubuntu.html) (latest version) | |
| Create and activate a Python virtual environment before running the scripts: | |
| ```bash | |
| python3 -m venv .venv --system-site-packages | |
| source .venv/bin/activate | |
| ``` | |
| > **Note:** The `--system-site-packages` flag is required so the virtual | |
| > environment can access the system-installed OpenVINO and DLStreamer Python | |
| > packages. | |
| --- | |
| ## Getting Started | |
| ### Download the Sample Video | |
| This use case does not export or quantize a model. | |
| Run the provided script to download the sample test video: | |
| ```bash | |
| chmod +x export_and_quantize.sh | |
| ./export_and_quantize.sh | |
| ``` | |
| The script downloads `test_video.mp4` into the current directory. | |
| ### OpenCV Sample | |
| The sample below reads `test_video.mp4`, applies MOG2 background subtraction, | |
| removes shadows and noise, groups foreground pixels into bounding boxes, and | |
| writes the annotated result to `output_opencv.mp4`. | |
| It prints one line per frame with the number of motion regions found. | |
| ```python | |
| import cv2 | |
| import numpy as np | |
| INPUT_VIDEO = "test_video.mp4" | |
| MIN_AREA = 500 # ignore motion blobs smaller than this many pixels | |
| cap = cv2.VideoCapture(INPUT_VIDEO) | |
| fps = cap.get(cv2.CAP_PROP_FPS) or 30.0 | |
| width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) | |
| height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) | |
| bg = cv2.createBackgroundSubtractorMOG2( | |
| history=200, varThreshold=25, detectShadows=True) | |
| writer = cv2.VideoWriter( | |
| "output_opencv.mp4", cv2.VideoWriter_fourcc(*"mp4v"), fps, (width, height)) | |
| kernel = np.ones((3, 3), np.uint8) | |
| frame_idx = 0 | |
| motion_frames = 0 | |
| while True: | |
| ok, frame = cap.read() | |
| if not ok: | |
| break | |
| frame_idx += 1 | |
| fg = bg.apply(frame) | |
| # MOG2 marks shadows as 127; keep only strong foreground (255). | |
| fg = cv2.threshold(fg, 200, 255, cv2.THRESH_BINARY)[1] | |
| fg = cv2.morphologyEx(fg, cv2.MORPH_OPEN, kernel) | |
| contours, _ = cv2.findContours( | |
| fg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) | |
| regions = [c for c in contours if cv2.contourArea(c) >= MIN_AREA] | |
| if regions: | |
| motion_frames += 1 | |
| for c in regions: | |
| x, y, w, h = cv2.boundingRect(c) | |
| cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2) | |
| status_text = "Motion Detected" if regions else "No Motion" | |
| status_color = (0, 0, 255) if regions else (0, 255, 0) | |
| cv2.putText(frame, status_text, (10, 30), | |
| cv2.FONT_HERSHEY_SIMPLEX, 0.9, status_color, 2) | |
| cv2.putText(frame, f"Motion regions: {len(regions)}", (10, 60), | |
| cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2) | |
| print(f"Frame {frame_idx}: motion regions={len(regions)}", flush=True) | |
| writer.write(frame) | |
| cap.release() | |
| writer.release() | |
| print(f"Motion detected in {motion_frames} frames", flush=True) | |
| ``` | |
| **Device targets:** | |
| - `"CPU"` -- default for OpenCV background subtraction. | |
| - `"GPU"` -- enable OpenCV transparent API by wrapping frames in `cv2.UMat(frame)` on systems with an OpenCL-capable Intel GPU. | |
| - `"NPU"` -- not applicable; background subtraction is not a neural workload. | |
| #### Expected Output | |
|  | |
| ### DLStreamer Sample | |
| The sample below uses the DLStreamer GStreamer decode stack | |
| (`decodebin3 ! videoconvert`) to pull frames into Python via `appsink`, | |
| applies the same MOG2 background subtraction, and encodes the annotated | |
| result to `output_dlstreamer.mp4`. | |
| Using `appsink` keeps the pipeline headless-safe and avoids VA-API | |
| zero-copy elements that fail over SSH. | |
| ```python | |
| import gi | |
| gi.require_version("Gst", "1.0") | |
| from gi.repository import Gst | |
| import numpy as np | |
| Gst.init([]) | |
| # Import cv2 after Gst.init to avoid GStreamer re-initialization conflicts. | |
| import cv2 | |
| INPUT_VIDEO = "test_video.mp4" | |
| MIN_AREA = 500 | |
| # Decode with the DLStreamer/GStreamer stack and hand BGR frames to OpenCV. | |
| pipeline_str = ( | |
| f"filesrc location={INPUT_VIDEO} ! decodebin3 ! videoconvert ! " | |
| "video/x-raw,format=BGR ! " | |
| "appsink name=sink emit-signals=false sync=false" | |
| ) | |
| pipeline = Gst.parse_launch(pipeline_str) | |
| sink = pipeline.get_by_name("sink") | |
| pipeline.set_state(Gst.State.PLAYING) | |
| bg = cv2.createBackgroundSubtractorMOG2( | |
| history=200, varThreshold=25, detectShadows=True) | |
| kernel = np.ones((3, 3), np.uint8) | |
| writer = None | |
| frame_idx = 0 | |
| motion_frames = 0 | |
| while True: | |
| sample = sink.emit("pull-sample") | |
| if sample is None: | |
| break | |
| buf = sample.get_buffer() | |
| caps = sample.get_caps().get_structure(0) | |
| width = caps.get_value("width") | |
| height = caps.get_value("height") | |
| ok, mapinfo = buf.map(Gst.MapFlags.READ) | |
| if not ok: | |
| continue | |
| frame = np.ndarray((height, width, 3), dtype=np.uint8, | |
| buffer=mapinfo.data).copy() | |
| buf.unmap(mapinfo) | |
| frame_idx += 1 | |
| fg = bg.apply(frame) | |
| fg = cv2.threshold(fg, 200, 255, cv2.THRESH_BINARY)[1] | |
| fg = cv2.morphologyEx(fg, cv2.MORPH_OPEN, kernel) | |
| contours, _ = cv2.findContours( | |
| fg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) | |
| regions = [c for c in contours if cv2.contourArea(c) >= MIN_AREA] | |
| if regions: | |
| motion_frames += 1 | |
| for c in regions: | |
| x, y, w, h = cv2.boundingRect(c) | |
| cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2) | |
| status_text = "Motion Detected" if regions else "No Motion" | |
| status_color = (0, 0, 255) if regions else (0, 255, 0) | |
| cv2.putText(frame, status_text, (10, 30), | |
| cv2.FONT_HERSHEY_SIMPLEX, 0.9, status_color, 2) | |
| if writer is None: | |
| writer = cv2.VideoWriter( | |
| "output_dlstreamer.mp4", cv2.VideoWriter_fourcc(*"mp4v"), | |
| 30.0, (width, height)) | |
| writer.write(frame) | |
| print(f"Frame {frame_idx}: motion regions={len(regions)}", flush=True) | |
| pipeline.set_state(Gst.State.NULL) | |
| if writer: | |
| writer.release() | |
| print(f"Motion detected in {motion_frames} frames", flush=True) | |
| ``` | |
| The decode stack runs on the CPU; to offload decode to an Intel GPU, install | |
| the DLStreamer VA-API plugins and prepend `vaapidecodebin` in environments that | |
| support it (not recommended on headless or SSH systems). | |
| #### Expected Output | |
|  | |
| --- | |
| ## License | |
| Licensed under the MIT License. See [LICENSE](LICENSE) for details. | |
| ## References | |
| - [OpenCV Background Subtraction Tutorial](https://docs.opencv.org/4.x/d1/dc5/tutorial_background_subtraction.html) | |
| - [OpenCV MOG2 Background Subtractor](https://docs.opencv.org/4.x/d7/d7b/classcv_1_1BackgroundSubtractorMOG2.html) | |
| - [Intel DLStreamer](https://docs.openedgeplatform.intel.com/2026.0/edge-ai-libraries/dlstreamer/index.html) | |
| - [OpenVINO Documentation](https://docs.openvino.ai/) | |