colorspace.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. # Copyright (c) OpenMMLab. All rights reserved.
  2. import math
  3. from typing import Optional
  4. import mmcv
  5. import numpy as np
  6. from mmcv.transforms import BaseTransform
  7. from mmcv.transforms.utils import cache_randomness
  8. from mmdet.registry import TRANSFORMS
  9. from .augment_wrappers import _MAX_LEVEL, level_to_mag
  10. @TRANSFORMS.register_module()
  11. class ColorTransform(BaseTransform):
  12. """Base class for color transformations. All color transformations need to
  13. inherit from this base class. ``ColorTransform`` unifies the class
  14. attributes and class functions of color transformations (Color, Brightness,
  15. Contrast, Sharpness, Solarize, SolarizeAdd, Equalize, AutoContrast, Invert,
  16. and Posterize), and only distort color channels, without impacting the
  17. locations of the instances.
  18. Required Keys:
  19. - img
  20. Modified Keys:
  21. - img
  22. Args:
  23. prob (float): The probability for performing the geometric
  24. transformation and should be in range [0, 1]. Defaults to 1.0.
  25. level (int, optional): The level should be in range [0, _MAX_LEVEL].
  26. If level is None, it will generate from [0, _MAX_LEVEL] randomly.
  27. Defaults to None.
  28. min_mag (float): The minimum magnitude for color transformation.
  29. Defaults to 0.1.
  30. max_mag (float): The maximum magnitude for color transformation.
  31. Defaults to 1.9.
  32. """
  33. def __init__(self,
  34. prob: float = 1.0,
  35. level: Optional[int] = None,
  36. min_mag: float = 0.1,
  37. max_mag: float = 1.9) -> None:
  38. assert 0 <= prob <= 1.0, f'The probability of the transformation ' \
  39. f'should be in range [0,1], got {prob}.'
  40. assert level is None or isinstance(level, int), \
  41. f'The level should be None or type int, got {type(level)}.'
  42. assert level is None or 0 <= level <= _MAX_LEVEL, \
  43. f'The level should be in range [0,{_MAX_LEVEL}], got {level}.'
  44. assert isinstance(min_mag, float), \
  45. f'min_mag should be type float, got {type(min_mag)}.'
  46. assert isinstance(max_mag, float), \
  47. f'max_mag should be type float, got {type(max_mag)}.'
  48. assert min_mag <= max_mag, \
  49. f'min_mag should smaller than max_mag, ' \
  50. f'got min_mag={min_mag} and max_mag={max_mag}'
  51. self.prob = prob
  52. self.level = level
  53. self.min_mag = min_mag
  54. self.max_mag = max_mag
  55. def _transform_img(self, results: dict, mag: float) -> None:
  56. """Transform the image."""
  57. pass
  58. @cache_randomness
  59. def _random_disable(self):
  60. """Randomly disable the transform."""
  61. return np.random.rand() > self.prob
  62. @cache_randomness
  63. def _get_mag(self):
  64. """Get the magnitude of the transform."""
  65. return level_to_mag(self.level, self.min_mag, self.max_mag)
  66. def transform(self, results: dict) -> dict:
  67. """Transform function for images.
  68. Args:
  69. results (dict): Result dict from loading pipeline.
  70. Returns:
  71. dict: Transformed results.
  72. """
  73. if self._random_disable():
  74. return results
  75. mag = self._get_mag()
  76. self._transform_img(results, mag)
  77. return results
  78. def __repr__(self) -> str:
  79. repr_str = self.__class__.__name__
  80. repr_str += f'(prob={self.prob}, '
  81. repr_str += f'level={self.level}, '
  82. repr_str += f'min_mag={self.min_mag}, '
  83. repr_str += f'max_mag={self.max_mag})'
  84. return repr_str
  85. @TRANSFORMS.register_module()
  86. class Color(ColorTransform):
  87. """Adjust the color balance of the image, in a manner similar to the
  88. controls on a colour TV set. A magnitude=0 gives a black & white image,
  89. whereas magnitude=1 gives the original image. The bboxes, masks and
  90. segmentations are not modified.
  91. Required Keys:
  92. - img
  93. Modified Keys:
  94. - img
  95. Args:
  96. prob (float): The probability for performing Color transformation.
  97. Defaults to 1.0.
  98. level (int, optional): Should be in range [0,_MAX_LEVEL].
  99. If level is None, it will generate from [0, _MAX_LEVEL] randomly.
  100. Defaults to None.
  101. min_mag (float): The minimum magnitude for Color transformation.
  102. Defaults to 0.1.
  103. max_mag (float): The maximum magnitude for Color transformation.
  104. Defaults to 1.9.
  105. """
  106. def __init__(self,
  107. prob: float = 1.0,
  108. level: Optional[int] = None,
  109. min_mag: float = 0.1,
  110. max_mag: float = 1.9) -> None:
  111. assert 0. <= min_mag <= 2.0, \
  112. f'min_mag for Color should be in range [0,2], got {min_mag}.'
  113. assert 0. <= max_mag <= 2.0, \
  114. f'max_mag for Color should be in range [0,2], got {max_mag}.'
  115. super().__init__(
  116. prob=prob, level=level, min_mag=min_mag, max_mag=max_mag)
  117. def _transform_img(self, results: dict, mag: float) -> None:
  118. """Apply Color transformation to image."""
  119. # NOTE defaultly the image should be BGR format
  120. img = results['img']
  121. results['img'] = mmcv.adjust_color(img, mag).astype(img.dtype)
  122. @TRANSFORMS.register_module()
  123. class Brightness(ColorTransform):
  124. """Adjust the brightness of the image. A magnitude=0 gives a black image,
  125. whereas magnitude=1 gives the original image. The bboxes, masks and
  126. segmentations are not modified.
  127. Required Keys:
  128. - img
  129. Modified Keys:
  130. - img
  131. Args:
  132. prob (float): The probability for performing Brightness transformation.
  133. Defaults to 1.0.
  134. level (int, optional): Should be in range [0,_MAX_LEVEL].
  135. If level is None, it will generate from [0, _MAX_LEVEL] randomly.
  136. Defaults to None.
  137. min_mag (float): The minimum magnitude for Brightness transformation.
  138. Defaults to 0.1.
  139. max_mag (float): The maximum magnitude for Brightness transformation.
  140. Defaults to 1.9.
  141. """
  142. def __init__(self,
  143. prob: float = 1.0,
  144. level: Optional[int] = None,
  145. min_mag: float = 0.1,
  146. max_mag: float = 1.9) -> None:
  147. assert 0. <= min_mag <= 2.0, \
  148. f'min_mag for Brightness should be in range [0,2], got {min_mag}.'
  149. assert 0. <= max_mag <= 2.0, \
  150. f'max_mag for Brightness should be in range [0,2], got {max_mag}.'
  151. super().__init__(
  152. prob=prob, level=level, min_mag=min_mag, max_mag=max_mag)
  153. def _transform_img(self, results: dict, mag: float) -> None:
  154. """Adjust the brightness of image."""
  155. img = results['img']
  156. results['img'] = mmcv.adjust_brightness(img, mag).astype(img.dtype)
  157. @TRANSFORMS.register_module()
  158. class Contrast(ColorTransform):
  159. """Control the contrast of the image. A magnitude=0 gives a gray image,
  160. whereas magnitude=1 gives the original imageThe bboxes, masks and
  161. segmentations are not modified.
  162. Required Keys:
  163. - img
  164. Modified Keys:
  165. - img
  166. Args:
  167. prob (float): The probability for performing Contrast transformation.
  168. Defaults to 1.0.
  169. level (int, optional): Should be in range [0,_MAX_LEVEL].
  170. If level is None, it will generate from [0, _MAX_LEVEL] randomly.
  171. Defaults to None.
  172. min_mag (float): The minimum magnitude for Contrast transformation.
  173. Defaults to 0.1.
  174. max_mag (float): The maximum magnitude for Contrast transformation.
  175. Defaults to 1.9.
  176. """
  177. def __init__(self,
  178. prob: float = 1.0,
  179. level: Optional[int] = None,
  180. min_mag: float = 0.1,
  181. max_mag: float = 1.9) -> None:
  182. assert 0. <= min_mag <= 2.0, \
  183. f'min_mag for Contrast should be in range [0,2], got {min_mag}.'
  184. assert 0. <= max_mag <= 2.0, \
  185. f'max_mag for Contrast should be in range [0,2], got {max_mag}.'
  186. super().__init__(
  187. prob=prob, level=level, min_mag=min_mag, max_mag=max_mag)
  188. def _transform_img(self, results: dict, mag: float) -> None:
  189. """Adjust the image contrast."""
  190. img = results['img']
  191. results['img'] = mmcv.adjust_contrast(img, mag).astype(img.dtype)
  192. @TRANSFORMS.register_module()
  193. class Sharpness(ColorTransform):
  194. """Adjust images sharpness. A positive magnitude would enhance the
  195. sharpness and a negative magnitude would make the image blurry. A
  196. magnitude=0 gives the origin img.
  197. Required Keys:
  198. - img
  199. Modified Keys:
  200. - img
  201. Args:
  202. prob (float): The probability for performing Sharpness transformation.
  203. Defaults to 1.0.
  204. level (int, optional): Should be in range [0,_MAX_LEVEL].
  205. If level is None, it will generate from [0, _MAX_LEVEL] randomly.
  206. Defaults to None.
  207. min_mag (float): The minimum magnitude for Sharpness transformation.
  208. Defaults to 0.1.
  209. max_mag (float): The maximum magnitude for Sharpness transformation.
  210. Defaults to 1.9.
  211. """
  212. def __init__(self,
  213. prob: float = 1.0,
  214. level: Optional[int] = None,
  215. min_mag: float = 0.1,
  216. max_mag: float = 1.9) -> None:
  217. assert 0. <= min_mag <= 2.0, \
  218. f'min_mag for Sharpness should be in range [0,2], got {min_mag}.'
  219. assert 0. <= max_mag <= 2.0, \
  220. f'max_mag for Sharpness should be in range [0,2], got {max_mag}.'
  221. super().__init__(
  222. prob=prob, level=level, min_mag=min_mag, max_mag=max_mag)
  223. def _transform_img(self, results: dict, mag: float) -> None:
  224. """Adjust the image sharpness."""
  225. img = results['img']
  226. results['img'] = mmcv.adjust_sharpness(img, mag).astype(img.dtype)
  227. @TRANSFORMS.register_module()
  228. class Solarize(ColorTransform):
  229. """Solarize images (Invert all pixels above a threshold value of
  230. magnitude.).
  231. Required Keys:
  232. - img
  233. Modified Keys:
  234. - img
  235. Args:
  236. prob (float): The probability for performing Solarize transformation.
  237. Defaults to 1.0.
  238. level (int, optional): Should be in range [0,_MAX_LEVEL].
  239. If level is None, it will generate from [0, _MAX_LEVEL] randomly.
  240. Defaults to None.
  241. min_mag (float): The minimum magnitude for Solarize transformation.
  242. Defaults to 0.0.
  243. max_mag (float): The maximum magnitude for Solarize transformation.
  244. Defaults to 256.0.
  245. """
  246. def __init__(self,
  247. prob: float = 1.0,
  248. level: Optional[int] = None,
  249. min_mag: float = 0.0,
  250. max_mag: float = 256.0) -> None:
  251. assert 0. <= min_mag <= 256.0, f'min_mag for Solarize should be ' \
  252. f'in range [0, 256], got {min_mag}.'
  253. assert 0. <= max_mag <= 256.0, f'max_mag for Solarize should be ' \
  254. f'in range [0, 256], got {max_mag}.'
  255. super().__init__(
  256. prob=prob, level=level, min_mag=min_mag, max_mag=max_mag)
  257. def _transform_img(self, results: dict, mag: float) -> None:
  258. """Invert all pixel values above magnitude."""
  259. img = results['img']
  260. results['img'] = mmcv.solarize(img, mag).astype(img.dtype)
  261. @TRANSFORMS.register_module()
  262. class SolarizeAdd(ColorTransform):
  263. """SolarizeAdd images. For each pixel in the image that is less than 128,
  264. add an additional amount to it decided by the magnitude.
  265. Required Keys:
  266. - img
  267. Modified Keys:
  268. - img
  269. Args:
  270. prob (float): The probability for performing SolarizeAdd
  271. transformation. Defaults to 1.0.
  272. level (int, optional): Should be in range [0,_MAX_LEVEL].
  273. If level is None, it will generate from [0, _MAX_LEVEL] randomly.
  274. Defaults to None.
  275. min_mag (float): The minimum magnitude for SolarizeAdd transformation.
  276. Defaults to 0.0.
  277. max_mag (float): The maximum magnitude for SolarizeAdd transformation.
  278. Defaults to 110.0.
  279. """
  280. def __init__(self,
  281. prob: float = 1.0,
  282. level: Optional[int] = None,
  283. min_mag: float = 0.0,
  284. max_mag: float = 110.0) -> None:
  285. assert 0. <= min_mag <= 110.0, f'min_mag for SolarizeAdd should be ' \
  286. f'in range [0, 110], got {min_mag}.'
  287. assert 0. <= max_mag <= 110.0, f'max_mag for SolarizeAdd should be ' \
  288. f'in range [0, 110], got {max_mag}.'
  289. super().__init__(
  290. prob=prob, level=level, min_mag=min_mag, max_mag=max_mag)
  291. def _transform_img(self, results: dict, mag: float) -> None:
  292. """SolarizeAdd the image."""
  293. img = results['img']
  294. img_solarized = np.where(img < 128, np.minimum(img + mag, 255), img)
  295. results['img'] = img_solarized.astype(img.dtype)
  296. @TRANSFORMS.register_module()
  297. class Posterize(ColorTransform):
  298. """Posterize images (reduce the number of bits for each color channel).
  299. Required Keys:
  300. - img
  301. Modified Keys:
  302. - img
  303. Args:
  304. prob (float): The probability for performing Posterize
  305. transformation. Defaults to 1.0.
  306. level (int, optional): Should be in range [0,_MAX_LEVEL].
  307. If level is None, it will generate from [0, _MAX_LEVEL] randomly.
  308. Defaults to None.
  309. min_mag (float): The minimum magnitude for Posterize transformation.
  310. Defaults to 0.0.
  311. max_mag (float): The maximum magnitude for Posterize transformation.
  312. Defaults to 4.0.
  313. """
  314. def __init__(self,
  315. prob: float = 1.0,
  316. level: Optional[int] = None,
  317. min_mag: float = 0.0,
  318. max_mag: float = 4.0) -> None:
  319. assert 0. <= min_mag <= 8.0, f'min_mag for Posterize should be ' \
  320. f'in range [0, 8], got {min_mag}.'
  321. assert 0. <= max_mag <= 8.0, f'max_mag for Posterize should be ' \
  322. f'in range [0, 8], got {max_mag}.'
  323. super().__init__(
  324. prob=prob, level=level, min_mag=min_mag, max_mag=max_mag)
  325. def _transform_img(self, results: dict, mag: float) -> None:
  326. """Posterize the image."""
  327. img = results['img']
  328. results['img'] = mmcv.posterize(img, math.ceil(mag)).astype(img.dtype)
  329. @TRANSFORMS.register_module()
  330. class Equalize(ColorTransform):
  331. """Equalize the image histogram. The bboxes, masks and segmentations are
  332. not modified.
  333. Required Keys:
  334. - img
  335. Modified Keys:
  336. - img
  337. Args:
  338. prob (float): The probability for performing Equalize transformation.
  339. Defaults to 1.0.
  340. level (int, optional): No use for Equalize transformation.
  341. Defaults to None.
  342. min_mag (float): No use for Equalize transformation. Defaults to 0.1.
  343. max_mag (float): No use for Equalize transformation. Defaults to 1.9.
  344. """
  345. def _transform_img(self, results: dict, mag: float) -> None:
  346. """Equalizes the histogram of one image."""
  347. img = results['img']
  348. results['img'] = mmcv.imequalize(img).astype(img.dtype)
  349. @TRANSFORMS.register_module()
  350. class AutoContrast(ColorTransform):
  351. """Auto adjust image contrast.
  352. Required Keys:
  353. - img
  354. Modified Keys:
  355. - img
  356. Args:
  357. prob (float): The probability for performing AutoContrast should
  358. be in range [0, 1]. Defaults to 1.0.
  359. level (int, optional): No use for AutoContrast transformation.
  360. Defaults to None.
  361. min_mag (float): No use for AutoContrast transformation.
  362. Defaults to 0.1.
  363. max_mag (float): No use for AutoContrast transformation.
  364. Defaults to 1.9.
  365. """
  366. def _transform_img(self, results: dict, mag: float) -> None:
  367. """Auto adjust image contrast."""
  368. img = results['img']
  369. results['img'] = mmcv.auto_contrast(img).astype(img.dtype)
  370. @TRANSFORMS.register_module()
  371. class Invert(ColorTransform):
  372. """Invert images.
  373. Required Keys:
  374. - img
  375. Modified Keys:
  376. - img
  377. Args:
  378. prob (float): The probability for performing invert therefore should
  379. be in range [0, 1]. Defaults to 1.0.
  380. level (int, optional): No use for Invert transformation.
  381. Defaults to None.
  382. min_mag (float): No use for Invert transformation. Defaults to 0.1.
  383. max_mag (float): No use for Invert transformation. Defaults to 1.9.
  384. """
  385. def _transform_img(self, results: dict, mag: float) -> None:
  386. """Invert the image."""
  387. img = results['img']
  388. results['img'] = mmcv.iminvert(img).astype(img.dtype)