mirror of
https://github.com/leigest519/ScreenCoder.git
synced 2026-02-13 02:02:48 +00:00
239 lines
9.7 KiB
Python
239 lines
9.7 KiB
Python
from detect_compo.lib_ip.Bbox import Bbox
|
|
import detect_compo.lib_ip.ip_draw as draw
|
|
|
|
import cv2
|
|
|
|
|
|
def cvt_compos_relative_pos(compos, col_min_base, row_min_base):
|
|
for compo in compos:
|
|
compo.compo_relative_position(col_min_base, row_min_base)
|
|
|
|
|
|
def compos_containment(compos):
|
|
for i in range(len(compos) - 1):
|
|
for j in range(i + 1, len(compos)):
|
|
relation = compos[i].compo_relation(compos[j])
|
|
if relation == -1:
|
|
compos[j].contain.append(i)
|
|
if relation == 1:
|
|
compos[i].contain.append(j)
|
|
|
|
|
|
def compos_update(compos, org_shape):
|
|
for i, compo in enumerate(compos):
|
|
# start from 1, id 0 is background
|
|
compo.compo_update(i + 1, org_shape)
|
|
|
|
|
|
class Component:
|
|
def __init__(self, region, image_shape):
|
|
self.id = None
|
|
self.region = region
|
|
self.boundary = self.compo_get_boundary()
|
|
self.bbox = self.compo_get_bbox()
|
|
self.bbox_area = self.bbox.box_area
|
|
|
|
self.region_area = len(region)
|
|
self.width = len(self.boundary[0])
|
|
self.height = len(self.boundary[2])
|
|
self.image_shape = image_shape
|
|
self.area = self.width * self.height
|
|
|
|
self.category = 'Compo'
|
|
self.contain = []
|
|
|
|
self.rect_ = None
|
|
self.line_ = None
|
|
self.redundant = False
|
|
|
|
def compo_update(self, id, org_shape):
|
|
self.id = id
|
|
self.image_shape = org_shape
|
|
self.width = self.bbox.width
|
|
self.height = self.bbox.height
|
|
self.bbox_area = self.bbox.box_area
|
|
self.area = self.width * self.height
|
|
|
|
def put_bbox(self):
|
|
return self.bbox.put_bbox()
|
|
|
|
def compo_update_bbox_area(self):
|
|
self.bbox_area = self.bbox.bbox_cal_area()
|
|
|
|
def compo_get_boundary(self):
|
|
'''
|
|
get the bounding boundary of an object(region)
|
|
boundary: [top, bottom, left, right]
|
|
-> up, bottom: (column_index, min/max row border)
|
|
-> left, right: (row_index, min/max column border) detect range of each row
|
|
'''
|
|
border_up, border_bottom, border_left, border_right = {}, {}, {}, {}
|
|
for point in self.region:
|
|
# point: (row_index, column_index)
|
|
# up, bottom: (column_index, min/max row border) detect range of each column
|
|
if point[1] not in border_up or border_up[point[1]] > point[0]:
|
|
border_up[point[1]] = point[0]
|
|
if point[1] not in border_bottom or border_bottom[point[1]] < point[0]:
|
|
border_bottom[point[1]] = point[0]
|
|
# left, right: (row_index, min/max column border) detect range of each row
|
|
if point[0] not in border_left or border_left[point[0]] > point[1]:
|
|
border_left[point[0]] = point[1]
|
|
if point[0] not in border_right or border_right[point[0]] < point[1]:
|
|
border_right[point[0]] = point[1]
|
|
|
|
boundary = [border_up, border_bottom, border_left, border_right]
|
|
# descending sort
|
|
for i in range(len(boundary)):
|
|
boundary[i] = [[k, boundary[i][k]] for k in boundary[i].keys()]
|
|
boundary[i] = sorted(boundary[i], key=lambda x: x[0])
|
|
return boundary
|
|
|
|
def compo_get_bbox(self):
|
|
"""
|
|
Get the top left and bottom right points of boundary
|
|
:param boundaries: boundary: [top, bottom, left, right]
|
|
-> up, bottom: (column_index, min/max row border)
|
|
-> left, right: (row_index, min/max column border) detect range of each row
|
|
:return: corners: [(top_left, bottom_right)]
|
|
-> top_left: (column_min, row_min)
|
|
-> bottom_right: (column_max, row_max)
|
|
"""
|
|
col_min, row_min = (int(min(self.boundary[0][0][0], self.boundary[1][-1][0])), int(min(self.boundary[2][0][0], self.boundary[3][-1][0])))
|
|
col_max, row_max = (int(max(self.boundary[0][0][0], self.boundary[1][-1][0])), int(max(self.boundary[2][0][0], self.boundary[3][-1][0])))
|
|
bbox = Bbox(col_min, row_min, col_max, row_max)
|
|
return bbox
|
|
|
|
def compo_is_rectangle(self, min_rec_evenness, max_dent_ratio, test=False):
|
|
'''
|
|
detect if an object is rectangle by evenness and dent of each border
|
|
'''
|
|
dent_direction = [1, -1, 1, -1] # direction for convex
|
|
|
|
flat = 0
|
|
parameter = 0
|
|
for n, border in enumerate(self.boundary):
|
|
parameter += len(border)
|
|
# dent detection
|
|
pit = 0 # length of pit
|
|
depth = 0 # the degree of surface changing
|
|
if n <= 1:
|
|
adj_side = max(len(self.boundary[2]), len(self.boundary[3])) # get maximum length of adjacent side
|
|
else:
|
|
adj_side = max(len(self.boundary[0]), len(self.boundary[1]))
|
|
|
|
# -> up, bottom: (column_index, min/max row border)
|
|
# -> left, right: (row_index, min/max column border) detect range of each row
|
|
abnm = 0
|
|
for i in range(int(3 + len(border) * 0.02), len(border) - 1):
|
|
# calculate gradient
|
|
difference = border[i][1] - border[i + 1][1]
|
|
# the degree of surface changing
|
|
depth += difference
|
|
# ignore noise at the start of each direction
|
|
if i / len(border) < 0.08 and (dent_direction[n] * difference) / adj_side > 0.5:
|
|
depth = 0 # reset
|
|
|
|
# print(border[i][1], i / len(border), depth, (dent_direction[n] * difference) / adj_side)
|
|
# if the change of the surface is too large, count it as part of abnormal change
|
|
if abs(depth) / adj_side > 0.3:
|
|
abnm += 1 # count the size of the abnm
|
|
# if the abnm is too big, the shape should not be a rectangle
|
|
if abnm / len(border) > 0.1:
|
|
if test:
|
|
print('abnms', abnm, abnm / len(border))
|
|
draw.draw_boundary([self], self.image_shape, show=True)
|
|
self.rect_ = False
|
|
return False
|
|
continue
|
|
else:
|
|
# reset the abnm if the depth back to normal
|
|
abnm = 0
|
|
|
|
# if sunken and the surface changing is large, then counted as pit
|
|
if dent_direction[n] * depth < 0 and abs(depth) / adj_side > 0.15:
|
|
pit += 1
|
|
continue
|
|
|
|
# if the surface is not changing to a pit and the gradient is zero, then count it as flat
|
|
if abs(depth) < 1 + adj_side * 0.015:
|
|
flat += 1
|
|
if test:
|
|
print(depth, adj_side, flat)
|
|
# if the pit is too big, the shape should not be a rectangle
|
|
if pit / len(border) > max_dent_ratio:
|
|
if test:
|
|
print('pit', pit, pit / len(border))
|
|
draw.draw_boundary([self], self.image_shape, show=True)
|
|
self.rect_ = False
|
|
return False
|
|
if test:
|
|
print(flat / parameter, '\n')
|
|
draw.draw_boundary([self], self.image_shape, show=True)
|
|
# ignore text and irregular shape
|
|
if self.height / self.image_shape[0] > 0.3:
|
|
min_rec_evenness = 0.85
|
|
if (flat / parameter) < min_rec_evenness:
|
|
self.rect_ = False
|
|
return False
|
|
self.rect_ = True
|
|
return True
|
|
|
|
def compo_is_line(self, min_line_thickness):
|
|
"""
|
|
Check this object is line by checking its boundary
|
|
:param boundary: boundary: [border_top, border_bottom, border_left, border_right]
|
|
-> top, bottom: list of (column_index, min/max row border)
|
|
-> left, right: list of (row_index, min/max column border) detect range of each row
|
|
:param min_line_thickness:
|
|
:return: Boolean
|
|
"""
|
|
# horizontally
|
|
slim = 0
|
|
for i in range(self.width):
|
|
if abs(self.boundary[1][i][1] - self.boundary[0][i][1]) <= min_line_thickness:
|
|
slim += 1
|
|
if slim / len(self.boundary[0]) > 0.93:
|
|
self.line_ = True
|
|
return True
|
|
# vertically
|
|
slim = 0
|
|
for i in range(self.height):
|
|
if abs(self.boundary[2][i][1] - self.boundary[3][i][1]) <= min_line_thickness:
|
|
slim += 1
|
|
if slim / len(self.boundary[2]) > 0.93:
|
|
self.line_ = True
|
|
return True
|
|
self.line_ = False
|
|
return False
|
|
|
|
def compo_relation(self, compo_b, bias=(0, 0)):
|
|
"""
|
|
:return: -1 : a in b
|
|
0 : a, b are not intersected
|
|
1 : b in a
|
|
2 : a, b are identical or intersected
|
|
"""
|
|
return self.bbox.bbox_relation_nms(compo_b.bbox, bias)
|
|
|
|
def compo_relative_position(self, col_min_base, row_min_base):
|
|
'''
|
|
Convert to relative position based on base coordinator
|
|
'''
|
|
self.bbox.bbox_cvt_relative_position(col_min_base, row_min_base)
|
|
|
|
def compo_merge(self, compo_b):
|
|
self.bbox = self.bbox.bbox_merge(compo_b.bbox)
|
|
self.compo_update(self.id, self.image_shape)
|
|
|
|
def compo_clipping(self, img, pad=0, show=False):
|
|
(column_min, row_min, column_max, row_max) = self.put_bbox()
|
|
column_min = max(column_min - pad, 0)
|
|
column_max = min(column_max + pad, img.shape[1])
|
|
row_min = max(row_min - pad, 0)
|
|
row_max = min(row_max + pad, img.shape[0])
|
|
clip = img[row_min:row_max, column_min:column_max]
|
|
if show:
|
|
cv2.imshow('clipping', clip)
|
|
cv2.waitKey()
|
|
return clip
|