Spaces:
Sleeping
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). | |