Scene Change Detection
| Property | Value |
|---|---|
| Category | Scene Analytics (classical computer vision) |
| Base Model | Not applicable -- uses frame histogram comparison |
| Source Framework | OpenCV |
| Supported Precisions | Not applicable |
| Inference Engine | OpenCV (CPU) |
| Hardware | CPU, GPU (OpenCV UMat optional) |
| Detected Class(es) | Scene-change events |
Overview
Scene Change Detection is a Metro Analytics use case that flags abrupt or sustained changes in what a camera is showing, such as a shot cut, a camera being repositioned, or a large change in the field of view. It compares the color-histogram signature of each frame against the previous frame using the Bhattacharyya distance and raises an event when the distance exceeds a threshold.
Histogram and similarity scoring is more robust and far cheaper than running an object detector for this signal, so this use case intentionally avoids a neural model. For semantic scene understanding (for example "platform" versus "concourse"), pair this with the object-detection use case.
Typical Metro deployments include:
- Camera Repositioning Alerts -- detect when a PTZ camera moves to a new view.
- Video Segmentation -- split long recordings into scenes for indexing.
- Content Validation -- confirm a feed switched to the expected source.
- Pre-filter for Analytics -- re-initialize trackers when the scene changes.
Prerequisites
- Python 3.11+
- Install OpenVINO (latest version)
ffmpeg(used byexport_and_quantize.shto build the sample montage)
Create and activate a Python virtual environment before running the scripts:
python3 -m venv .venv --system-site-packages
source .venv/bin/activate
Note: The
--system-site-packagesflag is required so the virtual environment can access the system-installed OpenVINO Python packages (which provide OpenCV).
Getting Started
Download the Sample Video
This use case does not export or quantize a model. Run the provided script to prepare the sample test video:
chmod +x export_and_quantize.sh
./export_and_quantize.sh
A single continuous shot never triggers a scene change, so the script
downloads several distinct sample clips and joins them with hard cuts into
test_video.mp4 (four 2-second scenes). This produces a clear scene change
every two seconds for the detector to flag. The script requires ffmpeg to
build the montage.
OpenCV Sample
The sample below computes a normalized HSV histogram for each frame, compares
it to the previous frame with the Bhattacharyya distance, and flags a scene
change when the distance exceeds CHANGE_THRESHOLD.
The annotated frames are written to output_opencv.mp4.
import cv2
import numpy as np
INPUT_VIDEO = "test_video.mp4"
CHANGE_THRESHOLD = 0.45 # Bhattacharyya distance in [0, 1]; higher = more change
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))
writer = cv2.VideoWriter(
"output_opencv.mp4", cv2.VideoWriter_fourcc(*"mp4v"), fps, (width, height))
def frame_histogram(bgr):
hsv = cv2.cvtColor(bgr, cv2.COLOR_BGR2HSV)
hist = cv2.calcHist([hsv], [0, 1], None, [50, 60], [0, 180, 0, 256])
cv2.normalize(hist, hist, 0, 1, cv2.NORM_MINMAX)
return hist
prev_hist = None
frame_idx = 0
scene_changes = 0
while True:
ok, frame = cap.read()
if not ok:
break
frame_idx += 1
hist = frame_histogram(frame)
distance = 0.0
changed = False
if prev_hist is not None:
distance = cv2.compareHist(prev_hist, hist, cv2.HISTCMP_BHATTACHARYYA)
changed = distance >= CHANGE_THRESHOLD
prev_hist = hist
if changed:
scene_changes += 1
print(f"Frame {frame_idx}: SCENE CHANGE (distance={distance:.3f})",
flush=True)
color = (0, 0, 255) if changed else (0, 255, 0)
label = f"dist={distance:.3f}" + (" CHANGE" if changed else "")
cv2.putText(frame, label, (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2)
writer.write(frame)
cap.release()
writer.release()
print(f"Scene changes detected: {scene_changes}", flush=True)
Device targets:
"CPU"-- default for OpenCV histogram comparison."GPU"-- wrap frames incv2.UMatto use the OpenCV transparent API on Intel GPUs."NPU"-- not applicable; histogram comparison is not a neural workload.
Scene-Change Terminal Logging
Every time the Bhattacharyya distance crosses CHANGE_THRESHOLD, the sample
treats it as a new scene and prints a line to the terminal with the frame
number and the distance that triggered it. A running total is printed when the
video ends. This makes the terminal a lightweight event log you can pipe to a
file or another process without inspecting the annotated video.
The relevant lines in the sample are:
if changed:
scene_changes += 1
print(f"Frame {frame_idx}: SCENE CHANGE (distance={distance:.3f})",
flush=True)
Expected Terminal Output
Running the sample against the four-scene montage produces one log line per cut (at ~2s, ~4s, and ~6s), followed by the summary:
Frame 61: SCENE CHANGE (distance=0.949)
Frame 121: SCENE CHANGE (distance=0.988)
Frame 181: SCENE CHANGE (distance=0.854)
Scene changes detected: 3
Expected Output
The annotated video draws each frame's distance in green and turns the label red on the frame where a scene change is detected:
License
Licensed under the MIT License. See LICENSE for details.
