contrast.py 66 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588
  1. """
  2. Augmenters that perform contrast changes.
  3. List of augmenters:
  4. * :class:`GammaContrast`
  5. * :class:`SigmoidContrast`
  6. * :class:`LogContrast`
  7. * :class:`LinearContrast`
  8. * :class:`AllChannelsHistogramEqualization`
  9. * :class:`HistogramEqualization`
  10. * :class:`AllChannelsCLAHE`
  11. * :class:`CLAHE`
  12. """
  13. from __future__ import print_function, division, absolute_import
  14. import numpy as np
  15. import six.moves as sm
  16. import skimage.exposure as ski_exposure
  17. import cv2
  18. import imgaug as ia
  19. from imgaug.imgaug import _normalize_cv2_input_arr_
  20. from . import meta
  21. from . import color as color_lib
  22. from .. import parameters as iap
  23. from .. import dtypes as iadt
  24. from ..augmentables.batches import _BatchInAugmentation
  25. class _ContrastFuncWrapper(meta.Augmenter):
  26. def __init__(self, func, params1d, per_channel, dtypes_allowed=None,
  27. dtypes_disallowed=None,
  28. seed=None, name=None,
  29. random_state="deprecated", deterministic="deprecated"):
  30. super(_ContrastFuncWrapper, self).__init__(
  31. seed=seed, name=name,
  32. random_state=random_state, deterministic=deterministic)
  33. self.func = func
  34. self.params1d = params1d
  35. self.per_channel = iap.handle_probability_param(per_channel,
  36. "per_channel")
  37. self.dtypes_allowed = dtypes_allowed
  38. self.dtypes_disallowed = dtypes_disallowed
  39. # Added in 0.4.0.
  40. def _augment_batch_(self, batch, random_state, parents, hooks):
  41. if batch.images is None:
  42. return batch
  43. images = batch.images
  44. if self.dtypes_allowed is not None:
  45. iadt.gate_dtypes(images,
  46. allowed=self.dtypes_allowed,
  47. disallowed=self.dtypes_disallowed,
  48. augmenter=self)
  49. nb_images = len(images)
  50. rss = random_state.duplicate(1+nb_images)
  51. per_channel = self.per_channel.draw_samples((nb_images,),
  52. random_state=rss[0])
  53. gen = enumerate(zip(images, per_channel, rss[1:]))
  54. for i, (image, per_channel_i, rs) in gen:
  55. nb_channels = 1 if per_channel_i <= 0.5 else image.shape[2]
  56. # TODO improve efficiency by sampling once
  57. samples_i = [
  58. param.draw_samples((nb_channels,), random_state=rs)
  59. for param in self.params1d]
  60. if per_channel_i > 0.5:
  61. input_dtype = image.dtype
  62. # TODO This was previously a cast of image to float64. Do the
  63. # adjust_* functions return float64?
  64. result = []
  65. for c in sm.xrange(nb_channels):
  66. samples_i_c = [sample_i[c] for sample_i in samples_i]
  67. args = tuple([image[..., c]] + samples_i_c)
  68. result.append(self.func(*args))
  69. image_aug = np.stack(result, axis=-1)
  70. image_aug = image_aug.astype(input_dtype)
  71. else:
  72. # don't use something like samples_i[...][0] here, because
  73. # that returns python scalars and is slightly less accurate
  74. # than keeping the numpy values
  75. args = tuple([image] + samples_i)
  76. image_aug = self.func(*args)
  77. batch.images[i] = image_aug
  78. return batch
  79. def get_parameters(self):
  80. """See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`."""
  81. return self.params1d
  82. # TODO quite similar to the other adjust_contrast_*() functions, make DRY
  83. def adjust_contrast_gamma(arr, gamma):
  84. """
  85. Adjust image contrast by scaling pixel values to ``255*((v/255)**gamma)``.
  86. **Supported dtypes**:
  87. * ``uint8``: yes; fully tested (1) (2) (3)
  88. * ``uint16``: yes; tested (2) (3)
  89. * ``uint32``: yes; tested (2) (3)
  90. * ``uint64``: yes; tested (2) (3) (4)
  91. * ``int8``: limited; tested (2) (3) (5)
  92. * ``int16``: limited; tested (2) (3) (5)
  93. * ``int32``: limited; tested (2) (3) (5)
  94. * ``int64``: limited; tested (2) (3) (4) (5)
  95. * ``float16``: limited; tested (5)
  96. * ``float32``: limited; tested (5)
  97. * ``float64``: limited; tested (5)
  98. * ``float128``: no (6)
  99. * ``bool``: no (7)
  100. - (1) Handled by ``cv2``. Other dtypes are handled by ``skimage``.
  101. - (2) Normalization is done as ``I_ij/max``, where ``max`` is the
  102. maximum value of the dtype, e.g. 255 for ``uint8``. The
  103. normalization is reversed afterwards, e.g. ``result*255`` for
  104. ``uint8``.
  105. - (3) Integer-like values are not rounded after applying the contrast
  106. adjustment equation (before inverting the normalization to
  107. ``[0.0, 1.0]`` space), i.e. projection from continuous
  108. space to discrete happens according to floor function.
  109. - (4) Note that scikit-image doc says that integers are converted to
  110. ``float64`` values before applying the contrast normalization
  111. method. This might lead to inaccuracies for large 64bit integer
  112. values. Tests showed no indication of that happening though.
  113. - (5) Must not contain negative values. Values >=0 are fully supported.
  114. - (6) Leads to error in scikit-image.
  115. - (7) Does not make sense for contrast adjustments.
  116. Parameters
  117. ----------
  118. arr : numpy.ndarray
  119. Array for which to adjust the contrast. Dtype ``uint8`` is fastest.
  120. gamma : number
  121. Exponent for the contrast adjustment. Higher values darken the image.
  122. Returns
  123. -------
  124. numpy.ndarray
  125. Array with adjusted contrast.
  126. """
  127. if arr.size == 0:
  128. return np.copy(arr)
  129. # int8 is also possible according to docs
  130. # https://docs.opencv.org/3.0-beta/modules/core/doc/operations_on_arrays.html#cv2.LUT ,
  131. # but here it seemed like `d` was 0 for CV_8S, causing that to fail
  132. if arr.dtype.name == "uint8":
  133. min_value, _center_value, max_value = \
  134. iadt.get_value_range_of_dtype(arr.dtype)
  135. dynamic_range = max_value - min_value
  136. value_range = np.linspace(0, 1.0, num=dynamic_range+1,
  137. dtype=np.float32)
  138. # 255 * ((I_ij/255)**gamma)
  139. # using np.float32(.) here still works when the input is a numpy array
  140. # of size 1
  141. table = (min_value
  142. + (value_range ** np.float32(gamma))
  143. * dynamic_range)
  144. table = np.clip(table, min_value, max_value).astype(arr.dtype)
  145. arr_aug = ia.apply_lut(arr, table)
  146. return arr_aug
  147. return ski_exposure.adjust_gamma(arr, gamma)
  148. # TODO quite similar to the other adjust_contrast_*() functions, make DRY
  149. def adjust_contrast_sigmoid(arr, gain, cutoff):
  150. """
  151. Adjust image contrast to ``255*1/(1+exp(gain*(cutoff-I_ij/255)))``.
  152. **Supported dtypes**:
  153. * ``uint8``: yes; fully tested (1) (2) (3)
  154. * ``uint16``: yes; tested (2) (3)
  155. * ``uint32``: yes; tested (2) (3)
  156. * ``uint64``: yes; tested (2) (3) (4)
  157. * ``int8``: limited; tested (2) (3) (5)
  158. * ``int16``: limited; tested (2) (3) (5)
  159. * ``int32``: limited; tested (2) (3) (5)
  160. * ``int64``: limited; tested (2) (3) (4) (5)
  161. * ``float16``: limited; tested (5)
  162. * ``float32``: limited; tested (5)
  163. * ``float64``: limited; tested (5)
  164. * ``float128``: no (6)
  165. * ``bool``: no (7)
  166. - (1) Handled by ``cv2``. Other dtypes are handled by ``skimage``.
  167. - (2) Normalization is done as ``I_ij/max``, where ``max`` is the
  168. maximum value of the dtype, e.g. 255 for ``uint8``. The
  169. normalization is reversed afterwards, e.g. ``result*255``
  170. for ``uint8``.
  171. - (3) Integer-like values are not rounded after applying the contrast
  172. adjustment equation before inverting the normalization
  173. to ``[0.0, 1.0]`` space), i.e. projection from continuous
  174. space to discrete happens according to floor function.
  175. - (4) Note that scikit-image doc says that integers are converted to
  176. ``float64`` values before applying the contrast normalization
  177. method. This might lead to inaccuracies for large 64bit integer
  178. values. Tests showed no indication of that happening though.
  179. - (5) Must not contain negative values. Values >=0 are fully supported.
  180. - (6) Leads to error in scikit-image.
  181. - (7) Does not make sense for contrast adjustments.
  182. Parameters
  183. ----------
  184. arr : numpy.ndarray
  185. Array for which to adjust the contrast. Dtype ``uint8`` is fastest.
  186. gain : number
  187. Multiplier for the sigmoid function's output.
  188. Higher values lead to quicker changes from dark to light pixels.
  189. cutoff : number
  190. Cutoff that shifts the sigmoid function in horizontal direction.
  191. Higher values mean that the switch from dark to light pixels happens
  192. later, i.e. the pixels will remain darker.
  193. Returns
  194. -------
  195. numpy.ndarray
  196. Array with adjusted contrast.
  197. """
  198. if arr.size == 0:
  199. return np.copy(arr)
  200. # int8 is also possible according to docs
  201. # https://docs.opencv.org/3.0-beta/modules/core/doc/operations_on_arrays.html#cv2.LUT ,
  202. # but here it seemed like `d` was 0 for CV_8S, causing that to fail
  203. if arr.dtype.name == "uint8":
  204. min_value, _center_value, max_value = \
  205. iadt.get_value_range_of_dtype(arr.dtype)
  206. dynamic_range = max_value - min_value
  207. value_range = np.linspace(0, 1.0, num=dynamic_range+1,
  208. dtype=np.float32)
  209. # 255 * 1/(1 + exp(gain*(cutoff - I_ij/255)))
  210. # using np.float32(.) here still works when the input is a numpy array
  211. # of size 1
  212. gain = np.float32(gain)
  213. cutoff = np.float32(cutoff)
  214. table = (min_value
  215. + dynamic_range
  216. * 1/(1 + np.exp(gain * (cutoff - value_range))))
  217. table = np.clip(table, min_value, max_value).astype(arr.dtype)
  218. arr_aug = ia.apply_lut(arr, table)
  219. return arr_aug
  220. return ski_exposure.adjust_sigmoid(arr, cutoff=cutoff, gain=gain)
  221. # TODO quite similar to the other adjust_contrast_*() functions, make DRY
  222. # TODO add dtype gating
  223. def adjust_contrast_log(arr, gain):
  224. """
  225. Adjust image contrast by scaling pixels to ``255*gain*log_2(1+v/255)``.
  226. **Supported dtypes**:
  227. * ``uint8``: yes; fully tested (1) (2) (3)
  228. * ``uint16``: yes; tested (2) (3)
  229. * ``uint32``: no; tested (2) (3) (8)
  230. * ``uint64``: no; tested (2) (3) (4) (8)
  231. * ``int8``: limited; tested (2) (3) (5)
  232. * ``int16``: limited; tested (2) (3) (5)
  233. * ``int32``: no; tested (2) (3) (5) (8)
  234. * ``int64``: no; tested (2) (3) (4) (5) (8)
  235. * ``float16``: limited; tested (5)
  236. * ``float32``: limited; tested (5)
  237. * ``float64``: limited; tested (5)
  238. * ``float128``: no (6)
  239. * ``bool``: no (7)
  240. - (1) Handled by ``cv2``. Other dtypes are handled by ``skimage``.
  241. - (2) Normalization is done as ``I_ij/max``, where ``max`` is the
  242. maximum value of the dtype, e.g. 255 for ``uint8``. The
  243. normalization is reversed afterwards, e.g. ``result*255`` for
  244. ``uint8``.
  245. - (3) Integer-like values are not rounded after applying the contrast
  246. adjustment equation (before inverting the normalization
  247. to ``[0.0, 1.0]`` space), i.e. projection from continuous
  248. space to discrete happens according to floor function.
  249. - (4) Note that scikit-image doc says that integers are converted to
  250. ``float64`` values before applying the contrast normalization
  251. method. This might lead to inaccuracies for large 64bit integer
  252. values. Tests showed no indication of that happening though.
  253. - (5) Must not contain negative values. Values >=0 are fully supported.
  254. - (6) Leads to error in scikit-image.
  255. - (7) Does not make sense for contrast adjustments.
  256. - (8) No longer supported since numpy 1.17. Previously: 'yes' for
  257. ``uint32``, ``uint64``; 'limited' for ``int32``, ``int64``.
  258. Parameters
  259. ----------
  260. arr : numpy.ndarray
  261. Array for which to adjust the contrast. Dtype ``uint8`` is fastest.
  262. gain : number
  263. Multiplier for the logarithm result. Values around 1.0 lead to a
  264. contrast-adjusted images. Values above 1.0 quickly lead to partially
  265. broken images due to exceeding the datatype's value range.
  266. Returns
  267. -------
  268. numpy.ndarray
  269. Array with adjusted contrast.
  270. """
  271. if arr.size == 0:
  272. return np.copy(arr)
  273. # int8 is also possible according to docs
  274. # https://docs.opencv.org/3.0-beta/modules/core/doc/operations_on_arrays.html#cv2.LUT ,
  275. # but here it seemed like `d` was 0 for CV_8S, causing that to fail
  276. if arr.dtype.name == "uint8":
  277. min_value, _center_value, max_value = \
  278. iadt.get_value_range_of_dtype(arr.dtype)
  279. dynamic_range = max_value - min_value
  280. value_range = np.linspace(0, 1.0, num=dynamic_range+1,
  281. dtype=np.float32)
  282. # 255 * 1/(1 + exp(gain*(cutoff - I_ij/255)))
  283. # using np.float32(.) here still works when the input is a numpy array
  284. # of size 1
  285. gain = np.float32(gain)
  286. table = min_value + dynamic_range * gain * np.log2(1 + value_range)
  287. table = np.clip(table, min_value, max_value).astype(arr.dtype)
  288. arr_aug = ia.apply_lut(arr, table)
  289. return arr_aug
  290. return ski_exposure.adjust_log(arr, gain=gain)
  291. # TODO quite similar to the other adjust_contrast_*() functions, make DRY
  292. def adjust_contrast_linear(arr, alpha):
  293. """Adjust contrast by scaling each pixel to ``127 + alpha*(v-127)``.
  294. **Supported dtypes**:
  295. * ``uint8``: yes; fully tested (1) (2)
  296. * ``uint16``: yes; tested (2)
  297. * ``uint32``: yes; tested (2)
  298. * ``uint64``: no (3)
  299. * ``int8``: yes; tested (2)
  300. * ``int16``: yes; tested (2)
  301. * ``int32``: yes; tested (2)
  302. * ``int64``: no (2)
  303. * ``float16``: yes; tested (2)
  304. * ``float32``: yes; tested (2)
  305. * ``float64``: yes; tested (2)
  306. * ``float128``: no (2)
  307. * ``bool``: no (4)
  308. - (1) Handled by ``cv2``. Other dtypes are handled by raw ``numpy``.
  309. - (2) Only tested for reasonable alphas with up to a value of
  310. around ``100``.
  311. - (3) Conversion to ``float64`` is done during augmentation, hence
  312. ``uint64``, ``int64``, and ``float128`` support cannot be
  313. guaranteed.
  314. - (4) Does not make sense for contrast adjustments.
  315. Parameters
  316. ----------
  317. arr : numpy.ndarray
  318. Array for which to adjust the contrast. Dtype ``uint8`` is fastest.
  319. alpha : number
  320. Multiplier to linearly pronounce (``>1.0``), dampen (``0.0`` to
  321. ``1.0``) or invert (``<0.0``) the difference between each pixel value
  322. and the dtype's center value, e.g. ``127`` for ``uint8``.
  323. Returns
  324. -------
  325. numpy.ndarray
  326. Array with adjusted contrast.
  327. """
  328. # pylint: disable=no-else-return
  329. if arr.size == 0:
  330. return np.copy(arr)
  331. # int8 is also possible according to docs
  332. # https://docs.opencv.org/3.0-beta/modules/core/doc/operations_on_arrays.html#cv2.LUT ,
  333. # but here it seemed like `d` was 0 for CV_8S, causing that to fail
  334. if arr.dtype.name == "uint8":
  335. min_value, center_value, max_value = \
  336. iadt.get_value_range_of_dtype(arr.dtype)
  337. # TODO get rid of this int(...)
  338. center_value = int(center_value)
  339. value_range = np.arange(0, 256, dtype=np.float32)
  340. # 127 + alpha*(I_ij-127)
  341. # using np.float32(.) here still works when the input is a numpy array
  342. # of size 1
  343. alpha = np.float32(alpha)
  344. table = center_value + alpha * (value_range - center_value)
  345. table = np.clip(table, min_value, max_value).astype(arr.dtype)
  346. arr_aug = ia.apply_lut(arr, table)
  347. return arr_aug
  348. else:
  349. input_dtype = arr.dtype
  350. _min_value, center_value, _max_value = \
  351. iadt.get_value_range_of_dtype(input_dtype)
  352. # TODO get rid of this int(...)
  353. if input_dtype.kind in ["u", "i"]:
  354. center_value = int(center_value)
  355. image_aug = (center_value
  356. + alpha
  357. * (arr.astype(np.float64)-center_value))
  358. image_aug = iadt.restore_dtypes_(image_aug, input_dtype)
  359. return image_aug
  360. class GammaContrast(_ContrastFuncWrapper):
  361. """
  362. Adjust image contrast by scaling pixel values to ``255*((v/255)**gamma)``.
  363. Values in the range ``gamma=(0.5, 2.0)`` seem to be sensible.
  364. **Supported dtypes**:
  365. See :func:`~imgaug.augmenters.contrast.adjust_contrast_gamma`.
  366. Parameters
  367. ----------
  368. gamma : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  369. Exponent for the contrast adjustment. Higher values darken the image.
  370. * If a number, then that value will be used for all images.
  371. * If a tuple ``(a, b)``, then a value from the range ``[a, b]``
  372. will be used per image.
  373. * If a list, then a random value will be sampled from that list
  374. per image.
  375. * If a ``StochasticParameter``, then a value will be sampled per
  376. image from that parameter.
  377. per_channel : bool or float, optional
  378. Whether to use the same value for all channels (``False``) or to
  379. sample a new value for each channel (``True``). If this value is a
  380. float ``p``, then for ``p`` percent of all images `per_channel` will
  381. be treated as ``True``, otherwise as ``False``.
  382. seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
  383. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  384. name : None or str, optional
  385. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  386. random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
  387. Old name for parameter `seed`.
  388. Its usage will not yet cause a deprecation warning,
  389. but it is still recommended to use `seed` now.
  390. Outdated since 0.4.0.
  391. deterministic : bool, optional
  392. Deprecated since 0.4.0.
  393. See method ``to_deterministic()`` for an alternative and for
  394. details about what the "deterministic mode" actually does.
  395. Examples
  396. --------
  397. >>> import imgaug.augmenters as iaa
  398. >>> aug = iaa.GammaContrast((0.5, 2.0))
  399. Modify the contrast of images according to ``255*((v/255)**gamma)``,
  400. where ``v`` is a pixel value and ``gamma`` is sampled uniformly from
  401. the interval ``[0.5, 2.0]`` (once per image).
  402. >>> aug = iaa.GammaContrast((0.5, 2.0), per_channel=True)
  403. Same as in the previous example, but ``gamma`` is sampled once per image
  404. *and* channel.
  405. """
  406. def __init__(self, gamma=(0.7, 1.7), per_channel=False,
  407. seed=None, name=None,
  408. random_state="deprecated", deterministic="deprecated"):
  409. params1d = [iap.handle_continuous_param(
  410. gamma, "gamma", value_range=None, tuple_to_uniform=True,
  411. list_to_choice=True)]
  412. func = adjust_contrast_gamma
  413. super(GammaContrast, self).__init__(
  414. func, params1d, per_channel,
  415. dtypes_allowed=["uint8", "uint16", "uint32", "uint64",
  416. "int8", "int16", "int32", "int64",
  417. "float16", "float32", "float64"],
  418. dtypes_disallowed=["float96", "float128", "float256", "bool"],
  419. seed=seed, name=name,
  420. random_state=random_state, deterministic=deterministic)
  421. class SigmoidContrast(_ContrastFuncWrapper):
  422. """
  423. Adjust image contrast to ``255*1/(1+exp(gain*(cutoff-I_ij/255)))``.
  424. Values in the range ``gain=(5, 20)`` and ``cutoff=(0.25, 0.75)`` seem to
  425. be sensible.
  426. A combination of ``gain=5.5`` and ``cutof=0.45`` is fairly close to
  427. the identity function.
  428. **Supported dtypes**:
  429. See :func:`~imgaug.augmenters.contrast.adjust_contrast_sigmoid`.
  430. Parameters
  431. ----------
  432. gain : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  433. Multiplier for the sigmoid function's output.
  434. Higher values lead to quicker changes from dark to light pixels.
  435. * If a number, then that value will be used for all images.
  436. * If a tuple ``(a, b)``, then a value from the interval
  437. ``[a, b]`` will be sampled uniformly per image.
  438. * If a list, then a random value will be sampled from that list
  439. per image.
  440. * If a ``StochasticParameter``, then a value will be sampled per
  441. image from that parameter.
  442. cutoff : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  443. Cutoff that shifts the sigmoid function in horizontal direction.
  444. Higher values mean that the switch from dark to light pixels happens
  445. later, i.e. the pixels will remain darker.
  446. * If a number, then that value will be used for all images.
  447. * If a tuple ``(a, b)``, then a value from the range ``[a, b]``
  448. will be used per image.
  449. * If a list, then a random value will be sampled from that list
  450. per image.
  451. * If a ``StochasticParameter``, then a value will be sampled per
  452. image from that parameter.
  453. per_channel : bool or float, optional
  454. Whether to use the same value for all channels (``False``) or to
  455. sample a new value for each channel (``True``). If this value is a
  456. float ``p``, then for ``p`` percent of all images `per_channel` will
  457. be treated as ``True``, otherwise as ``False``.
  458. seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
  459. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  460. name : None or str, optional
  461. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  462. random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
  463. Old name for parameter `seed`.
  464. Its usage will not yet cause a deprecation warning,
  465. but it is still recommended to use `seed` now.
  466. Outdated since 0.4.0.
  467. deterministic : bool, optional
  468. Deprecated since 0.4.0.
  469. See method ``to_deterministic()`` for an alternative and for
  470. details about what the "deterministic mode" actually does.
  471. Examples
  472. --------
  473. >>> import imgaug.augmenters as iaa
  474. >>> aug = iaa.SigmoidContrast(gain=(3, 10), cutoff=(0.4, 0.6))
  475. Modify the contrast of images according to
  476. ``255*1/(1+exp(gain*(cutoff-v/255)))``, where ``v`` is a pixel value,
  477. ``gain`` is sampled uniformly from the interval ``[3, 10]`` (once per
  478. image) and ``cutoff`` is sampled uniformly from the interval
  479. ``[0.4, 0.6]`` (also once per image).
  480. >>> aug = iaa.SigmoidContrast(
  481. >>> gain=(3, 10), cutoff=(0.4, 0.6), per_channel=True)
  482. Same as in the previous example, but ``gain`` and ``cutoff`` are each
  483. sampled once per image *and* channel.
  484. """
  485. def __init__(self, gain=(5, 6), cutoff=(0.3, 0.6), per_channel=False,
  486. seed=None, name=None,
  487. random_state="deprecated", deterministic="deprecated"):
  488. # TODO add inv parameter?
  489. params1d = [
  490. iap.handle_continuous_param(
  491. gain, "gain", value_range=(0, None), tuple_to_uniform=True,
  492. list_to_choice=True),
  493. iap.handle_continuous_param(
  494. cutoff, "cutoff", value_range=(0, 1.0), tuple_to_uniform=True,
  495. list_to_choice=True)
  496. ]
  497. func = adjust_contrast_sigmoid
  498. super(SigmoidContrast, self).__init__(
  499. func, params1d, per_channel,
  500. dtypes_allowed=["uint8", "uint16", "uint32", "uint64",
  501. "int8", "int16", "int32", "int64",
  502. "float16", "float32", "float64"],
  503. dtypes_disallowed=["float96", "float128", "float256", "bool"],
  504. seed=seed, name=name,
  505. random_state=random_state, deterministic=deterministic)
  506. class LogContrast(_ContrastFuncWrapper):
  507. """Adjust image contrast by scaling pixels to ``255*gain*log_2(1+v/255)``.
  508. This augmenter is fairly similar to
  509. ``imgaug.augmenters.arithmetic.Multiply``.
  510. **Supported dtypes**:
  511. See :func:`~imgaug.augmenters.contrast.adjust_contrast_log`.
  512. Parameters
  513. ----------
  514. gain : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  515. Multiplier for the logarithm result. Values around ``1.0`` lead to a
  516. contrast-adjusted images. Values above ``1.0`` quickly lead to
  517. partially broken images due to exceeding the datatype's value range.
  518. * If a number, then that value will be used for all images.
  519. * If a tuple ``(a, b)``, then a value from the interval ``[a, b]``
  520. will uniformly sampled be used per image.
  521. * If a list, then a random value will be sampled from that list
  522. per image.
  523. * If a ``StochasticParameter``, then a value will be sampled per
  524. image from that parameter.
  525. per_channel : bool or float, optional
  526. Whether to use the same value for all channels (``False``) or to
  527. sample a new value for each channel (``True``). If this value is a
  528. float ``p``, then for ``p`` percent of all images `per_channel` will
  529. be treated as ``True``, otherwise as ``False``.
  530. seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
  531. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  532. name : None or str, optional
  533. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  534. random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
  535. Old name for parameter `seed`.
  536. Its usage will not yet cause a deprecation warning,
  537. but it is still recommended to use `seed` now.
  538. Outdated since 0.4.0.
  539. deterministic : bool, optional
  540. Deprecated since 0.4.0.
  541. See method ``to_deterministic()`` for an alternative and for
  542. details about what the "deterministic mode" actually does.
  543. Examples
  544. --------
  545. >>> import imgaug.augmenters as iaa
  546. >>> aug = iaa.LogContrast(gain=(0.6, 1.4))
  547. Modify the contrast of images according to ``255*gain*log_2(1+v/255)``,
  548. where ``v`` is a pixel value and ``gain`` is sampled uniformly from the
  549. interval ``[0.6, 1.4]`` (once per image).
  550. >>> aug = iaa.LogContrast(gain=(0.6, 1.4), per_channel=True)
  551. Same as in the previous example, but ``gain`` is sampled once per image
  552. *and* channel.
  553. """
  554. def __init__(self, gain=(0.4, 1.6), per_channel=False,
  555. seed=None, name=None,
  556. random_state="deprecated", deterministic="deprecated"):
  557. # TODO add inv parameter?
  558. params1d = [iap.handle_continuous_param(
  559. gain, "gain", value_range=(0, None), tuple_to_uniform=True,
  560. list_to_choice=True)]
  561. func = adjust_contrast_log
  562. super(LogContrast, self).__init__(
  563. func, params1d, per_channel,
  564. dtypes_allowed=["uint8", "uint16", "uint32", "uint64",
  565. "int8", "int16", "int32", "int64",
  566. "float16", "float32", "float64"],
  567. dtypes_disallowed=["float96", "float128", "float256", "bool"],
  568. seed=seed, name=name,
  569. random_state=random_state, deterministic=deterministic)
  570. class LinearContrast(_ContrastFuncWrapper):
  571. """Adjust contrast by scaling each pixel to ``127 + alpha*(v-127)``.
  572. **Supported dtypes**:
  573. See :func:`~imgaug.augmenters.contrast.adjust_contrast_linear`.
  574. Parameters
  575. ----------
  576. alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  577. Multiplier to linearly pronounce (``>1.0``), dampen (``0.0`` to
  578. ``1.0``) or invert (``<0.0``) the difference between each pixel value
  579. and the dtype's center value, e.g. ``127`` for ``uint8``.
  580. * If a number, then that value will be used for all images.
  581. * If a tuple ``(a, b)``, then a value from the interval ``[a, b]``
  582. will be used per image.
  583. * If a list, then a random value will be sampled from that list
  584. per image.
  585. * If a ``StochasticParameter``, then a value will be sampled per
  586. image from that parameter.
  587. per_channel : bool or float, optional
  588. Whether to use the same value for all channels (``False``) or to
  589. sample a new value for each channel (``True``). If this value is a
  590. float ``p``, then for ``p`` percent of all images `per_channel` will
  591. be treated as ``True``, otherwise as ``False``.
  592. seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
  593. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  594. name : None or str, optional
  595. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  596. random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
  597. Old name for parameter `seed`.
  598. Its usage will not yet cause a deprecation warning,
  599. but it is still recommended to use `seed` now.
  600. Outdated since 0.4.0.
  601. deterministic : bool, optional
  602. Deprecated since 0.4.0.
  603. See method ``to_deterministic()`` for an alternative and for
  604. details about what the "deterministic mode" actually does.
  605. Examples
  606. --------
  607. >>> import imgaug.augmenters as iaa
  608. >>> aug = iaa.LinearContrast((0.4, 1.6))
  609. Modify the contrast of images according to `127 + alpha*(v-127)``,
  610. where ``v`` is a pixel value and ``alpha`` is sampled uniformly from the
  611. interval ``[0.4, 1.6]`` (once per image).
  612. >>> aug = iaa.LinearContrast((0.4, 1.6), per_channel=True)
  613. Same as in the previous example, but ``alpha`` is sampled once per image
  614. *and* channel.
  615. """
  616. def __init__(self, alpha=(0.6, 1.4), per_channel=False,
  617. seed=None, name=None,
  618. random_state="deprecated", deterministic="deprecated"):
  619. params1d = [
  620. iap.handle_continuous_param(
  621. alpha, "alpha", value_range=None, tuple_to_uniform=True,
  622. list_to_choice=True)
  623. ]
  624. func = adjust_contrast_linear
  625. super(LinearContrast, self).__init__(
  626. func, params1d, per_channel,
  627. dtypes_allowed=["uint8", "uint16", "uint32",
  628. "int8", "int16", "int32",
  629. "float16", "float32", "float64"],
  630. dtypes_disallowed=["uint64", "int64", "float96", "float128",
  631. "float256", "bool"],
  632. seed=seed, name=name,
  633. random_state=random_state, deterministic=deterministic)
  634. # TODO maybe offer the other contrast augmenters also wrapped in this, similar
  635. # to CLAHE and HistogramEqualization?
  636. # this is essentially tested by tests for CLAHE
  637. class _IntensityChannelBasedApplier(object):
  638. RGB = color_lib.CSPACE_RGB
  639. BGR = color_lib.CSPACE_BGR
  640. HSV = color_lib.CSPACE_HSV
  641. HLS = color_lib.CSPACE_HLS
  642. Lab = color_lib.CSPACE_Lab
  643. _CHANNEL_MAPPING = {
  644. HSV: 2,
  645. HLS: 1,
  646. Lab: 0
  647. }
  648. def __init__(self, from_colorspace, to_colorspace):
  649. super(_IntensityChannelBasedApplier, self).__init__()
  650. # TODO maybe add CIE, Luv?
  651. valid_from_colorspaces = [self.RGB, self.BGR, self.Lab, self.HLS,
  652. self.HSV]
  653. assert from_colorspace in valid_from_colorspaces, (
  654. "Expected 'from_colorspace' to be one of %s, got %s." % (
  655. valid_from_colorspaces, from_colorspace))
  656. valid_to_colorspaces = [self.Lab, self.HLS, self.HSV]
  657. assert to_colorspace in valid_to_colorspaces, (
  658. "Expected 'to_colorspace' to be one of %s, got %s." % (
  659. valid_to_colorspaces, to_colorspace))
  660. self.from_colorspace = from_colorspace
  661. self.to_colorspace = to_colorspace
  662. def apply(self, images, random_state, parents, hooks, func):
  663. input_was_array = ia.is_np_array(images)
  664. rss = random_state.duplicate(3)
  665. # normalize images
  666. # (H, W, 1) will be used directly in AllChannelsCLAHE
  667. # (H, W, 3) will be converted to target colorspace in the next
  668. # block
  669. # (H, W, 4) will be reduced to (H, W, 3) (remove 4th channel) and
  670. # converted to target colorspace in next block
  671. # (H, W, <else>) will raise a warning and be treated channelwise by
  672. # AllChannelsCLAHE
  673. images_normalized = []
  674. images_change_cs = []
  675. images_change_cs_indices = []
  676. for i, image in enumerate(images):
  677. nb_channels = image.shape[2]
  678. if nb_channels == 1:
  679. images_normalized.append(image)
  680. elif nb_channels == 3:
  681. images_normalized.append(None)
  682. images_change_cs.append(image)
  683. images_change_cs_indices.append(i)
  684. elif nb_channels == 4:
  685. # assume that 4th channel is an alpha channel, e.g. in RGBA
  686. images_normalized.append(None)
  687. images_change_cs.append(image[..., 0:3])
  688. images_change_cs_indices.append(i)
  689. else:
  690. ia.warn(
  691. "Got image with %d channels in "
  692. "_IntensityChannelBasedApplier (parents: %s), "
  693. "expected 0, 1, 3 or 4 channels." % (
  694. nb_channels, ", ".join(
  695. parent.name for parent in parents)))
  696. images_normalized.append(image)
  697. # convert colorspaces of normalized 3-channel images
  698. images_after_color_conversion = [None] * len(images_normalized)
  699. if len(images_change_cs) > 0:
  700. images_new_cs = color_lib.change_colorspaces_(
  701. images_change_cs,
  702. to_colorspaces=self.to_colorspace,
  703. from_colorspaces=self.from_colorspace)
  704. for image_new_cs, target_idx in zip(images_new_cs,
  705. images_change_cs_indices):
  706. chan_idx = self._CHANNEL_MAPPING[self.to_colorspace]
  707. images_normalized[target_idx] = image_new_cs[
  708. ..., chan_idx:chan_idx+1]
  709. images_after_color_conversion[target_idx] = image_new_cs
  710. # apply function channelwise
  711. images_aug = func(images_normalized, rss[1])
  712. # denormalize
  713. result = []
  714. images_change_cs = []
  715. images_change_cs_indices = []
  716. gen = enumerate(zip(images, images_after_color_conversion, images_aug))
  717. for i, (image, image_conv, image_aug) in gen:
  718. nb_channels = image.shape[2]
  719. if nb_channels in [3, 4]:
  720. chan_idx = self._CHANNEL_MAPPING[self.to_colorspace]
  721. image_tmp = image_conv
  722. image_tmp[..., chan_idx:chan_idx+1] = image_aug
  723. result.append(None if nb_channels == 3 else image[..., 3:4])
  724. images_change_cs.append(image_tmp)
  725. images_change_cs_indices.append(i)
  726. else:
  727. result.append(image_aug)
  728. # invert colorspace conversion
  729. if len(images_change_cs) > 0:
  730. images_new_cs = color_lib.change_colorspaces_(
  731. images_change_cs,
  732. to_colorspaces=self.from_colorspace,
  733. from_colorspaces=self.to_colorspace)
  734. for image_new_cs, target_idx in zip(images_new_cs,
  735. images_change_cs_indices):
  736. if result[target_idx] is None:
  737. result[target_idx] = image_new_cs
  738. else:
  739. # input image had four channels, 4th channel is already
  740. # in result
  741. result[target_idx] = np.dstack((image_new_cs,
  742. result[target_idx]))
  743. # convert to array if necessary
  744. if input_was_array:
  745. result = np.array(result, dtype=result[0].dtype)
  746. return result
  747. def get_parameters(self):
  748. """See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`."""
  749. return [self.from_colorspace, self.to_colorspace]
  750. # TODO add parameter `tile_grid_size_percent`
  751. class AllChannelsCLAHE(meta.Augmenter):
  752. """Apply CLAHE to all channels of images in their original colorspaces.
  753. CLAHE (Contrast Limited Adaptive Histogram Equalization) performs
  754. histogram equilization within image patches, i.e. over local
  755. neighbourhoods.
  756. In contrast to ``imgaug.augmenters.contrast.CLAHE``, this augmenter
  757. operates directly on all channels of the input images. It does not
  758. perform any colorspace transformations and does not focus on specific
  759. channels (e.g. ``L`` in ``Lab`` colorspace).
  760. **Supported dtypes**:
  761. * ``uint8``: yes; fully tested
  762. * ``uint16``: yes; tested
  763. * ``uint32``: no (1)
  764. * ``uint64``: no (2)
  765. * ``int8``: no (2)
  766. * ``int16``: no (2)
  767. * ``int32``: no (2)
  768. * ``int64``: no (2)
  769. * ``float16``: no (2)
  770. * ``float32``: no (2)
  771. * ``float64``: no (2)
  772. * ``float128``: no (1)
  773. * ``bool``: no (1)
  774. - (1) rejected by cv2
  775. - (2) results in error in cv2: ``cv2.error:
  776. OpenCV(3.4.2) (...)/clahe.cpp:351: error: (-215:Assertion
  777. failed) src.type() == (((0) & ((1 << 3) - 1)) + (((1)-1) << 3))
  778. || _src.type() == (((2) & ((1 << 3) - 1)) + (((1)-1) << 3)) in
  779. function 'apply'``
  780. Parameters
  781. ----------
  782. clip_limit : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  783. See ``imgaug.augmenters.contrast.CLAHE``.
  784. tile_grid_size_px : int or tuple of int or list of int or imgaug.parameters.StochasticParameter or tuple of tuple of int or tuple of list of int or tuple of imgaug.parameters.StochasticParameter, optional
  785. See ``imgaug.augmenters.contrast.CLAHE``.
  786. tile_grid_size_px_min : int, optional
  787. See ``imgaug.augmenters.contrast.CLAHE``.
  788. per_channel : bool or float, optional
  789. Whether to use the same value for all channels (``False``) or to
  790. sample a new value for each channel (``True``). If this value is a
  791. float ``p``, then for ``p`` percent of all images `per_channel` will
  792. be treated as ``True``, otherwise as ``False``.
  793. seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
  794. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  795. name : None or str, optional
  796. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  797. random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
  798. Old name for parameter `seed`.
  799. Its usage will not yet cause a deprecation warning,
  800. but it is still recommended to use `seed` now.
  801. Outdated since 0.4.0.
  802. deterministic : bool, optional
  803. Deprecated since 0.4.0.
  804. See method ``to_deterministic()`` for an alternative and for
  805. details about what the "deterministic mode" actually does.
  806. Examples
  807. --------
  808. >>> import imgaug.augmenters as iaa
  809. >>> aug = iaa.AllChannelsCLAHE()
  810. Create an augmenter that applies CLAHE to all channels of input images.
  811. >>> aug = iaa.AllChannelsCLAHE(clip_limit=(1, 10))
  812. Same as in the previous example, but the `clip_limit` used by CLAHE is
  813. uniformly sampled per image from the interval ``[1, 10]``. Some images
  814. will therefore have stronger contrast than others (i.e. higher clip limit
  815. values).
  816. >>> aug = iaa.AllChannelsCLAHE(clip_limit=(1, 10), per_channel=True)
  817. Same as in the previous example, but the `clip_limit` is sampled per
  818. image *and* channel, leading to different levels of contrast for each
  819. channel.
  820. """
  821. def __init__(self, clip_limit=(0.1, 8), tile_grid_size_px=(3, 12),
  822. tile_grid_size_px_min=3, per_channel=False,
  823. seed=None, name=None,
  824. random_state="deprecated", deterministic="deprecated"):
  825. super(AllChannelsCLAHE, self).__init__(
  826. seed=seed, name=name,
  827. random_state=random_state, deterministic=deterministic)
  828. self.clip_limit = iap.handle_continuous_param(
  829. clip_limit, "clip_limit", value_range=(0+1e-4, None),
  830. tuple_to_uniform=True, list_to_choice=True)
  831. self.tile_grid_size_px = iap.handle_discrete_kernel_size_param(
  832. tile_grid_size_px, "tile_grid_size_px", value_range=(0, None),
  833. allow_floats=False)
  834. self.tile_grid_size_px_min = tile_grid_size_px_min
  835. self.per_channel = iap.handle_probability_param(per_channel,
  836. "per_channel")
  837. # Added in 0.4.0.
  838. def _augment_batch_(self, batch, random_state, parents, hooks):
  839. if batch.images is None:
  840. return batch
  841. images = batch.images
  842. iadt.gate_dtypes(
  843. images,
  844. allowed=["uint8", "uint16"],
  845. disallowed=["bool",
  846. "uint32", "uint64", "uint128", "uint256",
  847. "int8", "int16", "int32", "int64", "int128", "int256",
  848. "float16", "float32", "float64", "float96",
  849. "float128", "float256"],
  850. augmenter=self)
  851. nb_images = len(images)
  852. nb_channels = meta.estimate_max_number_of_channels(images)
  853. mode = "single" if self.tile_grid_size_px[1] is None else "two"
  854. rss = random_state.duplicate(3 if mode == "single" else 4)
  855. per_channel = self.per_channel.draw_samples((nb_images,),
  856. random_state=rss[0])
  857. clip_limit = self.clip_limit.draw_samples((nb_images, nb_channels),
  858. random_state=rss[1])
  859. tile_grid_size_px_h = self.tile_grid_size_px[0].draw_samples(
  860. (nb_images, nb_channels), random_state=rss[2])
  861. if mode == "single":
  862. tile_grid_size_px_w = tile_grid_size_px_h
  863. else:
  864. tile_grid_size_px_w = self.tile_grid_size_px[1].draw_samples(
  865. (nb_images, nb_channels), random_state=rss[3])
  866. tile_grid_size_px_w = np.maximum(tile_grid_size_px_w,
  867. self.tile_grid_size_px_min)
  868. tile_grid_size_px_h = np.maximum(tile_grid_size_px_h,
  869. self.tile_grid_size_px_min)
  870. gen = enumerate(zip(images, clip_limit, tile_grid_size_px_h,
  871. tile_grid_size_px_w, per_channel))
  872. for i, (image, clip_limit_i, tgs_px_h_i, tgs_px_w_i, pchannel_i) in gen:
  873. if image.size == 0:
  874. continue
  875. nb_channels = image.shape[2]
  876. c_param = 0
  877. image_warped = []
  878. for c in sm.xrange(nb_channels):
  879. if tgs_px_w_i[c_param] > 1 or tgs_px_h_i[c_param] > 1:
  880. clahe = cv2.createCLAHE(
  881. clipLimit=clip_limit_i[c_param],
  882. tileGridSize=(tgs_px_w_i[c_param], tgs_px_h_i[c_param])
  883. )
  884. channel_warped = clahe.apply(
  885. _normalize_cv2_input_arr_(image[..., c])
  886. )
  887. image_warped.append(channel_warped)
  888. else:
  889. image_warped.append(image[..., c])
  890. if pchannel_i > 0.5:
  891. c_param += 1
  892. # combine channels to one image
  893. image_warped = np.stack(image_warped, axis=-1)
  894. batch.images[i] = image_warped
  895. return batch
  896. def get_parameters(self):
  897. """See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`."""
  898. return [self.clip_limit, self.tile_grid_size_px,
  899. self.tile_grid_size_px_min, self.per_channel]
  900. class CLAHE(meta.Augmenter):
  901. """Apply CLAHE to L/V/L channels in HLS/HSV/Lab colorspaces.
  902. This augmenter applies CLAHE (Contrast Limited Adaptive Histogram
  903. Equalization) to images, a form of histogram equalization that normalizes
  904. within local image patches.
  905. The augmenter transforms input images to a target colorspace (e.g.
  906. ``Lab``), extracts an intensity-related channel from the converted
  907. images (e.g. ``L`` for ``Lab``), applies CLAHE to the channel and then
  908. converts the resulting image back to the original colorspace.
  909. Grayscale images (images without channel axis or with only one channel
  910. axis) are automatically handled, `from_colorspace` does not have to be
  911. adjusted for them. For images with four channels (e.g. ``RGBA``), the
  912. fourth channel is ignored in the colorspace conversion (e.g. from an
  913. ``RGBA`` image, only the ``RGB`` part is converted, normalized, converted
  914. back and concatenated with the input ``A`` channel). Images with unusual
  915. channel numbers (2, 5 or more than 5) are normalized channel-by-channel
  916. (same behaviour as ``AllChannelsCLAHE``, though a warning will be raised).
  917. If you want to apply CLAHE to each channel of the original input image's
  918. colorspace (without any colorspace conversion), use
  919. ``imgaug.augmenters.contrast.AllChannelsCLAHE`` instead.
  920. **Supported dtypes**:
  921. * ``uint8``: yes; fully tested
  922. * ``uint16``: no (1)
  923. * ``uint32``: no (1)
  924. * ``uint64``: no (1)
  925. * ``int8``: no (1)
  926. * ``int16``: no (1)
  927. * ``int32``: no (1)
  928. * ``int64``: no (1)
  929. * ``float16``: no (1)
  930. * ``float32``: no (1)
  931. * ``float64``: no (1)
  932. * ``float128``: no (1)
  933. * ``bool``: no (1)
  934. - (1) This augmenter uses
  935. :class:`~imgaug.augmenters.color.ChangeColorspace`, which is
  936. currently limited to ``uint8``.
  937. Parameters
  938. ----------
  939. clip_limit : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  940. Clipping limit. Higher values result in stronger contrast. OpenCV
  941. uses a default of ``40``, though values around ``5`` seem to already
  942. produce decent contrast.
  943. * If a number, then that value will be used for all images.
  944. * If a tuple ``(a, b)``, then a value from the range ``[a, b]``
  945. will be used per image.
  946. * If a list, then a random value will be sampled from that list
  947. per image.
  948. * If a ``StochasticParameter``, then a value will be sampled per
  949. image from that parameter.
  950. tile_grid_size_px : int or tuple of int or list of int or imgaug.parameters.StochasticParameter or tuple of tuple of int or tuple of list of int or tuple of imgaug.parameters.StochasticParameter, optional
  951. Kernel size, i.e. size of each local neighbourhood in pixels.
  952. * If an ``int``, then that value will be used for all images for
  953. both kernel height and width.
  954. * If a tuple ``(a, b)``, then a value from the discrete interval
  955. ``[a..b]`` will be uniformly sampled per image.
  956. * If a list, then a random value will be sampled from that list
  957. per image and used for both kernel height and width.
  958. * If a ``StochasticParameter``, then a value will be sampled per
  959. image from that parameter per image and used for both kernel
  960. height and width.
  961. * If a tuple of tuple of ``int`` given as ``((a, b), (c, d))``,
  962. then two values will be sampled independently from the discrete
  963. ranges ``[a..b]`` and ``[c..d]`` per image and used as the
  964. kernel height and width.
  965. * If a tuple of lists of ``int``, then two values will be sampled
  966. independently per image, one from the first list and one from
  967. the second, and used as the kernel height and width.
  968. * If a tuple of ``StochasticParameter``, then two values will be
  969. sampled indepdently per image, one from the first parameter and
  970. one from the second, and used as the kernel height and width.
  971. tile_grid_size_px_min : int, optional
  972. Minimum kernel size in px, per axis. If the sampling results in a
  973. value lower than this minimum, it will be clipped to this value.
  974. from_colorspace : {"RGB", "BGR", "HSV", "HLS", "Lab"}, optional
  975. Colorspace of the input images.
  976. If any input image has only one or zero channels, this setting will
  977. be ignored and it will be assumed that the input is grayscale.
  978. If a fourth channel is present in an input image, it will be removed
  979. before the colorspace conversion and later re-added.
  980. See also :func:`~imgaug.augmenters.color.change_colorspace_` for
  981. details.
  982. to_colorspace : {"Lab", "HLS", "HSV"}, optional
  983. Colorspace in which to perform CLAHE. For ``Lab``, CLAHE will only be
  984. applied to the first channel (``L``), for ``HLS`` to the
  985. second (``L``) and for ``HSV`` to the third (``V``). To apply CLAHE
  986. to all channels of an input image (without colorspace conversion),
  987. see ``imgaug.augmenters.contrast.AllChannelsCLAHE``.
  988. seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
  989. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  990. name : None or str, optional
  991. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  992. random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
  993. Old name for parameter `seed`.
  994. Its usage will not yet cause a deprecation warning,
  995. but it is still recommended to use `seed` now.
  996. Outdated since 0.4.0.
  997. deterministic : bool, optional
  998. Deprecated since 0.4.0.
  999. See method ``to_deterministic()`` for an alternative and for
  1000. details about what the "deterministic mode" actually does.
  1001. Examples
  1002. --------
  1003. >>> import imgaug.augmenters as iaa
  1004. >>> aug = iaa.CLAHE()
  1005. Create a standard CLAHE augmenter.
  1006. >>> aug = iaa.CLAHE(clip_limit=(1, 10))
  1007. Create a CLAHE augmenter with a clip limit uniformly sampled from
  1008. ``[1..10]``, where ``1`` is rather low contrast and ``10`` is rather
  1009. high contrast.
  1010. >>> aug = iaa.CLAHE(tile_grid_size_px=(3, 21))
  1011. Create a CLAHE augmenter with kernel sizes of ``SxS``, where ``S`` is
  1012. uniformly sampled from ``[3..21]``. Sampling happens once per image.
  1013. >>> aug = iaa.CLAHE(
  1014. >>> tile_grid_size_px=iap.Discretize(iap.Normal(loc=7, scale=2)),
  1015. >>> tile_grid_size_px_min=3)
  1016. Create a CLAHE augmenter with kernel sizes of ``SxS``, where ``S`` is
  1017. sampled from ``N(7, 2)``, but does not go below ``3``.
  1018. >>> aug = iaa.CLAHE(tile_grid_size_px=((3, 21), [3, 5, 7]))
  1019. Create a CLAHE augmenter with kernel sizes of ``HxW``, where ``H`` is
  1020. uniformly sampled from ``[3..21]`` and ``W`` is randomly picked from the
  1021. list ``[3, 5, 7]``.
  1022. >>> aug = iaa.CLAHE(
  1023. >>> from_colorspace=iaa.CSPACE_BGR,
  1024. >>> to_colorspace=iaa.CSPACE_HSV)
  1025. Create a CLAHE augmenter that converts images from BGR colorspace to
  1026. HSV colorspace and then applies the local histogram equalization to the
  1027. ``V`` channel of the images (before converting back to ``BGR``).
  1028. Alternatively, ``Lab`` (default) or ``HLS`` can be used as the target
  1029. colorspace. Grayscale images (no channels / one channel) are never
  1030. converted and are instead directly normalized (i.e. `from_colorspace`
  1031. does not have to be changed for them).
  1032. """
  1033. RGB = color_lib.CSPACE_RGB
  1034. BGR = color_lib.CSPACE_BGR
  1035. HSV = color_lib.CSPACE_HSV
  1036. HLS = color_lib.CSPACE_HLS
  1037. Lab = color_lib.CSPACE_Lab
  1038. def __init__(self, clip_limit=(0.1, 8), tile_grid_size_px=(3, 12),
  1039. tile_grid_size_px_min=3,
  1040. from_colorspace=color_lib.CSPACE_RGB,
  1041. to_colorspace=color_lib.CSPACE_Lab,
  1042. seed=None, name=None,
  1043. random_state="deprecated", deterministic="deprecated"):
  1044. super(CLAHE, self).__init__(
  1045. seed=seed, name=name,
  1046. random_state=random_state, deterministic=deterministic)
  1047. self.all_channel_clahe = AllChannelsCLAHE(
  1048. clip_limit=clip_limit,
  1049. tile_grid_size_px=tile_grid_size_px,
  1050. tile_grid_size_px_min=tile_grid_size_px_min,
  1051. name="%s_AllChannelsCLAHE" % (name,))
  1052. self.intensity_channel_based_applier = _IntensityChannelBasedApplier(
  1053. from_colorspace, to_colorspace)
  1054. # Added in 0.4.0.
  1055. def _augment_batch_(self, batch, random_state, parents, hooks):
  1056. if batch.images is None:
  1057. return batch
  1058. images = batch.images
  1059. iadt.gate_dtypes(
  1060. images,
  1061. allowed=["uint8"],
  1062. disallowed=["bool",
  1063. "uint16", "uint32", "uint64", "uint128", "uint256",
  1064. "int8", "int16", "int32", "int64", "int128", "int256",
  1065. "float16", "float32", "float64", "float96", "float128",
  1066. "float256"],
  1067. augmenter=self)
  1068. def _augment_all_channels_clahe(images_normalized,
  1069. random_state_derived):
  1070. # pylint: disable=protected-access
  1071. # TODO would .augment_batch() be sufficient here?
  1072. batch_imgs = _BatchInAugmentation(
  1073. images=images_normalized)
  1074. return self.all_channel_clahe._augment_batch_(
  1075. batch_imgs, random_state_derived, parents + [self],
  1076. hooks
  1077. ).images
  1078. batch.images = self.intensity_channel_based_applier.apply(
  1079. images, random_state, parents + [self], hooks,
  1080. _augment_all_channels_clahe)
  1081. return batch
  1082. def get_parameters(self):
  1083. """See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`."""
  1084. ac_clahe = self.all_channel_clahe
  1085. intb_applier = self.intensity_channel_based_applier
  1086. return [
  1087. ac_clahe.clip_limit,
  1088. ac_clahe.tile_grid_size_px,
  1089. ac_clahe.tile_grid_size_px_min
  1090. ] + intb_applier.get_parameters()
  1091. class AllChannelsHistogramEqualization(meta.Augmenter):
  1092. """
  1093. Apply Histogram Eq. to all channels of images in their original colorspaces.
  1094. In contrast to ``imgaug.augmenters.contrast.HistogramEqualization``, this
  1095. augmenter operates directly on all channels of the input images. It does
  1096. not perform any colorspace transformations and does not focus on specific
  1097. channels (e.g. ``L`` in ``Lab`` colorspace).
  1098. **Supported dtypes**:
  1099. * ``uint8``: yes; fully tested
  1100. * ``uint16``: no (1)
  1101. * ``uint32``: no (2)
  1102. * ``uint64``: no (1)
  1103. * ``int8``: no (1)
  1104. * ``int16``: no (1)
  1105. * ``int32``: no (1)
  1106. * ``int64``: no (1)
  1107. * ``float16``: no (2)
  1108. * ``float32``: no (1)
  1109. * ``float64``: no (1)
  1110. * ``float128``: no (2)
  1111. * ``bool``: no (1)
  1112. - (1) causes cv2 error: ``cv2.error:
  1113. OpenCV(3.4.5) (...)/histogram.cpp:3345: error: (-215:Assertion
  1114. failed) src.type() == CV_8UC1 in function 'equalizeHist'``
  1115. - (2) rejected by cv2
  1116. Parameters
  1117. ----------
  1118. seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
  1119. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  1120. name : None or str, optional
  1121. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  1122. random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
  1123. Old name for parameter `seed`.
  1124. Its usage will not yet cause a deprecation warning,
  1125. but it is still recommended to use `seed` now.
  1126. Outdated since 0.4.0.
  1127. deterministic : bool, optional
  1128. Deprecated since 0.4.0.
  1129. See method ``to_deterministic()`` for an alternative and for
  1130. details about what the "deterministic mode" actually does.
  1131. Examples
  1132. --------
  1133. >>> import imgaug.augmenters as iaa
  1134. >>> aug = iaa.AllChannelsHistogramEqualization()
  1135. Create an augmenter that applies histogram equalization to all channels
  1136. of input images in the original colorspaces.
  1137. >>> aug = iaa.Alpha((0.0, 1.0), iaa.AllChannelsHistogramEqualization())
  1138. Same as in the previous example, but alpha-blends the contrast-enhanced
  1139. augmented images with the original input images using random blend
  1140. strengths. This leads to random strengths of the contrast adjustment.
  1141. """
  1142. def __init__(self, seed=None, name=None,
  1143. random_state="deprecated", deterministic="deprecated"):
  1144. super(AllChannelsHistogramEqualization, self).__init__(
  1145. seed=seed, name=name,
  1146. random_state=random_state, deterministic=deterministic)
  1147. # Added in 0.4.0.
  1148. def _augment_batch_(self, batch, random_state, parents, hooks):
  1149. if batch.images is None:
  1150. return batch
  1151. images = batch.images
  1152. iadt.gate_dtypes(
  1153. images,
  1154. allowed=["uint8"],
  1155. disallowed=["bool",
  1156. "uint16", "uint32", "uint64", "uint128", "uint256",
  1157. "int8", "int16", "int32", "int64", "int128", "int256",
  1158. "float16", "float32", "float64", "float96", "float128",
  1159. "float256"],
  1160. augmenter=self)
  1161. for i, image in enumerate(images):
  1162. if image.size == 0:
  1163. continue
  1164. image_warped = [
  1165. cv2.equalizeHist(_normalize_cv2_input_arr_(image[..., c]))
  1166. for c in sm.xrange(image.shape[2])]
  1167. image_warped = np.array(image_warped, dtype=image_warped[0].dtype)
  1168. image_warped = image_warped.transpose((1, 2, 0))
  1169. batch.images[i] = image_warped
  1170. return batch
  1171. def get_parameters(self):
  1172. """See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`."""
  1173. return []
  1174. class HistogramEqualization(meta.Augmenter):
  1175. """
  1176. Apply Histogram Eq. to L/V/L channels of images in HLS/HSV/Lab colorspaces.
  1177. This augmenter is similar to ``imgaug.augmenters.contrast.CLAHE``.
  1178. The augmenter transforms input images to a target colorspace (e.g.
  1179. ``Lab``), extracts an intensity-related channel from the converted images
  1180. (e.g. ``L`` for ``Lab``), applies Histogram Equalization to the channel
  1181. and then converts the resulting image back to the original colorspace.
  1182. Grayscale images (images without channel axis or with only one channel
  1183. axis) are automatically handled, `from_colorspace` does not have to be
  1184. adjusted for them. For images with four channels (e.g. RGBA), the fourth
  1185. channel is ignored in the colorspace conversion (e.g. from an ``RGBA``
  1186. image, only the ``RGB`` part is converted, normalized, converted back and
  1187. concatenated with the input ``A`` channel). Images with unusual channel
  1188. numbers (2, 5 or more than 5) are normalized channel-by-channel (same
  1189. behaviour as ``AllChannelsHistogramEqualization``, though a warning will
  1190. be raised).
  1191. If you want to apply HistogramEqualization to each channel of the original
  1192. input image's colorspace (without any colorspace conversion), use
  1193. ``imgaug.augmenters.contrast.AllChannelsHistogramEqualization`` instead.
  1194. **Supported dtypes**:
  1195. * ``uint8``: yes; fully tested
  1196. * ``uint16``: no (1)
  1197. * ``uint32``: no (1)
  1198. * ``uint64``: no (1)
  1199. * ``int8``: no (1)
  1200. * ``int16``: no (1)
  1201. * ``int32``: no (1)
  1202. * ``int64``: no (1)
  1203. * ``float16``: no (1)
  1204. * ``float32``: no (1)
  1205. * ``float64``: no (1)
  1206. * ``float128``: no (1)
  1207. * ``bool``: no (1)
  1208. - (1) This augmenter uses :class:`AllChannelsHistogramEqualization`,
  1209. which only supports ``uint8``.
  1210. Parameters
  1211. ----------
  1212. from_colorspace : {"RGB", "BGR", "HSV", "HLS", "Lab"}, optional
  1213. Colorspace of the input images.
  1214. If any input image has only one or zero channels, this setting will be
  1215. ignored and it will be assumed that the input is grayscale.
  1216. If a fourth channel is present in an input image, it will be removed
  1217. before the colorspace conversion and later re-added.
  1218. See also :func:`~imgaug.augmenters.color.change_colorspace_` for
  1219. details.
  1220. to_colorspace : {"Lab", "HLS", "HSV"}, optional
  1221. Colorspace in which to perform Histogram Equalization. For ``Lab``,
  1222. the equalization will only be applied to the first channel (``L``),
  1223. for ``HLS`` to the second (``L``) and for ``HSV`` to the third (``V``).
  1224. To apply histogram equalization to all channels of an input image
  1225. (without colorspace conversion), see
  1226. ``imgaug.augmenters.contrast.AllChannelsHistogramEqualization``.
  1227. seed : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
  1228. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  1229. name : None or str, optional
  1230. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  1231. random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
  1232. Old name for parameter `seed`.
  1233. Its usage will not yet cause a deprecation warning,
  1234. but it is still recommended to use `seed` now.
  1235. Outdated since 0.4.0.
  1236. deterministic : bool, optional
  1237. Deprecated since 0.4.0.
  1238. See method ``to_deterministic()`` for an alternative and for
  1239. details about what the "deterministic mode" actually does.
  1240. Examples
  1241. --------
  1242. >>> import imgaug.augmenters as iaa
  1243. >>> aug = iaa.HistogramEqualization()
  1244. Create an augmenter that converts images to ``HLS``/``HSV``/``Lab``
  1245. colorspaces, extracts intensity-related channels (i.e. ``L``/``V``/``L``),
  1246. applies histogram equalization to these channels and converts back to the
  1247. input colorspace.
  1248. >>> aug = iaa.Alpha((0.0, 1.0), iaa.HistogramEqualization())
  1249. Same as in the previous example, but alpha blends the result, leading
  1250. to various strengths of contrast normalization.
  1251. >>> aug = iaa.HistogramEqualization(
  1252. >>> from_colorspace=iaa.CSPACE_BGR,
  1253. >>> to_colorspace=iaa.CSPACE_HSV)
  1254. Same as in the first example, but the colorspace of input images has
  1255. to be ``BGR`` (instead of default ``RGB``) and the histogram equalization
  1256. is applied to the ``V`` channel in ``HSV`` colorspace.
  1257. """
  1258. RGB = color_lib.CSPACE_RGB
  1259. BGR = color_lib.CSPACE_BGR
  1260. HSV = color_lib.CSPACE_HSV
  1261. HLS = color_lib.CSPACE_HLS
  1262. Lab = color_lib.CSPACE_Lab
  1263. def __init__(self, from_colorspace=color_lib.CSPACE_RGB,
  1264. to_colorspace=color_lib.CSPACE_Lab,
  1265. seed=None, name=None,
  1266. random_state="deprecated", deterministic="deprecated"):
  1267. super(HistogramEqualization, self).__init__(
  1268. seed=seed, name=name,
  1269. random_state=random_state, deterministic=deterministic)
  1270. self.all_channel_histogram_equalization = \
  1271. AllChannelsHistogramEqualization(
  1272. name="%s_AllChannelsHistogramEqualization" % (name,))
  1273. self.intensity_channel_based_applier = _IntensityChannelBasedApplier(
  1274. from_colorspace, to_colorspace)
  1275. # Added in 0.4.0.
  1276. def _augment_batch_(self, batch, random_state, parents, hooks):
  1277. if batch.images is None:
  1278. return batch
  1279. images = batch.images
  1280. iadt.gate_dtypes(
  1281. images,
  1282. allowed=["uint8"],
  1283. disallowed=["bool",
  1284. "uint16", "uint32", "uint64", "uint128", "uint256",
  1285. "int8", "int16", "int32", "int64", "int128", "int256",
  1286. "float16", "float32", "float64", "float96", "float128",
  1287. "float256"],
  1288. augmenter=self)
  1289. def _augment_all_channels_histogram_equalization(images_normalized,
  1290. random_state_derived):
  1291. # pylint: disable=protected-access
  1292. # TODO would .augment_batch() be sufficient here
  1293. batch_imgs = _BatchInAugmentation(
  1294. images=images_normalized)
  1295. return self.all_channel_histogram_equalization._augment_batch_(
  1296. batch_imgs, random_state_derived, parents + [self],
  1297. hooks
  1298. ).images
  1299. batch.images = self.intensity_channel_based_applier.apply(
  1300. images, random_state, parents + [self], hooks,
  1301. _augment_all_channels_histogram_equalization)
  1302. return batch
  1303. def get_parameters(self):
  1304. """See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`."""
  1305. icb_applier = self.intensity_channel_based_applier
  1306. return icb_applier.get_parameters()