face-intel / cores /vision /quality.py
Marwan
Restructure + add reverse face search (PimEyes-style)
f5eeb1c
Raw
History Blame Contribute Delete
1.8 kB
"""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)
# Brightness: ideal ~128
b_score = 1.0 - min(1.0, abs(b - 128.0) / 128.0)
# Contrast: ideal std ~50-80
c_score = 1.0 - min(1.0, abs(c - 60.0) / 100.0)
# Sharpness: log-scale, ~100 = good, ~1000 = excellent
s_score = min(1.0, np.log1p(s) / np.log1p(1000.0))
# Noise: lower is better; >20 is bad
n_score = max(0.0, 1.0 - n / 30.0)
return 0.25 * (b_score + c_score + s_score + n_score)