Files
docling-eval/docling_eval/evaluators/ocr/geometry_utils.py
17e9fde84f feat: Update OCREvaluator with additional metrics (#78)
* Add README for Docling-DPBench

Signed-off-by: Christoph Auer <cau@zurich.ibm.com>

* feat: Update OCREvaluator with additional metrics

* fix: bug fix

* add edit-distance lib

* update pure ocr metrics

* Establish SegmentedPage support in DatasetRecord and DatasetRecordWithPrediction

Signed-off-by: Christoph Auer <cau@zurich.ibm.com>

* Add SegmentedPage usage to PixParse dataset provider

Signed-off-by: Christoph Auer <cau@zurich.ibm.com>

* add pure ocr metrics

* refactor: update dependencies

* fix dependencies and build errors

* feat: add optype and scipy-stubs packages

* fix: fix type error

* fix package name

* fix bugs and add funsd ocr test

* fix type error

* finalize changes

* fix build errors

* fix: ignore edit_distance missing import

* Add functionality to merge cells in Google OCR prediction (#103)

* feat: add global_merge function in google prediction provider for word cell merging

* address review comment

* remove unused imports

* address review comments and remove dictionary conversions

---------

Co-authored-by: samiullahchattha <Sami.Ullah1@ibm.com>

* refactor and address review comments

* fix regression bug

* refactor code and reduce metrics to three

* make ocr classes private

* fix type error

* refactor: update geometry utils to use BoundingBox and TextCell

Signed-off-by: samiullahchattha <Sami.Ullah1@ibm.com>

* refactor: rename metrics variables for consistency and clarity

Signed-off-by: samiullahchattha <Sami.Ullah1@ibm.com>

* Update lock for docling-core

Signed-off-by: Christoph Auer <cau@zurich.ibm.com>

---------

Signed-off-by: Christoph Auer <cau@zurich.ibm.com>
Signed-off-by: samiuc <sami.ullah.chat@gmail.com>
Signed-off-by: samiullahchattha <Sami.Ullah1@ibm.com>
Co-authored-by: Christoph Auer <cau@zurich.ibm.com>
Co-authored-by: samiullahchattha <Sami.Ullah1@ibm.com>
2025-06-02 14:48:32 +02:00

131 lines
3.8 KiB
Python

from typing import List, Tuple
from docling_core.types.doc import BoundingBox, CoordOrigin
from docling_eval.evaluators.ocr.evaluation_models import Word, _CalculationConstants
def create_polygon_from_bbox(bbox: BoundingBox) -> List[List[float]]:
return [
[bbox.l, bbox.t],
[bbox.r, bbox.t],
[bbox.r, bbox.b],
[bbox.l, bbox.b],
]
def calculate_box_intersection_info(
bbox1: BoundingBox, bbox2: BoundingBox
) -> Tuple[float, float, float, float, float, float, float]:
if bbox1.coord_origin != bbox2.coord_origin:
raise ValueError("BoundingBoxes must have the same CoordOrigin.")
x_axis_overlap_val: float = bbox1.x_overlap_with(bbox2)
if x_axis_overlap_val <= _CalculationConstants.EPS:
return 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
y_axis_overlap_val: float = bbox1.y_overlap_with(bbox2)
if y_axis_overlap_val <= _CalculationConstants.EPS:
return x_axis_overlap_val, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
intersection_area_val: float = bbox1.intersection_area_with(bbox2)
box1_area: float = bbox1.area()
box2_area: float = bbox2.area()
union_area_val: float = bbox1.union_area_with(bbox2)
iou_val: float = (
intersection_area_val / union_area_val
if union_area_val > _CalculationConstants.EPS
else 0.0
)
box1_portion_covered: float = (
intersection_area_val / box1_area
if box1_area > _CalculationConstants.EPS
else 0.0
)
box2_portion_covered: float = (
intersection_area_val / box2_area
if box2_area > _CalculationConstants.EPS
else 0.0
)
return (
x_axis_overlap_val,
y_axis_overlap_val,
intersection_area_val,
union_area_val,
iou_val,
box1_portion_covered,
box2_portion_covered,
)
def calculate_box_intersection_info_extended(
bbox1: BoundingBox, bbox2: BoundingBox
) -> Tuple[float, float, float, float, float, float, float]:
if bbox1.coord_origin != bbox2.coord_origin:
raise ValueError("BoundingBoxes must have the same CoordOrigin.")
x_axis_overlap_val: float = bbox1.x_overlap_with(bbox2)
y_axis_overlap_val: float = bbox1.y_overlap_with(bbox2)
intersection_area_val: float = 0.0
if (
x_axis_overlap_val > _CalculationConstants.EPS
and y_axis_overlap_val > _CalculationConstants.EPS
):
intersection_area_val = bbox1.intersection_area_with(bbox2)
union_area_val: float = bbox1.union_area_with(bbox2)
iou_val: float = (
intersection_area_val / union_area_val
if union_area_val > _CalculationConstants.EPS
else 0.0
)
x_axis_union_val: float = bbox1.x_union_with(bbox2)
y_axis_union_val: float = bbox1.y_union_with(bbox2)
x_axis_iou_val: float = (
x_axis_overlap_val / x_axis_union_val
if x_axis_union_val > _CalculationConstants.EPS
else 0.0
)
y_axis_iou_val: float = (
y_axis_overlap_val / y_axis_union_val
if y_axis_union_val > _CalculationConstants.EPS
else 0.0
)
return (
x_axis_overlap_val,
y_axis_overlap_val,
intersection_area_val,
union_area_val,
iou_val,
x_axis_iou_val,
y_axis_iou_val,
)
def box_to_key(bbox: BoundingBox) -> Tuple[float, float, float, float]:
if bbox.coord_origin == CoordOrigin.TOPLEFT:
return (bbox.t, bbox.l, bbox.r, bbox.b)
elif bbox.coord_origin == CoordOrigin.BOTTOMLEFT:
return (bbox.t, bbox.l, bbox.r, bbox.b)
else:
raise ValueError(f"Unsupported CoordOrigin: {bbox.coord_origin}")
def is_horizontal(word: Word) -> bool:
bbox = word.bbox
h: float = bbox.height
w: float = bbox.width
if w > _CalculationConstants.EPS and h > (2 * w) and len(word.text) > 1:
return False
return True