diff --git a/admin-lehrer/components/ocr-pipeline/StepCrop.tsx b/admin-lehrer/components/ocr-pipeline/StepCrop.tsx
index 7363fb4..e578e3c 100644
--- a/admin-lehrer/components/ocr-pipeline/StepCrop.tsx
+++ b/admin-lehrer/components/ocr-pipeline/StepCrop.tsx
@@ -108,10 +108,21 @@ export function StepCrop({ sessionId, onNext }: StepCropProps) {
{cropResult && (
- {cropResult.crop_applied ? (
+ {(cropResult as Record
).multi_page ? (
+ <>
+
+ Mehrseitig: {(cropResult as Record).page_count as number} Seiten erkannt
+
+ {((cropResult as Record).sub_sessions as Array<{id: string; name: string; page_index: number}> | undefined)?.map((sub) => (
+
+ Seite {sub.page_index + 1}
+
+ ))}
+ >
+ ) : cropResult.crop_applied ? (
<>
- ✂️ Zugeschnitten
+ Zugeschnitten
{cropResult.detected_format && (
<>
@@ -126,10 +137,14 @@ export function StepCrop({ sessionId, onNext }: StepCropProps) {
>
)}
-
-
- {cropResult.original_size.width}x{cropResult.original_size.height} → {cropResult.cropped_size.width}x{cropResult.cropped_size.height}
-
+ {cropResult.original_size && cropResult.cropped_size && (
+ <>
+
+
+ {cropResult.original_size.width}x{cropResult.original_size.height} → {cropResult.cropped_size.width}x{cropResult.cropped_size.height}
+
+ >
+ )}
{cropResult.border_fractions && (
<>
@@ -141,7 +156,7 @@ export function StepCrop({ sessionId, onNext }: StepCropProps) {
>
) : (
- ✓ Kein Zuschnitt noetig
+ Kein Zuschnitt noetig
)}
{cropResult.duration_seconds != null && (
diff --git a/klausur-service/backend/page_crop.py b/klausur-service/backend/page_crop.py
index ea51714..8ac8e3c 100644
--- a/klausur-service/backend/page_crop.py
+++ b/klausur-service/backend/page_crop.py
@@ -100,12 +100,33 @@ def detect_page_splits(
if not gaps:
return []
+ # Merge nearby gaps (< 5% of width apart) — the spine area may have
+ # thin ink strips between multiple gap segments
+ merge_dist = max(20, int(w * 0.05))
+ merged: list = [gaps[0]]
+ for g in gaps[1:]:
+ prev = merged[-1]
+ prev_end = prev["x"] + prev["width"]
+ if g["x"] - prev_end < merge_dist:
+ # Merge: extend previous gap to cover both
+ new_end = g["x"] + g["width"]
+ prev["width"] = new_end - prev["x"]
+ prev["center"] = prev["x"] + prev["width"] // 2
+ else:
+ merged.append(g)
+ gaps = merged
+
# Sort gaps by width (largest = most likely spine)
gaps.sort(key=lambda g: g["width"], reverse=True)
- # Use the widest gap(s) as split points
- # For now: support up to N-1 gaps → N pages
- split_points = sorted(g["center"] for g in gaps[:3]) # max 4 pages
+ # Use only gaps that are significant (>= 2% of image width)
+ significant_gaps = [g for g in gaps if g["width"] >= w * 0.02]
+ if not significant_gaps:
+ # Fall back to widest gap
+ significant_gaps = [gaps[0]]
+
+ # Use the significant gap(s) as split points
+ split_points = sorted(g["center"] for g in significant_gaps[:3])
# Build page rectangles
pages: list = []