feat: add border ghost filter + graphic detection tests + structure overlay

- Add _filter_border_ghost_words() to remove OCR artefacts from box borders
  (vertical + horizontal edge detection, column cleanup, re-indexing)
- Add 20 tests for border ghost filter (basic filtering + column cleanup)
- Add 24 tests for cv_graphic_detect (color detection, word overlap, boxes)
- Clean up cv_graphic_detect.py logging (per-candidate → DEBUG)
- Add structure overlay layer to StepReconstruction (boxes + graphics toggle)
- Show border_ghosts_removed badge in StepStructureDetection
- Update MkDocs with structure detection documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-16 18:28:53 +01:00
parent 6668661895
commit 729ebff63c
8 changed files with 1006 additions and 29 deletions

View File

@@ -149,6 +149,8 @@ klausur-service/backend/
├── ocr_pipeline_api.py # FastAPI Router (Schritte 2-10)
├── orientation_crop_api.py # FastAPI Router (Schritte 1 + 4)
├── cv_box_detect.py # Box-Erkennung + Zonen-Aufteilung
├── cv_graphic_detect.py # Grafik-/Bilderkennung (Region-basiert)
├── cv_color_detect.py # Farbtext-Erkennung (HSV-Analyse)
├── cv_words_first.py # Words-First Grid Builder (bottom-up)
├── page_crop.py # Content-basierter Crop-Algorithmus
├── ocr_pipeline_session_store.py # PostgreSQL Persistence
@@ -177,7 +179,8 @@ admin-lehrer/
├── StepColumnDetection.tsx # Schritt 5: Spaltenerkennung
├── StepRowDetection.tsx # Schritt 6: Zeilenerkennung
├── StepWordRecognition.tsx # Schritt 7: Worterkennung
├── StepLlmReview.tsx # Schritt 8: Korrektur (SSE-Stream)
├── StepStructureDetection.tsx # Schritt 8: Strukturerkennung
├── StepLlmReview.tsx # Schritt 9: Korrektur (SSE-Stream)
├── StepReconstruction.tsx # Schritt 9: Rekonstruktion (Canvas + Overlay)
├── usePixelWordPositions.ts # Shared Hook: Pixel-basierte Wortpositionierung
├── FabricReconstructionCanvas.tsx # Fabric.js Editor
@@ -281,14 +284,21 @@ Alle Endpoints unter `/api/v1/ocr-pipeline/`.
| `skip_heal_gaps` | `false` | Zeilen-Luecken nicht heilen (Overlay-Modus) |
| `grid_method` | `v2` | Grid-Strategie: `v2` (top-down) oder `words_first` (bottom-up) |
### Schritt 8: Korrektur
### Schritt 8: Strukturerkennung
| Methode | Pfad | Beschreibung |
|---------|------|--------------|
| `POST` | `/sessions/{id}/detect-structure` | Boxen, Zonen, Farben und Grafiken erkennen |
| `GET` | `/sessions/{id}/image/structure-overlay` | Overlay mit allen Strukturelementen |
### Schritt 9: 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 9: Rekonstruktion
### Schritt 10: Rekonstruktion
| Methode | Pfad | Beschreibung |
|---------|------|--------------|
@@ -853,6 +863,93 @@ Change-Format:
---
## Schritt 8: Strukturerkennung (Detail)
Erkennt Boxen, Zonen, Farbregionen und grafische Elemente auf der Seite.
Laeuft **nach** der Worterkennung (Schritt 7), damit OCR-Wortpositionen
fuer die Unterscheidung von Text vs. Grafik zur Verfuegung stehen.
### Teilschritte
1. **Box-Erkennung** (`cv_box_detect.py`): Linien-Rahmen und farbige Hintergruende
2. **Zonen-Aufteilung** (`split_page_into_zones`): Seite in Box- und Content-Zonen aufteilen
3. **Farb-Analyse** (`cv_color_detect.py`): HSV-basierte Erkennung farbiger Textbereiche
4. **Grafik-Erkennung** (`cv_graphic_detect.py`): Nicht-Text-Grafiken identifizieren
### Grafik-Erkennung: Region-basierter Ansatz
Zwei Paesse trennen farbige Grafiken von farbigem Text und erkennen
schwarze Illustrationen:
**Pass 1 — Farbige Bildregionen:**
1. HSV-Saturation-Kanal extrahieren (Schwelle > 40)
- Schwarzer Text hat Saettigung ≈ 0 → unsichtbar auf diesem Kanal
2. Starke Dilation (25×25 Ellipse) verschmilzt nahe Farbpixel zu Regionen
3. Fuer jede Region: Wort-Ueberlappung pruefen
- \> 50 % Ueberlappung mit OCR-Woertern → farbiger Text → ueberspringen
- ≤ 50 % → farbige Grafik/Bild → behalten
4. Minimum 200 Farbpixel erforderlich (kein Rauschen)
5. Regionen > 50 % der Bildbreite oder -hoehe → Seitenumfassend → ueberspringen
**Pass 2 — Schwarze Illustrationen:**
1. Otsu-Binarisierung fuer Tinten-Maske
2. Ausschlusszonen: OCR-Woerter (5 px Padding) + erkannte Boxen (8 px Inset)
3. Farbige Pixel aus Pass 1 ebenfalls ausschliessen
4. Nur Konturen mit Flaeche > 5000 px und min(Breite, Hoehe) > 40 px
**Deduplizierung:** Ueberlappende Elemente (> 50 % IoU der kleineren
Bounding-Box) werden zusammengefasst. Ergebnis nach Flaeche absteigend
sortiert.
### Response-Format
```json
{
"boxes": [
{"x": 50, "y": 300, "w": 1100, "h": 200, "confidence": 0.85,
"border_thickness": 3, "bg_color_name": "blue", "bg_color_hex": "#2563eb"}
],
"zones": [
{"index": 0, "zone_type": "content", "x": 50, "y": 50, "w": 1100, "h": 250},
{"index": 1, "zone_type": "box", "x": 50, "y": 300, "w": 1100, "h": 200}
],
"graphics": [
{"x": 100, "y": 500, "w": 150, "h": 120, "area": 8500,
"shape": "image", "color_name": "red", "color_hex": "#dc2626",
"confidence": 0.72}
],
"color_pixel_counts": {"red": 1234, "blue": 5678},
"has_words": true,
"word_count": 96,
"duration_seconds": 0.45
}
```
### Grafik-Shape-Typen
| Shape | Quelle | Beschreibung |
|-------|--------|--------------|
| `image` | Pass 1 | Farbige Grafik/Bild (Ballons, Pfeile, Icons) |
| `illustration` | Pass 2 | Grosse schwarze Zeichnung/Illustration |
### Erkannte Farben
`red`, `orange`, `yellow`, `green`, `blue`, `purple`, `black`
— basierend auf dem Median-Hue der saturierten Pixel in der Region.
### Frontend-Anzeige
`StepStructureDetection.tsx` zeigt:
- Boxen-Liste mit Position, Hintergrundfarbe und Confidence
- Zonen-Uebersicht (Content vs. Box)
- Farb-Zusammenfassung (Pixel-Counts)
- Grafik-Liste mit Shape, Abmessungen, Farbe und Confidence
---
## Schritt 9: Rekonstruktion (Detail)
Drei Modi verfuegbar:
@@ -1263,6 +1360,7 @@ cd klausur-service/backend && pytest tests/test_paddle_kombi.py -v # 36 Tests
| Datum | Version | Aenderung |
|-------|---------|----------|
| 2026-03-16 | 4.6.0 | Strukturerkennung (Schritt 8): Region-basierte Grafikerkennung (`cv_graphic_detect.py`) mit Zwei-Pass-Verfahren (Farbregionen + schwarze Illustrationen), Wort-Ueberlappungs-Filter, Box/Zonen/Farb-Analyse. Schritt laeuft nach Worterkennung. |
| 2026-03-12 | 4.5.0 | Kombi-Modus (PaddleOCR + Tesseract): Beide Engines laufen parallel, Koordinaten werden IoU-basiert gematcht und confidence-gewichtet gemittelt. Ungematchte Tesseract-Woerter (Bullets, Symbole) werden hinzugefuegt. 3er-Toggle in OCR Overlay. |
| 2026-03-12 | 4.4.0 | PaddleOCR Remote-Engine (`engine=paddle`): PP-OCRv5 Latin auf Hetzner x86_64. Neuer Microservice (`paddleocr-service/`), HTTP-Client (`paddleocr_remote.py`), Frontend-Dropdown-Option. Nutzt words_first Grid-Methode. |
| 2026-03-12 | 4.3.0 | Words-First Grid Builder (`cv_words_first.py`): Bottom-up-Algorithmus clustert Tesseract word_boxes direkt zu Spalten/Zeilen/Zellen. Neuer `grid_method` Parameter im `/words` Endpoint. Frontend-Toggle in StepWordRecognition. |