YELY_AI_Module / app /quality.py
danielxdata's picture
Ajoute le suivi des echecs et la boucle de correction pompiste
3020394
Raw
History Blame Contribute Delete
2.42 kB
"""Détection de la qualité de l'image (floue / sombre / surexposée / valide).
Fonction indépendante du moteur de reconnaissance : elle s'applique à
l'image (ou au crop d'écran) avant toute tentative de lecture des chiffres,
conformément au §6.2 du cahier des charges.
"""
import cv2
import numpy as np
from typing import Dict, Any, Optional
from .config import RuleConfig, load_config
def estimate_image_quality(img: np.ndarray,
ocr_results: Optional[list] = None,
cfg: Optional[RuleConfig] = None) -> Dict[str, Any]:
"""Estime la qualité de l'image et retourne un label + un score.
Heuristiques : variance du Laplacien (flou) et luminosité moyenne.
"""
if cfg is None:
cfg = load_config()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) if len(img.shape) == 3 else img.copy()
blur = cv2.Laplacian(gray, cv2.CV_64F).var()
mean_brightness = float(np.mean(gray))
# La luminosité est vérifiée AVANT le flou. Raison (constatée sur des
# photos réelles sombres mais nettes) : la variance du Laplacien dépend
# elle-même du contraste, qui s'effondre dans une image sombre même
# quand les contours sont parfaitement nets. Tester le flou en premier
# classait donc à tort des photos sombres comme "blurry" au lieu de
# "dark" — deux causes qui appellent des messages/corrections différents
# côté utilisateur (reprendre la photo avec plus de lumière n'est pas le
# même geste que la stabiliser). Le flou n'est un diagnostic fiable que
# sur une image dont la luminosité est déjà normale.
if mean_brightness < cfg.dark_threshold:
label = "dark"
elif mean_brightness > cfg.bright_threshold:
label = "bright"
elif blur < cfg.blur_threshold:
label = "blurry"
elif ocr_results is not None and len(ocr_results) < 2:
label = "unclear"
else:
label = "valid"
quality_score = 0.0
if label == "valid":
quality_score = 0.95
elif label == "unclear":
quality_score = 0.20
elif label in ("blurry", "dark", "bright"):
quality_score = 0.35
else:
quality_score = 0.50
return {
"image_quality": label,
"quality_score": round(min(1.0, quality_score), 2),
"blur_variance": round(blur, 2),
"brightness": round(mean_brightness, 2),
}