From 75bca1f02dd7f936064084da23dd3f8d96a68ddb Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Tue, 3 Mar 2026 09:21:56 +0100 Subject: [PATCH] fix(ocr-cells): align cell bboxes exactly to column/row coordinates Decouple display bbox from OCR crop region. Display bbox now uses exact col.x/row.y/col.width/row.height (no padding), so adjacent cells touch without gaps. OCR crop keeps 4px internal padding for edge character detection. Co-Authored-By: Claude Sonnet 4.6 --- klausur-service/backend/cv_vocab_pipeline.py | 31 ++++++++++---------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/klausur-service/backend/cv_vocab_pipeline.py b/klausur-service/backend/cv_vocab_pipeline.py index bd2ed73..f0092ec 100644 --- a/klausur-service/backend/cv_vocab_pipeline.py +++ b/klausur-service/backend/cv_vocab_pipeline.py @@ -3807,19 +3807,20 @@ def _ocr_single_cell( preassigned_words: Optional[List[Dict]] = None, ) -> Dict[str, Any]: """Populate a single cell (column x row intersection) via word lookup.""" - pad = 8 # pixels + # Display bbox: exact column × row intersection (no padding) + disp_x = col.x + disp_y = row.y + disp_w = col.width + disp_h = row.height + + # OCR crop: slightly wider to catch edge characters (internal only) + pad = 4 cell_x = max(0, col.x - pad) cell_y = max(0, row.y - pad) - cell_w = col.width + 2 * pad - cell_h = row.height + 2 * pad + cell_w = min(col.width + 2 * pad, img_w - cell_x) + cell_h = min(row.height + 2 * pad, img_h - cell_y) - # Clamp to image bounds - if cell_x + cell_w > img_w: - cell_w = img_w - cell_x - if cell_y + cell_h > img_h: - cell_h = img_h - cell_y - - if cell_w <= 0 or cell_h <= 0: + if disp_w <= 0 or disp_h <= 0: return { 'cell_id': f"R{row_idx:02d}_C{col_idx}", 'row_index': row_idx, @@ -3929,12 +3930,12 @@ def _ocr_single_cell( 'col_type': col.type, 'text': text, 'confidence': avg_conf, - 'bbox_px': {'x': cell_x, 'y': cell_y, 'w': cell_w, 'h': cell_h}, + 'bbox_px': {'x': disp_x, 'y': disp_y, 'w': disp_w, 'h': disp_h}, 'bbox_pct': { - 'x': round(cell_x / img_w * 100, 2), - 'y': round(cell_y / img_h * 100, 2), - 'w': round(cell_w / img_w * 100, 2), - 'h': round(cell_h / img_h * 100, 2), + 'x': round(disp_x / img_w * 100, 2), + 'y': round(disp_y / img_h * 100, 2), + 'w': round(disp_w / img_w * 100, 2), + 'h': round(disp_h / img_h * 100, 2), }, 'ocr_engine': used_engine, }