103 lines
2.7 KiB
Python
103 lines
2.7 KiB
Python
from itertools import combinations
|
|
from typing import List
|
|
|
|
from cv_analysis.utils import until
|
|
from cv_analysis.utils.rectangle import Rectangle
|
|
from cv_analysis.utils.spacial import overlap
|
|
|
|
|
|
def is_near_enough(alpha: Rectangle, beta: Rectangle, max_gap=14):
|
|
x1, y1, w1, h1 = alpha
|
|
x2, y2, w2, h2 = beta
|
|
|
|
return any(
|
|
[
|
|
abs(x1 - (x2 + w2)) <= max_gap,
|
|
abs(x2 - (x1 + w1)) <= max_gap,
|
|
abs(y2 - (y1 + h1)) <= max_gap,
|
|
abs(y1 - (y2 + h2)) <= max_gap,
|
|
]
|
|
)
|
|
|
|
|
|
def is_on_same_line(rect_pair):
|
|
x1, y1, w1, h1 = rect_pair[0]
|
|
x2, y2, w2, h2 = rect_pair[1]
|
|
return any(
|
|
[
|
|
any([abs(y1 - y2) <= 10, abs(y1 + h1 - (y2 + h2)) <= 10]),
|
|
any([y2 <= y1 and y1 + h1 <= y2 + h2, y1 <= y2 and y2 + h2 <= y1 + h1]),
|
|
]
|
|
)
|
|
|
|
|
|
def has_correct_position(alpha: Rectangle, beta: Rectangle):
|
|
x1, y1, w1, h1 = alpha
|
|
x2, y2, w2, h2 = beta
|
|
return any(
|
|
[
|
|
any(
|
|
[abs(x1 - x2) <= 10, abs(y1 - y2) <= 10, abs(x1 + w1 - (x2 + w2)) <= 10, abs(y1 + h1 - (y2 + h2)) <= 10]
|
|
),
|
|
any(
|
|
[
|
|
y2 <= y1 and y1 + h1 <= y2 + h2,
|
|
y1 <= y2 and y2 + h2 <= y1 + h1,
|
|
x2 <= x1 and x1 + w1 <= x2 + w2,
|
|
x1 <= x2 and x2 + w2 <= x1 + w1,
|
|
]
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def is_related(alpha: Rectangle, beta: Rectangle):
|
|
return (is_near_enough(alpha, beta) and has_correct_position(alpha, beta)) or overlap(alpha, beta)
|
|
|
|
|
|
def bounding_rect(alpha: Rectangle, beta: Rectangle):
|
|
return Rectangle(
|
|
min(alpha.x1, beta.x1),
|
|
min(alpha.y1, beta.y1),
|
|
max(alpha.x2, beta.x2),
|
|
max(alpha.y2, beta.y2),
|
|
)
|
|
|
|
|
|
def rectangles_differ(r):
|
|
return r[0] != r[1]
|
|
|
|
|
|
def connect_related_rectangles(rectangles: List[Rectangle]):
|
|
assert isinstance(rectangles, list)
|
|
no_new_merges = make_merger_sentinel()
|
|
return until(no_new_merges, merge_rectangles_once, rectangles)
|
|
|
|
|
|
def merge_rectangles_once(rectangles: List[Rectangle]):
|
|
for alpha, beta in combinations(rectangles, 2):
|
|
if is_related(alpha, beta):
|
|
rectangles.remove(alpha)
|
|
rectangles.remove(beta)
|
|
rectangles.append(bounding_rect(alpha, beta))
|
|
return rectangles
|
|
return rectangles
|
|
|
|
|
|
def make_merger_sentinel():
|
|
def no_new_mergers(records):
|
|
nonlocal number_of_records_so_far
|
|
|
|
number_of_records_now = len(records)
|
|
|
|
if number_of_records_now == number_of_records_so_far:
|
|
return True
|
|
|
|
else:
|
|
number_of_records_so_far = number_of_records_now
|
|
return False
|
|
|
|
number_of_records_so_far = -1
|
|
|
|
return no_new_mergers
|