1
+ import math
1
2
from collections import OrderedDict
2
3
from dataclasses import dataclass
3
4
from typing import Dict , Tuple
4
5
6
+ import numpy as np
7
+
5
8
6
9
@dataclass
7
10
class BBox :
8
11
"""
9
- Box around some page object, the coordinate system starts from top left corner
12
+ Bounding box around some page object, the coordinate system starts from top left corner.
13
+ """
14
+ """
10
15
11
16
0------------------------------------------------------------------------------------------------> x
12
17
| BBox
@@ -21,10 +26,19 @@ class BBox:
21
26
|
22
27
V y
23
28
"""
24
- x_top_left : int
25
- y_top_left : int
26
- width : int
27
- height : int
29
+ def __init__ (self , x_top_left : int , y_top_left : int , width : int , height : int ) -> None :
30
+ """
31
+ The following parameters should have values of pixels number.
32
+
33
+ :param x_top_left: x coordinate of the bbox top left corner
34
+ :param y_top_left: y coordinate of the bbox top left corner
35
+ :param width: bounding box width
36
+ :param height: bounding box height
37
+ """
38
+ self .x_top_left = x_top_left
39
+ self .y_top_left = y_top_left
40
+ self .width = width
41
+ self .height = height
28
42
29
43
@property
30
44
def x_bottom_right (self ) -> int :
@@ -34,17 +48,56 @@ def x_bottom_right(self) -> int:
34
48
def y_bottom_right (self ) -> int :
35
49
return self .y_top_left + self .height
36
50
51
+ @staticmethod
52
+ def crop_image_by_box (image : np .ndarray , bbox : "BBox" ) -> np .ndarray :
53
+ return image [bbox .y_top_left :bbox .y_bottom_right , bbox .x_top_left :bbox .x_bottom_right ]
54
+
55
+ def rotate_coordinates (self , angle_rotate : float , image_shape : Tuple [int ]) -> None :
56
+ xb , yb = self .x_top_left , self .y_top_left
57
+ xe , ye = self .x_bottom_right , self .y_bottom_right
58
+ rad = angle_rotate * math .pi / 180
59
+
60
+ xc = image_shape [1 ] / 2
61
+ yc = image_shape [0 ] / 2
62
+
63
+ bbox_xb = min ((int (float (xb - xc ) * math .cos (rad ) - float (yb - yc ) * math .sin (rad ) + xc )), image_shape [1 ])
64
+ bbox_yb = min ((int (float (yb - yc ) * math .cos (rad ) + float (xb - xc ) * math .sin (rad ) + yc )), image_shape [0 ])
65
+ bbox_xe = min ((int (float (xe - xc ) * math .cos (rad ) - float (ye - yc ) * math .sin (rad ) + xc )), image_shape [1 ])
66
+ bbox_ye = min ((int (float (ye - yc ) * math .cos (rad ) + float (xe - xc ) * math .sin (rad ) + yc )), image_shape [0 ])
67
+ self .__init__ (bbox_xb , bbox_yb , bbox_xe - bbox_xb , bbox_ye - bbox_yb )
68
+
69
+ def __str__ (self ) -> str :
70
+ return f"BBox(x = { self .x_top_left } y = { self .y_top_left } , w = { self .width } , h = { self .height } )"
71
+
72
+ def __repr__ (self ) -> str :
73
+ return self .__str__ ()
74
+
37
75
@property
38
76
def square (self ) -> int :
77
+ """
78
+ Square of the bbox.
79
+ """
39
80
return self .height * self .width
40
81
41
82
@staticmethod
42
83
def from_two_points (top_left : Tuple [int , int ], bottom_right : Tuple [int , int ]) -> "BBox" :
84
+ """
85
+ Make the bounding box from two points.
86
+
87
+ :param top_left: (x, y) point of the bbox top left corner
88
+ :param bottom_right: (x, y) point of the bbox bottom right corner
89
+ """
43
90
x_top_left , y_top_left = top_left
44
91
x_bottom_right , y_bottom_right = bottom_right
45
92
return BBox (x_top_left = x_top_left , y_top_left = y_top_left , width = x_bottom_right - x_top_left , height = y_bottom_right - y_top_left )
46
93
47
94
def have_intersection_with_box (self , box : "BBox" , threshold : float = 0.3 ) -> bool :
95
+ """
96
+ Check if the current bounding box has the intersection with another one.
97
+
98
+ :param box: another bounding box to check intersection with
99
+ :param threshold: the lowest value of the intersection over union used get boolean result
100
+ """
48
101
# determine the (x, y)-coordinates of the intersection rectangle
49
102
x_min = max (self .x_top_left , box .x_top_left )
50
103
y_min = max (self .y_top_left , box .y_top_left )
0 commit comments