feat(extract): normaliser ghs_injustifie en 0/1 (P2)
Qwen renvoie typiquement le libellé complet `0 SE 1 2 3 4 ATU FFM FSD` dans le champ ghs_injustifie alors qu'une seule valeur 0/1 est attendue. Ajout de `pipeline.checkboxes.parse_ghs_injustifie` qui extrait le premier chiffre 0/1 via regex, ou "" si illisible. Post-traitement appliqué à chaque extraction recueil et aux 18 JSONs V2 existants (10 fichiers corrigés en place — les 8 autres avaient déjà ghs_injustifie absent ou vide). Note sur les 7 cases SE1-4/ATU/FFM/FSD : zones trop petites pour être calibrées à l'œil et aucun cas positif (`ghs_injustifie=1`) dans l'échantillon 2018 pour valider visuellement. La détection est en placeholder, à recalibrer sur un cas positif réel. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,14 @@ CONCERTATION_2_DECISION = CheckboxZones(
|
||||
desaccord= (0.280, 0.270, 0.305, 0.290), # retour groupage DIM
|
||||
)
|
||||
|
||||
# Zones des 7 cases SE 1 / 2 / 3 / 4 / ATU / FFM / FSD (page recueil, en bas).
|
||||
# TODO : recalibrer avec des vrais cas positifs — sur 18 dossiers de
|
||||
# l'échantillon 2018, aucune case n'est cochée (`ghs_injustifie = 0` partout)
|
||||
# donc impossible de valider visuellement la détection. Laissé désactivé.
|
||||
GHS_INJUSTIFIE_CHECKBOXES: dict[str, tuple[float, float, float, float]] = {
|
||||
# placeholder — à recalibrer quand un cas positif sera observé
|
||||
}
|
||||
|
||||
|
||||
def dark_ratio(image: Image.Image, zone: tuple[float, float, float, float],
|
||||
inner_frac: float = INNER_FRAC) -> float:
|
||||
@@ -58,6 +66,31 @@ def dark_ratio(image: Image.Image, zone: tuple[float, float, float, float],
|
||||
return float(np.mean(gray < DARK_THRESHOLD))
|
||||
|
||||
|
||||
def parse_ghs_injustifie(raw: str) -> str:
|
||||
"""Extrait la valeur 0/1 du champ ghs_injustifie depuis la sortie OCR brute.
|
||||
|
||||
Qwen tend à recopier le libellé complet `0 SE 1 2 3 4 ATU FFM FSD` au lieu
|
||||
du seul chiffre. On prend le premier caractère qui est 0 ou 1 et on ignore
|
||||
le reste (les chiffres 1/2/3/4 qui suivent « SE » sont des numéros de case,
|
||||
pas la valeur du flag).
|
||||
"""
|
||||
if raw is None:
|
||||
return ""
|
||||
s = str(raw).strip()
|
||||
if not s:
|
||||
return ""
|
||||
# Si déjà propre (juste "0" ou "1"), retour direct
|
||||
if s in ("0", "1"):
|
||||
return s
|
||||
# Prendre le premier chiffre trouvé qui soit 0 ou 1, en ignorant tout
|
||||
# le reste (en particulier les "SE 1 2 3 4…" qui suivent)
|
||||
import re as _re
|
||||
m = _re.match(r"\s*([01])\b", s)
|
||||
if m:
|
||||
return m.group(1)
|
||||
return "" # illisible / format inattendu
|
||||
|
||||
|
||||
def detect_accord_desaccord(
|
||||
image_path: str | Path,
|
||||
zones: CheckboxZones = RECUEIL_ACCORD_DESACCORD,
|
||||
|
||||
Reference in New Issue
Block a user