File size: 3,034 Bytes
b510add
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 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).