seg_transforms.py 36 KB

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