box_utils.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. # copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import numpy as np
  15. import random
  16. import math
  17. import cv2
  18. import scipy
  19. def bbox_area(src_bbox):
  20. if src_bbox[2] < src_bbox[0] or src_bbox[3] < src_bbox[1]:
  21. return 0.
  22. else:
  23. width = src_bbox[2] - src_bbox[0]
  24. height = src_bbox[3] - src_bbox[1]
  25. return width * height
  26. def jaccard_overlap(sample_bbox, object_bbox):
  27. if sample_bbox[0] >= object_bbox[2] or \
  28. sample_bbox[2] <= object_bbox[0] or \
  29. sample_bbox[1] >= object_bbox[3] or \
  30. sample_bbox[3] <= object_bbox[1]:
  31. return 0
  32. intersect_xmin = max(sample_bbox[0], object_bbox[0])
  33. intersect_ymin = max(sample_bbox[1], object_bbox[1])
  34. intersect_xmax = min(sample_bbox[2], object_bbox[2])
  35. intersect_ymax = min(sample_bbox[3], object_bbox[3])
  36. intersect_size = (intersect_xmax - intersect_xmin) * (
  37. intersect_ymax - intersect_ymin)
  38. sample_bbox_size = bbox_area(sample_bbox)
  39. object_bbox_size = bbox_area(object_bbox)
  40. overlap = intersect_size / (
  41. sample_bbox_size + object_bbox_size - intersect_size)
  42. return overlap
  43. def iou_matrix(a, b):
  44. tl_i = np.maximum(a[:, np.newaxis, :2], b[:, :2])
  45. br_i = np.minimum(a[:, np.newaxis, 2:], b[:, 2:])
  46. area_i = np.prod(br_i - tl_i, axis=2) * (tl_i < br_i).all(axis=2)
  47. area_a = np.prod(a[:, 2:] - a[:, :2], axis=1)
  48. area_b = np.prod(b[:, 2:] - b[:, :2], axis=1)
  49. area_o = (area_a[:, np.newaxis] + area_b - area_i)
  50. return area_i / (area_o + 1e-10)
  51. def crop_box_with_center_constraint(box, crop):
  52. cropped_box = box.copy()
  53. cropped_box[:, :2] = np.maximum(box[:, :2], crop[:2])
  54. cropped_box[:, 2:] = np.minimum(box[:, 2:], crop[2:])
  55. cropped_box[:, :2] -= crop[:2]
  56. cropped_box[:, 2:] -= crop[:2]
  57. centers = (box[:, :2] + box[:, 2:]) / 2
  58. valid = np.logical_and(crop[:2] <= centers, centers < crop[2:]).all(axis=1)
  59. valid = np.logical_and(
  60. valid, (cropped_box[:, :2] < cropped_box[:, 2:]).all(axis=1))
  61. return cropped_box, np.where(valid)[0]
  62. def is_poly(segm):
  63. if not isinstance(segm, (list, dict)):
  64. raise Exception("Invalid segm type: {}".format(type(segm)))
  65. return isinstance(segm, list)
  66. def crop_image(img, crop):
  67. x1, y1, x2, y2 = crop
  68. return img[y1:y2, x1:x2, :]
  69. def crop_segms(segms, valid_ids, crop, height, width):
  70. def _crop_poly(segm, crop):
  71. xmin, ymin, xmax, ymax = crop
  72. crop_coord = [xmin, ymin, xmin, ymax, xmax, ymax, xmax, ymin]
  73. crop_p = np.array(crop_coord).reshape(4, 2)
  74. crop_p = Polygon(crop_p)
  75. crop_segm = list()
  76. for poly in segm:
  77. poly = np.array(poly).reshape(len(poly) // 2, 2)
  78. polygon = Polygon(poly)
  79. if not polygon.is_valid:
  80. exterior = polygon.exterior
  81. multi_lines = exterior.intersection(exterior)
  82. polygons = shapely.ops.polygonize(multi_lines)
  83. polygon = MultiPolygon(polygons)
  84. multi_polygon = list()
  85. if isinstance(polygon, MultiPolygon):
  86. multi_polygon = copy.deepcopy(polygon)
  87. else:
  88. multi_polygon.append(copy.deepcopy(polygon))
  89. for per_polygon in multi_polygon:
  90. inter = per_polygon.intersection(crop_p)
  91. if not inter:
  92. continue
  93. if isinstance(inter, (MultiPolygon, GeometryCollection)):
  94. for part in inter:
  95. if not isinstance(part, Polygon):
  96. continue
  97. part = np.squeeze(
  98. np.array(part.exterior.coords[:-1]).reshape(1, -1))
  99. part[0::2] -= xmin
  100. part[1::2] -= ymin
  101. crop_segm.append(part.tolist())
  102. elif isinstance(inter, Polygon):
  103. crop_poly = np.squeeze(
  104. np.array(inter.exterior.coords[:-1]).reshape(1, -1))
  105. crop_poly[0::2] -= xmin
  106. crop_poly[1::2] -= ymin
  107. crop_segm.append(crop_poly.tolist())
  108. else:
  109. continue
  110. return crop_segm
  111. def _crop_rle(rle, crop, height, width):
  112. if 'counts' in rle and type(rle['counts']) == list:
  113. rle = mask_util.frPyObjects(rle, height, width)
  114. mask = mask_util.decode(rle)
  115. mask = mask[crop[1]:crop[3], crop[0]:crop[2]]
  116. rle = mask_util.encode(np.array(mask, order='F', dtype=np.uint8))
  117. return rle
  118. crop_segms = []
  119. for id in valid_ids:
  120. segm = segms[id]
  121. if is_poly(segm):
  122. import copy
  123. import shapely.ops
  124. import logging
  125. from shapely.geometry import Polygon, MultiPolygon, GeometryCollection
  126. logging.getLogger("shapely").setLevel(logging.WARNING)
  127. # Polygon format
  128. crop_segms.append(_crop_poly(segm, crop))
  129. else:
  130. # RLE format
  131. import pycocotools.mask as mask_util
  132. crop_segms.append(_crop_rle(segm, crop, height, width))
  133. return crop_segms
  134. def expand_segms(segms, x, y, height, width, ratio):
  135. def _expand_poly(poly, x, y):
  136. expanded_poly = np.array(poly)
  137. expanded_poly[0::2] += x
  138. expanded_poly[1::2] += y
  139. return expanded_poly.tolist()
  140. def _expand_rle(rle, x, y, height, width, ratio):
  141. if 'counts' in rle and type(rle['counts']) == list:
  142. rle = mask_util.frPyObjects(rle, height, width)
  143. mask = mask_util.decode(rle)
  144. expanded_mask = np.full((int(height * ratio), int(width * ratio)),
  145. 0).astype(mask.dtype)
  146. expanded_mask[y:y + height, x:x + width] = mask
  147. rle = mask_util.encode(
  148. np.array(expanded_mask, order='F', dtype=np.uint8))
  149. return rle
  150. expanded_segms = []
  151. for segm in segms:
  152. if is_poly(segm):
  153. # Polygon format
  154. expanded_segms.append([_expand_poly(poly, x, y) for poly in segm])
  155. else:
  156. # RLE format
  157. import pycocotools.mask as mask_util
  158. expanded_segms.append(
  159. _expand_rle(segm, x, y, height, width, ratio))
  160. return expanded_segms
  161. def box_horizontal_flip(bboxes, width):
  162. oldx1 = bboxes[:, 0].copy()
  163. oldx2 = bboxes[:, 2].copy()
  164. bboxes[:, 0] = width - oldx2 - 1
  165. bboxes[:, 2] = width - oldx1 - 1
  166. if bboxes.shape[0] != 0 and (bboxes[:, 2] < bboxes[:, 0]).all():
  167. raise ValueError(
  168. "RandomHorizontalFlip: invalid box, x2 should be greater than x1")
  169. return bboxes
  170. def segms_horizontal_flip(segms, height, width):
  171. def _flip_poly(poly, width):
  172. flipped_poly = np.array(poly)
  173. flipped_poly[0::2] = width - np.array(poly[0::2]) - 1
  174. return flipped_poly.tolist()
  175. def _flip_rle(rle, height, width):
  176. if 'counts' in rle and type(rle['counts']) == list:
  177. rle = mask_util.frPyObjects([rle], height, width)
  178. mask = mask_util.decode(rle)
  179. mask = mask[:, ::-1]
  180. rle = mask_util.encode(np.array(mask, order='F', dtype=np.uint8))
  181. return rle
  182. flipped_segms = []
  183. for segm in segms:
  184. if is_poly(segm):
  185. # Polygon format
  186. flipped_segms.append([_flip_poly(poly, width) for poly in segm])
  187. else:
  188. # RLE format
  189. import pycocotools.mask as mask_util
  190. flipped_segms.append(_flip_rle(segm, height, width))
  191. return flipped_segms