Nouveau module pipeline/zones_config.py : charge les zones d'extraction depuis un fichier zones_config.json (coordonnées relatives 0-1), avec fallback sur les constantes Python. Config partagée entre : - pipeline/extract.py (crop colonne Recodage) - pipeline/checkboxes.py (cases Accord/Désaccord) Zones configurables aujourd'hui (page recueil) : - codage_reco (crop zonal pour le second passage VLM) - accord_checkbox / desaccord_checkbox (densité de pixels) Mode "🔧 Calibration zones" ajouté dans pipeline/ui_overlay.py : - Sélection d'un PDF de référence (idéalement bien cadré) - Canvas interactif (streamlit-drawable-canvas) avec les zones existantes pré-dessinées en rouge - Dessin/déplacement/redimensionnement à la souris - Saisie d'un nom et description par zone - Sauvegarde en JSON (ou OGC_ZONES_CONFIG si défini) Permet au métier (Khalid) de recalibrer les zones sans toucher au code, par exemple si le formulaire ATIH évolue ou si les scans sont d'un autre établissement. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aivanov_scan_ogc
Pipeline d'extraction structurée des fiches OGC (contrôles T2A/PMSI de l'Assurance Maladie) à partir de PDFs scannés. Produit un JSON par dossier + validation automatique des codes médicaux contre les référentiels ATIH 2018.
Architecture en une page
PDF scanné (6 pages)
│
▼
┌───────────────┐ pipeline/ingest.py
│ ingest │ → PNG 300 dpi, cache par hash SHA256
└───────┬───────┘
▼
┌───────────────┐ pipeline/classify.py
│ routing │ → type de page (recueil, concertation_1/2, preuves…)
└───────┬───────┘ → vérif 1 seule page (ordre standard OGC respecté)
▼
┌───────────────┐ pipeline/ocr_qwen.py — Qwen2.5-VL-3B (VLM local ~7 Go VRAM)
│ extraction │ + pipeline/prompts.py — JSON schemas par type de page
│ OCR + JSON │ + pipeline/checkboxes.py — densité pixels pour Accord/Désaccord
└───────┬───────┘ (les VLM testés n'arrivent pas à lire les cases à cocher)
▼
┌───────────────┐ pipeline/validation.py + pipeline/referentials.py
│ validation │ → lookup ATIH (CIM-10, CCAM, GHM, GHS 2018 en SQLite local)
│ ATIH │ → suggestion Levenshtein ≤ 1 en cas de code invalide
└───────┬───────┘ → cross-check GHM ↔ GHS
▼
┌───────────────┐ pipeline/persist.py — JSON annoté + metadata
│ output JSON │ pipeline/ui_overlay.py — Streamlit review/annotation
└───────────────┘
Prérequis
- Python 3.10 – 3.12
- Linux ou macOS (testé sur Linux + RTX 5070, et macOS Apple Silicon)
- GPU : une carte CUDA avec ≥ 8 Go VRAM (Qwen2.5-VL-3B en bfloat16 tient en 7 Go). Sur Apple Silicon, MPS fonctionne mais n'a pas été validé ici.
- poppler pour
pdf2image - git, curl
Installation des deps système
Linux (Debian/Ubuntu) :
sudo apt update
sudo apt install -y python3.12 python3.12-venv python3-pip poppler-utils git curl
macOS :
brew install python@3.12 poppler git
Installation du projet
git clone http://localhost:3100/Dom/Aivanov_scan_ogc.git
cd Aivanov_scan_ogc
python3.12 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
# Deps principales (GPU)
pip install "torch>=2.6" torchvision --index-url https://download.pytorch.org/whl/cu128
pip install transformers "accelerate>=1.0" qwen-vl-utils
pip install pdf2image Pillow openpyxl numpy
# UI + tests
pip install streamlit pytest
Note VRAM : si la carte fait < 8 Go, réduire
max_pixelsdanspipeline/ocr_qwen.py(max_pixels=1280 * 28 * 28→640 * 28 * 28). Si pas de GPU du tout, le modèle tournera sur CPU mais très lentement (≈ 5-10 min par page au lieu de 3 s).
Données d'entrée
Déposer les PDFs à extraire dans un répertoire, par exemple 2018 CARC/ à la racine.
Le nom est libre, mais les fichiers doivent suivre le modèle OGC <N>.pdf (ex.
OGC 7.pdf). Le répertoire de données n'est pas dans le dépôt (voir
.gitignore) — c'est à chacun de récupérer ses scans.
Utilisation
1. Extraire un ou plusieurs dossiers
# Un PDF
python -m pipeline.cli "2018 CARC/OGC 7.pdf"
# Tout un répertoire
python -m pipeline.cli "2018 CARC" --out output/v2
Sortie : output/v2/<nom>.json avec la structure :
{
"fichier": "OGC 7",
"pdf_hash": "…",
"pages": [ { "page": 1, "type": "recueil", "elapsed_s": 6.3, … }, … ],
"extraction": {
"recueil": { "etablissement": "…", "ghm_etab": "21M162", …,
"_validation": { "summary": {…}, "cross_checks": {…} } },
"concertation_2": { "ghs_initial": "…", "decision": "…", "_validation": … },
"concertation_1": { "argumentaire": "…", "date_concertation": "…" },
"preuves": { "date": "…", "pieces": […] }
},
"_meta": { "pipeline_version": "v1", "ocr_model": "…", "generated_at": "…" }
}
Temps attendu : ~35 s / dossier (6 pages).
2. Interface de review & annotation
streamlit run pipeline/ui_overlay.py
Ouvre http://localhost:8501. Sélectionner un dossier dans la sidebar ; pour chaque
page, l'image est affichée à gauche et les champs extraits (éditables) à droite. Les
codes médicaux sont annotés d'un badge ATIH (🟢 valide / 🟡 invalide mais suggestion /
🔴 invalide). Les corrections sont sauvegardées dans gold/<nom>.json.
3. Validation ATIH sur les JSONs existants
python annotate_validation.py # annote output/v2/*.json et produit validation_report.md
Rapport produit : validation_report.md avec taux de validité par champ, suggestions de
correction OCR, incohérences GHM↔GHS.
4. Tests
pytest tests/ # 11 tests unitaires sur les référentiels ATIH
5. Reconstruction de la base ATIH (si besoin)
La base SQLite referentials/atih_2018.sqlite est déjà incluse dans le dépôt, donc
aucune action n'est normalement requise. Si les sources changent :
python -m pipeline.referentials --build # relit referentials/sources/ → SQLite
python -m pipeline.referentials --stats # affiche le nombre de codes par table
python -m pipeline.referentials --test # self-test rapide
Structure des répertoires
| Chemin | Contenu | Dans git ? |
|---|---|---|
pipeline/ |
Code de production (modules OCR, validation, UI) | ✅ |
pipeline/ui_overlay.py |
Interface Streamlit | ✅ |
referentials/sources/ |
Données ATIH brutes (XLSX, XML ClaML, ~8 Mo) | ✅ |
referentials/atih_2018.sqlite |
Base SQLite générée (3 Mo) | ✅ |
tests/ |
Tests unitaires | ✅ |
output/ |
Sorties legacy (pipeline V0 extract_ogc.py) |
✅ |
output/v2/ |
Sorties pipeline V2 (JSONs annotés ATIH, 18 dossiers) | ✅ |
scratch/ |
Scripts exploratoires (choix d'OCR) + README | ✅ |
bench_v2_report.md |
Comparaison V2 vs legacy | ✅ |
validation_report.md |
Rapport validation ATIH | ✅ |
2018 CARC/ |
PDFs scannés — à fournir, ignoré par git | ❌ |
.cache/images/ |
Cache PDF → PNG (reconstructible, gitignoré) | ❌ |
gold/ |
Annotations manuelles (créé au besoin via l'UI) | ❌ |
.venv/ |
Environnement Python virtuel | ❌ |
extract_ogc.py |
Pipeline legacy docTR+VLM (conservé pour comparaison) | ✅ |
generate_pdf.py |
Reconstruction de PDFs propres depuis JSON (legacy) | ✅ |
État du pipeline (au 2026-04-24)
Qualité mesurée sur 18 dossiers (validation ATIH, fix */+N appliqué) :
| Champ | Validité |
|---|---|
| ghm_etab / ghs_etab / ghm_reco / ghs_reco | 94 % |
| codage_etab.dp | 94 % |
| codage_etab.das / codage_reco.das | 100 % |
| codage_etab.dr | 79 % (suffixes PMSI) |
| accord_desaccord (checkboxes) | 17/17 sur échantillon vérifié |
Limites connues (cf. bench_v2_report.md) :
codage_reco.*sous-extrait (27 % de couverture, mais 100 % de validité quand extrait) — la colonne « Recodage » du tableau n'est pas lue systématiquement.praticien_conseilhalluciné (biais fréquentiel « DR VIGNAU »).- Pages manuscrites (p3 et parfois p6) : hors scope actuel.
Pistes pour la suite :
- Prompt explicite colonne Recodage, ou crop demi-page droite en second passage.
- Anti-hallucination praticien_conseil (consigne stricte + crop bas de page).
- Passage de
ghs_injustifiedanscheckboxes.py(comme Accord/Désaccord). - Annotation manuelle d'un gold set de 5-10 dossiers via l'UI pour mesurer chaque itération contre une vérité auditée (et pas seulement contre le legacy qui contient lui-même des erreurs).
Références
- Modèle OCR : Qwen/Qwen2.5-VL-3B-Instruct
- Référentiels ATIH : CIM-10 FR à usage PMSI, CCAM descriptive, Manuel des GHM, Tarifs MCO (atih.sante.fr)
- Licence : interne, projet non redistribué