vagheshpatel commited on
Commit
d6ea02e
·
verified ·
1 Parent(s): 9f027a1

Sync scene-change-detection from metro-analytics-catalog

Browse files
Files changed (5) hide show
  1. .gitattributes +1 -0
  2. LICENSE +21 -0
  3. README.md +204 -0
  4. expected_output_openvino.gif +3 -0
  5. export_and_quantize.sh +79 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ 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_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,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ license_link: LICENSE
4
+ library_name: opencv
5
+ tags:
6
+ - opencv
7
+ - intel
8
+ - scene-change-detection
9
+ - histogram
10
+ - edge-ai
11
+ - metro
12
+ language:
13
+ - en
14
+ ---
15
+
16
+ # Scene Change Detection
17
+
18
+ | Property | Value |
19
+ |---|---|
20
+ | **Category** | Scene Analytics (classical computer vision) |
21
+ | **Base Model** | Not applicable -- uses frame histogram comparison |
22
+ | **Source Framework** | OpenCV |
23
+ | **Supported Precisions** | Not applicable |
24
+ | **Inference Engine** | OpenCV (CPU) |
25
+ | **Hardware** | CPU, GPU (OpenCV UMat optional) |
26
+ | **Detected Class(es)** | Scene-change events |
27
+
28
+ ---
29
+
30
+ ## Overview
31
+
32
+ Scene Change Detection is a Metro Analytics use case that flags abrupt or
33
+ sustained changes in what a camera is showing, such as a shot cut, a camera
34
+ being repositioned, or a large change in the field of view.
35
+ It compares the color-histogram signature of each frame against the previous
36
+ frame using the Bhattacharyya distance and raises an event when the distance
37
+ exceeds a threshold.
38
+
39
+ Histogram and similarity scoring is more robust and far cheaper than running
40
+ an object detector for this signal, so this use case intentionally avoids a
41
+ neural model.
42
+ For semantic scene understanding (for example "platform" versus "concourse"),
43
+ pair this with the [object-detection](../object-detection/) use case.
44
+
45
+ Typical Metro deployments include:
46
+
47
+ - **Camera Repositioning Alerts** -- detect when a PTZ camera moves to a new view.
48
+ - **Video Segmentation** -- split long recordings into scenes for indexing.
49
+ - **Content Validation** -- confirm a feed switched to the expected source.
50
+ - **Pre-filter for Analytics** -- re-initialize trackers when the scene changes.
51
+
52
+ ---
53
+
54
+ ## Prerequisites
55
+
56
+ - Python 3.11+
57
+ - [Install OpenVINO](https://docs.openvino.ai/2026/get-started/install-openvino.html) (latest version)
58
+ - `ffmpeg` (used by `export_and_quantize.sh` to build the sample montage)
59
+
60
+ Create and activate a Python virtual environment before running the scripts:
61
+
62
+ ```bash
63
+ python3 -m venv .venv --system-site-packages
64
+ source .venv/bin/activate
65
+ ```
66
+
67
+ > **Note:** The `--system-site-packages` flag is required so the virtual
68
+ > environment can access the system-installed OpenVINO Python packages
69
+ > (which provide OpenCV).
70
+
71
+ ---
72
+
73
+ ## Getting Started
74
+
75
+ ### Download the Sample Video
76
+
77
+ This use case does not export or quantize a model.
78
+ Run the provided script to prepare the sample test video:
79
+
80
+ ```bash
81
+ chmod +x export_and_quantize.sh
82
+ ./export_and_quantize.sh
83
+ ```
84
+
85
+ A single continuous shot never triggers a scene change, so the script
86
+ downloads several distinct sample clips and joins them with hard cuts into
87
+ `test_video.mp4` (four 2-second scenes). This produces a clear scene change
88
+ every two seconds for the detector to flag. The script requires `ffmpeg` to
89
+ build the montage.
90
+
91
+ ### OpenCV Sample
92
+
93
+ The sample below computes a normalized HSV histogram for each frame, compares
94
+ it to the previous frame with the Bhattacharyya distance, and flags a scene
95
+ change when the distance exceeds `CHANGE_THRESHOLD`.
96
+ The annotated frames are written to `output_opencv.mp4`.
97
+
98
+ ```python
99
+ import cv2
100
+ import numpy as np
101
+
102
+ INPUT_VIDEO = "test_video.mp4"
103
+ CHANGE_THRESHOLD = 0.45 # Bhattacharyya distance in [0, 1]; higher = more change
104
+
105
+ cap = cv2.VideoCapture(INPUT_VIDEO)
106
+ fps = cap.get(cv2.CAP_PROP_FPS) or 30.0
107
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
108
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
109
+ writer = cv2.VideoWriter(
110
+ "output_opencv.mp4", cv2.VideoWriter_fourcc(*"mp4v"), fps, (width, height))
111
+
112
+
113
+ def frame_histogram(bgr):
114
+ hsv = cv2.cvtColor(bgr, cv2.COLOR_BGR2HSV)
115
+ hist = cv2.calcHist([hsv], [0, 1], None, [50, 60], [0, 180, 0, 256])
116
+ cv2.normalize(hist, hist, 0, 1, cv2.NORM_MINMAX)
117
+ return hist
118
+
119
+
120
+ prev_hist = None
121
+ frame_idx = 0
122
+ scene_changes = 0
123
+ while True:
124
+ ok, frame = cap.read()
125
+ if not ok:
126
+ break
127
+ frame_idx += 1
128
+ hist = frame_histogram(frame)
129
+
130
+ distance = 0.0
131
+ changed = False
132
+ if prev_hist is not None:
133
+ distance = cv2.compareHist(prev_hist, hist, cv2.HISTCMP_BHATTACHARYYA)
134
+ changed = distance >= CHANGE_THRESHOLD
135
+ prev_hist = hist
136
+
137
+ if changed:
138
+ scene_changes += 1
139
+ print(f"Frame {frame_idx}: SCENE CHANGE (distance={distance:.3f})",
140
+ flush=True)
141
+ color = (0, 0, 255) if changed else (0, 255, 0)
142
+ label = f"dist={distance:.3f}" + (" CHANGE" if changed else "")
143
+ cv2.putText(frame, label, (10, 30),
144
+ cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2)
145
+ writer.write(frame)
146
+
147
+ cap.release()
148
+ writer.release()
149
+ print(f"Scene changes detected: {scene_changes}", flush=True)
150
+ ```
151
+
152
+ **Device targets:**
153
+
154
+ - `"CPU"` -- default for OpenCV histogram comparison.
155
+ - `"GPU"` -- wrap frames in `cv2.UMat` to use the OpenCV transparent API on Intel GPUs.
156
+ - `"NPU"` -- not applicable; histogram comparison is not a neural workload.
157
+
158
+ ### Scene-Change Terminal Logging
159
+
160
+ Every time the Bhattacharyya distance crosses `CHANGE_THRESHOLD`, the sample
161
+ treats it as a new scene and prints a line to the terminal with the frame
162
+ number and the distance that triggered it. A running total is printed when the
163
+ video ends. This makes the terminal a lightweight event log you can pipe to a
164
+ file or another process without inspecting the annotated video.
165
+
166
+ The relevant lines in the sample are:
167
+
168
+ ```python
169
+ if changed:
170
+ scene_changes += 1
171
+ print(f"Frame {frame_idx}: SCENE CHANGE (distance={distance:.3f})",
172
+ flush=True)
173
+ ```
174
+
175
+ #### Expected Terminal Output
176
+
177
+ Running the sample against the four-scene montage produces one log line per cut
178
+ (at ~2s, ~4s, and ~6s), followed by the summary:
179
+
180
+ ```text
181
+ Frame 61: SCENE CHANGE (distance=0.949)
182
+ Frame 121: SCENE CHANGE (distance=0.988)
183
+ Frame 181: SCENE CHANGE (distance=0.854)
184
+ Scene changes detected: 3
185
+ ```
186
+
187
+ #### Expected Output
188
+
189
+ The annotated video draws each frame's distance in green and turns the label
190
+ red on the frame where a scene change is detected:
191
+
192
+ ![OpenCV expected output](expected_output_openvino.gif)
193
+
194
+ ---
195
+
196
+ ## License
197
+
198
+ Licensed under the MIT License. See [LICENSE](LICENSE) for details.
199
+
200
+ ## References
201
+
202
+ - [OpenCV Histogram Comparison](https://docs.opencv.org/4.x/d8/dc8/tutorial_histogram_comparison.html)
203
+ - [OpenCV calcHist Reference](https://docs.opencv.org/4.x/d6/dc7/group__imgproc__hist.html)
204
+ - [OpenVINO Documentation](https://docs.openvino.ai/)
expected_output_openvino.gif ADDED

Git LFS Details

  • SHA256: d0f9832c606c08edbce8a613c460c23234402e5756c77ff988e00e3ae01f15af
  • Pointer size: 132 Bytes
  • Size of remote file: 7.23 MB
export_and_quantize.sh ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # SPDX-License-Identifier: MIT
3
+ # Copyright (C) Intel Corporation
4
+ #
5
+ # Prepare the sample video for the scene-change-detection use case.
6
+ # This use case uses classical computer vision (frame histogram
7
+ # comparison) with OpenCV; no model export or quantization is required.
8
+ #
9
+ # A single continuous shot never triggers a scene change, so this script
10
+ # builds a short montage (test_video.mp4) from several distinct sample
11
+ # clips joined with hard cuts. Each clip is normalized to the same size and
12
+ # frame rate and trimmed to 2 seconds, producing a scene change every 2
13
+ # seconds that the histogram detector flags.
14
+ # Usage: ./export_and_quantize.sh
15
+
16
+ set -euo pipefail
17
+
18
+ SAMPLE_BASE_URL="https://github.com/intel-iot-devkit/sample-videos/raw/master"
19
+ # Distinct scenes joined into the montage, in order.
20
+ SAMPLE_CLIPS=(
21
+ "one-by-one-person-detection.mp4"
22
+ "bottle-detection.mp4"
23
+ "person-bicycle-car-detection.mp4"
24
+ "head-pose-face-detection-female.mp4"
25
+ )
26
+ CLIP_SECONDS=2 # length of each scene in the montage
27
+ CLIP_WIDTH=640
28
+ CLIP_HEIGHT=360
29
+ CLIP_FPS=30
30
+
31
+ # Ask for approval before downloading models and sample files
32
+ echo ""
33
+ echo "This script will download:"
34
+ echo " - Model weights and/or sample files"
35
+ echo ""
36
+ read -p "Continue with downloads? (yes/no): " APPROVAL
37
+ if [[ "${APPROVAL}" != "yes" ]]; then
38
+ echo "Download cancelled by user."
39
+ exit 0
40
+ fi
41
+
42
+ command -v ffmpeg >/dev/null 2>&1 || {
43
+ echo "ERROR: ffmpeg is required to build the montage sample video." >&2
44
+ exit 1
45
+ }
46
+
47
+ echo ""
48
+ if [[ -f test_video.mp4 ]]; then
49
+ echo "Already present: test_video.mp4"
50
+ else
51
+ echo "--- Downloading sample clips and building montage ---"
52
+ WORK_DIR="$(mktemp -d)"
53
+ trap 'rm -rf "${WORK_DIR}"' EXIT
54
+ CONCAT_LIST="${WORK_DIR}/concat.txt"
55
+ : > "${CONCAT_LIST}"
56
+
57
+ idx=0
58
+ for clip in "${SAMPLE_CLIPS[@]}"; do
59
+ src="${WORK_DIR}/src_${idx}.mp4"
60
+ norm="${WORK_DIR}/clip_${idx}.mp4"
61
+ echo "Downloading: ${clip}"
62
+ wget -q -O "${src}" "${SAMPLE_BASE_URL}/${clip}"
63
+ # Trim to CLIP_SECONDS and normalize size/fps so the clips concatenate
64
+ # cleanly and every join is a clean scene cut.
65
+ ffmpeg -nostdin -y -loglevel error -t "${CLIP_SECONDS}" -i "${src}" \
66
+ -vf "scale=${CLIP_WIDTH}:${CLIP_HEIGHT},fps=${CLIP_FPS},setsar=1" \
67
+ -an -pix_fmt yuv420p "${norm}"
68
+ echo "file 'clip_${idx}.mp4'" >> "${CONCAT_LIST}"
69
+ idx=$((idx + 1))
70
+ done
71
+
72
+ ffmpeg -nostdin -y -loglevel error -f concat -safe 0 -i "${CONCAT_LIST}" \
73
+ -c copy test_video.mp4
74
+ echo "Built: test_video.mp4 (${#SAMPLE_CLIPS[@]} scenes, ${CLIP_SECONDS}s each)"
75
+ fi
76
+
77
+ echo "--- Done ---"
78
+ echo "Sample : $(pwd)/test_video.mp4"
79
+ echo "Note : This use case requires no model; run the README samples directly."