| """Image quality metrics — brightness, contrast, sharpness, noise, composite. |
| |
| Consolidates the heuristic quality scoring that was previously duplicated |
| between the image_quality provider and the duplicate_detector provider. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import cv2 |
| import numpy as np |
|
|
| from cores.vision.color import to_gray |
|
|
|
|
| def brightness(img: np.ndarray) -> float: |
| """Mean pixel intensity (0-255).""" |
| return float(np.mean(to_gray(img))) |
|
|
|
|
| def contrast(img: np.ndarray) -> float: |
| """Standard deviation of pixel intensity.""" |
| return float(np.std(to_gray(img))) |
|
|
|
|
| def sharpness(img: np.ndarray) -> float: |
| """Variance of Laplacian — higher = sharper.""" |
| gray = to_gray(img) |
| return float(cv2.Laplacian(gray, cv2.CV_64F).var()) |
|
|
|
|
| def noise_level(img: np.ndarray) -> float: |
| """Estimate noise via median absolute deviation of the Laplacian. |
| |
| Robust, simple, no model required. |
| """ |
| gray = to_gray(img) |
| lap = cv2.Laplacian(gray, cv2.CV_64F) |
| return float(np.median(np.abs(lap - np.median(lap))) / 0.6745) |
|
|
|
|
| def quality_score(img: np.ndarray) -> float: |
| """Composite 0-1 quality score (heuristic). |
| |
| Combines brightness, contrast, sharpness, and noise into a single |
| 0-1 score where 1.0 = excellent quality. |
| """ |
| b = brightness(img) |
| c = contrast(img) |
| s = sharpness(img) |
| n = noise_level(img) |
|
|
| |
| b_score = 1.0 - min(1.0, abs(b - 128.0) / 128.0) |
| |
| c_score = 1.0 - min(1.0, abs(c - 60.0) / 100.0) |
| |
| s_score = min(1.0, np.log1p(s) / np.log1p(1000.0)) |
| |
| n_score = max(0.0, 1.0 - n / 30.0) |
| return 0.25 * (b_score + c_score + s_score + n_score) |
|
|