diff --git a/README.md b/README.md new file mode 100644 index 0000000..d8aa601 --- /dev/null +++ b/README.md @@ -0,0 +1,214 @@ +# 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) :** +```bash +sudo apt update +sudo apt install -y python3.12 python3.12-venv python3-pip poppler-utils git curl +``` + +**macOS :** +```bash +brew install python@3.12 poppler git +``` + +## Installation du projet + +```bash +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_pixels` dans +> `pipeline/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 .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 + +```bash +# 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/.json` avec la structure : + +```json +{ + "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 + +```bash +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/.json`. + +### 3. Validation ATIH sur les JSONs existants + +```bash +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 + +```bash +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 : + +```bash +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_conseil` halluciné (biais fréquentiel « DR VIGNAU »). +- Pages manuscrites (p3 et parfois p6) : hors scope actuel. + +**Pistes pour la suite** : + +1. Prompt explicite colonne Recodage, ou crop demi-page droite en second passage. +2. Anti-hallucination praticien_conseil (consigne stricte + crop bas de page). +3. Passage de `ghs_injustifie` dans `checkboxes.py` (comme Accord/Désaccord). +4. 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](https://huggingface.co/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](https://www.atih.sante.fr)) +- **Licence** : interne, projet non redistribué