Spaces:
Sleeping
Sleeping
| """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), | |
| } | |