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

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