File size: 6,263 Bytes
79914f7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import cv2
import numpy as np


def apply_adaptive_threshold(image: np.ndarray) -> np.ndarray:
    """
    Applies adaptive threshold to the given image
    """
    return cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 5, 0)


def is_contour_rectangular(contour: np.ndarray) -> bool:
    """
    Returns whether the given contour is rectangular or not
    """
    num_sides = 4
    perimeter = cv2.arcLength(contour, True)
    approx = cv2.approxPolyDP(contour, 0.01 * perimeter, True)
    return len(approx) == num_sides


def adaptive_vconcat(images: list[np.ndarray], fill_color: tuple[int, int, int] = (255, 255, 255)) -> np.ndarray:
    max_width = max(img.shape[1] for img in images)

    # Resize each image to match the largest dimensions
    resized_images = []
    for img in images:
        resized_img = cv2.copyMakeBorder(img, 
                                         top=0, bottom=0, 
                                         left=0, right=max_width - img.shape[1], 
                                         borderType=cv2.BORDER_CONSTANT, 
                                         value=fill_color)  
        resized_images.append(resized_img)

    # Concatenate vertically
    return np.vstack(resized_images)


def adaptive_hconcat(images: list[np.ndarray], fill_color: tuple[int, int, int] = (255, 255, 255)) -> np.ndarray:
    max_height = max(img.shape[0] for img in images)

    # Resize each image to match the largest dimensions
    resized_images = []
    for img in images:
        resized_img = cv2.copyMakeBorder(img, 
                                         top=0, bottom=max_height - img.shape[0], 
                                         left=0, right=0, 
                                         borderType=cv2.BORDER_CONSTANT, 
                                         value=fill_color)  
        resized_images.append(resized_img)

    # Concatenate horizontally
    return np.hstack(resized_images)


def group_contours_vertically(contours) -> list[list[np.ndarray]]:
    """
    Groups the given contours vertically
    """
    ERROR_THRESHOLD = 0.05
    contours = sorted(contours, key=lambda c: cv2.boundingRect(c)[1])
    grouped_contours = [[contours[0]]]
    for contour in contours[1:]:
        found_group = False
        contour_x, contour_y, contour_w, contour_h = cv2.boundingRect(contour)
        for group in grouped_contours[::-1]:
            group_x, group_y, group_w, group_h = cv2.boundingRect(group[-1])
            y_diff = abs(contour_y - group_y) - group_h
            if y_diff < 0 or y_diff > min(contour_h, group_h):
                continue 
            group_x_center = group_x + group_w / 2
            contour_x_center = contour_x + contour_w / 2
            if abs(group_x_center - contour_x_center) < ERROR_THRESHOLD * min(group_w, contour_w):
                group.append(contour)
                found_group = True
                break
        if not found_group:
            grouped_contours.append([contour])
    return grouped_contours


def group_contours_horizontally(contours) -> list[list[np.ndarray]]:
    """
    Groups the given contours horizontally
    """
    ERROR_THRESHOLD = 0.05
    contours = sorted(contours, key=lambda c: cv2.boundingRect(c)[0])
    grouped_contours = [[contours[0]]]
    for contour in contours[1:]:
        found_group = False
        contour_x, contour_y, contour_w, contour_h = cv2.boundingRect(contour)
        for group in grouped_contours[::-1]:
            group_x, group_y, group_w, group_h = cv2.boundingRect(group[-1])
            x_diff = abs(contour_x - group_x) - group_w
            if x_diff < 0 or x_diff > min(contour_w, group_w):
                continue 
            group_y_center = group_y + group_h / 2
            contour_y_center = contour_y + contour_h / 2
            if abs(group_y_center - contour_y_center) < ERROR_THRESHOLD * min(group_h, contour_h):
                group.append(contour)
                found_group = True
                break
        if not found_group:
            grouped_contours.append([contour])
    return grouped_contours

def group_bounding_boxes_vertically(bounding_boxes) -> list[list[tuple[int, int, int, int]]]:
    """
    Groups the given bounding boxes vertically
    """
    ERROR_THRESHOLD = 0.05
    bounding_boxes = sorted(bounding_boxes, key=lambda bb: bb[1])
    grouped_bounding_boxes = [[bounding_boxes[0]]]
    for bounding_box in bounding_boxes[1:]:
        found_group = False
        bb_x, bb_y, bb_w, bb_h = bounding_box
        for group in grouped_bounding_boxes[::-1]:
            group_x, group_y, group_w, group_h = group[-1]
            y_diff = abs(bb_y - group_y) - group_h
            if y_diff < 0 or y_diff > min(bb_h, group_h):
                continue 
            group_x_center = group_x + group_w / 2
            bb_x_center = bb_x + bb_w / 2
            if abs(group_x_center - bb_x_center) < ERROR_THRESHOLD * min(group_w, bb_w):
                group.append(bounding_box)
                found_group = True
                break
        if not found_group:
            grouped_bounding_boxes.append([bounding_box])
    return grouped_bounding_boxes

def group_bounding_boxes_horizontally(bounding_boxes) -> list[list[tuple[int, int, int, int]]]:
    """
    Groups the given bounding boxes horizontally
    """
    ERROR_THRESHOLD = 0.05
    bounding_boxes = sorted(bounding_boxes, key=lambda bb: bb[0])
    grouped_bounding_boxes = [[bounding_boxes[0]]]
    for bounding_box in bounding_boxes[1:]:
        found_group = False
        bb_x, bb_y, bb_w, bb_h = bounding_box
        for group in grouped_bounding_boxes[::-1]:
            group_x, group_y, group_w, group_h = group[-1]
            x_diff = abs(bb_x - group_x) - group_w
            if x_diff < 0 or x_diff > min(bb_w, group_w):
                continue 
            group_y_center = group_y + group_h / 2
            bb_y_center = bb_y + bb_h / 2
            if abs(group_y_center - bb_y_center) < ERROR_THRESHOLD * min(group_h, bb_h):
                group.append(bounding_box)
                found_group = True
                break
        if not found_group:
            grouped_bounding_boxes.append([bounding_box])
    return grouped_bounding_boxes