File size: 4,736 Bytes
3030a91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
---
license: mit
license_link: LICENSE
library_name: opencv
tags:
  - opencv
  - intel
  - light-level-anomaly
  - exposure
  - edge-ai
  - metro
language:
  - en
---

# Light-Level Anomaly Detection

| Property | Value |
|---|---|
| **Category** | Image-Quality Analytics (classical computer vision) |
| **Base Model** | Not applicable -- uses luminance statistics |
| **Source Framework** | OpenCV |
| **Supported Precisions** | Not applicable |
| **Inference Engine** | OpenCV (CPU) |
| **Hardware** | CPU, GPU (OpenCV UMat optional) |
| **Detected Class(es)** | Underexposure, overexposure, sudden light change |

---

## Overview

Light-Level Anomaly Detection is a Metro Analytics use case that monitors the
overall brightness of a camera feed and flags abnormal lighting conditions:
the scene going dark (lights off, lens covered, night), the scene blowing out
(glare, headlights, overexposure), or a sudden change in light level.
It tracks the mean luminance of each frame against a rolling baseline and
raises an event when the level leaves the acceptable band or jumps sharply.

A global luminance signal is best measured directly from pixels, so this use
case intentionally avoids a neural model.
It is a strong building block for real-time alerting use cases.

Typical Metro deployments include:

- **Lighting Fault Detection** -- alert when platform or tunnel lighting fails.
- **Day/Night Transition Handling** -- switch analytics profiles by light level.
- **Exposure QA** -- flag cameras that are blown out or too dark to analyze.
- **Tamper Indicator** -- a covered lens shows up as a sudden drop in light.

---

## Prerequisites

- Python 3.11+
- OpenCV and NumPy

Create and activate a Python virtual environment before running the sample:

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install opencv-python numpy
```

---

## 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 computes the mean luminance of each frame from the V channel
of HSV, compares it against fixed dark/bright bounds and against a rolling
baseline, and classifies each frame as `normal`, `dark`, `bright`, or
`sudden-change`.
The annotated frames are written to `output_opencv.mp4`.

```python
import cv2
import numpy as np

INPUT_VIDEO = "test_video.mp4"
DARK_BOUND = 40.0    # mean luminance below this is underexposed
BRIGHT_BOUND = 215.0  # mean luminance above this is overexposed
JUMP_BOUND = 35.0    # frame-to-frame luminance jump that counts as sudden

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

prev_level = None
frame_idx = 0
anomalies = 0
while True:
    ok, frame = cap.read()
    if not ok:
        break
    frame_idx += 1

    v = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)[:, :, 2]
    level = float(np.mean(v))

    status = "normal"
    if level < DARK_BOUND:
        status = "dark"
    elif level > BRIGHT_BOUND:
        status = "bright"
    elif prev_level is not None and abs(level - prev_level) >= JUMP_BOUND:
        status = "sudden-change"
    prev_level = level

    if status != "normal":
        anomalies += 1
        print(f"Frame {frame_idx}: LIGHT ANOMALY ({status}) level={level:.1f}",
              flush=True)
    color = (0, 255, 0) if status == "normal" else (0, 0, 255)
    label = f"level={level:.1f} {status}"
    (_, text_height), _ = cv2.getTextSize(
        label, cv2.FONT_HERSHEY_SIMPLEX, 5.0, 2)
    cv2.putText(frame, label, (10, text_height + 10),
                cv2.FONT_HERSHEY_SIMPLEX, 5.0, color, 2)
    writer.write(frame)

cap.release()
writer.release()
print(f"Light-level anomalies detected: {anomalies}", flush=True)
```

**Device targets:**

- `"CPU"` -- default for OpenCV luminance statistics.
- `"GPU"` -- wrap frames in `cv2.UMat` to use the OpenCV transparent API on Intel GPUs.
- `"NPU"` -- not applicable; luminance statistics are not a neural workload.

#### Expected Output

![OpenCV expected output](expected_output_openvino.gif)

---

## License

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

## References

- [OpenCV Color Space Conversions](https://docs.opencv.org/4.x/d8/d01/group__imgproc__color__conversions.html)
- [OpenCV Operations on Arrays (mean)](https://docs.opencv.org/4.x/d2/de8/group__core__array.html)