feat: Orientierung + Zuschneiden als Schritte 1-2 in OCR-Pipeline
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 28s
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 18s

Zwei neue Wizard-Schritte vor Begradigung:
- Step 1: Orientierungserkennung (0/90/180/270° via Tesseract OSD)
- Step 2: Seitenrand-Erkennung und Zuschnitt (Scannerraender entfernen)

Backend:
- orientation_crop_api.py: POST /orientation, POST /crop, POST /crop/skip
- page_crop.py: detect_and_crop_page() mit Format-Erkennung (A4/A5/Letter)
- Session-Store: orientation_result, crop_result Felder
- Pipeline nutzt zugeschnittenes Bild fuer Deskew/Dewarp

Frontend:
- StepOrientation.tsx: Upload + Auto-Orientierung + Vorher/Nachher
- StepCrop.tsx: Auto-Crop + Format-Badge + Ueberspringen-Option
- Pipeline-Stepper: 10 Schritte (war 8)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-08 23:55:23 +01:00
parent 9a5a35bff1
commit 2763631711
12 changed files with 1247 additions and 259 deletions

View File

@@ -3,6 +3,8 @@
import { useCallback, useEffect, useState } from 'react'
import { PagePurpose } from '@/components/common/PagePurpose'
import { PipelineStepper } from '@/components/ocr-pipeline/PipelineStepper'
import { StepOrientation } from '@/components/ocr-pipeline/StepOrientation'
import { StepCrop } from '@/components/ocr-pipeline/StepCrop'
import { StepDeskew } from '@/components/ocr-pipeline/StepDeskew'
import { StepDewarp } from '@/components/ocr-pipeline/StepDewarp'
import { StepColumnDetection } from '@/components/ocr-pipeline/StepColumnDetection'
@@ -196,7 +198,7 @@ export default function OcrPipelinePage() {
setCurrentStep(nextStep)
}
const handleDeskewComplete = (sid: string) => {
const handleOrientationComplete = (sid: string) => {
setSessionId(sid)
// Reload session list to show the new session
loadSessions()
@@ -270,14 +272,16 @@ export default function OcrPipelinePage() {
}
const stepNames: Record<number, string> = {
1: 'Begradigung',
2: 'Entzerrung',
3: 'Spalten',
4: 'Zeilen',
5: 'Woerter',
6: 'Korrektur',
7: 'Rekonstruktion',
8: 'Validierung',
1: 'Orientierung',
2: 'Zuschneiden',
3: 'Begradigung',
4: 'Entzerrung',
5: 'Spalten',
6: 'Zeilen',
7: 'Woerter',
8: 'Korrektur',
9: 'Rekonstruktion',
10: 'Validierung',
}
const reprocessFromStep = useCallback(async (uiStep: number) => {
@@ -306,20 +310,24 @@ export default function OcrPipelinePage() {
const renderStep = () => {
switch (currentStep) {
case 0:
return <StepDeskew sessionId={sessionId} onNext={handleDeskewComplete} />
return <StepOrientation sessionId={sessionId} onNext={handleOrientationComplete} />
case 1:
return <StepDewarp sessionId={sessionId} onNext={handleDewarpNext} />
return <StepCrop sessionId={sessionId} onNext={handleNext} />
case 2:
return <StepColumnDetection sessionId={sessionId} onNext={handleNext} />
return <StepDeskew sessionId={sessionId} onNext={handleNext} />
case 3:
return <StepRowDetection sessionId={sessionId} onNext={handleNext} />
return <StepDewarp sessionId={sessionId} onNext={handleDewarpNext} />
case 4:
return <StepWordRecognition sessionId={sessionId} onNext={handleNext} goToStep={goToStep} />
return <StepColumnDetection sessionId={sessionId} onNext={handleNext} />
case 5:
return <StepLlmReview sessionId={sessionId} onNext={handleNext} />
return <StepRowDetection sessionId={sessionId} onNext={handleNext} />
case 6:
return <StepReconstruction sessionId={sessionId} onNext={handleNext} />
return <StepWordRecognition sessionId={sessionId} onNext={handleNext} goToStep={goToStep} />
case 7:
return <StepLlmReview sessionId={sessionId} onNext={handleNext} />
case 8:
return <StepReconstruction sessionId={sessionId} onNext={handleNext} />
case 9:
return <StepGroundTruth sessionId={sessionId} onNext={handleNext} />
default:
return null

View File

@@ -57,6 +57,26 @@ export interface DocumentTypeResult {
duration_seconds?: number
}
export interface OrientationResult {
orientation_degrees: number
corrected: boolean
duration_seconds: number
}
export interface CropResult {
crop_applied: boolean
crop_rect?: { x: number; y: number; width: number; height: number }
crop_rect_pct?: { x: number; y: number; width: number; height: number }
original_size: { width: number; height: number }
cropped_size: { width: number; height: number }
detected_format?: string
format_confidence?: number
aspect_ratio?: number
border_fractions?: { top: number; bottom: number; left: number; right: number }
skipped?: boolean
duration_seconds?: number
}
export interface SessionInfo {
session_id: string
filename: string
@@ -67,6 +87,8 @@ export interface SessionInfo {
current_step?: number
document_category?: DocumentCategory
doc_type?: string
orientation_result?: OrientationResult
crop_result?: CropResult
deskew_result?: DeskewResult
dewarp_result?: DewarpResult
column_result?: ColumnResult
@@ -85,7 +107,6 @@ export interface DeskewResult {
angle_applied: number
method_used: 'hough' | 'word_alignment' | 'manual' | 'iterative' | 'two_pass' | 'three_pass' | 'manual_combined'
confidence: number
orientation_degrees?: number
duration_seconds: number
deskewed_image_url: string
binarized_image_url: string
@@ -288,6 +309,8 @@ export const IMAGE_STYLES: { value: ImageStyle; label: string }[] = [
]
export const PIPELINE_STEPS: PipelineStep[] = [
{ id: 'orientation', name: 'Orientierung', icon: '🔄', status: 'pending' },
{ id: 'crop', name: 'Zuschneiden', icon: '✂️', status: 'pending' },
{ id: 'deskew', name: 'Begradigung', icon: '📐', status: 'pending' },
{ id: 'dewarp', name: 'Entzerrung', icon: '🔧', status: 'pending' },
{ id: 'columns', name: 'Spalten', icon: '📊', status: 'pending' },