feat: OCR pipeline step 8 — validation view with image detection & generation
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 29s
CI / test-go-edu-search (push) Successful in 27s
CI / test-python-klausur (push) Failing after 2m4s
CI / test-python-agent-core (push) Successful in 19s
CI / test-nodejs-website (push) Successful in 19s
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 29s
CI / test-go-edu-search (push) Successful in 27s
CI / test-python-klausur (push) Failing after 2m4s
CI / test-python-agent-core (push) Successful in 19s
CI / test-nodejs-website (push) Successful in 19s
Replaces the stub StepGroundTruth with a full side-by-side Original vs Reconstruction view. Adds VLM-based image region detection (qwen2.5vl), mflux image generation proxy, sync scroll/zoom, manual region drawing, and score/notes persistence. New backend endpoints: detect-images, generate-image, validate, get validation. New standalone mflux-service (scripts/mflux-service.py) for Metal GPU generation. Dockerfile.base: adds fonts-liberation (Apache-2.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
# OCR Pipeline - Schrittweise Seitenrekonstruktion
|
||||
|
||||
**Version:** 2.0.0
|
||||
**Version:** 3.0.0
|
||||
**Status:** Produktiv (Schritte 1–8 implementiert)
|
||||
**URL:** https://macmini:3002/ai/ocr-pipeline
|
||||
|
||||
## Uebersicht
|
||||
|
||||
Die OCR Pipeline zerlegt den OCR-Prozess in **8 einzelne Schritte**, um eingescannte Vokabelseiten
|
||||
Die OCR Pipeline zerlegt den OCR-Prozess in **8 einzelne Schritte**, um eingescannte Seiten
|
||||
aus mehrspaltig gedruckten Schulbuechern Wort fuer Wort zu rekonstruieren.
|
||||
Jeder Schritt kann individuell geprueft, korrigiert und mit Ground-Truth-Daten versehen werden.
|
||||
|
||||
@@ -20,13 +20,94 @@ Jeder Schritt kann individuell geprueft, korrigiert und mit Ground-Truth-Daten v
|
||||
| 2 | Entzerrung (Dewarp) | Buchwoelbung entzerren (Vertikalkanten-Analyse) | Implementiert |
|
||||
| 3 | Spaltenerkennung | Unsichtbare Spalten finden (Projektionsprofile + Wortvalidierung) | Implementiert |
|
||||
| 4 | Zeilenerkennung | Horizontale Zeilen + Kopf-/Fusszeilen-Klassifikation + Luecken-Heilung | Implementiert |
|
||||
| 5 | Worterkennung | Grid aus Spalten x Zeilen, OCR pro Zelle, Post-Processing | Implementiert |
|
||||
| 5 | Worterkennung | Hybrid-Grid: Breite Spalten full-page, schmale cell-crop | Implementiert |
|
||||
| 6 | Korrektur | Zeichenverwirrung + regel-basierte Rechtschreibkorrektur (SSE-Stream) | Implementiert |
|
||||
| 7 | Rekonstruktion | Interaktive Zellenbearbeitung auf Bildhintergrund | Implementiert |
|
||||
| 7 | Rekonstruktion | Interaktive Zellenbearbeitung auf Bildhintergrund (Fabric.js) | Implementiert |
|
||||
| 8 | Validierung | Ground-Truth-Vergleich und Qualitaetspruefung | Implementiert |
|
||||
|
||||
---
|
||||
|
||||
## Dokumenttyp-Erkennung und Pipeline-Pfade
|
||||
|
||||
### Automatische Weiche: `detect_document_type()`
|
||||
|
||||
Nicht jedes Dokument durchlaeuft denselben Pfad. Nach den gemeinsamen Vorverarbeitungsschritten
|
||||
(Deskew, Dewarp, Binarisierung) analysiert `detect_document_type()` die Seitenstruktur
|
||||
**ohne OCR** — rein ueber Projektionsprofile und Textdichte-Analyse (< 2 Sekunden).
|
||||
|
||||
```
|
||||
detect_document_type(ocr_img, img_bgr) → DocumentTypeResult
|
||||
```
|
||||
|
||||
#### Entscheidungslogik
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Bild-Input] --> B[Vertikales Projektionsprofil]
|
||||
B --> C{Interne Spalten-Gaps >= 2?}
|
||||
C -->|Ja| D{Zeilen-Gaps >= 5?}
|
||||
D -->|Ja| E["vocab_table<br/>pipeline = cell_first<br/>confidence 0.7–0.95"]
|
||||
D -->|Nein| F{Zeilen-Gaps >= 3?}
|
||||
C -->|Nein| G{Interne Spalten-Gaps >= 1?}
|
||||
G -->|Ja| F
|
||||
G -->|Nein| H["full_text<br/>pipeline = full_page<br/>skip: columns, rows"]
|
||||
F -->|Ja| I["generic_table<br/>pipeline = cell_first<br/>confidence 0.5–0.85"]
|
||||
F -->|Nein| H
|
||||
```
|
||||
|
||||
| Dokumenttyp | Spalten-Gaps | Zeilen-Gaps | Pipeline | Beispiel |
|
||||
|-------------|-------------|-------------|----------|----------|
|
||||
| `vocab_table` | ≥ 2 | ≥ 5 | `cell_first` | 3-spaltige Schulbuch-Vokabeltabelle |
|
||||
| `generic_table` | ≥ 1 | ≥ 3 | `cell_first` | 2-spaltiges Glossar |
|
||||
| `full_text` | 0 | egal | `full_page` | Fliesstext, Aufsatz, Buchseite |
|
||||
|
||||
### Komplett-Flussdiagramm
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 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) │
|
||||
└─────────────────────────────────────┬───────────────────────────────┘
|
||||
│
|
||||
detect_document_type()
|
||||
│
|
||||
┌─────────────────┴──────────────────┐
|
||||
▼ ▼
|
||||
FULL-TEXT PFAD CELL-FIRST PFAD
|
||||
(pipeline='full_page') (pipeline='cell_first')
|
||||
│ │
|
||||
Keine Spalten/Zeilen Spaltenerkennung
|
||||
analyze_layout_by_words() detect_column_geometry()
|
||||
Lese-Reihenfolge _detect_sub_columns()
|
||||
│ expand_narrow_columns()
|
||||
│ Zeilenerkennung
|
||||
│ detect_row_geometry()
|
||||
│ │
|
||||
│ build_cell_grid_v2()
|
||||
│ │
|
||||
│ ┌─────────┴──────────┐
|
||||
│ ▼ ▼
|
||||
│ Breite Spalten Schmale Spalten
|
||||
│ (>= 15% Breite) (< 15% Breite)
|
||||
│ Full-Page Words Cell-Crop OCR
|
||||
│ word_lookup cell_crop_v2
|
||||
│ │ │
|
||||
└───────────────────────────┴────────────────────┘
|
||||
│
|
||||
Post-Processing Pipeline
|
||||
(Lautschrift, Komma-Split, etc.)
|
||||
│
|
||||
Schritt 6: Korrektur (Spell)
|
||||
Schritt 7: Rekonstruktion
|
||||
Schritt 8: Validierung
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architektur
|
||||
|
||||
```
|
||||
@@ -55,28 +136,31 @@ Admin-Lehrer (Next.js) klausur-service (FastAPI :8086)
|
||||
|
||||
```
|
||||
klausur-service/backend/
|
||||
├── ocr_pipeline_api.py # FastAPI Router (alle Endpoints)
|
||||
├── ocr_pipeline_session_store.py # PostgreSQL Persistence
|
||||
├── cv_vocab_pipeline.py # Computer Vision + NLP Algorithmen
|
||||
├── services/
|
||||
│ └── cv_vocab_pipeline.py # Computer Vision + NLP Algorithmen
|
||||
├── ocr_pipeline_api.py # FastAPI Router (alle Endpoints)
|
||||
├── ocr_pipeline_session_store.py # PostgreSQL Persistence
|
||||
├── layout_reconstruction_service.py # Fabric.js JSON + PDF/DOCX Export
|
||||
└── migrations/
|
||||
├── 002_ocr_pipeline_sessions.sql # Basis-Schema
|
||||
├── 003_add_row_result.sql # Row-Result Spalte
|
||||
└── 004_add_word_result.sql # Word-Result Spalte
|
||||
├── 002_ocr_pipeline_sessions.sql # Basis-Schema
|
||||
├── 003_add_row_result.sql # Row-Result Spalte
|
||||
└── 004_add_word_result.sql # Word-Result Spalte
|
||||
|
||||
admin-lehrer/
|
||||
├── app/(admin)/ai/ocr-pipeline/
|
||||
│ ├── page.tsx # Haupt-Page mit Session-Management
|
||||
│ └── types.ts # TypeScript Interfaces
|
||||
│ ├── page.tsx # Haupt-Page mit Session-Management
|
||||
│ └── 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)
|
||||
└── StepGroundTruth.tsx # Schritt 8: Validierung
|
||||
├── 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)
|
||||
├── FabricReconstructionCanvas.tsx # Fabric.js Editor
|
||||
└── StepGroundTruth.tsx # Schritt 8: Validierung
|
||||
```
|
||||
|
||||
---
|
||||
@@ -94,6 +178,7 @@ Alle Endpoints unter `/api/v1/ocr-pipeline/`.
|
||||
| `GET` | `/sessions/{id}` | Session-Info mit allen Step-Results |
|
||||
| `PUT` | `/sessions/{id}` | Session umbenennen |
|
||||
| `DELETE` | `/sessions/{id}` | Session loeschen |
|
||||
| `POST` | `/sessions/{id}/detect-type` | Dokumenttyp erkennen |
|
||||
|
||||
### Bilder
|
||||
|
||||
@@ -160,6 +245,34 @@ Alle Endpoints unter `/api/v1/ocr-pipeline/`.
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `POST` | `/sessions/{id}/reconstruction` | Zellaenderungen speichern |
|
||||
| `GET` | `/sessions/{id}/reconstruction/fabric-json` | Fabric.js Canvas-Daten |
|
||||
| `GET` | `/sessions/{id}/reconstruction/export/pdf` | PDF-Export (reportlab) |
|
||||
| `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) |
|
||||
| `GET` | `/sessions/{id}/reconstruction/validation` | Validierungsdaten abrufen |
|
||||
|
||||
---
|
||||
|
||||
## Schritt 2: Entzerrung/Dewarp (Detail)
|
||||
|
||||
### Algorithmus: Vertikalkanten-Drift
|
||||
|
||||
Die Dewarp-Erkennung misst die **vertikale Spaltenkippung** (dx/dy) statt Textzeilen-Neigung:
|
||||
|
||||
1. Woerter werden nach X-Position in vertikale Spaltencluster gruppiert
|
||||
2. Pro Cluster: Lineare Regression `x = a*y + b` → `a = dx/dy = tan(shear_angle)`
|
||||
3. Ensemble aus drei Methoden: Textzeilen (1.5× Gewicht), Projektionsprofil (2-Pass), Vertikalkanten
|
||||
4. Qualitaetspruefung: Horizontale Projektionsvarianz vor/nach Korrektur
|
||||
|
||||
**Schwellenwerte:**
|
||||
|
||||
| Parameter | Wert | Beschreibung |
|
||||
|-----------|------|--------------|
|
||||
| Min. Korrekturwinkel | 0.08° | Unter 0.08° wird nicht korrigiert |
|
||||
| Ensemble Min-Confidence | 0.35 | Mindest-Konfidenz fuer Korrektur |
|
||||
| Quality-Gate Skip | < 0.5° | Kleine Korrekturen ueberspringen Quality-Gate |
|
||||
|
||||
---
|
||||
|
||||
@@ -180,6 +293,38 @@ Bild → Binarisierung → Vertikalprofil → Lueckenerkennung → Wort-Validier
|
||||
- **Phantom-Spalten-Filter (Step 9):** Spalten mit Breite < 3 % der Content-Breite UND < 3 Woerter werden als Artefakte entfernt; die angrenzenden Spalten schliessen die Luecke.
|
||||
- **Spaltenzuweisung:** Woerter werden anhand des groessten horizontalen Ueberlappungsbereichs einer Spalte zugeordnet.
|
||||
|
||||
### Sub-Spalten-Erkennung: `_detect_sub_columns()`
|
||||
|
||||
Erkennt versteckte Sub-Spalten innerhalb breiter Spalten (z.B. Seitenzahl-Spalte links neben EN-Vokabeln).
|
||||
|
||||
**Algorithmus (Left-Edge Alignment Clustering):**
|
||||
|
||||
1. Fuer jede Spalte mit `width_ratio >= 0.15` und `word_count >= 5`:
|
||||
2. Left-Edges aller Woerter mit `conf >= 30` sammeln
|
||||
3. In Alignment-Bins clustern (8px Toleranz)
|
||||
4. Linkester Bin mit >= 10% der Woerter = wahrer Spaltenanfang
|
||||
5. Woerter links davon = Sub-Spalte, wenn >= 2 und < 35% Anteil
|
||||
6. Neue ColumnGeometry-Objekte mit korrekten Indizes erzeugen
|
||||
|
||||
**Koordinatensystem:** Word `left`-Werte sind relativ zum Content-ROI (`left_x`), `ColumnGeometry.x` ist absolut. `left_x` wird als Parameter durchgereicht.
|
||||
|
||||
### Spalten-Erweiterung: `expand_narrow_columns()`
|
||||
|
||||
Laeuft **nach** `_detect_sub_columns()`. Erweitert sehr schmale Spalten (< 10% Content-Breite,
|
||||
z.B. `page_ref`, `marker`) in den Weissraum zum Nachbar-Spalte hinein, aber nie ueber die
|
||||
naechsten Woerter im Nachbarn hinaus (4px Sicherheitsabstand).
|
||||
|
||||
### Spaltentyp-Klassifikation: `classify_column_types()`
|
||||
|
||||
| Spaltentyp | Beschreibung | Erkennung |
|
||||
|------------|--------------|-----------|
|
||||
| `column_en` | Englische Vokabeln | EN-Funktionswoerter (the, a, is...) |
|
||||
| `column_de` | Deutsche Uebersetzung | DE-Funktionswoerter (der, die, das...) |
|
||||
| `column_example` | Beispielsaetze | Abkuerzungen, Grammatik-Marker |
|
||||
| `page_ref` | Seitenzahlen | Schmal (< 20% Breite), wenige Woerter |
|
||||
| `column_marker` | Dekorative Markierungen | Sehr schmal, spezielle Zeichen |
|
||||
| `column_text` | Generischer Text | Fallback |
|
||||
|
||||
### Konfigurierbare Parameter
|
||||
|
||||
```python
|
||||
@@ -219,29 +364,95 @@ def _heal_row_gaps(rows, top_bound, bottom_bound):
|
||||
|
||||
---
|
||||
|
||||
## Schritt 5: Worterkennung (Detail)
|
||||
## Schritt 5: Worterkennung — Hybrid-Grid (Detail)
|
||||
|
||||
### Algorithmus: `build_cell_grid()`
|
||||
### Algorithmus: `build_cell_grid_v2()`
|
||||
|
||||
Schritt 5 nutzt die Ergebnisse von Schritt 3 (Spalten) und Schritt 4 (Zeilen), um ein Grid
|
||||
zu erstellen und jede Zelle per OCR auszulesen.
|
||||
Schritt 5 nutzt eine **Hybrid-Strategie**: Breite Spalten verwenden die Full-Page-Tesseract-Woerter,
|
||||
schmale Spalten werden isoliert per Cell-Crop OCR verarbeitet.
|
||||
|
||||
```
|
||||
Spalten (Step 3): column_en | column_de | column_example
|
||||
───────────┼─────────────┼────────────────
|
||||
Zeilen (Step 4): R0 │ hello │ hallo │ Hello, World!
|
||||
R1 │ world │ Welt │ The whole world
|
||||
R2 │ book │ Buch │ Read a book
|
||||
───────────┼─────────────┼────────────────
|
||||
!!! success "Warum Hybrid?"
|
||||
Full-Page OCR liefert gute Ergebnisse fuer breite Spalten (Saetze, IPA-Klammern, Interpunktion).
|
||||
Aber bei schmalen Spalten (Seitenzahlen, Marker) „bluten" Woerter aus Nachbar-Spalten ein.
|
||||
Cell-Crop isoliert jede Zelle und verhindert dieses Bleeding.
|
||||
|
||||
### Broad vs. Narrow — Die 15%-Schwelle
|
||||
|
||||
```python
|
||||
_NARROW_COL_THRESHOLD_PCT = 15.0 # cv_vocab_pipeline.py
|
||||
```
|
||||
|
||||
**Ablauf:**
|
||||
| Eigenschaft | Breite Spalten (>= 15%) | Schmale Spalten (< 15%) |
|
||||
|-------------|------------------------|------------------------|
|
||||
| **OCR-Quelle** | Full-Page Tesseract (vorher gelaufen) | Isolierter Cell-Crop |
|
||||
| **Wort-Zuweisung** | `_assign_row_words_to_columns()` | Direktes Zell-OCR |
|
||||
| **Confidence-Filter** | `conf >= 30` | `conf >= 30` |
|
||||
| **Text-Bereinigung** | `_clean_cell_text()` (mittel) | `_clean_cell_text_lite()` (aggressiv) |
|
||||
| **Neighbour-Bleeding** | Risiko vorhanden | Verhindert (isoliert) |
|
||||
| **Parallelisierung** | Sequentiell | Parallel (`max_workers=4`) |
|
||||
| **OCR-Engine Label** | `word_lookup` | `cell_crop_v2` |
|
||||
| **Typische Spalten** | EN-Vokabeln, DE-Uebersetzung, Beispielsaetze | Seitenzahlen, Marker |
|
||||
|
||||
1. **Initialer Scan:** Ganzes Bild einmal per Tesseract/RapidOCR → alle Wort-Bboxes
|
||||
2. **Zuweisung:** Jedes Wort der Spalte mit groesstem horizontalem Ueberlapp zuordnen
|
||||
3. **Zell-OCR Fallback:** Leere Zellen bekommen eigenen Crop + erneuten OCR-Aufruf (PSM 6/7)
|
||||
4. **Batch-Spalten-OCR:** Bei vielen leeren Zellen in einer Spalte: gesamte Spalte einmal OCR-en
|
||||
5. **Post-Processing:** Continuation-Rows zusammenfuehren, Lautschrift erkennen, Komma-Eintraege splitten
|
||||
**Empirische Grundlage:** Typische breite Spalten liegen bei 20–40% Bildbreite,
|
||||
typische schmale bei 3–12%. Die 15%-Grenze trennt diese Gruppen sauber.
|
||||
|
||||
!!! note "Offener Punkt: Schwellen-Validierung"
|
||||
Die 15%-Schwelle wurde an Vokabeltabellen mit 3–5 Spalten validiert.
|
||||
Fuer eine breitere Validierung werden diverse Schulbuchseiten mit unterschiedlichen
|
||||
Layouts (2-, 3-, 4-, 5-spaltig, verschiedene Verlage) benoetigt. Aktuell gibt es
|
||||
in der Datenbank nur Sessions mit demselben Arbeitsblatt-Typ.
|
||||
|
||||
### Cell-Crop OCR: `_ocr_cell_crop()`
|
||||
|
||||
Isolierte OCR einer einzelnen Zelle (Spalte × Zeile Schnittflaeche):
|
||||
|
||||
1. **Crop:** Exakte Spalten- × Zeilengrenzen mit 3px internem Padding
|
||||
2. **Density-Check:** Ueberspringe leere Zellen (`dark_ratio < 0.005`)
|
||||
3. **Upscaling:** Kleine Crops (Hoehe < 80px) werden 3× vergroessert
|
||||
4. **OCR:** Engine-spezifisch (Tesseract, TrOCR, RapidOCR, LightON)
|
||||
5. **Fallback:** Bei leerem Ergebnis → PSM 7 (Einzelzeile) statt PSM 6
|
||||
6. **Bereinigung:** `_clean_cell_text_lite()` (aggressives Noise-Filtering)
|
||||
|
||||
### Ablauf von `build_cell_grid_v2()`
|
||||
|
||||
```
|
||||
Eingabe: ocr_img, column_regions, row_geometries
|
||||
│
|
||||
┌───────────┴───────────┐
|
||||
│ Filter │
|
||||
│ • Phantom-Zeilen │
|
||||
│ • Artefakt-Zeilen │
|
||||
│ • Irrelevante Spalten │
|
||||
│ (header, footer, │
|
||||
│ margin, ignore) │
|
||||
└───────────┬───────────┘
|
||||
│
|
||||
┌───────────┴───────────┐
|
||||
│ Klassifizierung │
|
||||
│ Spalte.width / img_w │
|
||||
│ >= 15% → broad │
|
||||
│ < 15% → narrow │
|
||||
└───────────┬───────────┘
|
||||
│
|
||||
┌───────────┴────────────────┐
|
||||
│ │
|
||||
Phase 1: Broad Phase 2: Narrow
|
||||
(sequentiell) (parallel, max_workers=4)
|
||||
│ │
|
||||
Pro (row, col): Pro (row, col):
|
||||
1. Words aus Full-Page 1. _ocr_cell_crop()
|
||||
2. Filter conf >= 30 2. Isoliertes Zell-Bild
|
||||
3. _words_to_reading_order 3. Upscale wenn noetig
|
||||
4. _clean_cell_text() 4. _clean_cell_text_lite()
|
||||
│ │
|
||||
└───────────┬────────────────┘
|
||||
│
|
||||
Merge + Sortierung
|
||||
(row_index, col_index)
|
||||
Leere Zeilen entfernen
|
||||
│
|
||||
Ausgabe: cells[], columns_meta[]
|
||||
```
|
||||
|
||||
### Post-Processing Pipeline (in `build_vocab_pipeline_streaming`)
|
||||
|
||||
@@ -264,7 +475,7 @@ Zeilen (Step 4): R0 │ hello │ hallo │ Hello, World!
|
||||
|
||||
### Korrektur-Engine
|
||||
|
||||
Schritt 6 kombiniert zwei Korrektur-Stufen, beide als SSE-Stream:
|
||||
Schritt 6 kombiniert drei Korrektur-Stufen, alle als SSE-Stream:
|
||||
|
||||
**Stufe 1 — Zeichenverwirrungskorrektur** (`_fix_character_confusion`):
|
||||
|
||||
@@ -288,8 +499,9 @@ _SPELL_SUBS = {
|
||||
}
|
||||
```
|
||||
|
||||
Logik: Kandidaten werden durch Woerterbuch-Lookup validiert. Strukturregel: Verdaechtiges
|
||||
Zeichen an Position 0 + Rest klein → erstes Substitut (z.B. `8en` → `Ben`).
|
||||
**Stufe 3 — Seitenzahl-Korrektur** (`page_ref`-Felder):
|
||||
|
||||
Korrigiert haeufige OCR-Fehler in Seitenverweisen (z.B. `p.5g` → `p.59`).
|
||||
|
||||
### Umgebungsvariablen
|
||||
|
||||
@@ -318,7 +530,11 @@ Change-Format:
|
||||
|
||||
## Schritt 7: Rekonstruktion (Detail)
|
||||
|
||||
Interaktiver Canvas-Editor: Das entzerrte Originalbild wird mit 30 % Opazitaet als Hintergrund
|
||||
Zwei Modi verfuegbar:
|
||||
|
||||
### Einfacher Modus
|
||||
|
||||
Das entzerrte Originalbild wird mit 30 % Opazitaet als Hintergrund
|
||||
angezeigt, alle Grid-Zellen (auch leere!) werden als editierbare Textfelder darueber gelegt.
|
||||
|
||||
**Features:**
|
||||
@@ -331,6 +547,14 @@ angezeigt, alle Grid-Zellen (auch leere!) werden als editierbare Textfelder daru
|
||||
- Zoom 50–200 %
|
||||
- Per-Zell-Reset-Button bei geaenderten Zellen
|
||||
|
||||
### Fabric.js Editor
|
||||
|
||||
Erweiterter Canvas-Editor (`FabricReconstructionCanvas.tsx`):
|
||||
|
||||
- Drag & Drop fuer Zellen
|
||||
- Freie Positionierung auf dem Canvas
|
||||
- Export als PDF (reportlab) oder DOCX (python-docx)
|
||||
|
||||
```
|
||||
POST /sessions/{id}/reconstruction
|
||||
Body: {"cells": [{"cell_id": "r5_c2", "text": "corrected text"}]}
|
||||
@@ -338,6 +562,19 @@ Body: {"cells": [{"cell_id": "r5_c2", "text": "corrected text"}]}
|
||||
|
||||
---
|
||||
|
||||
## Wichtige Konstanten
|
||||
|
||||
| Konstante | Wert | Datei | Beschreibung |
|
||||
|-----------|------|-------|--------------|
|
||||
| `_NARROW_COL_THRESHOLD_PCT` | 15.0% | cv_vocab_pipeline.py | Schwelle breit/schmal fuer Hybrid-OCR |
|
||||
| `_NARROW_THRESHOLD_PCT` | 10.0% | cv_vocab_pipeline.py | Schwelle fuer Spalten-Erweiterung |
|
||||
| `_MIN_WORD_CONF` | 30 | cv_vocab_pipeline.py | Mindest-Confidence fuer OCR-Woerter |
|
||||
| `_PAD` | 3px | cv_vocab_pipeline.py | Internes Padding bei Cell-Crop |
|
||||
| `PDF_ZOOM` | 3.0 | cv_vocab_pipeline.py | PDF-Rendering (= 432 DPI) |
|
||||
| `_MIN_WORD_MARGIN` | 4px | cv_vocab_pipeline.py | Sicherheitsabstand bei Spalten-Erweiterung |
|
||||
|
||||
---
|
||||
|
||||
## Datenbank-Schema
|
||||
|
||||
```sql
|
||||
@@ -348,6 +585,10 @@ CREATE TABLE ocr_pipeline_sessions (
|
||||
status VARCHAR(50) DEFAULT 'active',
|
||||
current_step INT DEFAULT 1,
|
||||
|
||||
-- Dokumenttyp-Erkennung
|
||||
doc_type VARCHAR(50), -- 'vocab_table', 'generic_table', 'full_text'
|
||||
doc_type_result JSONB, -- Vollstaendiges DetectionResult
|
||||
|
||||
-- Bilder (BYTEA)
|
||||
original_png BYTEA,
|
||||
deskewed_png BYTEA,
|
||||
@@ -374,7 +615,7 @@ CREATE TABLE ocr_pipeline_sessions (
|
||||
```json
|
||||
{
|
||||
"vocab_entries": [...],
|
||||
"cells": [{"cell_id": "r0_c0", "text": "hello", "bbox_pct": {...}, ...}],
|
||||
"cells": [{"cell_id": "r0_c0", "text": "hello", "bbox_pct": {...}, "ocr_engine": "word_lookup", ...}],
|
||||
"columns_used": [...],
|
||||
"llm_review": {
|
||||
"changes": [{"row_index": 5, "field": "english", "old": "...", "new": "..."}],
|
||||
@@ -399,10 +640,13 @@ CREATE TABLE ocr_pipeline_sessions (
|
||||
| `onnxruntime` | latest | MIT | ONNX-Inferenz fuer RapidOCR |
|
||||
| `pyspellchecker` | ≥0.8.1 | MIT | Regel-basierte OCR-Korrektur (Schritt 6) |
|
||||
| `eng-to-ipa` | latest | MIT | IPA-Lautschrift-Lookup (Schritt 5) |
|
||||
| `reportlab` | latest | BSD | PDF-Export (Schritt 7) |
|
||||
| `python-docx` | ≥1.1.0 | MIT | DOCX-Export (Schritt 7) |
|
||||
| `fabric` (JS) | ^6 | MIT | Canvas-Editor (Frontend) |
|
||||
|
||||
!!! info "pyspellchecker (neu seit 2026-03)"
|
||||
`pyspellchecker` (MIT-Lizenz) ersetzt die LLM-basierte Korrektur als Standard-Engine.
|
||||
EN+DE-Woerterbuch, ~134k Woerter. Kein Ollama notig.
|
||||
EN+DE-Woerterbuch, ~134k Woerter. Kein Ollama noetig.
|
||||
Umschaltbar via `REVIEW_ENGINE=llm` fuer den LLM-Pfad.
|
||||
|
||||
---
|
||||
@@ -413,8 +657,10 @@ CREATE TABLE ocr_pipeline_sessions (
|
||||
|---------|---------|------------|
|
||||
| Schraeg gedruckte Seiten | Deskew erkennt Text-Rotation, nicht Seiten-Rotation | Manueller Winkel |
|
||||
| Sehr kleine Schrift (< 8pt) | Tesseract PSM 7 braucht min. Zeichengroesse | Vorher zoomen |
|
||||
| Handgeschriebene Eintraege | Tesseract/RapidOCR sind fuer Druckschrift optimiert | TrOCR-Engine (geplant) |
|
||||
| Handgeschriebene Eintraege | Tesseract/RapidOCR sind fuer Druckschrift optimiert | TrOCR-Engine |
|
||||
| Mehr als 4 Spalten | Projektionsprofil kann verschmelzen | Manuelle Spalten |
|
||||
| Farbige Marker (rot/blau) | HSV-Erkennung erzeugt False Positives | Manuell im Rekonstruktions-Editor |
|
||||
| 15%-Schwelle nicht breit validiert | Nur an einem Arbeitsblatt-Typ getestet | Diverse Schulbuchseiten testen |
|
||||
|
||||
---
|
||||
|
||||
@@ -425,17 +671,15 @@ CREATE TABLE ocr_pipeline_sessions (
|
||||
git push origin main
|
||||
|
||||
# 2. Mac Mini pull + build
|
||||
ssh macmini "cd /Users/benjaminadmin/Projekte/breakpilot-lehrer && git pull --no-rebase origin main"
|
||||
ssh macmini "git -C /Users/benjaminadmin/Projekte/breakpilot-lehrer pull --no-rebase origin main"
|
||||
|
||||
# klausur-service (Backend) — bei requirements.txt Aenderungen: klausur-base neu bauen
|
||||
ssh macmini "cd /Users/benjaminadmin/Projekte/breakpilot-lehrer && \
|
||||
/usr/local/bin/docker compose build klausur-service && \
|
||||
/usr/local/bin/docker compose up -d klausur-service"
|
||||
# klausur-service (Backend)
|
||||
ssh macmini "/usr/local/bin/docker compose -f /Users/benjaminadmin/Projekte/breakpilot-lehrer/docker-compose.yml build klausur-service"
|
||||
ssh macmini "/usr/local/bin/docker compose -f /Users/benjaminadmin/Projekte/breakpilot-lehrer/docker-compose.yml up -d klausur-service"
|
||||
|
||||
# admin-lehrer (Frontend)
|
||||
ssh macmini "cd /Users/benjaminadmin/Projekte/breakpilot-lehrer && \
|
||||
/usr/local/bin/docker compose build admin-lehrer && \
|
||||
/usr/local/bin/docker compose up -d admin-lehrer"
|
||||
ssh macmini "/usr/local/bin/docker compose -f /Users/benjaminadmin/Projekte/breakpilot-lehrer/docker-compose.yml build admin-lehrer"
|
||||
ssh macmini "/usr/local/bin/docker compose -f /Users/benjaminadmin/Projekte/breakpilot-lehrer/docker-compose.yml up -d admin-lehrer"
|
||||
|
||||
# 3. Testen unter:
|
||||
# https://macmini:3002/ai/ocr-pipeline
|
||||
@@ -445,9 +689,8 @@ ssh macmini "cd /Users/benjaminadmin/Projekte/breakpilot-lehrer && \
|
||||
Wenn `requirements.txt` geaendert wird (z.B. neues Paket hinzugefuegt), muss zuerst
|
||||
das Base-Image neu gebaut werden:
|
||||
```bash
|
||||
ssh macmini "cd ~/Projekte/breakpilot-lehrer && \
|
||||
/usr/local/bin/docker build -f klausur-service/Dockerfile.base \
|
||||
-t klausur-base:latest klausur-service/"
|
||||
ssh macmini "/usr/local/bin/docker build -f /Users/benjaminadmin/Projekte/breakpilot-lehrer/klausur-service/Dockerfile.base \
|
||||
-t klausur-base:latest /Users/benjaminadmin/Projekte/breakpilot-lehrer/klausur-service/"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -456,6 +699,9 @@ ssh macmini "cd /Users/benjaminadmin/Projekte/breakpilot-lehrer && \
|
||||
|
||||
| Datum | Version | Aenderung |
|
||||
|-------|---------|----------|
|
||||
| 2026-03-05 | 3.0.0 | Doku-Update: Dokumenttyp-Erkennung, Hybrid-Grid, Sub-Column-Detection, Pipeline-Pfade |
|
||||
| 2026-03-04 | 2.2.0 | Dewarp: Vertikalkanten-Drift statt Textzeilen-Neigung, Schwellenwerte gesenkt |
|
||||
| 2026-03-04 | 2.1.0 | Sub-Column-Detection, expand_narrow_columns, Fabric.js Editor, PDF/DOCX-Export |
|
||||
| 2026-03-03 | 2.0.0 | Schritte 6–7 implementiert; Spell-Checker, Rekonstruktions-Canvas |
|
||||
| 2026-03-03 | 1.5.0 | Spaltenerkennung: volle Bildbreite fuer initialen Scan, Phantom-Filter |
|
||||
| 2026-03-03 | 1.4.0 | Zeilenerkennung: Artefakt-Zeilen entfernen + Luecken-Heilung |
|
||||
|
||||
Reference in New Issue
Block a user