random_crop_data.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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. """
  15. This code is refer from:
  16. https://github.com/WenmuZhou/DBNet.pytorch/blob/master/data_loader/modules/random_crop_data.py
  17. """
  18. from __future__ import absolute_import
  19. from __future__ import division
  20. from __future__ import print_function
  21. from __future__ import unicode_literals
  22. import numpy as np
  23. import cv2
  24. import random
  25. def is_poly_in_rect(poly, x, y, w, h):
  26. poly = np.array(poly)
  27. if poly[:, 0].min() < x or poly[:, 0].max() > x + w:
  28. return False
  29. if poly[:, 1].min() < y or poly[:, 1].max() > y + h:
  30. return False
  31. return True
  32. def is_poly_outside_rect(poly, x, y, w, h):
  33. poly = np.array(poly)
  34. if poly[:, 0].max() < x or poly[:, 0].min() > x + w:
  35. return True
  36. if poly[:, 1].max() < y or poly[:, 1].min() > y + h:
  37. return True
  38. return False
  39. def split_regions(axis):
  40. regions = []
  41. min_axis = 0
  42. for i in range(1, axis.shape[0]):
  43. if axis[i] != axis[i - 1] + 1:
  44. region = axis[min_axis:i]
  45. min_axis = i
  46. regions.append(region)
  47. return regions
  48. def random_select(axis, max_size):
  49. xx = np.random.choice(axis, size=2)
  50. xmin = np.min(xx)
  51. xmax = np.max(xx)
  52. xmin = np.clip(xmin, 0, max_size - 1)
  53. xmax = np.clip(xmax, 0, max_size - 1)
  54. return xmin, xmax
  55. def region_wise_random_select(regions, max_size):
  56. selected_index = list(np.random.choice(len(regions), 2))
  57. selected_values = []
  58. for index in selected_index:
  59. axis = regions[index]
  60. xx = int(np.random.choice(axis, size=1))
  61. selected_values.append(xx)
  62. xmin = min(selected_values)
  63. xmax = max(selected_values)
  64. return xmin, xmax
  65. def crop_area(im, text_polys, min_crop_side_ratio, max_tries):
  66. h, w, _ = im.shape
  67. h_array = np.zeros(h, dtype=np.int32)
  68. w_array = np.zeros(w, dtype=np.int32)
  69. for points in text_polys:
  70. points = np.round(points, decimals=0).astype(np.int32)
  71. minx = np.min(points[:, 0])
  72. maxx = np.max(points[:, 0])
  73. w_array[minx:maxx] = 1
  74. miny = np.min(points[:, 1])
  75. maxy = np.max(points[:, 1])
  76. h_array[miny:maxy] = 1
  77. # ensure the cropped area not across a text
  78. h_axis = np.where(h_array == 0)[0]
  79. w_axis = np.where(w_array == 0)[0]
  80. if len(h_axis) == 0 or len(w_axis) == 0:
  81. return 0, 0, w, h
  82. h_regions = split_regions(h_axis)
  83. w_regions = split_regions(w_axis)
  84. for i in range(max_tries):
  85. if len(w_regions) > 1:
  86. xmin, xmax = region_wise_random_select(w_regions, w)
  87. else:
  88. xmin, xmax = random_select(w_axis, w)
  89. if len(h_regions) > 1:
  90. ymin, ymax = region_wise_random_select(h_regions, h)
  91. else:
  92. ymin, ymax = random_select(h_axis, h)
  93. if xmax - xmin < min_crop_side_ratio * w or ymax - ymin < min_crop_side_ratio * h:
  94. # area too small
  95. continue
  96. num_poly_in_rect = 0
  97. for poly in text_polys:
  98. if not is_poly_outside_rect(poly, xmin, ymin, xmax - xmin,
  99. ymax - ymin):
  100. num_poly_in_rect += 1
  101. break
  102. if num_poly_in_rect > 0:
  103. return xmin, ymin, xmax - xmin, ymax - ymin
  104. return 0, 0, w, h
  105. class EastRandomCropData(object):
  106. def __init__(self,
  107. size=(640, 640),
  108. max_tries=10,
  109. min_crop_side_ratio=0.1,
  110. keep_ratio=True,
  111. **kwargs):
  112. self.size = size
  113. self.max_tries = max_tries
  114. self.min_crop_side_ratio = min_crop_side_ratio
  115. self.keep_ratio = keep_ratio
  116. def __call__(self, data):
  117. img = data['image']
  118. text_polys = data['polys']
  119. ignore_tags = data['ignore_tags']
  120. texts = data['texts']
  121. all_care_polys = [
  122. text_polys[i] for i, tag in enumerate(ignore_tags) if not tag
  123. ]
  124. # 计算crop区域
  125. crop_x, crop_y, crop_w, crop_h = crop_area(
  126. img, all_care_polys, self.min_crop_side_ratio, self.max_tries)
  127. # crop 图片 保持比例填充
  128. scale_w = self.size[0] / crop_w
  129. scale_h = self.size[1] / crop_h
  130. scale = min(scale_w, scale_h)
  131. h = int(crop_h * scale)
  132. w = int(crop_w * scale)
  133. if self.keep_ratio:
  134. padimg = np.zeros((self.size[1], self.size[0], img.shape[2]),
  135. img.dtype)
  136. padimg[:h, :w] = cv2.resize(
  137. img[crop_y:crop_y + crop_h, crop_x:crop_x + crop_w], (w, h))
  138. img = padimg
  139. else:
  140. img = cv2.resize(
  141. img[crop_y:crop_y + crop_h, crop_x:crop_x + crop_w],
  142. tuple(self.size))
  143. # crop 文本框
  144. text_polys_crop = []
  145. ignore_tags_crop = []
  146. texts_crop = []
  147. for poly, text, tag in zip(text_polys, texts, ignore_tags):
  148. poly = ((poly - (crop_x, crop_y)) * scale).tolist()
  149. if not is_poly_outside_rect(poly, 0, 0, w, h):
  150. text_polys_crop.append(poly)
  151. ignore_tags_crop.append(tag)
  152. texts_crop.append(text)
  153. data['image'] = img
  154. data['polys'] = np.array(text_polys_crop)
  155. data['ignore_tags'] = ignore_tags_crop
  156. data['texts'] = texts_crop
  157. return data
  158. class RandomCropImgMask(object):
  159. def __init__(self, size, main_key, crop_keys, p=3 / 8, **kwargs):
  160. self.size = size
  161. self.main_key = main_key
  162. self.crop_keys = crop_keys
  163. self.p = p
  164. def __call__(self, data):
  165. image = data['image']
  166. h, w = image.shape[0:2]
  167. th, tw = self.size
  168. if w == tw and h == th:
  169. return data
  170. mask = data[self.main_key]
  171. if np.max(mask) > 0 and random.random() > self.p:
  172. # make sure to crop the text region
  173. tl = np.min(np.where(mask > 0), axis=1) - (th, tw)
  174. tl[tl < 0] = 0
  175. br = np.max(np.where(mask > 0), axis=1) - (th, tw)
  176. br[br < 0] = 0
  177. br[0] = min(br[0], h - th)
  178. br[1] = min(br[1], w - tw)
  179. i = random.randint(tl[0], br[0]) if tl[0] < br[0] else 0
  180. j = random.randint(tl[1], br[1]) if tl[1] < br[1] else 0
  181. else:
  182. i = random.randint(0, h - th) if h - th > 0 else 0
  183. j = random.randint(0, w - tw) if w - tw > 0 else 0
  184. # return i, j, th, tw
  185. for k in data:
  186. if k in self.crop_keys:
  187. if len(data[k].shape) == 3:
  188. if np.argmin(data[k].shape) == 0:
  189. img = data[k][:, i:i + th, j:j + tw]
  190. if img.shape[1] != img.shape[2]:
  191. a = 1
  192. elif np.argmin(data[k].shape) == 2:
  193. img = data[k][i:i + th, j:j + tw, :]
  194. if img.shape[1] != img.shape[0]:
  195. a = 1
  196. else:
  197. img = data[k]
  198. else:
  199. img = data[k][i:i + th, j:j + tw]
  200. if img.shape[0] != img.shape[1]:
  201. a = 1
  202. data[k] = img
  203. return data