Spaces:
Sleeping
Prétraitement (app/preprocessing.py)
detect_screen_region(img)
Cherche automatiquement la zone rectangulaire de l'écran LCD dans la photo, avant tout traitement.
Méthode : niveaux de gris → flou gaussien → détection de contours (Canny) → fermeture morphologique pour relier les segments → on garde le plus grand rectangle candidat dont le ratio largeur/hauteur est plausible pour un écran (entre 1.0 et 8.0) et dont l'aire dépasse 2% de l'image.
Limite connue et importante : cette heuristique cherche "le plus grand
rectangle contrasté", pas spécifiquement un écran LCD. Sur certaines
photos, elle capture par erreur le bandeau de marque du carburant (ex.
"Shell FuelSave Super") au lieu de l'écran numérique, parce que ce bandeau
forme un rectangle net et contrasté lui aussi. Repéré concrètement pendant
cette session sur 20260622_112333_023.jpg/024.jpg : la détection
automatique renvoyait le bandeau jaune, pas l'écran noir avec les chiffres.
Si le pipeline ne trouve aucune donnée exploitable sur une photo qui en contient pourtant, c'est le premier endroit à vérifier — visualiser le crop retourné par cette fonction avant d'aller chercher plus loin.
Si aucun rectangle plausible n'est trouvé, la fonction retourne l'image complète inchangée (charge alors au découpage en lignes de s'en sortir).
split_lcd_lines(screen_crop, n_lines=3)
Découpe le crop d'écran en n_lines bandes horizontales, une par valeur
affichée.
Méthode principale : projection horizontale des pixels de texte (binarisation Otsu + CLAHE, puis comptage de pixels "actifs" par ligne) pour repérer automatiquement où sont les bandes de texte et où sont les espaces vides entre elles.
Repli : si la projection ne trouve pas exactement n_lines bandes
(image bruitée, reflet, contraste insuffisant), découpage en bandes de
hauteur égale. Ce repli est peu précis — voir docs/LIMITATIONS.md,
c'est la cause principale du plafond de précision actuel du modèle (86% du
jeu d'entraînement est passé par ce repli plutôt que par la détection
précise).
Pourquoi n_lines=3 par défaut et pas 2 : la majorité des écrans du
jeu d'annotation affichent trois valeurs (prix total, volume, prix du
litre). Un défaut à 2 aurait systématiquement tronqué la troisième ligne —
piège découvert en inspectant src/fine_tuned_inference.py (l'ancienne
version, dans le prototype racine, avait ce défaut à 2).
FIELD_NAMES = ["prix", "volume", "prix_litre"]
Associe chaque ligne découpée (dans l'ordre où elle apparaît, de haut en
bas) à un nom de champ métier. Cet ordre correspond à la convention
observée sur les pompes photographiées : "Prix" en haut = montant total
payé (pas le prix unitaire, malgré le nom), "Volume" au milieu = litres,
"Prix Unitaire"/"Prix du litre" en bas = prix par litre. Voir
docs/POSTPROCESS_RULES.md pour pourquoi cette distinction a causé un bug
réel (confusion montant/prix unitaire).