Spaces:
Running
Running
File size: 9,892 Bytes
a383d0e |
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 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
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
|