@@ -312,17 +312,26 @@ def supplement_element_with_table_extraction(
312312 table_elements = elements .slice (table_ele_indices )
313313 padding = env_config .TABLE_IMAGE_CROP_PAD
314314 for i , element_coords in enumerate (table_elements .element_coords ):
315+ table_bbox = (
316+ float (element_coords [0 ]),
317+ float (element_coords [1 ]),
318+ float (element_coords [2 ]),
319+ float (element_coords [3 ]),
320+ )
315321 cropped_image = image .crop (
316322 (
317- element_coords [0 ] - padding ,
318- element_coords [1 ] - padding ,
319- element_coords [2 ] + padding ,
320- element_coords [3 ] + padding ,
323+ table_bbox [0 ] - padding ,
324+ table_bbox [1 ] - padding ,
325+ table_bbox [2 ] + padding ,
326+ table_bbox [3 ] + padding ,
321327 ),
322328 )
323329 table_tokens = get_table_tokens (
324330 table_element_image = cropped_image ,
325331 ocr_agent = ocr_agent ,
332+ extracted_regions = extracted_regions ,
333+ table_bbox = table_bbox ,
334+ padding = padding ,
326335 )
327336 tatr_cells = tables_agent .predict (
328337 cropped_image , ocr_tokens = table_tokens , result_format = "cells"
@@ -344,13 +353,15 @@ def supplement_element_with_table_extraction(
344353def get_table_tokens (
345354 table_element_image : PILImage .Image ,
346355 ocr_agent : OCRAgent ,
356+ extracted_regions : Optional [TextRegions ] = None ,
357+ table_bbox : Optional [tuple [float , float , float , float ]] = None ,
358+ padding : float = 0 ,
347359) -> List [dict [str , Any ]]:
348- """Get OCR tokens from either paddleocr or tesseract"""
349-
360+ """Get table tokens, preferring embedded PDF text when coverage is sufficient."""
350361 ocr_layout = ocr_agent .get_layout_from_image (image = table_element_image )
351- table_tokens = []
362+ ocr_tokens = []
352363 for i , text in enumerate (ocr_layout .texts ):
353- table_tokens .append (
364+ ocr_tokens .append (
354365 {
355366 "bbox" : [
356367 ocr_layout .x1 [i ],
@@ -367,6 +378,99 @@ def get_table_tokens(
367378 }
368379 )
369380
381+ if extracted_regions is None or table_bbox is None :
382+ return ocr_tokens
383+
384+ extracted_tokens = _get_table_tokens_from_extracted_regions (
385+ extracted_regions = extracted_regions ,
386+ table_bbox = table_bbox ,
387+ table_image_size = table_element_image .size ,
388+ padding = padding ,
389+ )
390+ if _prefer_extracted_table_tokens (extracted_tokens , ocr_tokens ):
391+ return extracted_tokens
392+
393+ return ocr_tokens
394+
395+
396+ def _prefer_extracted_table_tokens (
397+ extracted_tokens : List [dict [str , Any ]],
398+ ocr_tokens : List [dict [str , Any ]],
399+ token_ratio_threshold : float = 0.8 ,
400+ text_ratio_threshold : float = 0.8 ,
401+ ) -> bool :
402+ """Choose extracted tokens only when they have comparable coverage to OCR."""
403+ if not extracted_tokens :
404+ return False
405+ if not ocr_tokens :
406+ return True
407+
408+ extracted_count = len (extracted_tokens )
409+ ocr_count = len (ocr_tokens )
410+ extracted_chars = sum (len (str (token .get ("text" , "" ))) for token in extracted_tokens )
411+ ocr_chars = sum (len (str (token .get ("text" , "" ))) for token in ocr_tokens )
412+
413+ return (
414+ extracted_count >= token_ratio_threshold * ocr_count
415+ and extracted_chars >= text_ratio_threshold * ocr_chars
416+ )
417+
418+
419+ def _get_table_tokens_from_extracted_regions (
420+ extracted_regions : TextRegions ,
421+ table_bbox : tuple [float , float , float , float ],
422+ table_image_size : tuple [int , int ],
423+ padding : float ,
424+ ) -> List [dict [str , Any ]]:
425+ if len (extracted_regions ) == 0 :
426+ return []
427+
428+ mask = (
429+ bboxes1_is_almost_subregion_of_bboxes2 (
430+ extracted_regions .element_coords ,
431+ np .array ([table_bbox ]),
432+ env_config .OCR_LAYOUT_SUBREGION_THRESHOLD ,
433+ )
434+ .sum (axis = 1 )
435+ .astype (bool )
436+ )
437+ if not np .any (mask ):
438+ return []
439+
440+ selected_regions = extracted_regions .slice (mask )
441+ left = table_bbox [0 ] - padding
442+ top = table_bbox [1 ] - padding
443+ width , height = table_image_size
444+
445+ valid = [
446+ (idx , text ) for idx , text in enumerate (selected_regions .texts ) if text and str (text ).strip ()
447+ ]
448+ if not valid :
449+ return []
450+
451+ # Keep deterministic reading order (top-to-bottom then left-to-right).
452+ sorted_indices = sorted (
453+ valid ,
454+ key = lambda item : (selected_regions .y1 [item [0 ]], selected_regions .x1 [item [0 ]]),
455+ )
456+ table_tokens = []
457+ for span_num , (idx , text ) in enumerate (sorted_indices ):
458+ x1 = max (0 , min (width , int (round (selected_regions .x1 [idx ] - left ))))
459+ y1 = max (0 , min (height , int (round (selected_regions .y1 [idx ] - top ))))
460+ x2 = max (0 , min (width , int (round (selected_regions .x2 [idx ] - left ))))
461+ y2 = max (0 , min (height , int (round (selected_regions .y2 [idx ] - top ))))
462+ if x2 <= x1 or y2 <= y1 :
463+ continue
464+ table_tokens .append (
465+ {
466+ "bbox" : [x1 , y1 , x2 , y2 ],
467+ "text" : str (text ),
468+ "span_num" : span_num ,
469+ "line_num" : 0 ,
470+ "block_num" : 0 ,
471+ }
472+ )
473+
370474 return table_tokens
371475
372476
0 commit comments