make_border_map.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. # -*- coding:utf-8 -*-
  2. from __future__ import absolute_import
  3. from __future__ import division
  4. from __future__ import print_function
  5. from __future__ import unicode_literals
  6. import numpy as np
  7. import cv2
  8. np.seterr(divide='ignore', invalid='ignore')
  9. import pyclipper
  10. from shapely.geometry import Polygon
  11. import sys
  12. import warnings
  13. warnings.simplefilter("ignore")
  14. __all__ = ['MakeBorderMap']
  15. class MakeBorderMap(object):
  16. def __init__(self,
  17. shrink_ratio=0.4,
  18. thresh_min=0.3,
  19. thresh_max=0.7,
  20. **kwargs):
  21. self.shrink_ratio = shrink_ratio
  22. self.thresh_min = thresh_min
  23. self.thresh_max = thresh_max
  24. def __call__(self, data):
  25. img = data['image']
  26. text_polys = data['polys']
  27. ignore_tags = data['ignore_tags']
  28. canvas = np.zeros(img.shape[:2], dtype=np.float32)
  29. mask = np.zeros(img.shape[:2], dtype=np.float32)
  30. for i in range(len(text_polys)):
  31. if ignore_tags[i]:
  32. continue
  33. self.draw_border_map(text_polys[i], canvas, mask=mask)
  34. canvas = canvas * (self.thresh_max - self.thresh_min) + self.thresh_min
  35. data['threshold_map'] = canvas
  36. data['threshold_mask'] = mask
  37. return data
  38. def draw_border_map(self, polygon, canvas, mask):
  39. polygon = np.array(polygon)
  40. assert polygon.ndim == 2
  41. assert polygon.shape[1] == 2
  42. polygon_shape = Polygon(polygon)
  43. if polygon_shape.area <= 0:
  44. return
  45. distance = polygon_shape.area * (
  46. 1 - np.power(self.shrink_ratio, 2)) / polygon_shape.length
  47. subject = [tuple(l) for l in polygon]
  48. padding = pyclipper.PyclipperOffset()
  49. padding.AddPath(subject, pyclipper.JT_ROUND, pyclipper.ET_CLOSEDPOLYGON)
  50. padded_polygon = np.array(padding.Execute(distance)[0])
  51. cv2.fillPoly(mask, [padded_polygon.astype(np.int32)], 1.0)
  52. xmin = padded_polygon[:, 0].min()
  53. xmax = padded_polygon[:, 0].max()
  54. ymin = padded_polygon[:, 1].min()
  55. ymax = padded_polygon[:, 1].max()
  56. width = xmax - xmin + 1
  57. height = ymax - ymin + 1
  58. polygon[:, 0] = polygon[:, 0] - xmin
  59. polygon[:, 1] = polygon[:, 1] - ymin
  60. xs = np.broadcast_to(
  61. np.linspace(
  62. 0, width - 1, num=width).reshape(1, width), (height, width))
  63. ys = np.broadcast_to(
  64. np.linspace(
  65. 0, height - 1, num=height).reshape(height, 1), (height, width))
  66. distance_map = np.zeros(
  67. (polygon.shape[0], height, width), dtype=np.float32)
  68. for i in range(polygon.shape[0]):
  69. j = (i + 1) % polygon.shape[0]
  70. absolute_distance = self._distance(xs, ys, polygon[i], polygon[j])
  71. distance_map[i] = np.clip(absolute_distance / distance, 0, 1)
  72. distance_map = distance_map.min(axis=0)
  73. xmin_valid = min(max(0, xmin), canvas.shape[1] - 1)
  74. xmax_valid = min(max(0, xmax), canvas.shape[1] - 1)
  75. ymin_valid = min(max(0, ymin), canvas.shape[0] - 1)
  76. ymax_valid = min(max(0, ymax), canvas.shape[0] - 1)
  77. canvas[ymin_valid:ymax_valid + 1, xmin_valid:xmax_valid + 1] = np.fmax(
  78. 1 - distance_map[ymin_valid - ymin:ymax_valid - ymax + height,
  79. xmin_valid - xmin:xmax_valid - xmax + width],
  80. canvas[ymin_valid:ymax_valid + 1, xmin_valid:xmax_valid + 1])
  81. def _distance(self, xs, ys, point_1, point_2):
  82. '''
  83. compute the distance from point to a line
  84. ys: coordinates in the first axis
  85. xs: coordinates in the second axis
  86. point_1, point_2: (x, y), the end of the line
  87. '''
  88. height, width = xs.shape[:2]
  89. square_distance_1 = np.square(xs - point_1[0]) + np.square(ys - point_1[
  90. 1])
  91. square_distance_2 = np.square(xs - point_2[0]) + np.square(ys - point_2[
  92. 1])
  93. square_distance = np.square(point_1[0] - point_2[0]) + np.square(
  94. point_1[1] - point_2[1])
  95. cosin = (square_distance - square_distance_1 - square_distance_2) / (
  96. 2 * np.sqrt(square_distance_1 * square_distance_2))
  97. square_sin = 1 - np.square(cosin)
  98. square_sin = np.nan_to_num(square_sin)
  99. result = np.sqrt(square_distance_1 * square_distance_2 * square_sin /
  100. square_distance)
  101. result[cosin <
  102. 0] = np.sqrt(np.fmin(square_distance_1, square_distance_2))[cosin
  103. < 0]
  104. # self.extend_line(point_1, point_2, result)
  105. return result
  106. def extend_line(self, point_1, point_2, result, shrink_ratio):
  107. ex_point_1 = (int(
  108. round(point_1[0] + (point_1[0] - point_2[0]) * (1 + shrink_ratio))),
  109. int(
  110. round(point_1[1] + (point_1[1] - point_2[1]) * (
  111. 1 + shrink_ratio))))
  112. cv2.line(
  113. result,
  114. tuple(ex_point_1),
  115. tuple(point_1),
  116. 4096.0,
  117. 1,
  118. lineType=cv2.LINE_AA,
  119. shift=0)
  120. ex_point_2 = (int(
  121. round(point_2[0] + (point_2[0] - point_1[0]) * (1 + shrink_ratio))),
  122. int(
  123. round(point_2[1] + (point_2[1] - point_1[1]) * (
  124. 1 + shrink_ratio))))
  125. cv2.line(
  126. result,
  127. tuple(ex_point_2),
  128. tuple(point_2),
  129. 4096.0,
  130. 1,
  131. lineType=cv2.LINE_AA,
  132. shift=0)
  133. return ex_point_1, ex_point_2