fix: alle Post-Crop-Schritte nutzen cropped statt dewarped Bild
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 27s
CI / test-go-edu-search (push) Successful in 27s
CI / test-python-klausur (push) Failing after 1m59s
CI / test-python-agent-core (push) Successful in 17s
CI / test-nodejs-website (push) Successful in 24s

Spalten-, Zeilen-, Woerter-Overlay und alle nachfolgenden Steps
(LLM-Review, Rekonstruktion) lesen jetzt image/cropped mit Fallback
auf image/dewarped. Tests fuer page_crop.py hinzugefuegt (25 Tests).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-09 09:10:10 +01:00
parent 156a818246
commit e60254bc75
9 changed files with 471 additions and 59 deletions

View File

@@ -1,6 +1,6 @@
# OCR Pipeline - Schrittweise Seitenrekonstruktion
**Version:** 4.0.0
**Version:** 4.1.0
**Status:** Produktiv (Schritte 110 implementiert)
**URL:** https://macmini:3002/ai/ocr-pipeline
@@ -17,9 +17,9 @@ Jeder Schritt kann individuell geprueft, korrigiert und mit Ground-Truth-Daten v
| Schritt | Name | Beschreibung | Status |
|---------|------|--------------|--------|
| 1 | Orientierung | 90/180/270° Drehungen von Scannern korrigieren | Implementiert |
| 2 | Zuschneiden (Crop) | Scannerraender entfernen, Papierformat (A4) erkennen | Implementiert |
| 3 | Begradigung (Deskew) | Scan begradigen (Hough Lines + Word Alignment) | Implementiert |
| 4 | Entzerrung (Dewarp) | Buchwoelbung entzerren (Vertikalkanten-Analyse) | Implementiert |
| 2 | Begradigung (Deskew) | Scan begradigen (Hough Lines + Word Alignment) | Implementiert |
| 3 | Entzerrung (Dewarp) | Buchwoelbung entzerren (Vertikalkanten-Analyse) | Implementiert |
| 4 | Zuschneiden (Crop) | Content-basierter Crop: Buchruecken-Schatten + Ink-Projektion | Implementiert |
| 5 | Spaltenerkennung | Unsichtbare Spalten finden (Projektionsprofile + Wortvalidierung) | Implementiert |
| 6 | Zeilenerkennung | Horizontale Zeilen + Kopf-/Fusszeilen-Klassifikation + Luecken-Heilung | Implementiert |
| 7 | Worterkennung | Hybrid-Grid: Breite Spalten full-page, schmale cell-crop | Implementiert |
@@ -27,6 +27,11 @@ Jeder Schritt kann individuell geprueft, korrigiert und mit Ground-Truth-Daten v
| 9 | Rekonstruktion | Interaktive Zellenbearbeitung auf Bildhintergrund (Fabric.js) | Implementiert |
| 10 | Validierung | Ground-Truth-Vergleich und Qualitaetspruefung | Implementiert |
!!! note "Reihenfolge-Aenderung (v4.1)"
Crop wurde hinter Deskew/Dewarp verschoben. Das Bild ist dann bereits gerade,
was den Content-basierten Crop deutlich zuverlaessiger macht — insbesondere
bei Buchscans mit Ruecken-Schatten und weissem Scanner-Hintergrund.
---
## Dokumenttyp-Erkennung und Pipeline-Pfade
@@ -34,7 +39,7 @@ Jeder Schritt kann individuell geprueft, korrigiert und mit Ground-Truth-Daten v
### Automatische Weiche: `detect_document_type()`
Nicht jedes Dokument durchlaeuft denselben Pfad. Nach den gemeinsamen Vorverarbeitungsschritten
(Deskew, Dewarp, Binarisierung) analysiert `detect_document_type()` die Seitenstruktur
(Orientierung, Deskew, Dewarp, Crop) analysiert `detect_document_type()` die Seitenstruktur
**ohne OCR** — rein ueber Projektionsprofile und Textdichte-Analyse (< 2 Sekunden).
```
@@ -69,10 +74,10 @@ flowchart TD
┌─────────────────────────────────────────────────────────────────────┐
│ GEMEINSAME VORVERARBEITUNG (alle Dokumente) │
│ │
│ Stage 1: Render (432 DPI, 3× Zoom)
│ Stage 2: Deskew (Hough Lines + Ensemble)
│ Stage 3: Dewarp (Vertikalkanten-Drift, Ensemble Shear)
│ Stage 4: Dual-Bild (ocr_img = binarisiert, layout_img = CLAHE)
│ Schritt 1: Orientierung (90/180/270° Drehung korrigieren)
│ Schritt 2: Deskew (Hough Lines + Iterative Projektion + Ensemble)
│ Schritt 3: Dewarp (Vertikalkanten-Drift, Ensemble Shear) │
│ Schritt 4: Crop (Content-basiert: Schatten + Ink-Projektion)
└─────────────────────────────────────┬───────────────────────────────┘
detect_document_type()
@@ -103,9 +108,9 @@ flowchart TD
Post-Processing Pipeline
(Lautschrift, Komma-Split, etc.)
Schritt 6: Korrektur (Spell)
Schritt 7: Rekonstruktion
Schritt 8: Validierung
Schritt 8: Korrektur (Spell)
Schritt 9: Rekonstruktion
Schritt 10: Validierung
```
---
@@ -140,7 +145,9 @@ Admin-Lehrer (Next.js) klausur-service (FastAPI :8086)
klausur-service/backend/
├── services/
│ └── cv_vocab_pipeline.py # Computer Vision + NLP Algorithmen
├── ocr_pipeline_api.py # FastAPI Router (alle Endpoints)
├── ocr_pipeline_api.py # FastAPI Router (Schritte 2-10)
├── orientation_crop_api.py # FastAPI Router (Schritte 1 + 4)
├── page_crop.py # Content-basierter Crop-Algorithmus
├── ocr_pipeline_session_store.py # PostgreSQL Persistence
├── layout_reconstruction_service.py # Fabric.js JSON + PDF/DOCX Export
└── migrations/
@@ -154,15 +161,17 @@ admin-lehrer/
│ └── types.ts # TypeScript Interfaces
└── components/ocr-pipeline/
├── PipelineStepper.tsx # Fortschritts-Stepper
├── StepDeskew.tsx # Schritt 1: Begradigung
├── StepDewarp.tsx # Schritt 2: Entzerrung
├── StepColumnDetection.tsx # Schritt 3: Spaltenerkennung
├── StepRowDetection.tsx # Schritt 4: Zeilenerkennung
├── StepWordRecognition.tsx # Schritt 5: Worterkennung
├── StepLlmReview.tsx # Schritt 6: Korrektur (SSE-Stream)
├── StepReconstruction.tsx # Schritt 7: Rekonstruktion (Canvas)
├── StepOrientation.tsx # Schritt 1: Orientierung
├── StepDeskew.tsx # Schritt 2: Begradigung
├── StepDewarp.tsx # Schritt 3: Entzerrung
├── StepCrop.tsx # Schritt 4: Zuschneiden
├── StepColumnDetection.tsx # Schritt 5: Spaltenerkennung
├── StepRowDetection.tsx # Schritt 6: Zeilenerkennung
├── StepWordRecognition.tsx # Schritt 7: Worterkennung
├── StepLlmReview.tsx # Schritt 8: Korrektur (SSE-Stream)
├── StepReconstruction.tsx # Schritt 9: Rekonstruktion (Canvas)
├── FabricReconstructionCanvas.tsx # Fabric.js Editor
└── StepGroundTruth.tsx # Schritt 8: Validierung
└── StepGroundTruth.tsx # Schritt 10: Validierung
```
---
@@ -187,14 +196,22 @@ Alle Endpoints unter `/api/v1/ocr-pipeline/`.
| Methode | Pfad | Beschreibung |
|---------|------|--------------|
| `GET` | `/sessions/{id}/image/original` | Originalbild |
| `GET` | `/sessions/{id}/image/oriented` | Orientiertes Bild |
| `GET` | `/sessions/{id}/image/deskewed` | Begradigtes Bild |
| `GET` | `/sessions/{id}/image/dewarped` | Entzerrtes Bild |
| `GET` | `/sessions/{id}/image/cropped` | Zugeschnittenes Bild |
| `GET` | `/sessions/{id}/image/binarized` | Binarisiertes Bild |
| `GET` | `/sessions/{id}/image/columns-overlay` | Spalten-Overlay |
| `GET` | `/sessions/{id}/image/rows-overlay` | Zeilen-Overlay |
| `GET` | `/sessions/{id}/image/words-overlay` | Wort-Grid-Overlay |
### Schritt 1: Begradigung
### Schritt 1: Orientierung
| Methode | Pfad | Beschreibung |
|---------|------|--------------|
| `POST` | `/sessions/{id}/orientation` | 90/180/270° Drehung erkennen und korrigieren |
### Schritt 2: Begradigung
| Methode | Pfad | Beschreibung |
|---------|------|--------------|
@@ -202,7 +219,7 @@ Alle Endpoints unter `/api/v1/ocr-pipeline/`.
| `POST` | `/sessions/{id}/deskew/manual` | Manuelle Winkelkorrektur |
| `POST` | `/sessions/{id}/ground-truth/deskew` | Ground Truth speichern |
### Schritt 2: Entzerrung
### Schritt 3: Entzerrung
| Methode | Pfad | Beschreibung |
|---------|------|--------------|
@@ -211,7 +228,15 @@ Alle Endpoints unter `/api/v1/ocr-pipeline/`.
| `POST` | `/sessions/{id}/adjust-combined` | Kombinierte Rotation + Shear Feinabstimmung |
| `POST` | `/sessions/{id}/ground-truth/dewarp` | Ground Truth speichern |
### Schritt 3: Spalten
### Schritt 4: Zuschneiden
| Methode | Pfad | Beschreibung |
|---------|------|--------------|
| `POST` | `/sessions/{id}/crop` | Automatischer Content-Crop |
| `POST` | `/sessions/{id}/crop/manual` | Manueller Crop (Prozent-Koordinaten) |
| `POST` | `/sessions/{id}/crop/skip` | Crop ueberspringen |
### Schritt 5: Spalten
| Methode | Pfad | Beschreibung |
|---------|------|--------------|
@@ -219,7 +244,7 @@ Alle Endpoints unter `/api/v1/ocr-pipeline/`.
| `POST` | `/sessions/{id}/columns/manual` | Manuelle Spalten-Definition |
| `POST` | `/sessions/{id}/ground-truth/columns` | Ground Truth speichern |
### Schritt 4: Zeilen
### Schritt 6: Zeilen
| Methode | Pfad | Beschreibung |
|---------|------|--------------|
@@ -228,7 +253,7 @@ Alle Endpoints unter `/api/v1/ocr-pipeline/`.
| `POST` | `/sessions/{id}/ground-truth/rows` | Ground Truth speichern |
| `GET` | `/sessions/{id}/ground-truth/rows` | Ground Truth abrufen |
### Schritt 5: Worterkennung
### Schritt 7: Worterkennung
| Methode | Pfad | Beschreibung |
|---------|------|--------------|
@@ -236,14 +261,14 @@ Alle Endpoints unter `/api/v1/ocr-pipeline/`.
| `POST` | `/sessions/{id}/ground-truth/words` | Ground Truth speichern |
| `GET` | `/sessions/{id}/ground-truth/words` | Ground Truth abrufen |
### Schritt 6: Korrektur
### Schritt 8: Korrektur
| Methode | Pfad | Beschreibung |
|---------|------|--------------|
| `POST` | `/sessions/{id}/llm-review?stream=true` | SSE-Stream Korrektur starten |
| `POST` | `/sessions/{id}/llm-review/apply` | Ausgewaehlte Korrekturen speichern |
### Schritt 7: Rekonstruktion
### Schritt 9: Rekonstruktion
| Methode | Pfad | Beschreibung |
|---------|------|--------------|
@@ -253,12 +278,66 @@ Alle Endpoints unter `/api/v1/ocr-pipeline/`.
| `GET` | `/sessions/{id}/reconstruction/export/docx` | DOCX-Export (python-docx) |
| `POST` | `/sessions/{id}/reconstruction/detect-images` | Bildbereiche per VLM erkennen |
| `POST` | `/sessions/{id}/reconstruction/generate-image` | Bild per mflux generieren |
| `POST` | `/sessions/{id}/reconstruction/validate` | Validierung speichern (Step 8) |
| `POST` | `/sessions/{id}/reconstruction/validate` | Validierung speichern (Step 10) |
| `GET` | `/sessions/{id}/reconstruction/validation` | Validierungsdaten abrufen |
---
## Schritt 2: Entzerrung/Dewarp (Detail)
## Schritt 4: Zuschneiden/Crop (Detail)
### Warum Crop nach Deskew/Dewarp?
In frueheren Versionen lief Crop als Schritt 2 (vor Deskew). Das fuehrte zu Problemen:
- **Schiefes Bild**: `boundingRect` einer schiefen Seite schliesst viel Scanner-Hintergrund ein
- **Buchscans**: Otsu-Binarisierung versagt bei weiss-auf-weiss (Seite auf weissem Scanner)
- **Buchruecken**: Gradueller Schatten-Uebergang wird nicht als Kante erkannt
**Loesung (v4.1):** Crop laeuft jetzt nach Dewarp — das Bild ist dann gerade.
### Algorithmus: Content-basierte 4-Kanten-Erkennung
Datei: `page_crop.py`
```
Input: Entzerrtes BGR-Bild
├─ Adaptive Threshold (Gauss, blockSize=51)
│ → binary (Text=255, Hintergrund=0)
├─ Linker Rand (Buchruecken-Schatten):
│ 1. Grauwert-Spaltenmittel in linken 25%
│ 2. Glaetten mit Boxcar-Kernel
│ 3. Transition hell→dunkel finden (> 60% des Helligkeitsbereichs)
│ 4. Fallback: Binaere Vertikal-Projektion
├─ Rechter Rand: Binaere Vertikal-Projektion (letzte Ink-Spalte)
├─ Oben/Unten: Binaere Horizontal-Projektion (erste/letzte Ink-Zeile)
├─ Rausch-Filter: Runs < 0.5% der Dimension ignorieren
├─ Sanity-Checks:
│ - Mindestens eine Kante > 2% Border
│ - Crop-Flaeche >= 40% des Originals
└─ Crop + konfigurierbarer Rand (default 1%)
```
### Vergleich alt vs. neu
| Eigenschaft | Alt (Otsu + Kontur) | Neu (Content-basiert) |
|-------------|--------------------|-----------------------|
| Binarisierung | Otsu (global) | Adaptive Threshold |
| Methode | Groesste Kontur → boundingRect | 4-Kanten Ink-Projektion |
| Buchruecken | Nicht erkannt | Schatten-Gradient-Erkennung |
| Weiss-auf-weiss | Versagt | Funktioniert (adaptive) |
| Format-Matching | A4/Letter erzwungen | Kein Format-Matching (Content-Bounds) |
| Position in Pipeline | Vor Deskew (Schritt 2) | Nach Dewarp (Schritt 4) |
---
## Schritt 3: Entzerrung/Dewarp (Detail)
### Algorithmus: Vertikalkanten-Drift
@@ -311,7 +390,7 @@ Response: {"method_used": "manual_combined", "shear_degrees": -0.45, "dewarped_i
---
## Schritt 3: Spaltenerkennung (Detail)
## Schritt 5: Spaltenerkennung (Detail)
### Algorithmus: `detect_column_geometry()`
@@ -417,7 +496,7 @@ min_real_col_w = max(20, int(content_w * 0.03))
---
## Schritt 4: Zeilenerkennung (Detail)
## Schritt 6: Zeilenerkennung (Detail)
### Algorithmus: `detect_row_geometry()`
@@ -447,7 +526,7 @@ def _heal_row_gaps(rows, top_bound, bottom_bound):
---
## Schritt 5: Worterkennung — Hybrid-Grid (Detail)
## Schritt 7: Worterkennung — Hybrid-Grid (Detail)
### Algorithmus: `build_cell_grid_v2()`
@@ -554,7 +633,7 @@ Eingabe: ocr_img, column_regions, row_geometries
---
## Schritt 6: Korrektur (Detail)
## Schritt 8: Korrektur (Detail)
### Korrektur-Engine
@@ -611,7 +690,7 @@ Change-Format:
---
## Schritt 7: Rekonstruktion (Detail)
## Schritt 9: Rekonstruktion (Detail)
Zwei Modi verfuegbar: