seg_transforms.py 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060
  1. # coding: utf8
  2. # copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve.
  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. from .ops import *
  16. import random
  17. import os.path as osp
  18. import numpy as np
  19. from PIL import Image
  20. import cv2
  21. from collections import OrderedDict
  22. class Compose:
  23. """根据数据预处理/增强算子对输入数据进行操作。
  24. 所有操作的输入图像流形状均是[H, W, C],其中H为图像高,W为图像宽,C为图像通道数。
  25. Args:
  26. transforms (list): 数据预处理/增强算子。
  27. Raises:
  28. TypeError: transforms不是list对象
  29. ValueError: transforms元素个数小于1。
  30. """
  31. def __init__(self, transforms):
  32. if not isinstance(transforms, list):
  33. raise TypeError('The transforms must be a list!')
  34. if len(transforms) < 1:
  35. raise ValueError('The length of transforms ' + \
  36. 'must be equal or larger than 1!')
  37. self.transforms = transforms
  38. self.to_rgb = False
  39. def __call__(self, im, im_info=None, label=None):
  40. """
  41. Args:
  42. im (str/np.ndarray): 图像路径/图像np.ndarray数据。
  43. im_info (list): 存储图像reisze或padding前的shape信息,如
  44. [('resize', [200, 300]), ('padding', [400, 600])]表示
  45. 图像在过resize前shape为(200, 300), 过padding前shape为
  46. (400, 600)
  47. label (str/np.ndarray): 标注图像路径/标注图像np.ndarray数据。
  48. Returns:
  49. tuple: 根据网络所需字段所组成的tuple;字段由transforms中的最后一个数据预处理操作决定。
  50. """
  51. if im_info is None:
  52. im_info = list()
  53. try:
  54. im = cv2.imread(im).astype('float32')
  55. except:
  56. raise ValueError('Can\'t read The image file {}!'.format(im))
  57. if self.to_rgb:
  58. im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
  59. if label is not None:
  60. if not isinstance(label, np.ndarray):
  61. label = np.asarray(Image.open(label))
  62. for op in self.transforms:
  63. outputs = op(im, im_info, label)
  64. im = outputs[0]
  65. if len(outputs) >= 2:
  66. im_info = outputs[1]
  67. if len(outputs) == 3:
  68. label = outputs[2]
  69. return outputs
  70. class RandomHorizontalFlip:
  71. """以一定的概率对图像进行水平翻转。当存在标注图像时,则同步进行翻转。
  72. Args:
  73. prob (float): 随机水平翻转的概率。默认值为0.5。
  74. """
  75. def __init__(self, prob=0.5):
  76. self.prob = prob
  77. def __call__(self, im, im_info=None, label=None):
  78. """
  79. Args:
  80. im (np.ndarray): 图像np.ndarray数据。
  81. im_info (list): 存储图像reisze或padding前的shape信息,如
  82. [('resize', [200, 300]), ('padding', [400, 600])]表示
  83. 图像在过resize前shape为(200, 300), 过padding前shape为
  84. (400, 600)
  85. label (np.ndarray): 标注图像np.ndarray数据。
  86. Returns:
  87. tuple: 当label为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
  88. 当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
  89. 存储与图像相关信息的字典和标注图像np.ndarray数据。
  90. """
  91. if random.random() < self.prob:
  92. im = horizontal_flip(im)
  93. if label is not None:
  94. label = horizontal_flip(label)
  95. if label is None:
  96. return (im, im_info)
  97. else:
  98. return (im, im_info, label)
  99. class RandomVerticalFlip:
  100. """以一定的概率对图像进行垂直翻转。当存在标注图像时,则同步进行翻转。
  101. Args:
  102. prob (float): 随机垂直翻转的概率。默认值为0.1。
  103. """
  104. def __init__(self, prob=0.1):
  105. self.prob = prob
  106. def __call__(self, im, im_info=None, label=None):
  107. """
  108. Args:
  109. im (np.ndarray): 图像np.ndarray数据。
  110. im_info (list): 存储图像reisze或padding前的shape信息,如
  111. [('resize', [200, 300]), ('padding', [400, 600])]表示
  112. 图像在过resize前shape为(200, 300), 过padding前shape为
  113. (400, 600)
  114. label (np.ndarray): 标注图像np.ndarray数据。
  115. Returns:
  116. tuple: 当label为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
  117. 当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
  118. 存储与图像相关信息的字典和标注图像np.ndarray数据。
  119. """
  120. if random.random() < self.prob:
  121. im = vertical_flip(im)
  122. if label is not None:
  123. label = vertical_flip(label)
  124. if label is None:
  125. return (im, im_info)
  126. else:
  127. return (im, im_info, label)
  128. class Resize:
  129. """调整图像大小(resize),当存在标注图像时,则同步进行处理。
  130. - 当目标大小(target_size)类型为int时,根据插值方式,
  131. 将图像resize为[target_size, target_size]。
  132. - 当目标大小(target_size)类型为list或tuple时,根据插值方式,
  133. 将图像resize为target_size, target_size的输入应为[w, h]或(w, h)。
  134. Args:
  135. target_size (int|list|tuple): 目标大小。
  136. interp (str): resize的插值方式,与opencv的插值方式对应,
  137. 可选的值为['NEAREST', 'LINEAR', 'CUBIC', 'AREA', 'LANCZOS4'],默认为"LINEAR"。
  138. Raises:
  139. TypeError: target_size不是int/list/tuple。
  140. ValueError: target_size为list/tuple时元素个数不等于2。
  141. AssertionError: interp的取值不在['NEAREST', 'LINEAR', 'CUBIC', 'AREA', 'LANCZOS4']之内。
  142. """
  143. # The interpolation mode
  144. interp_dict = {
  145. 'NEAREST': cv2.INTER_NEAREST,
  146. 'LINEAR': cv2.INTER_LINEAR,
  147. 'CUBIC': cv2.INTER_CUBIC,
  148. 'AREA': cv2.INTER_AREA,
  149. 'LANCZOS4': cv2.INTER_LANCZOS4
  150. }
  151. def __init__(self, target_size, interp='LINEAR'):
  152. self.interp = interp
  153. assert interp in self.interp_dict, "interp should be one of {}".format(
  154. interp_dict.keys())
  155. if isinstance(target_size, list) or isinstance(target_size, tuple):
  156. if len(target_size) != 2:
  157. raise ValueError(
  158. 'when target is list or tuple, it should include 2 elements, but it is {}'
  159. .format(target_size))
  160. elif not isinstance(target_size, int):
  161. raise TypeError(
  162. "Type of target_size is invalid. Must be Integer or List or tuple, now is {}"
  163. .format(type(target_size)))
  164. self.target_size = target_size
  165. def __call__(self, im, im_info=None, label=None):
  166. """
  167. Args:
  168. im (np.ndarray): 图像np.ndarray数据。
  169. im_info (list): 存储图像reisze或padding前的shape信息,如
  170. [('resize', [200, 300]), ('padding', [400, 600])]表示
  171. 图像在过resize前shape为(200, 300), 过padding前shape为
  172. (400, 600)
  173. label (np.ndarray): 标注图像np.ndarray数据。
  174. Returns:
  175. tuple: 当label为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
  176. 当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
  177. 存储与图像相关信息的字典和标注图像np.ndarray数据。
  178. 其中,im_info跟新字段为:
  179. -shape_before_resize (tuple): 保存resize之前图像的形状(h, w)。
  180. Raises:
  181. ZeroDivisionError: im的短边为0。
  182. TypeError: im不是np.ndarray数据。
  183. ValueError: im不是3维nd.ndarray。
  184. """
  185. if im_info is None:
  186. im_info = OrderedDict()
  187. im_info.append(('resize', im.shape[:2]))
  188. if not isinstance(im, np.ndarray):
  189. raise TypeError("ResizeImage: image type is not np.ndarray.")
  190. if len(im.shape) != 3:
  191. raise ValueError('ResizeImage: image is not 3-dimensional.')
  192. im_shape = im.shape
  193. im_size_min = np.min(im_shape[0:2])
  194. im_size_max = np.max(im_shape[0:2])
  195. if float(im_size_min) == 0:
  196. raise ZeroDivisionError('ResizeImage: min size of image is 0')
  197. if isinstance(self.target_size, int):
  198. resize_w = self.target_size
  199. resize_h = self.target_size
  200. else:
  201. resize_w = self.target_size[0]
  202. resize_h = self.target_size[1]
  203. im_scale_x = float(resize_w) / float(im_shape[1])
  204. im_scale_y = float(resize_h) / float(im_shape[0])
  205. im = cv2.resize(
  206. im,
  207. None,
  208. None,
  209. fx=im_scale_x,
  210. fy=im_scale_y,
  211. interpolation=self.interp_dict[self.interp])
  212. if label is not None:
  213. label = cv2.resize(
  214. label,
  215. None,
  216. None,
  217. fx=im_scale_x,
  218. fy=im_scale_y,
  219. interpolation=self.interp_dict['NEAREST'])
  220. if label is None:
  221. return (im, im_info)
  222. else:
  223. return (im, im_info, label)
  224. class ResizeByLong:
  225. """对图像长边resize到固定值,短边按比例进行缩放。当存在标注图像时,则同步进行处理。
  226. Args:
  227. long_size (int): resize后图像的长边大小。
  228. """
  229. def __init__(self, long_size):
  230. self.long_size = long_size
  231. def __call__(self, im, im_info=None, label=None):
  232. """
  233. Args:
  234. im (np.ndarray): 图像np.ndarray数据。
  235. im_info (list): 存储图像reisze或padding前的shape信息,如
  236. [('resize', [200, 300]), ('padding', [400, 600])]表示
  237. 图像在过resize前shape为(200, 300), 过padding前shape为
  238. (400, 600)
  239. label (np.ndarray): 标注图像np.ndarray数据。
  240. Returns:
  241. tuple: 当label为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
  242. 当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
  243. 存储与图像相关信息的字典和标注图像np.ndarray数据。
  244. 其中,im_info新增字段为:
  245. -shape_before_resize (tuple): 保存resize之前图像的形状(h, w)。
  246. """
  247. if im_info is None:
  248. im_info = OrderedDict()
  249. im_info.append(('resize', im.shape[:2]))
  250. im = resize_long(im, self.long_size)
  251. if label is not None:
  252. label = resize_long(label, self.long_size, cv2.INTER_NEAREST)
  253. if label is None:
  254. return (im, im_info)
  255. else:
  256. return (im, im_info, label)
  257. class ResizeByShort:
  258. """根据图像的短边调整图像大小(resize)。
  259. 1. 获取图像的长边和短边长度。
  260. 2. 根据短边与short_size的比例,计算长边的目标长度,
  261. 此时高、宽的resize比例为short_size/原图短边长度。
  262. 3. 如果max_size>0,调整resize比例:
  263. 如果长边的目标长度>max_size,则高、宽的resize比例为max_size/原图长边长度。
  264. 4. 根据调整大小的比例对图像进行resize。
  265. Args:
  266. target_size (int): 短边目标长度。默认为800。
  267. max_size (int): 长边目标长度的最大限制。默认为1333。
  268. Raises:
  269. TypeError: 形参数据类型不满足需求。
  270. """
  271. def __init__(self, short_size=800, max_size=1333):
  272. self.max_size = int(max_size)
  273. if not isinstance(short_size, int):
  274. raise TypeError(
  275. "Type of short_size is invalid. Must be Integer, now is {}".
  276. format(type(short_size)))
  277. self.short_size = short_size
  278. if not (isinstance(self.max_size, int)):
  279. raise TypeError("max_size: input type is invalid.")
  280. def __call__(self, im, im_info=None, label=None):
  281. """
  282. Args:
  283. im (numnp.ndarraypy): 图像np.ndarray数据。
  284. im_info (list): 存储图像reisze或padding前的shape信息,如
  285. [('resize', [200, 300]), ('padding', [400, 600])]表示
  286. 图像在过resize前shape为(200, 300), 过padding前shape为
  287. (400, 600)
  288. label (np.ndarray): 标注图像np.ndarray数据。
  289. Returns:
  290. tuple: 当label为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
  291. 当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
  292. 存储与图像相关信息的字典和标注图像np.ndarray数据。
  293. 其中,im_info更新字段为:
  294. -shape_before_resize (tuple): 保存resize之前图像的形状(h, w)。
  295. Raises:
  296. TypeError: 形参数据类型不满足需求。
  297. ValueError: 数据长度不匹配。
  298. """
  299. if im_info is None:
  300. im_info = OrderedDict()
  301. if not isinstance(im, np.ndarray):
  302. raise TypeError("ResizeByShort: image type is not numpy.")
  303. if len(im.shape) != 3:
  304. raise ValueError('ResizeByShort: image is not 3-dimensional.')
  305. im_info.append(('resize', im.shape[:2]))
  306. im_short_size = min(im.shape[0], im.shape[1])
  307. im_long_size = max(im.shape[0], im.shape[1])
  308. scale = float(self.short_size) / im_short_size
  309. if self.max_size > 0 and np.round(
  310. scale * im_long_size) > self.max_size:
  311. scale = float(self.max_size) / float(im_long_size)
  312. resized_width = int(round(im.shape[1] * scale))
  313. resized_height = int(round(im.shape[0] * scale))
  314. im = cv2.resize(
  315. im, (resized_width, resized_height),
  316. interpolation=cv2.INTER_NEAREST)
  317. if label is not None:
  318. im = cv2.resize(
  319. label, (resized_width, resized_height),
  320. interpolation=cv2.INTER_NEAREST)
  321. if label is None:
  322. return (im, im_info)
  323. else:
  324. return (im, im_info, label)
  325. class ResizeRangeScaling:
  326. """对图像长边随机resize到指定范围内,短边按比例进行缩放。当存在标注图像时,则同步进行处理。
  327. Args:
  328. min_value (int): 图像长边resize后的最小值。默认值400。
  329. max_value (int): 图像长边resize后的最大值。默认值600。
  330. Raises:
  331. ValueError: min_value大于max_value
  332. """
  333. def __init__(self, min_value=400, max_value=600):
  334. if min_value > max_value:
  335. raise ValueError('min_value must be less than max_value, '
  336. 'but they are {} and {}.'.format(
  337. min_value, max_value))
  338. self.min_value = min_value
  339. self.max_value = max_value
  340. def __call__(self, im, im_info=None, label=None):
  341. """
  342. Args:
  343. im (np.ndarray): 图像np.ndarray数据。
  344. im_info (list): 存储图像reisze或padding前的shape信息,如
  345. [('resize', [200, 300]), ('padding', [400, 600])]表示
  346. 图像在过resize前shape为(200, 300), 过padding前shape为
  347. (400, 600)
  348. label (np.ndarray): 标注图像np.ndarray数据。
  349. Returns:
  350. tuple: 当label为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
  351. 当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
  352. 存储与图像相关信息的字典和标注图像np.ndarray数据。
  353. """
  354. if self.min_value == self.max_value:
  355. random_size = self.max_value
  356. else:
  357. random_size = int(
  358. np.random.uniform(self.min_value, self.max_value) + 0.5)
  359. im = resize_long(im, random_size, cv2.INTER_LINEAR)
  360. if label is not None:
  361. label = resize_long(label, random_size, cv2.INTER_NEAREST)
  362. if label is None:
  363. return (im, im_info)
  364. else:
  365. return (im, im_info, label)
  366. class ResizeStepScaling:
  367. """对图像按照某一个比例resize,这个比例以scale_step_size为步长
  368. 在[min_scale_factor, max_scale_factor]随机变动。当存在标注图像时,则同步进行处理。
  369. Args:
  370. min_scale_factor(float), resize最小尺度。默认值0.75。
  371. max_scale_factor (float), resize最大尺度。默认值1.25。
  372. scale_step_size (float), resize尺度范围间隔。默认值0.25。
  373. Raises:
  374. ValueError: min_scale_factor大于max_scale_factor
  375. """
  376. def __init__(self,
  377. min_scale_factor=0.75,
  378. max_scale_factor=1.25,
  379. scale_step_size=0.25):
  380. if min_scale_factor > max_scale_factor:
  381. raise ValueError(
  382. 'min_scale_factor must be less than max_scale_factor, '
  383. 'but they are {} and {}.'.format(min_scale_factor,
  384. max_scale_factor))
  385. self.min_scale_factor = min_scale_factor
  386. self.max_scale_factor = max_scale_factor
  387. self.scale_step_size = scale_step_size
  388. def __call__(self, im, im_info=None, label=None):
  389. """
  390. Args:
  391. im (np.ndarray): 图像np.ndarray数据。
  392. im_info (list): 存储图像reisze或padding前的shape信息,如
  393. [('resize', [200, 300]), ('padding', [400, 600])]表示
  394. 图像在过resize前shape为(200, 300), 过padding前shape为
  395. (400, 600)
  396. label (np.ndarray): 标注图像np.ndarray数据。
  397. Returns:
  398. tuple: 当label为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
  399. 当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
  400. 存储与图像相关信息的字典和标注图像np.ndarray数据。
  401. """
  402. if self.min_scale_factor == self.max_scale_factor:
  403. scale_factor = self.min_scale_factor
  404. elif self.scale_step_size == 0:
  405. scale_factor = np.random.uniform(self.min_scale_factor,
  406. self.max_scale_factor)
  407. else:
  408. num_steps = int((self.max_scale_factor - self.min_scale_factor) /
  409. self.scale_step_size + 1)
  410. scale_factors = np.linspace(self.min_scale_factor,
  411. self.max_scale_factor,
  412. num_steps).tolist()
  413. np.random.shuffle(scale_factors)
  414. scale_factor = scale_factors[0]
  415. im = cv2.resize(
  416. im, (0, 0),
  417. fx=scale_factor,
  418. fy=scale_factor,
  419. interpolation=cv2.INTER_LINEAR)
  420. if label is not None:
  421. label = cv2.resize(
  422. label, (0, 0),
  423. fx=scale_factor,
  424. fy=scale_factor,
  425. interpolation=cv2.INTER_NEAREST)
  426. if label is None:
  427. return (im, im_info)
  428. else:
  429. return (im, im_info, label)
  430. class Normalize:
  431. """对图像进行标准化。
  432. 1.尺度缩放到 [0,1]。
  433. 2.对图像进行减均值除以标准差操作。
  434. Args:
  435. mean (list): 图像数据集的均值。默认值[0.5, 0.5, 0.5]。
  436. std (list): 图像数据集的标准差。默认值[0.5, 0.5, 0.5]。
  437. Raises:
  438. ValueError: mean或std不是list对象。std包含0。
  439. """
  440. def __init__(self, mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]):
  441. self.mean = mean
  442. self.std = std
  443. if not (isinstance(self.mean, list) and isinstance(self.std, list)):
  444. raise ValueError("{}: input type is invalid.".format(self))
  445. from functools import reduce
  446. if reduce(lambda x, y: x * y, self.std) == 0:
  447. raise ValueError('{}: std is invalid!'.format(self))
  448. def __call__(self, im, im_info=None, label=None):
  449. """
  450. Args:
  451. im (np.ndarray): 图像np.ndarray数据。
  452. im_info (list): 存储图像reisze或padding前的shape信息,如
  453. [('resize', [200, 300]), ('padding', [400, 600])]表示
  454. 图像在过resize前shape为(200, 300), 过padding前shape为
  455. (400, 600)
  456. label (np.ndarray): 标注图像np.ndarray数据。
  457. Returns:
  458. tuple: 当label为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
  459. 当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
  460. 存储与图像相关信息的字典和标注图像np.ndarray数据。
  461. """
  462. mean = np.array(self.mean)[np.newaxis, np.newaxis, :]
  463. std = np.array(self.std)[np.newaxis, np.newaxis, :]
  464. im = normalize(im, mean, std)
  465. if label is None:
  466. return (im, im_info)
  467. else:
  468. return (im, im_info, label)
  469. class Padding:
  470. """对图像或标注图像进行padding,padding方向为右和下。
  471. 根据提供的值对图像或标注图像进行padding操作。
  472. Args:
  473. target_size (int|list|tuple): padding后图像的大小。
  474. im_padding_value (list): 图像padding的值。默认为[127.5, 127.5, 127.5]。
  475. label_padding_value (int): 标注图像padding的值。默认值为255。
  476. Raises:
  477. TypeError: target_size不是int|list|tuple。
  478. ValueError: target_size为list|tuple时元素个数不等于2。
  479. """
  480. def __init__(self,
  481. target_size,
  482. im_padding_value=[127.5, 127.5, 127.5],
  483. label_padding_value=255):
  484. if isinstance(target_size, list) or isinstance(target_size, tuple):
  485. if len(target_size) != 2:
  486. raise ValueError(
  487. 'when target is list or tuple, it should include 2 elements, but it is {}'
  488. .format(target_size))
  489. elif not isinstance(target_size, int):
  490. raise TypeError(
  491. "Type of target_size is invalid. Must be Integer or List or tuple, now is {}"
  492. .format(type(target_size)))
  493. self.target_size = target_size
  494. self.im_padding_value = im_padding_value
  495. self.label_padding_value = label_padding_value
  496. def __call__(self, im, im_info=None, label=None):
  497. """
  498. Args:
  499. im (np.ndarray): 图像np.ndarray数据。
  500. im_info (list): 存储图像reisze或padding前的shape信息,如
  501. [('resize', [200, 300]), ('padding', [400, 600])]表示
  502. 图像在过resize前shape为(200, 300), 过padding前shape为
  503. (400, 600)
  504. label (np.ndarray): 标注图像np.ndarray数据。
  505. Returns:
  506. tuple: 当label为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
  507. 当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
  508. 存储与图像相关信息的字典和标注图像np.ndarray数据。
  509. 其中,im_info新增字段为:
  510. -shape_before_padding (tuple): 保存padding之前图像的形状(h, w)。
  511. Raises:
  512. ValueError: 输入图像im或label的形状大于目标值
  513. """
  514. if im_info is None:
  515. im_info = OrderedDict()
  516. im_info.append(('padding', im.shape[:2]))
  517. im_height, im_width = im.shape[0], im.shape[1]
  518. if isinstance(self.target_size, int):
  519. target_height = self.target_size
  520. target_width = self.target_size
  521. else:
  522. target_height = self.target_size[1]
  523. target_width = self.target_size[0]
  524. pad_height = target_height - im_height
  525. pad_width = target_width - im_width
  526. if pad_height < 0 or pad_width < 0:
  527. raise ValueError(
  528. 'the size of image should be less than target_size, but the size of image ({}, {}), is larger than target_size ({}, {})'
  529. .format(im_width, im_height, target_width, target_height))
  530. else:
  531. im = cv2.copyMakeBorder(
  532. im,
  533. 0,
  534. pad_height,
  535. 0,
  536. pad_width,
  537. cv2.BORDER_CONSTANT,
  538. value=self.im_padding_value)
  539. if label is not None:
  540. label = cv2.copyMakeBorder(
  541. label,
  542. 0,
  543. pad_height,
  544. 0,
  545. pad_width,
  546. cv2.BORDER_CONSTANT,
  547. value=self.label_padding_value)
  548. if label is None:
  549. return (im, im_info)
  550. else:
  551. return (im, im_info, label)
  552. class RandomPaddingCrop:
  553. """对图像和标注图进行随机裁剪,当所需要的裁剪尺寸大于原图时,则进行padding操作。
  554. Args:
  555. crop_size (int|list|tuple): 裁剪图像大小。默认为512。
  556. im_padding_value (list): 图像padding的值。默认为[127.5, 127.5, 127.5]。
  557. label_padding_value (int): 标注图像padding的值。默认值为255。
  558. Raises:
  559. TypeError: crop_size不是int/list/tuple。
  560. ValueError: target_size为list/tuple时元素个数不等于2。
  561. """
  562. def __init__(self,
  563. crop_size=512,
  564. im_padding_value=[127.5, 127.5, 127.5],
  565. label_padding_value=255):
  566. if isinstance(crop_size, list) or isinstance(crop_size, tuple):
  567. if len(crop_size) != 2:
  568. raise ValueError(
  569. 'when crop_size is list or tuple, it should include 2 elements, but it is {}'
  570. .format(crop_size))
  571. elif not isinstance(crop_size, int):
  572. raise TypeError(
  573. "Type of crop_size is invalid. Must be Integer or List or tuple, now is {}"
  574. .format(type(crop_size)))
  575. self.crop_size = crop_size
  576. self.im_padding_value = im_padding_value
  577. self.label_padding_value = label_padding_value
  578. def __call__(self, im, im_info=None, label=None):
  579. """
  580. Args:
  581. im (np.ndarray): 图像np.ndarray数据。
  582. im_info (list): 存储图像reisze或padding前的shape信息,如
  583. [('resize', [200, 300]), ('padding', [400, 600])]表示
  584. 图像在过resize前shape为(200, 300), 过padding前shape为
  585. (400, 600)
  586. label (np.ndarray): 标注图像np.ndarray数据。
  587. Returns:
  588. tuple: 当label为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
  589. 当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
  590. 存储与图像相关信息的字典和标注图像np.ndarray数据。
  591. """
  592. if isinstance(self.crop_size, int):
  593. crop_width = self.crop_size
  594. crop_height = self.crop_size
  595. else:
  596. crop_width = self.crop_size[0]
  597. crop_height = self.crop_size[1]
  598. img_height = im.shape[0]
  599. img_width = im.shape[1]
  600. if img_height == crop_height and img_width == crop_width:
  601. if label is None:
  602. return (im, im_info)
  603. else:
  604. return (im, im_info, label)
  605. else:
  606. pad_height = max(crop_height - img_height, 0)
  607. pad_width = max(crop_width - img_width, 0)
  608. if (pad_height > 0 or pad_width > 0):
  609. im = cv2.copyMakeBorder(
  610. im,
  611. 0,
  612. pad_height,
  613. 0,
  614. pad_width,
  615. cv2.BORDER_CONSTANT,
  616. value=self.im_padding_value)
  617. if label is not None:
  618. label = cv2.copyMakeBorder(
  619. label,
  620. 0,
  621. pad_height,
  622. 0,
  623. pad_width,
  624. cv2.BORDER_CONSTANT,
  625. value=self.label_padding_value)
  626. img_height = im.shape[0]
  627. img_width = im.shape[1]
  628. if crop_height > 0 and crop_width > 0:
  629. h_off = np.random.randint(img_height - crop_height + 1)
  630. w_off = np.random.randint(img_width - crop_width + 1)
  631. im = im[h_off:(crop_height + h_off), w_off:(
  632. w_off + crop_width), :]
  633. if label is not None:
  634. label = label[h_off:(crop_height + h_off), w_off:(
  635. w_off + crop_width)]
  636. if label is None:
  637. return (im, im_info)
  638. else:
  639. return (im, im_info, label)
  640. class RandomBlur:
  641. """以一定的概率对图像进行高斯模糊。
  642. Args:
  643. prob (float): 图像模糊概率。默认为0.1。
  644. """
  645. def __init__(self, prob=0.1):
  646. self.prob = prob
  647. def __call__(self, im, im_info=None, label=None):
  648. """
  649. Args:
  650. im (np.ndarray): 图像np.ndarray数据。
  651. im_info (list): 存储图像reisze或padding前的shape信息,如
  652. [('resize', [200, 300]), ('padding', [400, 600])]表示
  653. 图像在过resize前shape为(200, 300), 过padding前shape为
  654. (400, 600)
  655. label (np.ndarray): 标注图像np.ndarray数据。
  656. Returns:
  657. tuple: 当label为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
  658. 当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
  659. 存储与图像相关信息的字典和标注图像np.ndarray数据。
  660. """
  661. if self.prob <= 0:
  662. n = 0
  663. elif self.prob >= 1:
  664. n = 1
  665. else:
  666. n = int(1.0 / self.prob)
  667. if n > 0:
  668. if np.random.randint(0, n) == 0:
  669. radius = np.random.randint(3, 10)
  670. if radius % 2 != 1:
  671. radius = radius + 1
  672. if radius > 9:
  673. radius = 9
  674. im = cv2.GaussianBlur(im, (radius, radius), 0, 0)
  675. if label is None:
  676. return (im, im_info)
  677. else:
  678. return (im, im_info, label)
  679. class RandomRotate:
  680. """对图像进行随机旋转, 模型训练时的数据增强操作。
  681. 在旋转区间[-rotate_range, rotate_range]内,对图像进行随机旋转,当存在标注图像时,同步进行,
  682. 并对旋转后的图像和标注图像进行相应的padding。
  683. Args:
  684. rotate_range (float): 最大旋转角度。默认为15度。
  685. im_padding_value (list): 图像padding的值。默认为[127.5, 127.5, 127.5]。
  686. label_padding_value (int): 标注图像padding的值。默认为255。
  687. """
  688. def __init__(self,
  689. rotate_range=15,
  690. im_padding_value=[127.5, 127.5, 127.5],
  691. label_padding_value=255):
  692. self.rotate_range = rotate_range
  693. self.im_padding_value = im_padding_value
  694. self.label_padding_value = label_padding_value
  695. def __call__(self, im, im_info=None, label=None):
  696. """
  697. Args:
  698. im (np.ndarray): 图像np.ndarray数据。
  699. im_info (list): 存储图像reisze或padding前的shape信息,如
  700. [('resize', [200, 300]), ('padding', [400, 600])]表示
  701. 图像在过resize前shape为(200, 300), 过padding前shape为
  702. (400, 600)
  703. label (np.ndarray): 标注图像np.ndarray数据。
  704. Returns:
  705. tuple: 当label为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
  706. 当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
  707. 存储与图像相关信息的字典和标注图像np.ndarray数据。
  708. """
  709. if self.rotate_range > 0:
  710. (h, w) = im.shape[:2]
  711. do_rotation = np.random.uniform(-self.rotate_range,
  712. self.rotate_range)
  713. pc = (w // 2, h // 2)
  714. r = cv2.getRotationMatrix2D(pc, do_rotation, 1.0)
  715. cos = np.abs(r[0, 0])
  716. sin = np.abs(r[0, 1])
  717. nw = int((h * sin) + (w * cos))
  718. nh = int((h * cos) + (w * sin))
  719. (cx, cy) = pc
  720. r[0, 2] += (nw / 2) - cx
  721. r[1, 2] += (nh / 2) - cy
  722. dsize = (nw, nh)
  723. im = cv2.warpAffine(
  724. im,
  725. r,
  726. dsize=dsize,
  727. flags=cv2.INTER_LINEAR,
  728. borderMode=cv2.BORDER_CONSTANT,
  729. borderValue=self.im_padding_value)
  730. label = cv2.warpAffine(
  731. label,
  732. r,
  733. dsize=dsize,
  734. flags=cv2.INTER_NEAREST,
  735. borderMode=cv2.BORDER_CONSTANT,
  736. borderValue=self.label_padding_value)
  737. if label is None:
  738. return (im, im_info)
  739. else:
  740. return (im, im_info, label)
  741. class RandomScaleAspect:
  742. """裁剪并resize回原始尺寸的图像和标注图像。
  743. 按照一定的面积比和宽高比对图像进行裁剪,并reszie回原始图像的图像,当存在标注图时,同步进行。
  744. Args:
  745. min_scale (float):裁取图像占原始图像的面积比,取值[0,1],为0时则返回原图。默认为0.5。
  746. aspect_ratio (float): 裁取图像的宽高比范围,非负值,为0时返回原图。默认为0.33。
  747. """
  748. def __init__(self, min_scale=0.5, aspect_ratio=0.33):
  749. self.min_scale = min_scale
  750. self.aspect_ratio = aspect_ratio
  751. def __call__(self, im, im_info=None, label=None):
  752. """
  753. Args:
  754. im (np.ndarray): 图像np.ndarray数据。
  755. im_info (list): 存储图像reisze或padding前的shape信息,如
  756. [('resize', [200, 300]), ('padding', [400, 600])]表示
  757. 图像在过resize前shape为(200, 300), 过padding前shape为
  758. (400, 600)
  759. label (np.ndarray): 标注图像np.ndarray数据。
  760. Returns:
  761. tuple: 当label为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
  762. 当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
  763. 存储与图像相关信息的字典和标注图像np.ndarray数据。
  764. """
  765. if self.min_scale != 0 and self.aspect_ratio != 0:
  766. img_height = im.shape[0]
  767. img_width = im.shape[1]
  768. for i in range(0, 10):
  769. area = img_height * img_width
  770. target_area = area * np.random.uniform(self.min_scale, 1.0)
  771. aspectRatio = np.random.uniform(self.aspect_ratio,
  772. 1.0 / self.aspect_ratio)
  773. dw = int(np.sqrt(target_area * 1.0 * aspectRatio))
  774. dh = int(np.sqrt(target_area * 1.0 / aspectRatio))
  775. if (np.random.randint(10) < 5):
  776. tmp = dw
  777. dw = dh
  778. dh = tmp
  779. if (dh < img_height and dw < img_width):
  780. h1 = np.random.randint(0, img_height - dh)
  781. w1 = np.random.randint(0, img_width - dw)
  782. im = im[h1:(h1 + dh), w1:(w1 + dw), :]
  783. label = label[h1:(h1 + dh), w1:(w1 + dw)]
  784. im = cv2.resize(
  785. im, (img_width, img_height),
  786. interpolation=cv2.INTER_LINEAR)
  787. label = cv2.resize(
  788. label, (img_width, img_height),
  789. interpolation=cv2.INTER_NEAREST)
  790. break
  791. if label is None:
  792. return (im, im_info)
  793. else:
  794. return (im, im_info, label)
  795. class RandomDistort:
  796. """对图像进行随机失真。
  797. 1. 对变换的操作顺序进行随机化操作。
  798. 2. 按照1中的顺序以一定的概率对图像进行随机像素内容变换。
  799. Args:
  800. brightness_range (float): 明亮度因子的范围。默认为0.5。
  801. brightness_prob (float): 随机调整明亮度的概率。默认为0.5。
  802. contrast_range (float): 对比度因子的范围。默认为0.5。
  803. contrast_prob (float): 随机调整对比度的概率。默认为0.5。
  804. saturation_range (float): 饱和度因子的范围。默认为0.5。
  805. saturation_prob (float): 随机调整饱和度的概率。默认为0.5。
  806. hue_range (int): 色调因子的范围。默认为18。
  807. hue_prob (float): 随机调整色调的概率。默认为0.5。
  808. """
  809. def __init__(self,
  810. brightness_range=0.5,
  811. brightness_prob=0.5,
  812. contrast_range=0.5,
  813. contrast_prob=0.5,
  814. saturation_range=0.5,
  815. saturation_prob=0.5,
  816. hue_range=18,
  817. hue_prob=0.5):
  818. self.brightness_range = brightness_range
  819. self.brightness_prob = brightness_prob
  820. self.contrast_range = contrast_range
  821. self.contrast_prob = contrast_prob
  822. self.saturation_range = saturation_range
  823. self.saturation_prob = saturation_prob
  824. self.hue_range = hue_range
  825. self.hue_prob = hue_prob
  826. def __call__(self, im, im_info=None, label=None):
  827. """
  828. Args:
  829. im (np.ndarray): 图像np.ndarray数据。
  830. im_info (list): 存储图像reisze或padding前的shape信息,如
  831. [('resize', [200, 300]), ('padding', [400, 600])]表示
  832. 图像在过resize前shape为(200, 300), 过padding前shape为
  833. (400, 600)
  834. label (np.ndarray): 标注图像np.ndarray数据。
  835. Returns:
  836. tuple: 当label为空时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
  837. 当label不为空时,返回的tuple为(im, im_info, label),分别对应图像np.ndarray数据、
  838. 存储与图像相关信息的字典和标注图像np.ndarray数据。
  839. """
  840. brightness_lower = 1 - self.brightness_range
  841. brightness_upper = 1 + self.brightness_range
  842. contrast_lower = 1 - self.contrast_range
  843. contrast_upper = 1 + self.contrast_range
  844. saturation_lower = 1 - self.saturation_range
  845. saturation_upper = 1 + self.saturation_range
  846. hue_lower = -self.hue_range
  847. hue_upper = self.hue_range
  848. ops = [brightness, contrast, saturation, hue]
  849. random.shuffle(ops)
  850. params_dict = {
  851. 'brightness': {
  852. 'brightness_lower': brightness_lower,
  853. 'brightness_upper': brightness_upper
  854. },
  855. 'contrast': {
  856. 'contrast_lower': contrast_lower,
  857. 'contrast_upper': contrast_upper
  858. },
  859. 'saturation': {
  860. 'saturation_lower': saturation_lower,
  861. 'saturation_upper': saturation_upper
  862. },
  863. 'hue': {
  864. 'hue_lower': hue_lower,
  865. 'hue_upper': hue_upper
  866. }
  867. }
  868. prob_dict = {
  869. 'brightness': self.brightness_prob,
  870. 'contrast': self.contrast_prob,
  871. 'saturation': self.saturation_prob,
  872. 'hue': self.hue_prob
  873. }
  874. for id in range(4):
  875. params = params_dict[ops[id].__name__]
  876. prob = prob_dict[ops[id].__name__]
  877. params['im'] = im
  878. if np.random.uniform(0, 1) < prob:
  879. im = ops[id](**params)
  880. if label is None:
  881. return (im, im_info)
  882. else:
  883. return (im, im_info, label)
  884. class ArrangeSegmenter:
  885. """获取训练/验证/预测所需的信息。
  886. Args:
  887. mode (str): 指定数据用于何种用途,取值范围为['train', 'eval', 'test', 'quant']。
  888. Raises:
  889. ValueError: mode的取值不在['train', 'eval', 'test', 'quant']之内
  890. """
  891. def __init__(self, mode):
  892. if mode not in ['train', 'eval', 'test', 'quant']:
  893. raise ValueError(
  894. "mode should be defined as one of ['train', 'eval', 'test', 'quant']!"
  895. )
  896. self.mode = mode
  897. def __call__(self, im, im_info, label=None):
  898. """
  899. Args:
  900. im (np.ndarray): 图像np.ndarray数据。
  901. im_info (list): 存储图像reisze或padding前的shape信息,如
  902. [('resize', [200, 300]), ('padding', [400, 600])]表示
  903. 图像在过resize前shape为(200, 300), 过padding前shape为
  904. (400, 600)
  905. label (np.ndarray): 标注图像np.ndarray数据。
  906. Returns:
  907. tuple: 当mode为'train'或'eval'时,返回的tuple为(im, label),分别对应图像np.ndarray数据、存储与图像相关信息的字典;
  908. 当mode为'test'时,返回的tuple为(im, im_info),分别对应图像np.ndarray数据、存储与图像相关信息的字典;当mode为
  909. 'quant'时,返回的tuple为(im,),为图像np.ndarray数据。
  910. """
  911. im = permute(im, False)
  912. if self.mode == 'train' or self.mode == 'eval':
  913. label = label[np.newaxis, :, :]
  914. return (im, label)
  915. elif self.mode == 'test':
  916. return (im, im_info)
  917. else:
  918. return (im, )