make_shrink_map.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import numpy as np
  2. import cv2
  3. __all__ = ['MakeShrinkMap']
  4. def shrink_polygon_py(polygon, shrink_ratio):
  5. """
  6. 对框进行缩放,返回去的比例为1/shrink_ratio 即可
  7. """
  8. cx = polygon[:, 0].mean()
  9. cy = polygon[:, 1].mean()
  10. polygon[:, 0] = cx + (polygon[:, 0] - cx) * shrink_ratio
  11. polygon[:, 1] = cy + (polygon[:, 1] - cy) * shrink_ratio
  12. return polygon
  13. def shrink_polygon_pyclipper(polygon, shrink_ratio):
  14. from shapely.geometry import Polygon
  15. import pyclipper
  16. polygon_shape = Polygon(polygon)
  17. subject = [tuple(l) for l in polygon]
  18. padding = pyclipper.PyclipperOffset()
  19. padding.AddPath(subject, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON)
  20. shrinked = []
  21. possible_ratios = np.arange(shrink_ratio, 1, shrink_ratio)
  22. np.append(possible_ratios, 1)
  23. for ratio in possible_ratios:
  24. distance = polygon_shape.area * (
  25. 1 - np.power(ratio, 2)) / polygon_shape.length
  26. shrinked = padding.Execute(-distance)
  27. if len(shrinked) == 1:
  28. break
  29. return shrinked
  30. class MakeShrinkMap():
  31. r'''
  32. Making binary mask from detection data with ICDAR format.
  33. Typically following the process of class `MakeICDARData`.
  34. '''
  35. def __init__(self, min_text_size=8, shrink_ratio=0.4, shrink_type='pyclipper'):
  36. shrink_func_dict = {'py': shrink_polygon_py, 'pyclipper': shrink_polygon_pyclipper}
  37. self.shrink_func = shrink_func_dict[shrink_type]
  38. self.min_text_size = min_text_size
  39. self.shrink_ratio = shrink_ratio
  40. def __call__(self, data: dict) -> dict:
  41. """
  42. 从scales中随机选择一个尺度,对图片和文本框进行缩放
  43. :param data: {'img':,'text_polys':,'texts':,'ignore_tags':}
  44. :return:
  45. """
  46. image = data['img']
  47. text_polys = data['text_polys']
  48. ignore_tags = data['ignore_tags']
  49. h, w = image.shape[:2]
  50. text_polys, ignore_tags = self.validate_polygons(text_polys, ignore_tags, h, w)
  51. gt = np.zeros((h, w), dtype=np.float32)
  52. mask = np.ones((h, w), dtype=np.float32)
  53. for i in range(len(text_polys)):
  54. polygon = text_polys[i]
  55. height = max(polygon[:, 1]) - min(polygon[:, 1])
  56. width = max(polygon[:, 0]) - min(polygon[:, 0])
  57. if ignore_tags[i] or min(height, width) < self.min_text_size:
  58. cv2.fillPoly(mask, polygon.astype(np.int32)[np.newaxis, :, :], 0)
  59. ignore_tags[i] = True
  60. else:
  61. shrinked = self.shrink_func(polygon, self.shrink_ratio)
  62. if shrinked == []:
  63. cv2.fillPoly(mask, polygon.astype(np.int32)[np.newaxis, :, :], 0)
  64. ignore_tags[i] = True
  65. continue
  66. for each_shirnk in shrinked:
  67. shirnk = np.array(each_shirnk).reshape(-1, 2)
  68. cv2.fillPoly(gt, [shirnk.astype(np.int32)], 1)
  69. data['shrink_map'] = gt
  70. data['shrink_mask'] = mask
  71. data['ignore_tags'] = ignore_tags
  72. return data
  73. def validate_polygons(self, polygons, ignore_tags, h, w):
  74. '''
  75. polygons (numpy.array, required): of shape (num_instances, num_points, 2)
  76. '''
  77. if len(polygons) == 0:
  78. return polygons, ignore_tags
  79. assert len(polygons) == len(ignore_tags)
  80. for polygon in polygons:
  81. polygon[:, 0] = np.clip(polygon[:, 0], 0, w - 1)
  82. polygon[:, 1] = np.clip(polygon[:, 1], 0, h - 1)
  83. for i in range(len(polygons)):
  84. area = self.polygon_area(polygons[i])
  85. if abs(area) < 1:
  86. ignore_tags[i] = True
  87. if area > 0:
  88. polygons[i] = polygons[i][::-1, :]
  89. return polygons, ignore_tags
  90. def polygon_area(self, polygon):
  91. polygon = polygon.reshape(-1, 2)
  92. edge = 0
  93. for i in range(polygon.shape[0]):
  94. next_index = (i + 1) % polygon.shape[0]
  95. edge += (polygon[next_index, 0] - polygon[i, 0]) * (
  96. polygon[next_index, 1] + polygon[i, 1])
  97. return edge / 2.
  98. if __name__ == '__main__':
  99. from shapely.geometry import Polygon
  100. import pyclipper
  101. polygon = np.array([[0, 0], [100, 10], [100, 100], [10, 90]])
  102. a = shrink_polygon_py(polygon, 0.4)
  103. print(a)
  104. print(shrink_polygon_py(a, 1 / 0.4))
  105. b = shrink_polygon_pyclipper(polygon, 0.4)
  106. print(b)
  107. poly = Polygon(b)
  108. distance = poly.area * 1.5 / poly.length
  109. offset = pyclipper.PyclipperOffset()
  110. offset.AddPath(b, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON)
  111. expanded = np.array(offset.Execute(distance))
  112. bounding_box = cv2.minAreaRect(expanded)
  113. points = cv2.boxPoints(bounding_box)
  114. print(points)