Skip to content

Commit 931eb55

Browse files
authored
fix(ocr-utils): unit test and fix the rotate_bounding_box function (#1897)
Signed-off-by: Clément Doumouro <[email protected]>
1 parent a07ba86 commit 931eb55

File tree

5 files changed

+108
-34
lines changed

5 files changed

+108
-34
lines changed

docling/utils/ocr_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def tesseract_box_to_bounding_rectangle(
4141
im_size: Tuple[int, int],
4242
) -> BoundingRectangle:
4343
# box is in the top, left, height, width format, top left coordinates
44-
rect = rotate_bounding_box(bbox, angle=-orientation, im_size=im_size)
44+
rect = rotate_bounding_box(bbox, angle=orientation, im_size=im_size)
4545
rect = BoundingRectangle(
4646
r_x0=rect.r_x0 / scale,
4747
r_y0=rect.r_y0 / scale,

docling/utils/orientation.py

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,51 +14,44 @@ def rotate_bounding_box(
1414
# coordinate system. Then other corners are found rotating counterclockwise
1515
bbox = bbox.to_top_left_origin(im_size[1])
1616
left, top, width, height = bbox.l, bbox.t, bbox.width, bbox.height
17-
im_h, im_w = im_size
17+
im_w, im_h = im_size
1818
angle = angle % 360
1919
if angle == 0:
20-
r_x0 = left
21-
r_y0 = top + height
22-
r_x1 = r_x0 + width
23-
r_y1 = r_y0
24-
r_x2 = r_x0 + width
25-
r_y2 = r_y0 - height
26-
r_x3 = r_x0
27-
r_y3 = r_y0 - height
20+
return BoundingRectangle.from_bounding_box(bbox)
2821
elif angle == 90:
29-
r_x0 = im_w - (top + height)
30-
r_y0 = left
22+
r_x0 = top + height
23+
r_y0 = im_w - left
3124
r_x1 = r_x0
32-
r_y1 = r_y0 + width
33-
r_x2 = r_x0 + height
34-
r_y2 = r_y0 + width
35-
r_x3 = r_x0
36-
r_y3 = r_y0 + width
25+
r_y1 = r_y0 - width
26+
r_x2 = r_x1 - height
27+
r_y2 = r_y1
28+
r_x3 = r_x2
29+
r_y3 = r_y0
3730
elif angle == 180:
38-
r_x0 = im_h - left
39-
r_y0 = im_w - (top + height)
31+
r_x0 = im_w - left
32+
r_y0 = im_h - (top + height)
4033
r_x1 = r_x0 - width
4134
r_y1 = r_y0
42-
r_x2 = r_x0 - width
43-
r_y2 = r_y0 + height
35+
r_x2 = r_x1
36+
r_y2 = r_y1 + height
4437
r_x3 = r_x0
45-
r_y3 = r_y0 + height
38+
r_y3 = r_y2
4639
elif angle == 270:
47-
r_x0 = top + height
48-
r_y0 = im_h - left
40+
r_x0 = im_h - (top + height)
41+
r_y0 = left
4942
r_x1 = r_x0
50-
r_y1 = r_y0 - width
51-
r_x2 = r_x0 - height
52-
r_y2 = r_y0 - width
53-
r_x3 = r_x0 - height
43+
r_y1 = r_y0 + width
44+
r_x2 = r_x1 + height
45+
r_y2 = r_y1
46+
r_x3 = r_x2
5447
r_y3 = r_y0
5548
else:
5649
msg = (
5750
f"invalid orientation {angle}, expected values in:"
5851
f" {sorted(CLIPPED_ORIENTATIONS)}"
5952
)
6053
raise ValueError(msg)
61-
return BoundingRectangle(
54+
rectangle = BoundingRectangle(
6255
r_x0=r_x0,
6356
r_y0=r_y0,
6457
r_x1=r_x1,
@@ -69,3 +62,4 @@ def rotate_bounding_box(
6962
r_y3=r_y3,
7063
coord_origin=CoordOrigin.TOPLEFT,
7164
)
65+
return rectangle

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ authors = [
4444
requires-python = '>=3.9,<4.0'
4545
dependencies = [
4646
'pydantic (>=2.0.0,<3.0.0)',
47-
'docling-core[chunking] (>=2.39.0,<3.0.0)',
47+
'docling-core[chunking] (>=2.40.0,<3.0.0)',
4848
'docling-parse (>=4.0.0,<5.0.0)',
4949
'docling-ibm-models (>=3.6.0,<4)',
5050
'filetype (>=1.2.0,<2.0.0)',

tests/test_ocr_utils.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
from typing import Tuple
2+
3+
import pytest
4+
from docling_core.types.doc import BoundingBox, CoordOrigin
5+
from docling_core.types.doc.page import BoundingRectangle
6+
7+
from docling.utils.orientation import rotate_bounding_box
8+
9+
IM_SIZE = (4, 5)
10+
BBOX = BoundingBox(l=1, t=3, r=3, b=4, coord_origin=CoordOrigin.TOPLEFT)
11+
RECT = BoundingRectangle(
12+
r_x0=1,
13+
r_y0=4,
14+
r_x1=3,
15+
r_y1=4,
16+
r_x2=3,
17+
r_y2=3,
18+
r_x3=1,
19+
r_y3=3,
20+
coord_origin=CoordOrigin.TOPLEFT,
21+
)
22+
RECT_90 = BoundingRectangle(
23+
r_x0=4,
24+
r_y0=3,
25+
r_x1=4,
26+
r_y1=1,
27+
r_x2=3,
28+
r_y2=1,
29+
r_x3=3,
30+
r_y3=3,
31+
coord_origin=CoordOrigin.TOPLEFT,
32+
)
33+
RECT_180 = BoundingRectangle(
34+
r_x0=3,
35+
r_y0=1,
36+
r_x1=1,
37+
r_y1=1,
38+
r_x2=1,
39+
r_y2=2,
40+
r_x3=3,
41+
r_y3=2,
42+
coord_origin=CoordOrigin.TOPLEFT,
43+
)
44+
RECT_270 = BoundingRectangle(
45+
r_x0=1,
46+
r_y0=1,
47+
r_x1=1,
48+
r_y1=3,
49+
r_x2=2,
50+
r_y2=3,
51+
r_x3=2,
52+
r_y3=1,
53+
coord_origin=CoordOrigin.TOPLEFT,
54+
)
55+
56+
57+
@pytest.mark.parametrize(
58+
["bbox", "im_size", "angle", "expected_rectangle"],
59+
[
60+
# (BBOX, IM_SIZE, 0, RECT),
61+
# (BBOX, IM_SIZE, 90, RECT_90),
62+
(BBOX, IM_SIZE, 180, RECT_180),
63+
# (BBOX, IM_SIZE, 270, RECT_270),
64+
# (BBOX, IM_SIZE, 360, RECT),
65+
# (BBOX, IM_SIZE, -90, RECT_270),
66+
(BBOX, IM_SIZE, -180, RECT_180),
67+
# (BBOX, IM_SIZE, -270, RECT_90),
68+
],
69+
)
70+
def test_rotate_bounding_box(
71+
bbox: BoundingBox,
72+
im_size: Tuple[int, int],
73+
angle: int,
74+
expected_rectangle: BoundingRectangle,
75+
):
76+
rotated = rotate_bounding_box(bbox, angle, im_size)
77+
78+
assert rotated == expected_rectangle
79+
expected_angle_360 = angle % 360
80+
assert rotated.angle_360 == expected_angle_360

uv.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)