utils.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. # Copyright (c) Alibaba, Inc. and its affiliates.
  2. import cv2
  3. import numpy as np
  4. import pyclipper
  5. from shapely.geometry import Polygon
  6. def rboxes_to_polygons(rboxes):
  7. """
  8. Convert rboxes to polygons
  9. ARGS
  10. `rboxes`: [n, 5]
  11. RETURN
  12. `polygons`: [n, 8]
  13. """
  14. theta = rboxes[:, 4:5]
  15. cxcy = rboxes[:, :2]
  16. half_w = rboxes[:, 2:3] / 2.
  17. half_h = rboxes[:, 3:4] / 2.
  18. v1 = np.hstack([np.cos(theta) * half_w, np.sin(theta) * half_w])
  19. v2 = np.hstack([-np.sin(theta) * half_h, np.cos(theta) * half_h])
  20. p1 = cxcy - v1 - v2
  21. p2 = cxcy + v1 - v2
  22. p3 = cxcy + v1 + v2
  23. p4 = cxcy - v1 + v2
  24. polygons = np.hstack([p1, p2, p3, p4])
  25. return polygons
  26. def cal_width(box):
  27. pd1 = point_dist(box[0], box[1], box[2], box[3])
  28. pd2 = point_dist(box[4], box[5], box[6], box[7])
  29. return (pd1 + pd2) / 2
  30. def point_dist(x1, y1, x2, y2):
  31. return np.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
  32. def draw_polygons(img, polygons):
  33. for p in polygons.tolist():
  34. p = [int(o) for o in p]
  35. cv2.line(img, (p[0], p[1]), (p[2], p[3]), (0, 255, 0), 1)
  36. cv2.line(img, (p[2], p[3]), (p[4], p[5]), (0, 255, 0), 1)
  37. cv2.line(img, (p[4], p[5]), (p[6], p[7]), (0, 255, 0), 1)
  38. cv2.line(img, (p[6], p[7]), (p[0], p[1]), (0, 255, 0), 1)
  39. return img
  40. def nms_python(boxes):
  41. boxes = sorted(boxes, key=lambda x: -x[8])
  42. nms_flag = [True] * len(boxes)
  43. for i, a in enumerate(boxes):
  44. if not nms_flag[i]:
  45. continue
  46. else:
  47. for j, b in enumerate(boxes):
  48. if not j > i:
  49. continue
  50. if not nms_flag[j]:
  51. continue
  52. score_a = a[8]
  53. score_b = b[8]
  54. rbox_a = polygon2rbox(a[:8])
  55. rbox_b = polygon2rbox(b[:8])
  56. if point_in_rbox(rbox_a[:2], rbox_b) or point_in_rbox(
  57. rbox_b[:2], rbox_a):
  58. if score_a > score_b:
  59. nms_flag[j] = False
  60. boxes_nms = []
  61. for i, box in enumerate(boxes):
  62. if nms_flag[i]:
  63. boxes_nms.append(box)
  64. return boxes_nms
  65. def point_in_rbox(c, rbox):
  66. cx0, cy0 = c[0], c[1]
  67. cx1, cy1 = rbox[0], rbox[1]
  68. w, h = rbox[2], rbox[3]
  69. theta = rbox[4]
  70. dist_x = np.abs((cx1 - cx0) * np.cos(theta) + (cy1 - cy0) * np.sin(theta))
  71. dist_y = np.abs(-(cx1 - cx0) * np.sin(theta) + (cy1 - cy0) * np.cos(theta))
  72. return ((dist_x < w / 2.0) and (dist_y < h / 2.0))
  73. def polygon2rbox(polygon):
  74. x1, x2, x3, x4 = polygon[0], polygon[2], polygon[4], polygon[6]
  75. y1, y2, y3, y4 = polygon[1], polygon[3], polygon[5], polygon[7]
  76. c_x = (x1 + x2 + x3 + x4) / 4
  77. c_y = (y1 + y2 + y3 + y4) / 4
  78. w1 = point_dist(x1, y1, x2, y2)
  79. w2 = point_dist(x3, y3, x4, y4)
  80. h1 = point_line_dist(c_x, c_y, x1, y1, x2, y2)
  81. h2 = point_line_dist(c_x, c_y, x3, y3, x4, y4)
  82. h = h1 + h2
  83. w = (w1 + w2) / 2
  84. theta1 = np.arctan2(y2 - y1, x2 - x1)
  85. theta2 = np.arctan2(y3 - y4, x3 - x4)
  86. theta = (theta1 + theta2) / 2.0
  87. return [c_x, c_y, w, h, theta]
  88. def point_line_dist(px, py, x1, y1, x2, y2):
  89. eps = 1e-6
  90. dx = x2 - x1
  91. dy = y2 - y1
  92. div = np.sqrt(dx * dx + dy * dy) + eps
  93. dist = np.abs(px * dy - py * dx + x2 * y1 - y2 * x1) / div
  94. return dist
  95. # Part of the implementation is borrowed and modified from DB,
  96. # publicly available at https://github.com/MhLiao/DB.
  97. def polygons_from_bitmap(pred, _bitmap, dest_width, dest_height):
  98. """
  99. _bitmap: single map with shape (1, H, W),
  100. whose values are binarized as {0, 1}
  101. """
  102. assert _bitmap.size(0) == 1
  103. bitmap = _bitmap.cpu().numpy()[0]
  104. pred = pred.cpu().detach().numpy()[0]
  105. height, width = bitmap.shape
  106. boxes = []
  107. scores = []
  108. contours, _ = cv2.findContours((bitmap * 255).astype(np.uint8),
  109. cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
  110. for contour in contours[:100]:
  111. epsilon = 0.01 * cv2.arcLength(contour, True)
  112. approx = cv2.approxPolyDP(contour, epsilon, True)
  113. points = approx.reshape((-1, 2))
  114. if points.shape[0] < 4:
  115. continue
  116. score = box_score_fast(pred, points.reshape(-1, 2))
  117. if 0.7 > score:
  118. continue
  119. if points.shape[0] > 2:
  120. box = unclip(points, unclip_ratio=2.0)
  121. if len(box) > 1:
  122. continue
  123. else:
  124. continue
  125. box = box.reshape(-1, 2)
  126. _, sside = get_mini_boxes(box.reshape((-1, 1, 2)))
  127. if sside < 3 + 2:
  128. continue
  129. if not isinstance(dest_width, int):
  130. dest_width = dest_width.item()
  131. dest_height = dest_height.item()
  132. box[:, 0] = np.clip(
  133. np.round(box[:, 0] / width * dest_width), 0, dest_width)
  134. box[:, 1] = np.clip(
  135. np.round(box[:, 1] / height * dest_height), 0, dest_height)
  136. boxes.append(box.tolist())
  137. scores.append(score)
  138. return boxes, scores
  139. def boxes_from_bitmap(pred, _bitmap, dest_width, dest_height, is_numpy=False):
  140. """
  141. _bitmap: single map with shape (1, H, W),
  142. whose values are binarized as {0, 1}
  143. """
  144. if is_numpy:
  145. bitmap = _bitmap[0]
  146. pred = pred[0]
  147. else:
  148. bitmap = _bitmap.cpu().numpy()[0]
  149. pred = pred.cpu().detach().numpy()[0]
  150. height, width = bitmap.shape
  151. boxes = []
  152. scores = []
  153. contours, _ = cv2.findContours((bitmap * 255).astype(np.uint8),
  154. cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
  155. for contour in contours[:1000]:
  156. points, sside = get_mini_boxes(contour)
  157. if sside < 3:
  158. continue
  159. points = np.array(points)
  160. score = box_score_fast(pred, points.reshape(-1, 2))
  161. if 0.3 > score:
  162. continue
  163. box = unclip(points, unclip_ratio=1.5).reshape(-1, 1, 2)
  164. box, sside = get_mini_boxes(box)
  165. if sside < 3 + 2:
  166. continue
  167. box = np.array(box).astype(np.int32)
  168. if not isinstance(dest_width, int):
  169. dest_width = dest_width.item()
  170. dest_height = dest_height.item()
  171. box[:, 0] = np.clip(
  172. np.round(box[:, 0] / width * dest_width), 0, dest_width)
  173. box[:, 1] = np.clip(
  174. np.round(box[:, 1] / height * dest_height), 0, dest_height)
  175. boxes.append(box.reshape(-1).tolist())
  176. scores.append(score)
  177. return boxes, scores
  178. def box_score_fast(bitmap, _box):
  179. h, w = bitmap.shape[:2]
  180. box = _box.copy()
  181. xmin = np.clip(np.floor(box[:, 0].min()).astype(np.int32), 0, w - 1)
  182. xmax = np.clip(np.ceil(box[:, 0].max()).astype(np.int32), 0, w - 1)
  183. ymin = np.clip(np.floor(box[:, 1].min()).astype(np.int32), 0, h - 1)
  184. ymax = np.clip(np.ceil(box[:, 1].max()).astype(np.int32), 0, h - 1)
  185. mask = np.zeros((ymax - ymin + 1, xmax - xmin + 1), dtype=np.uint8)
  186. box[:, 0] = box[:, 0] - xmin
  187. box[:, 1] = box[:, 1] - ymin
  188. cv2.fillPoly(mask, box.reshape(1, -1, 2).astype(np.int32), 1)
  189. return cv2.mean(bitmap[ymin:ymax + 1, xmin:xmax + 1], mask)[0]
  190. def unclip(box, unclip_ratio=1.5):
  191. poly = Polygon(box)
  192. distance = poly.area * unclip_ratio / poly.length
  193. offset = pyclipper.PyclipperOffset()
  194. offset.AddPath(box, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON)
  195. expanded = np.array(offset.Execute(distance))
  196. return expanded
  197. def get_mini_boxes(contour):
  198. bounding_box = cv2.minAreaRect(contour)
  199. points = sorted(list(cv2.boxPoints(bounding_box)), key=lambda x: x[0])
  200. index_1, index_2, index_3, index_4 = 0, 1, 2, 3
  201. if points[1][1] > points[0][1]:
  202. index_1 = 0
  203. index_4 = 1
  204. else:
  205. index_1 = 1
  206. index_4 = 0
  207. if points[3][1] > points[2][1]:
  208. index_2 = 2
  209. index_3 = 3
  210. else:
  211. index_2 = 3
  212. index_3 = 2
  213. box = [points[index_1], points[index_2], points[index_3], points[index_4]]
  214. return box, min(bounding_box[1])