| 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): |
| |
| 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: |
| |
| |
| 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] |
| |
| 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] |
| |
| 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] |
|
|
| flat = 0 |
| parameter = 0 |
| for n, border in enumerate(self.boundary): |
| parameter += len(border) |
| |
| pit = 0 |
| depth = 0 |
| if n <= 1: |
| adj_side = max(len(self.boundary[2]), len(self.boundary[3])) |
| else: |
| adj_side = max(len(self.boundary[0]), len(self.boundary[1])) |
|
|
| |
| |
| abnm = 0 |
| for i in range(int(3 + len(border) * 0.02), len(border) - 1): |
| |
| difference = border[i][1] - border[i + 1][1] |
| |
| depth += difference |
| |
| if i / len(border) < 0.08 and (dent_direction[n] * difference) / adj_side > 0.5: |
| depth = 0 |
|
|
| |
| |
| if abs(depth) / adj_side > 0.3: |
| abnm += 1 |
| |
| 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: |
| |
| abnm = 0 |
|
|
| |
| if dent_direction[n] * depth < 0 and abs(depth) / adj_side > 0.15: |
| pit += 1 |
| continue |
|
|
| |
| if abs(depth) < 1 + adj_side * 0.015: |
| flat += 1 |
| if test: |
| print(depth, adj_side, flat) |
| |
| 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) |
| |
| 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 |
| """ |
| |
| 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 |
| |
| 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 |
|
|