operators.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. """
  2. # Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. """
  16. from __future__ import absolute_import
  17. from __future__ import division
  18. from __future__ import print_function
  19. from __future__ import unicode_literals
  20. import sys
  21. import six
  22. import cv2
  23. import numpy as np
  24. from PIL import Image
  25. class DecodeImage(object):
  26. """ decode image """
  27. def __init__(self, img_mode='RGB', channel_first=False, **kwargs):
  28. self.img_mode = img_mode
  29. self.channel_first = channel_first
  30. def __call__(self, data):
  31. img = data['image']
  32. if six.PY2:
  33. assert type(img) is str and len(
  34. img) > 0, "invalid input 'img' in DecodeImage"
  35. else:
  36. assert type(img) is bytes and len(
  37. img) > 0, "invalid input 'img' in DecodeImage"
  38. img = np.frombuffer(img, dtype='uint8')
  39. img = cv2.imdecode(img, 1)
  40. if img is None:
  41. return None
  42. if self.img_mode == 'GRAY':
  43. img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
  44. elif self.img_mode == 'RGB':
  45. assert img.shape[2] == 3, 'invalid shape of image[%s]' % (img.shape)
  46. img = img[:, :, ::-1]
  47. if self.channel_first:
  48. img = img.transpose((2, 0, 1))
  49. data['image'] = img
  50. return data
  51. class NRTRDecodeImage(object):
  52. """ decode image """
  53. def __init__(self, img_mode='RGB', channel_first=False, **kwargs):
  54. self.img_mode = img_mode
  55. self.channel_first = channel_first
  56. def __call__(self, data):
  57. img = data['image']
  58. if six.PY2:
  59. assert type(img) is str and len(
  60. img) > 0, "invalid input 'img' in DecodeImage"
  61. else:
  62. assert type(img) is bytes and len(
  63. img) > 0, "invalid input 'img' in DecodeImage"
  64. img = np.frombuffer(img, dtype='uint8')
  65. img = cv2.imdecode(img, 1)
  66. if img is None:
  67. return None
  68. if self.img_mode == 'GRAY':
  69. img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
  70. elif self.img_mode == 'RGB':
  71. assert img.shape[2] == 3, 'invalid shape of image[%s]' % (img.shape)
  72. img = img[:, :, ::-1]
  73. img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  74. if self.channel_first:
  75. img = img.transpose((2, 0, 1))
  76. data['image'] = img
  77. return data
  78. class NormalizeImage(object):
  79. """ normalize image such as substract mean, divide std
  80. """
  81. def __init__(self, scale=None, mean=None, std=None, order='chw', **kwargs):
  82. if isinstance(scale, str):
  83. scale = eval(scale)
  84. self.scale = np.float32(scale if scale is not None else 1.0 / 255.0)
  85. mean = mean if mean is not None else [0.485, 0.456, 0.406]
  86. std = std if std is not None else [0.229, 0.224, 0.225]
  87. shape = (3, 1, 1) if order == 'chw' else (1, 1, 3)
  88. self.mean = np.array(mean).reshape(shape).astype('float32')
  89. self.std = np.array(std).reshape(shape).astype('float32')
  90. self.scale = self.scale / self.std
  91. self.mean = self.mean / self.std
  92. def __call__(self, data):
  93. img = data['image']
  94. if isinstance(img, Image.Image):
  95. img = np.array(img)
  96. data['image'] = img.astype('float32') * self.scale - self.mean
  97. return data
  98. class ToCHWImage(object):
  99. """ convert hwc image to chw image
  100. """
  101. def __init__(self, **kwargs):
  102. pass
  103. def __call__(self, data):
  104. img = data['image']
  105. from PIL import Image
  106. if isinstance(img, Image.Image):
  107. img = np.array(img)
  108. data['image'] = img.transpose((2, 0, 1))
  109. return data
  110. class Fasttext(object):
  111. def __init__(self, path="None", **kwargs):
  112. import fasttext
  113. self.fast_model = fasttext.load_model(path)
  114. def __call__(self, data):
  115. label = data['label']
  116. fast_label = self.fast_model[label]
  117. data['fast_label'] = fast_label
  118. return data
  119. class KeepKeys(object):
  120. def __init__(self, keep_keys, **kwargs):
  121. self.keep_keys = keep_keys
  122. def __call__(self, data):
  123. data_list = []
  124. for key in self.keep_keys:
  125. data_list.append(data[key])
  126. return data_list
  127. class Resize(object):
  128. def __init__(self, size=(640, 640), **kwargs):
  129. self.size = size
  130. def resize_image(self, img):
  131. resize_h, resize_w = self.size
  132. ori_h, ori_w = img.shape[:2] # (h, w, c)
  133. ratio_h = float(resize_h) / ori_h
  134. ratio_w = float(resize_w) / ori_w
  135. img = cv2.resize(img, (int(resize_w), int(resize_h)))
  136. return img, [ratio_h, ratio_w]
  137. def __call__(self, data):
  138. img = data['image']
  139. text_polys = data['polys']
  140. img_resize, [ratio_h, ratio_w] = self.resize_image(img)
  141. new_boxes = []
  142. for box in text_polys:
  143. new_box = []
  144. for cord in box:
  145. new_box.append([cord[0] * ratio_w, cord[1] * ratio_h])
  146. new_boxes.append(new_box)
  147. data['image'] = img_resize
  148. data['polys'] = np.array(new_boxes, dtype=np.float32)
  149. return data
  150. class DetResizeForTest(object):
  151. def __init__(self, **kwargs):
  152. super(DetResizeForTest, self).__init__()
  153. self.resize_type = 0
  154. if 'image_shape' in kwargs:
  155. self.image_shape = kwargs['image_shape']
  156. self.resize_type = 1
  157. elif 'limit_side_len' in kwargs:
  158. self.limit_side_len = kwargs['limit_side_len']
  159. self.limit_type = kwargs.get('limit_type', 'min')
  160. elif 'resize_long' in kwargs:
  161. self.resize_type = 2
  162. self.resize_long = kwargs.get('resize_long', 960)
  163. else:
  164. self.limit_side_len = 736
  165. self.limit_type = 'min'
  166. def __call__(self, data):
  167. img = data['image']
  168. src_h, src_w, _ = img.shape
  169. if self.resize_type == 0:
  170. # img, shape = self.resize_image_type0(img)
  171. img, [ratio_h, ratio_w] = self.resize_image_type0(img)
  172. elif self.resize_type == 2:
  173. img, [ratio_h, ratio_w] = self.resize_image_type2(img)
  174. else:
  175. # img, shape = self.resize_image_type1(img)
  176. img, [ratio_h, ratio_w] = self.resize_image_type1(img)
  177. data['image'] = img
  178. data['shape'] = np.array([src_h, src_w, ratio_h, ratio_w])
  179. return data
  180. def resize_image_type1(self, img):
  181. resize_h, resize_w = self.image_shape
  182. ori_h, ori_w = img.shape[:2] # (h, w, c)
  183. ratio_h = float(resize_h) / ori_h
  184. ratio_w = float(resize_w) / ori_w
  185. img = cv2.resize(img, (int(resize_w), int(resize_h)))
  186. # return img, np.array([ori_h, ori_w])
  187. return img, [ratio_h, ratio_w]
  188. def resize_image_type0(self, img):
  189. """
  190. resize image to a size multiple of 32 which is required by the network
  191. args:
  192. img(array): array with shape [h, w, c]
  193. return(tuple):
  194. img, (ratio_h, ratio_w)
  195. """
  196. limit_side_len = self.limit_side_len
  197. h, w, c = img.shape
  198. # limit the max side
  199. if self.limit_type == 'max':
  200. if max(h, w) > limit_side_len:
  201. if h > w:
  202. ratio = float(limit_side_len) / h
  203. else:
  204. ratio = float(limit_side_len) / w
  205. else:
  206. ratio = 1.
  207. elif self.limit_type == 'min':
  208. if min(h, w) < limit_side_len:
  209. if h < w:
  210. ratio = float(limit_side_len) / h
  211. else:
  212. ratio = float(limit_side_len) / w
  213. else:
  214. ratio = 1.
  215. elif self.limit_type == 'resize_long':
  216. ratio = float(limit_side_len) / max(h, w)
  217. else:
  218. raise Exception('not support limit type, image ')
  219. resize_h = int(h * ratio)
  220. resize_w = int(w * ratio)
  221. resize_h = max(int(round(resize_h / 32) * 32), 32)
  222. resize_w = max(int(round(resize_w / 32) * 32), 32)
  223. try:
  224. if int(resize_w) <= 0 or int(resize_h) <= 0:
  225. return None, (None, None)
  226. img = cv2.resize(img, (int(resize_w), int(resize_h)))
  227. except:
  228. print(img.shape, resize_w, resize_h)
  229. sys.exit(0)
  230. ratio_h = resize_h / float(h)
  231. ratio_w = resize_w / float(w)
  232. return img, [ratio_h, ratio_w]
  233. def resize_image_type2(self, img):
  234. h, w, _ = img.shape
  235. resize_w = w
  236. resize_h = h
  237. if resize_h > resize_w:
  238. ratio = float(self.resize_long) / resize_h
  239. else:
  240. ratio = float(self.resize_long) / resize_w
  241. resize_h = int(resize_h * ratio)
  242. resize_w = int(resize_w * ratio)
  243. max_stride = 128
  244. resize_h = (resize_h + max_stride - 1) // max_stride * max_stride
  245. resize_w = (resize_w + max_stride - 1) // max_stride * max_stride
  246. img = cv2.resize(img, (int(resize_w), int(resize_h)))
  247. ratio_h = resize_h / float(h)
  248. ratio_w = resize_w / float(w)
  249. return img, [ratio_h, ratio_w]
  250. class E2EResizeForTest(object):
  251. def __init__(self, **kwargs):
  252. super(E2EResizeForTest, self).__init__()
  253. self.max_side_len = kwargs['max_side_len']
  254. self.valid_set = kwargs['valid_set']
  255. def __call__(self, data):
  256. img = data['image']
  257. src_h, src_w, _ = img.shape
  258. if self.valid_set == 'totaltext':
  259. im_resized, [ratio_h, ratio_w] = self.resize_image_for_totaltext(
  260. img, max_side_len=self.max_side_len)
  261. else:
  262. im_resized, (ratio_h, ratio_w) = self.resize_image(
  263. img, max_side_len=self.max_side_len)
  264. data['image'] = im_resized
  265. data['shape'] = np.array([src_h, src_w, ratio_h, ratio_w])
  266. return data
  267. def resize_image_for_totaltext(self, im, max_side_len=512):
  268. h, w, _ = im.shape
  269. resize_w = w
  270. resize_h = h
  271. ratio = 1.25
  272. if h * ratio > max_side_len:
  273. ratio = float(max_side_len) / resize_h
  274. resize_h = int(resize_h * ratio)
  275. resize_w = int(resize_w * ratio)
  276. max_stride = 128
  277. resize_h = (resize_h + max_stride - 1) // max_stride * max_stride
  278. resize_w = (resize_w + max_stride - 1) // max_stride * max_stride
  279. im = cv2.resize(im, (int(resize_w), int(resize_h)))
  280. ratio_h = resize_h / float(h)
  281. ratio_w = resize_w / float(w)
  282. return im, (ratio_h, ratio_w)
  283. def resize_image(self, im, max_side_len=512):
  284. """
  285. resize image to a size multiple of max_stride which is required by the network
  286. :param im: the resized image
  287. :param max_side_len: limit of max image size to avoid out of memory in gpu
  288. :return: the resized image and the resize ratio
  289. """
  290. h, w, _ = im.shape
  291. resize_w = w
  292. resize_h = h
  293. # Fix the longer side
  294. if resize_h > resize_w:
  295. ratio = float(max_side_len) / resize_h
  296. else:
  297. ratio = float(max_side_len) / resize_w
  298. resize_h = int(resize_h * ratio)
  299. resize_w = int(resize_w * ratio)
  300. max_stride = 128
  301. resize_h = (resize_h + max_stride - 1) // max_stride * max_stride
  302. resize_w = (resize_w + max_stride - 1) // max_stride * max_stride
  303. im = cv2.resize(im, (int(resize_w), int(resize_h)))
  304. ratio_h = resize_h / float(h)
  305. ratio_w = resize_w / float(w)
  306. return im, (ratio_h, ratio_w)
  307. class KieResize(object):
  308. def __init__(self, **kwargs):
  309. super(KieResize, self).__init__()
  310. self.max_side, self.min_side = kwargs['img_scale'][0], kwargs[
  311. 'img_scale'][1]
  312. def __call__(self, data):
  313. img = data['image']
  314. points = data['points']
  315. src_h, src_w, _ = img.shape
  316. im_resized, scale_factor, [ratio_h, ratio_w
  317. ], [new_h, new_w] = self.resize_image(img)
  318. resize_points = self.resize_boxes(img, points, scale_factor)
  319. data['ori_image'] = img
  320. data['ori_boxes'] = points
  321. data['points'] = resize_points
  322. data['image'] = im_resized
  323. data['shape'] = np.array([new_h, new_w])
  324. return data
  325. def resize_image(self, img):
  326. norm_img = np.zeros([1024, 1024, 3], dtype='float32')
  327. scale = [512, 1024]
  328. h, w = img.shape[:2]
  329. max_long_edge = max(scale)
  330. max_short_edge = min(scale)
  331. scale_factor = min(max_long_edge / max(h, w),
  332. max_short_edge / min(h, w))
  333. resize_w, resize_h = int(w * float(scale_factor) + 0.5), int(h * float(
  334. scale_factor) + 0.5)
  335. max_stride = 32
  336. resize_h = (resize_h + max_stride - 1) // max_stride * max_stride
  337. resize_w = (resize_w + max_stride - 1) // max_stride * max_stride
  338. im = cv2.resize(img, (resize_w, resize_h))
  339. new_h, new_w = im.shape[:2]
  340. w_scale = new_w / w
  341. h_scale = new_h / h
  342. scale_factor = np.array(
  343. [w_scale, h_scale, w_scale, h_scale], dtype=np.float32)
  344. norm_img[:new_h, :new_w, :] = im
  345. return norm_img, scale_factor, [h_scale, w_scale], [new_h, new_w]
  346. def resize_boxes(self, im, points, scale_factor):
  347. points = points * scale_factor
  348. img_shape = im.shape[:2]
  349. points[:, 0::2] = np.clip(points[:, 0::2], 0, img_shape[1])
  350. points[:, 1::2] = np.clip(points[:, 1::2], 0, img_shape[0])
  351. return points