File size: 6,279 Bytes
d6ea02e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
---
license: mit
license_link: LICENSE
library_name: opencv
tags:
  - opencv
  - intel
  - scene-change-detection
  - histogram
  - edge-ai
  - metro
language:
  - en
---

# 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](../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](https://docs.openvino.ai/2026/get-started/install-openvino.html) (latest version)
- `ffmpeg` (used by `export_and_quantize.sh` to build the sample montage)

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 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:

```bash
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`.

```python
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 in `cv2.UMat` to 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:

```python
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:

```text
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:

![OpenCV expected output](expected_output_openvino.gif)

---

## License

Licensed under the MIT License. See [LICENSE](LICENSE) for details.

## References

- [OpenCV Histogram Comparison](https://docs.opencv.org/4.x/d8/dc8/tutorial_histogram_comparison.html)
- [OpenCV calcHist Reference](https://docs.opencv.org/4.x/d6/dc7/group__imgproc__hist.html)
- [OpenVINO Documentation](https://docs.openvino.ai/)