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:
@@ -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. |
|
||||
|
||||
Reference in New Issue
Block a user