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