fix: Overlay-Zellen ohne _heal_row_gaps positionieren (skip_heal_gaps)
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 36s
CI / test-go-edu-search (push) Successful in 35s
CI / test-python-klausur (push) Failing after 2m12s
CI / test-python-agent-core (push) Successful in 18s
CI / test-nodejs-website (push) Successful in 21s
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 36s
CI / test-go-edu-search (push) Successful in 35s
CI / test-python-klausur (push) Failing after 2m12s
CI / test-python-agent-core (push) Successful in 18s
CI / test-nodejs-website (push) Successful in 21s
_heal_row_gaps verschiebt Zell-Positionen nach Entfernung von Artefakt-Zeilen, was im Overlay zu sichtbarem Versatz fuehrt (z.B. 23px bei "badge"). Neuer skip_heal_gaps Parameter in build_cell_grid_v2 und words-Endpoint behaelt die exakten Zeilen-Positionen bei. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -218,7 +218,7 @@ export default function OcrOverlayPage() {
|
|||||||
case 4:
|
case 4:
|
||||||
return <StepRowDetection sessionId={sessionId} onNext={handleNext} />
|
return <StepRowDetection sessionId={sessionId} onNext={handleNext} />
|
||||||
case 5:
|
case 5:
|
||||||
return <StepWordRecognition sessionId={sessionId} onNext={handleNext} goToStep={goToStep} />
|
return <StepWordRecognition sessionId={sessionId} onNext={handleNext} goToStep={goToStep} skipHealGaps />
|
||||||
case 6:
|
case 6:
|
||||||
return <OverlayReconstruction sessionId={sessionId} onNext={handleNext} />
|
return <OverlayReconstruction sessionId={sessionId} onNext={handleNext} />
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -44,9 +44,11 @@ interface StepWordRecognitionProps {
|
|||||||
sessionId: string | null
|
sessionId: string | null
|
||||||
onNext: () => void
|
onNext: () => void
|
||||||
goToStep: (step: number) => void
|
goToStep: (step: number) => void
|
||||||
|
/** Skip _heal_row_gaps in cell grid (better overlay positioning) */
|
||||||
|
skipHealGaps?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function StepWordRecognition({ sessionId, onNext, goToStep }: StepWordRecognitionProps) {
|
export function StepWordRecognition({ sessionId, onNext, goToStep, skipHealGaps = false }: StepWordRecognitionProps) {
|
||||||
const [gridResult, setGridResult] = useState<GridResult | null>(null)
|
const [gridResult, setGridResult] = useState<GridResult | null>(null)
|
||||||
const [detecting, setDetecting] = useState(false)
|
const [detecting, setDetecting] = useState(false)
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
@@ -110,7 +112,7 @@ export function StepWordRecognition({ sessionId, onNext, goToStep }: StepWordRec
|
|||||||
let res: Response | null = null
|
let res: Response | null = null
|
||||||
for (let attempt = 0; attempt < 2; attempt++) {
|
for (let attempt = 0; attempt < 2; attempt++) {
|
||||||
res = await fetch(
|
res = await fetch(
|
||||||
`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/words?stream=true&engine=${eng}&pronunciation=${pronunciation}`,
|
`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/words?stream=true&engine=${eng}&pronunciation=${pronunciation}${skipHealGaps ? '&skip_heal_gaps=true' : ''}`,
|
||||||
{ method: 'POST' },
|
{ method: 'POST' },
|
||||||
)
|
)
|
||||||
if (res.ok) break
|
if (res.ok) break
|
||||||
|
|||||||
@@ -264,6 +264,7 @@ def build_cell_grid_v2(
|
|||||||
lang: str = "eng+deu",
|
lang: str = "eng+deu",
|
||||||
ocr_engine: str = "auto",
|
ocr_engine: str = "auto",
|
||||||
img_bgr: Optional[np.ndarray] = None,
|
img_bgr: Optional[np.ndarray] = None,
|
||||||
|
skip_heal_gaps: bool = False,
|
||||||
) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
|
) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
|
||||||
"""Hybrid Grid: full-page OCR for broad columns, cell-crop for narrow ones.
|
"""Hybrid Grid: full-page OCR for broad columns, cell-crop for narrow ones.
|
||||||
|
|
||||||
@@ -330,7 +331,12 @@ def build_cell_grid_v2(
|
|||||||
else:
|
else:
|
||||||
bottom_bound = content_rows[-1].y + content_rows[-1].height
|
bottom_bound = content_rows[-1].y + content_rows[-1].height
|
||||||
|
|
||||||
_heal_row_gaps(content_rows, top_bound=top_bound, bottom_bound=bottom_bound)
|
# skip_heal_gaps: When True, keep cell positions at their exact row geometry
|
||||||
|
# positions without expanding to fill gaps from removed rows. Useful for
|
||||||
|
# overlay rendering where pixel-precise positioning matters more than
|
||||||
|
# full-coverage OCR crops.
|
||||||
|
if not skip_heal_gaps:
|
||||||
|
_heal_row_gaps(content_rows, top_bound=top_bound, bottom_bound=bottom_bound)
|
||||||
|
|
||||||
relevant_cols.sort(key=lambda c: c.x)
|
relevant_cols.sort(key=lambda c: c.x)
|
||||||
|
|
||||||
|
|||||||
@@ -1857,6 +1857,7 @@ async def detect_words(
|
|||||||
engine: str = "auto",
|
engine: str = "auto",
|
||||||
pronunciation: str = "british",
|
pronunciation: str = "british",
|
||||||
stream: bool = False,
|
stream: bool = False,
|
||||||
|
skip_heal_gaps: bool = False,
|
||||||
):
|
):
|
||||||
"""Build word grid from columns × rows, OCR each cell.
|
"""Build word grid from columns × rows, OCR each cell.
|
||||||
|
|
||||||
@@ -1864,6 +1865,8 @@ async def detect_words(
|
|||||||
engine: 'auto' (default), 'tesseract', or 'rapid'
|
engine: 'auto' (default), 'tesseract', or 'rapid'
|
||||||
pronunciation: 'british' (default) or 'american' — for IPA dictionary lookup
|
pronunciation: 'british' (default) or 'american' — for IPA dictionary lookup
|
||||||
stream: false (default) for JSON response, true for SSE streaming
|
stream: false (default) for JSON response, true for SSE streaming
|
||||||
|
skip_heal_gaps: false (default). When true, cells keep exact row geometry
|
||||||
|
positions without gap-healing expansion. Better for overlay rendering.
|
||||||
"""
|
"""
|
||||||
if session_id not in _cache:
|
if session_id not in _cache:
|
||||||
logger.info("detect_words: session %s not in cache, loading from DB", session_id)
|
logger.info("detect_words: session %s not in cache, loading from DB", session_id)
|
||||||
@@ -2007,6 +2010,7 @@ async def detect_words(
|
|||||||
cells, columns_meta = build_cell_grid_v2(
|
cells, columns_meta = build_cell_grid_v2(
|
||||||
ocr_img, col_regions, row_geoms, img_w, img_h,
|
ocr_img, col_regions, row_geoms, img_w, img_h,
|
||||||
ocr_engine=engine, img_bgr=dewarped_bgr,
|
ocr_engine=engine, img_bgr=dewarped_bgr,
|
||||||
|
skip_heal_gaps=skip_heal_gaps,
|
||||||
)
|
)
|
||||||
duration = time.time() - t0
|
duration = time.time() - t0
|
||||||
|
|
||||||
@@ -2136,6 +2140,7 @@ async def _word_batch_stream_generator(
|
|||||||
lambda: build_cell_grid_v2(
|
lambda: build_cell_grid_v2(
|
||||||
ocr_img, col_regions, row_geoms, img_w, img_h,
|
ocr_img, col_regions, row_geoms, img_w, img_h,
|
||||||
ocr_engine=engine, img_bgr=dewarped_bgr,
|
ocr_engine=engine, img_bgr=dewarped_bgr,
|
||||||
|
skip_heal_gaps=skip_heal_gaps,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user