|
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) |
|
|
|
|
|
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) |
|
|
|
|
|
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) |
|
|
|
|
|
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) |
|
|
|
|
|
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 |