segmentation.py 75 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705
  1. """
  2. Augmenters that apply changes to images based on segmentation methods.
  3. List of augmenters:
  4. * :class:`Superpixels`
  5. * :class:`Voronoi`
  6. * :class:`UniformVoronoi`
  7. * :class:`RegularGridVoronoi`
  8. * :class:`RelativeRegularGridVoronoi`
  9. """
  10. from __future__ import print_function, division, absolute_import
  11. from abc import ABCMeta, abstractmethod
  12. import numpy as np
  13. # use skimage.segmentation instead `from skimage import segmentation` here,
  14. # because otherwise unittest seems to mix up imgaug.augmenters.segmentation
  15. # with skimage.segmentation for whatever reason
  16. import skimage.segmentation
  17. import skimage.measure
  18. import six
  19. import six.moves as sm
  20. import imgaug as ia
  21. from . import meta
  22. from .. import random as iarandom
  23. from .. import parameters as iap
  24. from .. import dtypes as iadt
  25. # TODO merge this into imresize?
  26. def _ensure_image_max_size(image, max_size, interpolation):
  27. """Ensure that images do not exceed a required maximum sidelength.
  28. This downscales to `max_size` if any side violates that maximum.
  29. The other side is downscaled too so that the aspect ratio is maintained.
  30. **Supported dtypes**:
  31. See :func:`~imgaug.imgaug.imresize_single_image`.
  32. Parameters
  33. ----------
  34. image : ndarray
  35. Image to potentially downscale.
  36. max_size : int
  37. Maximum length of any side of the image.
  38. interpolation : string or int
  39. See :func:`~imgaug.imgaug.imresize_single_image`.
  40. """
  41. if max_size is not None:
  42. size = max(image.shape[0], image.shape[1])
  43. if size > max_size:
  44. resize_factor = max_size / size
  45. new_height = int(image.shape[0] * resize_factor)
  46. new_width = int(image.shape[1] * resize_factor)
  47. image = ia.imresize_single_image(
  48. image,
  49. (new_height, new_width),
  50. interpolation=interpolation)
  51. return image
  52. # TODO add compactness parameter
  53. class Superpixels(meta.Augmenter):
  54. """Transform images parially/completely to their superpixel representation.
  55. This implementation uses skimage's version of the SLIC algorithm.
  56. .. note::
  57. This augmenter is fairly slow. See :ref:`performance`.
  58. **Supported dtypes**:
  59. if (image size <= max_size):
  60. * ``uint8``: yes; fully tested
  61. * ``uint16``: yes; tested
  62. * ``uint32``: yes; tested
  63. * ``uint64``: limited (1)
  64. * ``int8``: yes; tested
  65. * ``int16``: yes; tested
  66. * ``int32``: yes; tested
  67. * ``int64``: limited (1)
  68. * ``float16``: no (2)
  69. * ``float32``: no (2)
  70. * ``float64``: no (3)
  71. * ``float128``: no (2)
  72. * ``bool``: yes; tested
  73. - (1) Superpixel mean intensity replacement requires computing
  74. these means as ``float64`` s. This can cause inaccuracies for
  75. large integer values.
  76. - (2) Error in scikit-image.
  77. - (3) Loss of resolution in scikit-image.
  78. if (image size > max_size):
  79. minimum of (
  80. ``imgaug.augmenters.segmentation.Superpixels(image size <= max_size)``,
  81. :func:`~imgaug.augmenters.segmentation._ensure_image_max_size`
  82. )
  83. Parameters
  84. ----------
  85. p_replace : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  86. Defines for any segment the probability that the pixels within that
  87. segment are replaced by their average color (otherwise, the pixels
  88. are not changed).
  89. Examples:
  90. * A probability of ``0.0`` would mean, that the pixels in no
  91. segment are replaced by their average color (image is not
  92. changed at all).
  93. * A probability of ``0.5`` would mean, that around half of all
  94. segments are replaced by their average color.
  95. * A probability of ``1.0`` would mean, that all segments are
  96. replaced by their average color (resulting in a voronoi
  97. image).
  98. Behaviour based on chosen datatypes for this parameter:
  99. * If a ``number``, then that ``number`` will always be used.
  100. * If ``tuple`` ``(a, b)``, then a random probability will be
  101. sampled from the interval ``[a, b]`` per image.
  102. * If a ``list``, then a random value will be sampled from that
  103. ``list`` per image.
  104. * If a ``StochasticParameter``, it is expected to return
  105. values between ``0.0`` and ``1.0`` and will be queried *for each
  106. individual segment* to determine whether it is supposed to
  107. be averaged (``>0.5``) or not (``<=0.5``).
  108. Recommended to be some form of ``Binomial(...)``.
  109. n_segments : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional
  110. Rough target number of how many superpixels to generate (the algorithm
  111. may deviate from this number). Lower value will lead to coarser
  112. superpixels. Higher values are computationally more intensive and
  113. will hence lead to a slowdown.
  114. * If a single ``int``, then that value will always be used as the
  115. number of segments.
  116. * If a ``tuple`` ``(a, b)``, then a value from the discrete
  117. interval ``[a..b]`` will be sampled per image.
  118. * If a ``list``, then a random value will be sampled from that
  119. ``list`` per image.
  120. * If a ``StochasticParameter``, then that parameter will be
  121. queried to draw one value per image.
  122. max_size : int or None, optional
  123. Maximum image size at which the augmentation is performed.
  124. If the width or height of an image exceeds this value, it will be
  125. downscaled before the augmentation so that the longest side
  126. matches `max_size`.
  127. This is done to speed up the process. The final output image has the
  128. same size as the input image. Note that in case `p_replace` is below
  129. ``1.0``, the down-/upscaling will affect the not-replaced pixels too.
  130. Use ``None`` to apply no down-/upscaling.
  131. interpolation : int or str, optional
  132. Interpolation method to use during downscaling when `max_size` is
  133. exceeded. Valid methods are the same as in
  134. :func:`~imgaug.imgaug.imresize_single_image`.
  135. 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
  136. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  137. name : None or str, optional
  138. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  139. 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
  140. Old name for parameter `seed`.
  141. Its usage will not yet cause a deprecation warning,
  142. but it is still recommended to use `seed` now.
  143. Outdated since 0.4.0.
  144. deterministic : bool, optional
  145. Deprecated since 0.4.0.
  146. See method ``to_deterministic()`` for an alternative and for
  147. details about what the "deterministic mode" actually does.
  148. Examples
  149. --------
  150. >>> import imgaug.augmenters as iaa
  151. >>> aug = iaa.Superpixels(p_replace=1.0, n_segments=64)
  152. Generate around ``64`` superpixels per image and replace all of them with
  153. their average color (standard superpixel image).
  154. >>> aug = iaa.Superpixels(p_replace=0.5, n_segments=64)
  155. Generate around ``64`` superpixels per image and replace half of them
  156. with their average color, while the other half are left unchanged (i.e.
  157. they still show the input image's content).
  158. >>> aug = iaa.Superpixels(p_replace=(0.25, 1.0), n_segments=(16, 128))
  159. Generate between ``16`` and ``128`` superpixels per image and replace
  160. ``25`` to ``100`` percent of them with their average color.
  161. """
  162. def __init__(self, p_replace=(0.5, 1.0), n_segments=(50, 120),
  163. max_size=128, interpolation="linear",
  164. seed=None, name=None,
  165. random_state="deprecated", deterministic="deprecated"):
  166. super(Superpixels, self).__init__(
  167. seed=seed, name=name,
  168. random_state=random_state, deterministic=deterministic)
  169. self.p_replace = iap.handle_probability_param(
  170. p_replace, "p_replace", tuple_to_uniform=True, list_to_choice=True)
  171. self.n_segments = iap.handle_discrete_param(
  172. n_segments, "n_segments", value_range=(1, None),
  173. tuple_to_uniform=True, list_to_choice=True, allow_floats=False)
  174. self.max_size = max_size
  175. self.interpolation = interpolation
  176. # Added in 0.4.0.
  177. def _augment_batch_(self, batch, random_state, parents, hooks):
  178. if batch.images is None:
  179. return batch
  180. images = batch.images
  181. iadt.gate_dtypes(images,
  182. allowed=["bool",
  183. "uint8", "uint16", "uint32", "uint64",
  184. "int8", "int16", "int32", "int64"],
  185. disallowed=["uint128", "uint256",
  186. "int128", "int256",
  187. "float16", "float32", "float64",
  188. "float96", "float128", "float256"],
  189. augmenter=self)
  190. nb_images = len(images)
  191. rss = random_state.duplicate(1+nb_images)
  192. n_segments_samples = self.n_segments.draw_samples(
  193. (nb_images,), random_state=rss[0])
  194. # We cant reduce images to 0 or less segments, hence we pick the
  195. # lowest possible value in these cases (i.e. 1). The alternative
  196. # would be to not perform superpixel detection in these cases
  197. # (akin to n_segments=#pixels).
  198. # TODO add test for this
  199. n_segments_samples = np.clip(n_segments_samples, 1, None)
  200. for i, (image, rs) in enumerate(zip(images, rss[1:])):
  201. if image.size == 0:
  202. # Image with 0-sized axis, nothing to change.
  203. # Placing this before the sampling step should be fine.
  204. continue
  205. replace_samples = self.p_replace.draw_samples(
  206. (n_segments_samples[i],), random_state=rs)
  207. if np.max(replace_samples) == 0:
  208. # not a single superpixel would be replaced by its average
  209. # color, i.e. the image would not be changed, so just keep it
  210. continue
  211. orig_shape = image.shape
  212. image = _ensure_image_max_size(image, self.max_size,
  213. self.interpolation)
  214. segments = skimage.segmentation.slic(
  215. image, n_segments=n_segments_samples[i], compactness=10)
  216. image_aug = self._replace_segments(image, segments, replace_samples)
  217. if orig_shape != image_aug.shape:
  218. image_aug = ia.imresize_single_image(
  219. image_aug,
  220. orig_shape[0:2],
  221. interpolation=self.interpolation)
  222. batch.images[i] = image_aug
  223. return batch
  224. @classmethod
  225. def _replace_segments(cls, image, segments, replace_samples):
  226. min_value, _center_value, max_value = \
  227. iadt.get_value_range_of_dtype(image.dtype)
  228. image_sp = np.copy(image)
  229. nb_channels = image.shape[2]
  230. for c in sm.xrange(nb_channels):
  231. # segments+1 here because otherwise regionprops always
  232. # misses the last label
  233. regions = skimage.measure.regionprops(
  234. segments+1, intensity_image=image[..., c])
  235. for ridx, region in enumerate(regions):
  236. # with mod here, because slic can sometimes create more
  237. # superpixel than requested. replace_samples then does not
  238. # have enough values, so we just start over with the first one
  239. # again.
  240. if replace_samples[ridx % len(replace_samples)] > 0.5:
  241. mean_intensity = region.mean_intensity
  242. image_sp_c = image_sp[..., c]
  243. if image_sp_c.dtype.kind in ["i", "u", "b"]:
  244. # After rounding the value can end up slightly outside
  245. # of the value_range. Hence, we need to clip. We do
  246. # clip via min(max(...)) instead of np.clip because
  247. # the latter one does not seem to keep dtypes for
  248. # dtypes with large itemsizes (e.g. uint64).
  249. value = int(np.round(mean_intensity))
  250. value = min(max(value, min_value), max_value)
  251. else:
  252. value = mean_intensity
  253. image_sp_c[segments == ridx] = value
  254. return image_sp
  255. def get_parameters(self):
  256. """See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`."""
  257. return [self.p_replace, self.n_segments, self.max_size,
  258. self.interpolation]
  259. # TODO don't average the alpha channel for RGBA?
  260. def segment_voronoi(image, cell_coordinates, replace_mask=None):
  261. """Average colors within voronoi cells of an image.
  262. Parameters
  263. ----------
  264. image : ndarray
  265. The image to convert to a voronoi image. May be ``HxW`` or
  266. ``HxWxC``. Note that for ``RGBA`` images the alpha channel
  267. will currently also by averaged.
  268. cell_coordinates : ndarray
  269. A ``Nx2`` float array containing the center coordinates of voronoi
  270. cells on the image. Values are expected to be in the interval
  271. ``[0.0, height-1.0]`` for the y-axis (x-axis analogous).
  272. If this array contains no coordinate, the image will not be
  273. changed.
  274. replace_mask : None or ndarray, optional
  275. Boolean mask of the same length as `cell_coordinates`, denoting
  276. for each cell whether its pixels are supposed to be replaced
  277. by the cell's average color (``True``) or left untouched (``False``).
  278. If this is set to ``None``, all cells will be replaced.
  279. Returns
  280. -------
  281. ndarray
  282. Voronoi image.
  283. """
  284. input_dims = image.ndim
  285. if input_dims == 2:
  286. image = image[..., np.newaxis]
  287. if len(cell_coordinates) <= 0:
  288. if input_dims == 2:
  289. return image[..., 0]
  290. return image
  291. height, width = image.shape[0:2]
  292. pixel_coords, ids_of_nearest_cells = \
  293. _match_pixels_with_voronoi_cells(height, width, cell_coordinates)
  294. cell_colors = _compute_avg_segment_colors(
  295. image, pixel_coords, ids_of_nearest_cells,
  296. len(cell_coordinates))
  297. image_aug = _render_segments(image, ids_of_nearest_cells, cell_colors,
  298. replace_mask)
  299. if input_dims == 2:
  300. return image_aug[..., 0]
  301. return image_aug
  302. def _match_pixels_with_voronoi_cells(height, width, cell_coordinates):
  303. # deferred import so that scipy is an optional dependency
  304. from scipy.spatial import cKDTree as KDTree # TODO add scipy for reqs
  305. tree = KDTree(cell_coordinates)
  306. pixel_coords = _generate_pixel_coords(height, width)
  307. pixel_coords_subpixel = pixel_coords.astype(np.float32) + 0.5
  308. ids_of_nearest_cells = tree.query(pixel_coords_subpixel)[1]
  309. return pixel_coords, ids_of_nearest_cells
  310. def _generate_pixel_coords(height, width):
  311. xx, yy = np.meshgrid(np.arange(width), np.arange(height))
  312. return np.c_[xx.ravel(), yy.ravel()]
  313. def _compute_avg_segment_colors(image, pixel_coords, ids_of_nearest_segments,
  314. nb_segments):
  315. nb_channels = image.shape[2]
  316. cell_colors = np.zeros((nb_segments, nb_channels), dtype=np.float64)
  317. cell_counters = np.zeros((nb_segments,), dtype=np.uint32)
  318. # TODO vectorize
  319. for pixel_coord, id_of_nearest_cell in zip(pixel_coords,
  320. ids_of_nearest_segments):
  321. # pixel_coord is (x,y), so we have to swap it to access the HxW image
  322. pixel_coord_yx = pixel_coord[::-1]
  323. cell_colors[id_of_nearest_cell] += image[tuple(pixel_coord_yx)]
  324. cell_counters[id_of_nearest_cell] += 1
  325. # cells without associated pixels can have a count of 0, we clip
  326. # here to 1 as the result for these cells doesn't matter
  327. cell_counters = np.clip(cell_counters, 1, None)
  328. cell_colors = cell_colors / cell_counters[:, np.newaxis]
  329. return cell_colors.astype(np.uint8)
  330. def _render_segments(image, ids_of_nearest_segments, avg_segment_colors,
  331. replace_mask):
  332. ids_of_nearest_segments = np.copy(ids_of_nearest_segments)
  333. height, width, nb_channels = image.shape
  334. # without replace_mask we could reduce this down to:
  335. # data = cell_colors[ids_of_nearest_cells, :].reshape(
  336. # (width, height, 3))
  337. # data = np.transpose(data, (1, 0, 2))
  338. keep_mask = (~replace_mask) if replace_mask is not None else None
  339. if keep_mask is None or not np.any(keep_mask):
  340. data = avg_segment_colors[ids_of_nearest_segments, :]
  341. else:
  342. ids_to_keep = np.nonzero(keep_mask)[0]
  343. indices_to_keep = np.where(
  344. np.isin(ids_of_nearest_segments, ids_to_keep))[0]
  345. data = avg_segment_colors[ids_of_nearest_segments, :]
  346. image_data = image.reshape((height*width, -1))
  347. data[indices_to_keep] = image_data[indices_to_keep, :]
  348. data = data.reshape((height, width, nb_channels))
  349. return data
  350. # TODO this can be reduced down to a similar problem as Superpixels:
  351. # generate an integer-based class id map of segments, then replace all
  352. # segments with the same class id by the average color within that
  353. # segment
  354. class Voronoi(meta.Augmenter):
  355. """Average colors of an image within Voronoi cells.
  356. This augmenter performs the following steps:
  357. 1. Query `points_sampler` to sample random coordinates of cell
  358. centers. On the image.
  359. 2. Estimate for each pixel to which voronoi cell (i.e. segment)
  360. it belongs. Each pixel belongs to the cell with the closest center
  361. coordinate (euclidean distance).
  362. 3. Compute for each cell the average color of the pixels within it.
  363. 4. Replace the pixels of `p_replace` percent of all cells by their
  364. average color. Do not change the pixels of ``(1 - p_replace)``
  365. percent of all cells. (The percentages are average values over
  366. many images. Some images may get more/less cells replaced by
  367. their average color.)
  368. This code is very loosely based on
  369. https://codegolf.stackexchange.com/questions/50299/draw-an-image-as-a-voronoi-map/50345#50345
  370. **Supported dtypes**:
  371. if (image size <= max_size):
  372. * ``uint8``: yes; fully tested
  373. * ``uint16``: no; not tested
  374. * ``uint32``: no; not tested
  375. * ``uint64``: no; not tested
  376. * ``int8``: no; not tested
  377. * ``int16``: no; not tested
  378. * ``int32``: no; not tested
  379. * ``int64``: no; not tested
  380. * ``float16``: no; not tested
  381. * ``float32``: no; not tested
  382. * ``float64``: no; not tested
  383. * ``float128``: no; not tested
  384. * ``bool``: no; not tested
  385. if (image size > max_size):
  386. minimum of (
  387. ``imgaug.augmenters.segmentation.Voronoi(image size <= max_size)``,
  388. :func:`~imgaug.augmenters.segmentation._ensure_image_max_size`
  389. )
  390. Parameters
  391. ----------
  392. points_sampler : IPointsSampler
  393. A points sampler which will be queried per image to generate the
  394. coordinates of the centers of voronoi cells.
  395. p_replace : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  396. Defines for any segment the probability that the pixels within that
  397. segment are replaced by their average color (otherwise, the pixels
  398. are not changed).
  399. Examples:
  400. * A probability of ``0.0`` would mean, that the pixels in no
  401. segment are replaced by their average color (image is not
  402. changed at all).
  403. * A probability of ``0.5`` would mean, that around half of all
  404. segments are replaced by their average color.
  405. * A probability of ``1.0`` would mean, that all segments are
  406. replaced by their average color (resulting in a voronoi
  407. image).
  408. Behaviour based on chosen datatypes for this parameter:
  409. * If a ``number``, then that ``number`` will always be used.
  410. * If ``tuple`` ``(a, b)``, then a random probability will be
  411. sampled from the interval ``[a, b]`` per image.
  412. * If a ``list``, then a random value will be sampled from that
  413. ``list`` per image.
  414. * If a ``StochasticParameter``, it is expected to return
  415. values between ``0.0`` and ``1.0`` and will be queried *for each
  416. individual segment* to determine whether it is supposed to
  417. be averaged (``>0.5``) or not (``<=0.5``).
  418. Recommended to be some form of ``Binomial(...)``.
  419. max_size : int or None, optional
  420. Maximum image size at which the augmentation is performed.
  421. If the width or height of an image exceeds this value, it will be
  422. downscaled before the augmentation so that the longest side
  423. matches `max_size`.
  424. This is done to speed up the process. The final output image has the
  425. same size as the input image. Note that in case `p_replace` is below
  426. ``1.0``, the down-/upscaling will affect the not-replaced pixels too.
  427. Use ``None`` to apply no down-/upscaling.
  428. interpolation : int or str, optional
  429. Interpolation method to use during downscaling when `max_size` is
  430. exceeded. Valid methods are the same as in
  431. :func:`~imgaug.imgaug.imresize_single_image`.
  432. 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
  433. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  434. name : None or str, optional
  435. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  436. 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
  437. Old name for parameter `seed`.
  438. Its usage will not yet cause a deprecation warning,
  439. but it is still recommended to use `seed` now.
  440. Outdated since 0.4.0.
  441. deterministic : bool, optional
  442. Deprecated since 0.4.0.
  443. See method ``to_deterministic()`` for an alternative and for
  444. details about what the "deterministic mode" actually does.
  445. Examples
  446. --------
  447. >>> import imgaug.augmenters as iaa
  448. >>> points_sampler = iaa.RegularGridPointsSampler(n_cols=20, n_rows=40)
  449. >>> aug = iaa.Voronoi(points_sampler)
  450. Create an augmenter that places a ``20x40`` (``HxW``) grid of cells on
  451. the image and replaces all pixels within each cell by the cell's average
  452. color. The process is performed at an image size not exceeding ``128`` px
  453. on any side (default). If necessary, the downscaling is performed using
  454. ``linear`` interpolation (default).
  455. >>> points_sampler = iaa.DropoutPointsSampler(
  456. >>> iaa.RelativeRegularGridPointsSampler(
  457. >>> n_cols_frac=(0.05, 0.2),
  458. >>> n_rows_frac=0.1),
  459. >>> 0.2)
  460. >>> aug = iaa.Voronoi(points_sampler, p_replace=0.9, max_size=None)
  461. Create a voronoi augmenter that generates a grid of cells dynamically
  462. adapted to the image size. Larger images get more cells. On the x-axis,
  463. the distance between two cells is ``w * W`` pixels, where ``W`` is the
  464. width of the image and ``w`` is always ``0.1``. On the y-axis,
  465. the distance between two cells is ``h * H`` pixels, where ``H`` is the
  466. height of the image and ``h`` is sampled uniformly from the interval
  467. ``[0.05, 0.2]``. To make the voronoi pattern less regular, about ``20``
  468. percent of the cell coordinates are randomly dropped (i.e. the remaining
  469. cells grow in size). In contrast to the first example, the image is not
  470. resized (if it was, the sampling would happen *after* the resizing,
  471. which would affect ``W`` and ``H``). Not all voronoi cells are replaced
  472. by their average color, only around ``90`` percent of them. The
  473. remaining ``10`` percent's pixels remain unchanged.
  474. """
  475. def __init__(self, points_sampler, p_replace=1.0, max_size=128,
  476. interpolation="linear",
  477. seed=None, name=None,
  478. random_state="deprecated", deterministic="deprecated"):
  479. super(Voronoi, self).__init__(
  480. seed=seed, name=name,
  481. random_state=random_state, deterministic=deterministic)
  482. assert isinstance(points_sampler, IPointsSampler), (
  483. "Expected 'points_sampler' to be an instance of IPointsSampler, "
  484. "got %s." % (type(points_sampler),))
  485. self.points_sampler = points_sampler
  486. self.p_replace = iap.handle_probability_param(
  487. p_replace, "p_replace", tuple_to_uniform=True, list_to_choice=True)
  488. self.max_size = max_size
  489. self.interpolation = interpolation
  490. # Added in 0.4.0.
  491. def _augment_batch_(self, batch, random_state, parents, hooks):
  492. if batch.images is None:
  493. return batch
  494. images = batch.images
  495. iadt.gate_dtypes(images,
  496. allowed=["uint8"],
  497. disallowed=["bool",
  498. "uint16", "uint32", "uint64", "uint128",
  499. "uint256",
  500. "int8", "int16", "int32", "int64",
  501. "int128", "int256",
  502. "float16", "float32", "float64",
  503. "float96", "float128", "float256"],
  504. augmenter=self)
  505. rss = random_state.duplicate(len(images))
  506. for i, (image, rs) in enumerate(zip(images, rss)):
  507. batch.images[i] = self._augment_single_image(image, rs)
  508. return batch
  509. def _augment_single_image(self, image, random_state):
  510. rss = random_state.duplicate(2)
  511. orig_shape = image.shape
  512. image = _ensure_image_max_size(image, self.max_size, self.interpolation)
  513. cell_coordinates = self.points_sampler.sample_points([image], rss[0])[0]
  514. p_replace = self.p_replace.draw_samples((len(cell_coordinates),),
  515. rss[1])
  516. replace_mask = (p_replace > 0.5)
  517. image_aug = segment_voronoi(image, cell_coordinates, replace_mask)
  518. if orig_shape != image_aug.shape:
  519. image_aug = ia.imresize_single_image(
  520. image_aug,
  521. orig_shape[0:2],
  522. interpolation=self.interpolation)
  523. return image_aug
  524. def get_parameters(self):
  525. """See :func:`~imgaug.augmenters.meta.Augmenter.get_parameters`."""
  526. return [self.points_sampler, self.p_replace, self.max_size,
  527. self.interpolation]
  528. class UniformVoronoi(Voronoi):
  529. """Uniformly sample Voronoi cells on images and average colors within them.
  530. This augmenter is a shortcut for the combination of
  531. :class:`~imgaug.augmenters.segmentation.Voronoi` with
  532. :class:`~imgaug.augmenters.segmentation.UniformPointsSampler`. Hence, it
  533. generates a fixed amount of ``N`` random coordinates of voronoi cells on
  534. each image. The cell coordinates are sampled uniformly using the image
  535. height and width as maxima.
  536. **Supported dtypes**:
  537. See :class:`~imgaug.augmenters.segmentation.Voronoi`.
  538. Parameters
  539. ----------
  540. n_points : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional
  541. Number of points to sample on each image.
  542. * If a single ``int``, then that value will always be used.
  543. * If a ``tuple`` ``(a, b)``, then a value from the discrete
  544. interval ``[a..b]`` will be sampled per image.
  545. * If a ``list``, then a random value will be sampled from that
  546. ``list`` per image.
  547. * If a ``StochasticParameter``, then that parameter will be
  548. queried to draw one value per image.
  549. p_replace : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  550. Defines for any segment the probability that the pixels within that
  551. segment are replaced by their average color (otherwise, the pixels
  552. are not changed).
  553. Examples:
  554. * A probability of ``0.0`` would mean, that the pixels in no
  555. segment are replaced by their average color (image is not
  556. changed at all).
  557. * A probability of ``0.5`` would mean, that around half of all
  558. segments are replaced by their average color.
  559. * A probability of ``1.0`` would mean, that all segments are
  560. replaced by their average color (resulting in a voronoi
  561. image).
  562. Behaviour based on chosen datatypes for this parameter:
  563. * If a ``number``, then that ``number`` will always be used.
  564. * If ``tuple`` ``(a, b)``, then a random probability will be
  565. sampled from the interval ``[a, b]`` per image.
  566. * If a ``list``, then a random value will be sampled from that
  567. ``list`` per image.
  568. * If a ``StochasticParameter``, it is expected to return
  569. values between ``0.0`` and ``1.0`` and will be queried *for each
  570. individual segment* to determine whether it is supposed to
  571. be averaged (``>0.5``) or not (``<=0.5``).
  572. Recommended to be some form of ``Binomial(...)``.
  573. max_size : int or None, optional
  574. Maximum image size at which the augmentation is performed.
  575. If the width or height of an image exceeds this value, it will be
  576. downscaled before the augmentation so that the longest side
  577. matches `max_size`.
  578. This is done to speed up the process. The final output image has the
  579. same size as the input image. Note that in case `p_replace` is below
  580. ``1.0``, the down-/upscaling will affect the not-replaced pixels too.
  581. Use ``None`` to apply no down-/upscaling.
  582. interpolation : int or str, optional
  583. Interpolation method to use during downscaling when `max_size` is
  584. exceeded. Valid methods are the same as in
  585. :func:`~imgaug.imgaug.imresize_single_image`.
  586. 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
  587. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  588. name : None or str, optional
  589. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  590. 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
  591. Old name for parameter `seed`.
  592. Its usage will not yet cause a deprecation warning,
  593. but it is still recommended to use `seed` now.
  594. Outdated since 0.4.0.
  595. deterministic : bool, optional
  596. Deprecated since 0.4.0.
  597. See method ``to_deterministic()`` for an alternative and for
  598. details about what the "deterministic mode" actually does.
  599. Examples
  600. --------
  601. >>> import imgaug.augmenters as iaa
  602. >>> aug = iaa.UniformVoronoi((100, 500))
  603. Sample for each image uniformly the number of voronoi cells ``N`` from the
  604. interval ``[100, 500]``. Then generate ``N`` coordinates by sampling
  605. uniformly the x-coordinates from ``[0, W]`` and the y-coordinates from
  606. ``[0, H]``, where ``H`` is the image height and ``W`` the image width.
  607. Then use these coordinates to group the image pixels into voronoi
  608. cells and average the colors within them. The process is performed at an
  609. image size not exceeding ``128`` px on any side (default). If necessary,
  610. the downscaling is performed using ``linear`` interpolation (default).
  611. >>> aug = iaa.UniformVoronoi(250, p_replace=0.9, max_size=None)
  612. Same as above, but always samples ``N=250`` cells, replaces only
  613. ``90`` percent of them with their average color (the pixels of the
  614. remaining ``10`` percent are not changed) and performs the transformation
  615. at the original image size (``max_size=None``).
  616. """
  617. def __init__(self, n_points=(50, 500), p_replace=(0.5, 1.0), max_size=128,
  618. interpolation="linear",
  619. seed=None, name=None,
  620. random_state="deprecated", deterministic="deprecated"):
  621. super(UniformVoronoi, self).__init__(
  622. points_sampler=UniformPointsSampler(n_points),
  623. p_replace=p_replace,
  624. max_size=max_size,
  625. interpolation=interpolation,
  626. seed=seed, name=name,
  627. random_state=random_state, deterministic=deterministic)
  628. class RegularGridVoronoi(Voronoi):
  629. """Sample Voronoi cells from regular grids and color-average them.
  630. This augmenter is a shortcut for the combination of
  631. :class:`~imgaug.augmenters.segmentation.Voronoi`,
  632. :class:`~imgaug.augmenters.segmentation.RegularGridPointsSampler` and
  633. :class:`~imgaug.augmenters.segmentation.DropoutPointsSampler`. Hence, it
  634. generates a regular grid with ``R`` rows and ``C`` columns of coordinates
  635. on each image. Then, it drops ``p`` percent of the ``R*C`` coordinates
  636. to randomize the grid. Each image pixel then belongs to the voronoi
  637. cell with the closest coordinate.
  638. **Supported dtypes**:
  639. See :class:`~imgaug.augmenters.segmentation.Voronoi`.
  640. Parameters
  641. ----------
  642. n_rows : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional
  643. Number of rows of coordinates to place on each image, i.e. the number
  644. of coordinates on the y-axis. Note that for each image, the sampled
  645. value is clipped to the interval ``[1..H]``, where ``H`` is the image
  646. height.
  647. * If a single ``int``, then that value will always be used.
  648. * If a ``tuple`` ``(a, b)``, then a value from the discrete
  649. interval ``[a..b]`` will be sampled per image.
  650. * If a ``list``, then a random value will be sampled from that
  651. ``list`` per image.
  652. * If a ``StochasticParameter``, then that parameter will be
  653. queried to draw one value per image.
  654. n_cols : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional
  655. Number of columns of coordinates to place on each image, i.e. the
  656. number of coordinates on the x-axis. Note that for each image, the
  657. sampled value is clipped to the interval ``[1..W]``, where ``W`` is
  658. the image width.
  659. * If a single ``int``, then that value will always be used.
  660. * If a ``tuple`` ``(a, b)``, then a value from the discrete
  661. interval ``[a..b]`` will be sampled per image.
  662. * If a ``list``, then a random value will be sampled from that
  663. ``list`` per image.
  664. * If a ``StochasticParameter``, then that parameter will be
  665. queried to draw one value per image.
  666. p_drop_points : number or tuple of number or imgaug.parameters.StochasticParameter, optional
  667. The probability that a coordinate will be removed from the list
  668. of all sampled coordinates. A value of ``1.0`` would mean that (on
  669. average) ``100`` percent of all coordinates will be dropped,
  670. while ``0.0`` denotes ``0`` percent. Note that this sampler will
  671. always ensure that at least one coordinate is left after the dropout
  672. operation, i.e. even ``1.0`` will only drop all *except one*
  673. coordinate.
  674. * If a ``float``, then that value will be used for all images.
  675. * If a ``tuple`` ``(a, b)``, then a value ``p`` will be sampled
  676. from the interval ``[a, b]`` per image.
  677. * If a ``StochasticParameter``, then this parameter will be used to
  678. determine per coordinate whether it should be *kept* (sampled
  679. value of ``>0.5``) or shouldn't be kept (sampled value of
  680. ``<=0.5``). If you instead want to provide the probability as
  681. a stochastic parameter, you can usually do
  682. ``imgaug.parameters.Binomial(1-p)`` to convert parameter `p` to
  683. a 0/1 representation.
  684. p_replace : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  685. Defines for any segment the probability that the pixels within that
  686. segment are replaced by their average color (otherwise, the pixels
  687. are not changed).
  688. Examples:
  689. * A probability of ``0.0`` would mean, that the pixels in no
  690. segment are replaced by their average color (image is not
  691. changed at all).
  692. * A probability of ``0.5`` would mean, that around half of all
  693. segments are replaced by their average color.
  694. * A probability of ``1.0`` would mean, that all segments are
  695. replaced by their average color (resulting in a voronoi
  696. image).
  697. Behaviour based on chosen datatypes for this parameter:
  698. * If a ``number``, then that number will always be used.
  699. * If ``tuple`` ``(a, b)``, then a random probability will be
  700. sampled from the interval ``[a, b]`` per image.
  701. * If a ``list``, then a random value will be sampled from that
  702. ``list`` per image.
  703. * If a ``StochasticParameter``, it is expected to return
  704. values between ``0.0`` and ``1.0`` and will be queried *for each
  705. individual segment* to determine whether it is supposed to
  706. be averaged (``>0.5``) or not (``<=0.5``).
  707. Recommended to be some form of ``Binomial(...)``.
  708. max_size : int or None, optional
  709. Maximum image size at which the augmentation is performed.
  710. If the width or height of an image exceeds this value, it will be
  711. downscaled before the augmentation so that the longest side
  712. matches `max_size`.
  713. This is done to speed up the process. The final output image has the
  714. same size as the input image. Note that in case `p_replace` is below
  715. ``1.0``, the down-/upscaling will affect the not-replaced pixels too.
  716. Use ``None`` to apply no down-/upscaling.
  717. interpolation : int or str, optional
  718. Interpolation method to use during downscaling when `max_size` is
  719. exceeded. Valid methods are the same as in
  720. :func:`~imgaug.imgaug.imresize_single_image`.
  721. 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
  722. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  723. name : None or str, optional
  724. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  725. 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
  726. Old name for parameter `seed`.
  727. Its usage will not yet cause a deprecation warning,
  728. but it is still recommended to use `seed` now.
  729. Outdated since 0.4.0.
  730. deterministic : bool, optional
  731. Deprecated since 0.4.0.
  732. See method ``to_deterministic()`` for an alternative and for
  733. details about what the "deterministic mode" actually does.
  734. Examples
  735. --------
  736. >>> import imgaug.augmenters as iaa
  737. >>> aug = iaa.RegularGridVoronoi(10, 20)
  738. Place a regular grid of ``10x20`` (``height x width``) coordinates on
  739. each image. Randomly drop on average ``20`` percent of these points
  740. to create a less regular pattern. Then use the remaining coordinates
  741. to group the image pixels into voronoi cells and average the colors
  742. within them. The process is performed at an image size not exceeding
  743. ``128`` px on any side (default). If necessary, the downscaling is
  744. performed using ``linear`` interpolation (default).
  745. >>> aug = iaa.RegularGridVoronoi(
  746. >>> (10, 30), 20, p_drop_points=0.0, p_replace=0.9, max_size=None)
  747. Same as above, generates a grid with randomly ``10`` to ``30`` rows,
  748. drops none of the generates points, replaces only ``90`` percent of
  749. the voronoi cells with their average color (the pixels of the remaining
  750. ``10`` percent are not changed) and performs the transformation
  751. at the original image size (``max_size=None``).
  752. """
  753. def __init__(self, n_rows=(10, 30), n_cols=(10, 30),
  754. p_drop_points=(0.0, 0.5), p_replace=(0.5, 1.0),
  755. max_size=128, interpolation="linear",
  756. seed=None, name=None,
  757. random_state="deprecated", deterministic="deprecated"):
  758. super(RegularGridVoronoi, self).__init__(
  759. points_sampler=DropoutPointsSampler(
  760. RegularGridPointsSampler(n_rows, n_cols),
  761. p_drop_points
  762. ),
  763. p_replace=p_replace,
  764. max_size=max_size,
  765. interpolation=interpolation,
  766. seed=seed, name=name,
  767. random_state=random_state, deterministic=deterministic)
  768. class RelativeRegularGridVoronoi(Voronoi):
  769. """Sample Voronoi cells from image-dependent grids and color-average them.
  770. This augmenter is a shortcut for the combination of
  771. :class:`~imgaug.augmenters.segmentation.Voronoi`,
  772. :class:`~imgaug.augmenters.segmentation.RegularGridPointsSampler` and
  773. :class:`~imgaug.augmenters.segmentation.DropoutPointsSampler`. Hence, it
  774. generates a regular grid with ``R`` rows and ``C`` columns of coordinates
  775. on each image. Then, it drops ``p`` percent of the ``R*C`` coordinates
  776. to randomize the grid. Each image pixel then belongs to the voronoi
  777. cell with the closest coordinate.
  778. .. note::
  779. In contrast to the other voronoi augmenters, this one uses
  780. ``None`` as the default value for `max_size`, i.e. the color averaging
  781. is always performed at full resolution. This enables the augmenter to
  782. make use of the additional points on larger images. It does
  783. however slow down the augmentation process.
  784. **Supported dtypes**:
  785. See :class:`~imgaug.augmenters.segmentation.Voronoi`.
  786. Parameters
  787. ----------
  788. n_rows_frac : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  789. Relative number of coordinates to place on the y-axis. For a value
  790. ``y`` and image height ``H`` the number of actually placed coordinates
  791. (i.e. computed rows) is given by ``int(round(y*H))``.
  792. Note that for each image, the number of coordinates is clipped to the
  793. interval ``[1,H]``, where ``H`` is the image height.
  794. * If a single ``number``, then that value will always be used.
  795. * If a ``tuple`` ``(a, b)``, then a value from the interval
  796. ``[a, b]`` will be sampled per image.
  797. * If a ``list``, then a random value will be sampled from that
  798. ``list`` per image.
  799. * If a ``StochasticParameter``, then that parameter will be
  800. queried to draw one value per image.
  801. n_cols_frac : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  802. Relative number of coordinates to place on the x-axis. For a value
  803. ``x`` and image height ``W`` the number of actually placed coordinates
  804. (i.e. computed columns) is given by ``int(round(x*W))``.
  805. Note that for each image, the number of coordinates is clipped to the
  806. interval ``[1,W]``, where ``W`` is the image width.
  807. * If a single ``number``, then that value will always be used.
  808. * If a ``tuple`` ``(a, b)``, then a value from the interval
  809. ``[a, b]`` will be sampled per image.
  810. * If a ``list``, then a random value will be sampled from that
  811. ``list`` per image.
  812. * If a ``StochasticParameter``, then that parameter will be
  813. queried to draw one value per image.
  814. p_drop_points : number or tuple of number or imgaug.parameters.StochasticParameter, optional
  815. The probability that a coordinate will be removed from the list
  816. of all sampled coordinates. A value of ``1.0`` would mean that (on
  817. average) ``100`` percent of all coordinates will be dropped,
  818. while ``0.0`` denotes ``0`` percent. Note that this sampler will
  819. always ensure that at least one coordinate is left after the dropout
  820. operation, i.e. even ``1.0`` will only drop all *except one*
  821. coordinate.
  822. * If a ``float``, then that value will be used for all images.
  823. * If a ``tuple`` ``(a, b)``, then a value ``p`` will be sampled
  824. from the interval ``[a, b]`` per image.
  825. * If a ``StochasticParameter``, then this parameter will be used to
  826. determine per coordinate whether it should be *kept* (sampled
  827. value of ``>0.5``) or shouldn't be kept (sampled value of
  828. ``<=0.5``). If you instead want to provide the probability as
  829. a stochastic parameter, you can usually do
  830. ``imgaug.parameters.Binomial(1-p)`` to convert parameter `p` to
  831. a 0/1 representation.
  832. p_replace : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  833. Defines for any segment the probability that the pixels within that
  834. segment are replaced by their average color (otherwise, the pixels
  835. are not changed).
  836. Examples:
  837. * A probability of ``0.0`` would mean, that the pixels in no
  838. segment are replaced by their average color (image is not
  839. changed at all).
  840. * A probability of ``0.5`` would mean, that around half of all
  841. segments are replaced by their average color.
  842. * A probability of ``1.0`` would mean, that all segments are
  843. replaced by their average color (resulting in a voronoi
  844. image).
  845. Behaviour based on chosen datatypes for this parameter:
  846. * If a ``number``, then that ``number`` will always be used.
  847. * If ``tuple`` ``(a, b)``, then a random probability will be
  848. sampled from the interval ``[a, b]`` per image.
  849. * If a ``list``, then a random value will be sampled from that
  850. ``list`` per image.
  851. * If a ``StochasticParameter``, it is expected to return
  852. values between ``0.0`` and ``1.0`` and will be queried *for each
  853. individual segment* to determine whether it is supposed to
  854. be averaged (``>0.5``) or not (``<=0.5``).
  855. Recommended to be some form of ``Binomial(...)``.
  856. max_size : int or None, optional
  857. Maximum image size at which the augmentation is performed.
  858. If the width or height of an image exceeds this value, it will be
  859. downscaled before the augmentation so that the longest side
  860. matches `max_size`.
  861. This is done to speed up the process. The final output image has the
  862. same size as the input image. Note that in case `p_replace` is below
  863. ``1.0``, the down-/upscaling will affect the not-replaced pixels too.
  864. Use ``None`` to apply no down-/upscaling.
  865. interpolation : int or str, optional
  866. Interpolation method to use during downscaling when `max_size` is
  867. exceeded. Valid methods are the same as in
  868. :func:`~imgaug.imgaug.imresize_single_image`.
  869. 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
  870. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  871. name : None or str, optional
  872. See :func:`~imgaug.augmenters.meta.Augmenter.__init__`.
  873. 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
  874. Old name for parameter `seed`.
  875. Its usage will not yet cause a deprecation warning,
  876. but it is still recommended to use `seed` now.
  877. Outdated since 0.4.0.
  878. deterministic : bool, optional
  879. Deprecated since 0.4.0.
  880. See method ``to_deterministic()`` for an alternative and for
  881. details about what the "deterministic mode" actually does.
  882. Examples
  883. --------
  884. >>> import imgaug.augmenters as iaa
  885. >>> aug = iaa.RelativeRegularGridVoronoi(0.1, 0.25)
  886. Place a regular grid of ``R x C`` coordinates on each image, where
  887. ``R`` is the number of rows and computed as ``R=0.1*H`` with ``H`` being
  888. the height of the input image. ``C`` is the number of columns and
  889. analogously estimated from the image width ``W`` as ``C=0.25*W``.
  890. Larger images will lead to larger ``R`` and ``C`` values.
  891. On average, ``20`` percent of these grid coordinates are randomly
  892. dropped to create a less regular pattern. Then, the remaining coordinates
  893. are used to group the image pixels into voronoi cells and the colors
  894. within them are averaged.
  895. >>> aug = iaa.RelativeRegularGridVoronoi(
  896. >>> (0.03, 0.1), 0.1, p_drop_points=0.0, p_replace=0.9, max_size=512)
  897. Same as above, generates a grid with randomly ``R=r*H`` rows, where
  898. ``r`` is sampled uniformly from the interval ``[0.03, 0.1]`` and
  899. ``C=0.1*W`` rows. No points are dropped. The augmenter replaces only
  900. ``90`` percent of the voronoi cells with their average color (the pixels
  901. of the remaining ``10`` percent are not changed). Images larger than
  902. ``512`` px are temporarily downscaled (*before* sampling the grid points)
  903. so that no side exceeds ``512`` px. This improves performance, but
  904. degrades the quality of the resulting image.
  905. """
  906. def __init__(self, n_rows_frac=(0.05, 0.15), n_cols_frac=(0.05, 0.15),
  907. p_drop_points=(0.0, 0.5), p_replace=(0.5, 1.0),
  908. max_size=None, interpolation="linear",
  909. seed=None, name=None,
  910. random_state="deprecated", deterministic="deprecated"):
  911. super(RelativeRegularGridVoronoi, self).__init__(
  912. points_sampler=DropoutPointsSampler(
  913. RelativeRegularGridPointsSampler(n_rows_frac, n_cols_frac),
  914. p_drop_points
  915. ),
  916. p_replace=p_replace,
  917. max_size=max_size,
  918. interpolation=interpolation,
  919. seed=seed, name=name,
  920. random_state=random_state, deterministic=deterministic)
  921. @six.add_metaclass(ABCMeta)
  922. class IPointsSampler(object):
  923. """Interface for all point samplers.
  924. Point samplers return coordinate arrays of shape ``Nx2``.
  925. These coordinates can be used in other augmenters, see e.g.
  926. :class:`~imgaug.augmenters.segmentation.Voronoi`.
  927. """
  928. @abstractmethod
  929. def sample_points(self, images, random_state):
  930. """Generate coordinates of points on images.
  931. Parameters
  932. ----------
  933. images : ndarray or list of ndarray
  934. One or more images for which to generate points.
  935. If this is a ``list`` of arrays, each one of them is expected to
  936. have three dimensions.
  937. If this is an array, it must be four-dimensional and the first
  938. axis is expected to denote the image index. For ``RGB`` images
  939. the array would hence have to be of shape ``(N, H, W, 3)``.
  940. 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
  941. A random state to use for any probabilistic function required
  942. during the point sampling.
  943. See :func:`~imgaug.random.RNG` for details.
  944. Returns
  945. -------
  946. ndarray
  947. An ``(N,2)`` ``float32`` array containing ``(x,y)`` subpixel
  948. coordinates, all of which being within the intervals
  949. ``[0.0, width]`` and ``[0.0, height]``.
  950. """
  951. def _verify_sample_points_images(images):
  952. assert len(images) > 0, "Expected at least one image, got zero."
  953. if isinstance(images, list):
  954. assert all([ia.is_np_array(image) for image in images]), (
  955. "Expected list of numpy arrays, got list of types %s." % (
  956. ", ".join([str(type(image)) for image in images]),))
  957. assert all([image.ndim == 3 for image in images]), (
  958. "Expected each image to have three dimensions, "
  959. "got dimensions %s." % (
  960. ", ".join([str(image.ndim) for image in images]),))
  961. else:
  962. assert ia.is_np_array(images), (
  963. "Expected either a list of numpy arrays or a single numpy "
  964. "array of shape NxHxWxC. Got type %s." % (type(images),))
  965. assert images.ndim == 4, (
  966. "Expected a four-dimensional array of shape NxHxWxC. "
  967. "Got shape %d dimensions (shape: %s)." % (
  968. images.ndim, images.shape))
  969. class RegularGridPointsSampler(IPointsSampler):
  970. """Sampler that generates a regular grid of coordinates on an image.
  971. 'Regular grid' here means that on each axis all coordinates have the
  972. same distance from each other. Note that the distance may change between
  973. axis.
  974. Parameters
  975. ----------
  976. n_rows : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional
  977. Number of rows of coordinates to place on each image, i.e. the number
  978. of coordinates on the y-axis. Note that for each image, the sampled
  979. value is clipped to the interval ``[1..H]``, where ``H`` is the image
  980. height.
  981. * If a single ``int``, then that value will always be used.
  982. * If a ``tuple`` ``(a, b)``, then a value from the discrete
  983. interval ``[a..b]`` will be sampled per image.
  984. * If a ``list``, then a random value will be sampled from that
  985. ``list`` per image.
  986. * If a ``StochasticParameter``, then that parameter will be
  987. queried to draw one value per image.
  988. n_cols : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional
  989. Number of columns of coordinates to place on each image, i.e. the
  990. number of coordinates on the x-axis. Note that for each image, the
  991. sampled value is clipped to the interval ``[1..W]``, where ``W`` is
  992. the image width.
  993. * If a single ``int``, then that value will always be used.
  994. * If a ``tuple`` ``(a, b)``, then a value from the discrete
  995. interval ``[a..b]`` will be sampled per image.
  996. * If a ``list``, then a random value will be sampled from that
  997. ``list`` per image.
  998. * If a ``StochasticParameter``, then that parameter will be
  999. queried to draw one value per image.
  1000. Examples
  1001. --------
  1002. >>> import imgaug.augmenters as iaa
  1003. >>> sampler = iaa.RegularGridPointsSampler(
  1004. >>> n_rows=(5, 20),
  1005. >>> n_cols=50)
  1006. Create a point sampler that generates regular grids of points. These grids
  1007. contain ``r`` points on the y-axis, where ``r`` is sampled
  1008. uniformly from the discrete interval ``[5..20]`` per image.
  1009. On the x-axis, the grids always contain ``50`` points.
  1010. """
  1011. def __init__(self, n_rows, n_cols):
  1012. self.n_rows = iap.handle_discrete_param(
  1013. n_rows, "n_rows", value_range=(1, None),
  1014. tuple_to_uniform=True, list_to_choice=True, allow_floats=False)
  1015. self.n_cols = iap.handle_discrete_param(
  1016. n_cols, "n_cols", value_range=(1, None),
  1017. tuple_to_uniform=True, list_to_choice=True, allow_floats=False)
  1018. def sample_points(self, images, random_state):
  1019. random_state = iarandom.RNG(random_state)
  1020. _verify_sample_points_images(images)
  1021. n_rows_lst, n_cols_lst = self._draw_samples(images, random_state)
  1022. return self._generate_point_grids(images, n_rows_lst, n_cols_lst)
  1023. def _draw_samples(self, images, random_state):
  1024. rss = random_state.duplicate(2)
  1025. n_rows_lst = self.n_rows.draw_samples(len(images), random_state=rss[0])
  1026. n_cols_lst = self.n_cols.draw_samples(len(images), random_state=rss[1])
  1027. return self._clip_rows_and_cols(n_rows_lst, n_cols_lst, images)
  1028. @classmethod
  1029. def _clip_rows_and_cols(cls, n_rows_lst, n_cols_lst, images):
  1030. heights = np.int32([image.shape[0] for image in images])
  1031. widths = np.int32([image.shape[1] for image in images])
  1032. # We clip intentionally not to H-1 or W-1 here. If e.g. an image has
  1033. # a width of 1, we want to get a maximum of 1 column of coordinates.
  1034. # Note that we use two clips here instead of e.g. clip(., 1, height),
  1035. # because images can have height/width zero and in these cases numpy
  1036. # prefers the smaller value in clip(). But currently we want to get
  1037. # at least 1 point for such images.
  1038. n_rows_lst = np.clip(n_rows_lst, None, heights)
  1039. n_cols_lst = np.clip(n_cols_lst, None, widths)
  1040. n_rows_lst = np.clip(n_rows_lst, 1, None)
  1041. n_cols_lst = np.clip(n_cols_lst, 1, None)
  1042. return n_rows_lst, n_cols_lst
  1043. @classmethod
  1044. def _generate_point_grids(cls, images, n_rows_lst, n_cols_lst):
  1045. grids = []
  1046. for image, n_rows_i, n_cols_i in zip(images, n_rows_lst, n_cols_lst):
  1047. grids.append(cls._generate_point_grid(image, n_rows_i, n_cols_i))
  1048. return grids
  1049. @classmethod
  1050. def _generate_point_grid(cls, image, n_rows, n_cols):
  1051. height, width = image.shape[0:2]
  1052. # We do not have to subtract 1 here from height/width as these are
  1053. # subpixel coordinates. Technically, we could also place the cell
  1054. # centers outside of the image plane.
  1055. y_spacing = height / n_rows
  1056. y_start = 0.0 + y_spacing/2
  1057. y_end = height - y_spacing/2
  1058. if y_start - 1e-4 <= y_end <= y_start + 1e-4:
  1059. yy = np.float32([y_start])
  1060. else:
  1061. yy = np.linspace(y_start, y_end, num=n_rows)
  1062. x_spacing = width / n_cols
  1063. x_start = 0.0 + x_spacing/2
  1064. x_end = width - x_spacing/2
  1065. if x_start - 1e-4 <= x_end <= x_start + 1e-4:
  1066. xx = np.float32([x_start])
  1067. else:
  1068. xx = np.linspace(x_start, x_end, num=n_cols)
  1069. xx, yy = np.meshgrid(xx, yy)
  1070. grid = np.vstack([xx.ravel(), yy.ravel()]).T
  1071. return grid
  1072. def __repr__(self):
  1073. return "RegularGridPointsSampler(%s, %s)" % (self.n_rows, self.n_cols)
  1074. def __str__(self):
  1075. return self.__repr__()
  1076. class RelativeRegularGridPointsSampler(IPointsSampler):
  1077. """Regular grid coordinate sampler; places more points on larger images.
  1078. This is similar to ``RegularGridPointsSampler``, but the number of rows
  1079. and columns is given as fractions of each image's height and width.
  1080. Hence, more coordinates are generated for larger images.
  1081. Parameters
  1082. ----------
  1083. n_rows_frac : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  1084. Relative number of coordinates to place on the y-axis. For a value
  1085. ``y`` and image height ``H`` the number of actually placed coordinates
  1086. (i.e. computed rows) is given by ``int(round(y*H))``.
  1087. Note that for each image, the number of coordinates is clipped to the
  1088. interval ``[1,H]``, where ``H`` is the image height.
  1089. * If a single ``number``, then that value will always be used.
  1090. * If a ``tuple`` ``(a, b)``, then a value from the interval
  1091. ``[a, b]`` will be sampled per image.
  1092. * If a ``list``, then a random value will be sampled from that
  1093. ``list`` per image.
  1094. * If a ``StochasticParameter``, then that parameter will be
  1095. queried to draw one value per image.
  1096. n_cols_frac : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  1097. Relative number of coordinates to place on the x-axis. For a value
  1098. ``x`` and image height ``W`` the number of actually placed coordinates
  1099. (i.e. computed columns) is given by ``int(round(x*W))``.
  1100. Note that for each image, the number of coordinates is clipped to the
  1101. interval ``[1,W]``, where ``W`` is the image width.
  1102. * If a single ``number``, then that value will always be used.
  1103. * If a ``tuple`` ``(a, b)``, then a value from the interval
  1104. ``[a, b]`` will be sampled per image.
  1105. * If a ``list``, then a random value will be sampled from that
  1106. ``list`` per image.
  1107. * If a ``StochasticParameter``, then that parameter will be
  1108. queried to draw one value per image.
  1109. Examples
  1110. --------
  1111. >>> import imgaug.augmenters as iaa
  1112. >>> sampler = iaa.RelativeRegularGridPointsSampler(
  1113. >>> n_rows_frac=(0.01, 0.1),
  1114. >>> n_cols_frac=0.2)
  1115. Create a point sampler that generates regular grids of points. These grids
  1116. contain ``round(y*H)`` points on the y-axis, where ``y`` is sampled
  1117. uniformly from the interval ``[0.01, 0.1]`` per image and ``H`` is the
  1118. image height. On the x-axis, the grids always contain ``0.2*W`` points,
  1119. where ``W`` is the image width.
  1120. """
  1121. def __init__(self, n_rows_frac, n_cols_frac):
  1122. eps = 1e-4
  1123. self.n_rows_frac = iap.handle_continuous_param(
  1124. n_rows_frac, "n_rows_frac", value_range=(0.0+eps, 1.0),
  1125. tuple_to_uniform=True, list_to_choice=True)
  1126. self.n_cols_frac = iap.handle_continuous_param(
  1127. n_cols_frac, "n_cols_frac", value_range=(0.0+eps, 1.0),
  1128. tuple_to_uniform=True, list_to_choice=True)
  1129. def sample_points(self, images, random_state):
  1130. # pylint: disable=protected-access
  1131. random_state = iarandom.RNG(random_state)
  1132. _verify_sample_points_images(images)
  1133. n_rows, n_cols = self._draw_samples(images, random_state)
  1134. return RegularGridPointsSampler._generate_point_grids(images,
  1135. n_rows, n_cols)
  1136. def _draw_samples(self, images, random_state):
  1137. # pylint: disable=protected-access
  1138. n_augmentables = len(images)
  1139. rss = random_state.duplicate(2)
  1140. n_rows_frac = self.n_rows_frac.draw_samples(n_augmentables,
  1141. random_state=rss[0])
  1142. n_cols_frac = self.n_cols_frac.draw_samples(n_augmentables,
  1143. random_state=rss[1])
  1144. heights = np.int32([image.shape[0] for image in images])
  1145. widths = np.int32([image.shape[1] for image in images])
  1146. n_rows = np.round(n_rows_frac * heights)
  1147. n_cols = np.round(n_cols_frac * widths)
  1148. n_rows, n_cols = RegularGridPointsSampler._clip_rows_and_cols(
  1149. n_rows, n_cols, images)
  1150. return n_rows.astype(np.int32), n_cols.astype(np.int32)
  1151. def __repr__(self):
  1152. return "RelativeRegularGridPointsSampler(%s, %s)" % (
  1153. self.n_rows_frac, self.n_cols_frac)
  1154. def __str__(self):
  1155. return self.__repr__()
  1156. class DropoutPointsSampler(IPointsSampler):
  1157. """Remove a defined fraction of sampled points.
  1158. Parameters
  1159. ----------
  1160. other_points_sampler : IPointsSampler
  1161. Another point sampler that is queried to generate a list of points.
  1162. The dropout operation will be applied to that list.
  1163. p_drop : number or tuple of number or imgaug.parameters.StochasticParameter
  1164. The probability that a coordinate will be removed from the list
  1165. of all sampled coordinates. A value of ``1.0`` would mean that (on
  1166. average) ``100`` percent of all coordinates will be dropped,
  1167. while ``0.0`` denotes ``0`` percent. Note that this sampler will
  1168. always ensure that at least one coordinate is left after the dropout
  1169. operation, i.e. even ``1.0`` will only drop all *except one*
  1170. coordinate.
  1171. * If a ``float``, then that value will be used for all images.
  1172. * If a ``tuple`` ``(a, b)``, then a value ``p`` will be sampled
  1173. from the interval ``[a, b]`` per image.
  1174. * If a ``StochasticParameter``, then this parameter will be used to
  1175. determine per coordinate whether it should be *kept* (sampled
  1176. value of ``>0.5``) or shouldn't be kept (sampled value of
  1177. ``<=0.5``). If you instead want to provide the probability as
  1178. a stochastic parameter, you can usually do
  1179. ``imgaug.parameters.Binomial(1-p)`` to convert parameter `p` to
  1180. a 0/1 representation.
  1181. Examples
  1182. --------
  1183. >>> import imgaug.augmenters as iaa
  1184. >>> sampler = iaa.DropoutPointsSampler(
  1185. >>> iaa.RegularGridPointsSampler(10, 20),
  1186. >>> 0.2)
  1187. Create a point sampler that first generates points following a regular
  1188. grid of ``10`` rows and ``20`` columns, then randomly drops ``20`` percent
  1189. of these points.
  1190. """
  1191. def __init__(self, other_points_sampler, p_drop):
  1192. assert isinstance(other_points_sampler, IPointsSampler), (
  1193. "Expected to get an instance of IPointsSampler as argument "
  1194. "'other_points_sampler', got type %s." % (
  1195. type(other_points_sampler),))
  1196. self.other_points_sampler = other_points_sampler
  1197. self.p_drop = self._convert_p_drop_to_inverted_mask_param(p_drop)
  1198. @classmethod
  1199. def _convert_p_drop_to_inverted_mask_param(cls, p_drop):
  1200. # TODO this is the same as in Dropout, make DRY
  1201. # TODO add list as an option
  1202. if ia.is_single_number(p_drop):
  1203. p_drop = iap.Binomial(1 - p_drop)
  1204. elif ia.is_iterable(p_drop):
  1205. assert len(p_drop) == 2, (
  1206. "Expected 'p_drop' given as an iterable to contain exactly "
  1207. "2 values, got %d." % (len(p_drop),))
  1208. assert p_drop[0] < p_drop[1], (
  1209. "Expected 'p_drop' given as iterable to contain exactly 2 "
  1210. "values (a, b) with a < b. Got %.4f and %.4f." % (
  1211. p_drop[0], p_drop[1]))
  1212. assert 0 <= p_drop[0] <= 1.0 and 0 <= p_drop[1] <= 1.0, (
  1213. "Expected 'p_drop' given as iterable to only contain values "
  1214. "in the interval [0.0, 1.0], got %.4f and %.4f." % (
  1215. p_drop[0], p_drop[1]))
  1216. p_drop = iap.Binomial(iap.Uniform(1 - p_drop[1], 1 - p_drop[0]))
  1217. elif isinstance(p_drop, iap.StochasticParameter):
  1218. pass
  1219. else:
  1220. raise Exception(
  1221. "Expected p_drop to be float or int or StochasticParameter, "
  1222. "got %s." % (type(p_drop),))
  1223. return p_drop
  1224. def sample_points(self, images, random_state):
  1225. random_state = iarandom.RNG(random_state)
  1226. _verify_sample_points_images(images)
  1227. rss = random_state.duplicate(2)
  1228. points_on_images = self.other_points_sampler.sample_points(images,
  1229. rss[0])
  1230. drop_masks = self._draw_samples(points_on_images, rss[1])
  1231. return self._apply_dropout_masks(points_on_images, drop_masks)
  1232. def _draw_samples(self, points_on_images, random_state):
  1233. rss = random_state.duplicate(len(points_on_images))
  1234. drop_masks = [self._draw_samples_for_image(points_on_image, rs)
  1235. for points_on_image, rs
  1236. in zip(points_on_images, rss)]
  1237. return drop_masks
  1238. def _draw_samples_for_image(self, points_on_image, random_state):
  1239. drop_samples = self.p_drop.draw_samples((len(points_on_image),),
  1240. random_state)
  1241. keep_mask = (drop_samples > 0.5)
  1242. return keep_mask
  1243. @classmethod
  1244. def _apply_dropout_masks(cls, points_on_images, keep_masks):
  1245. points_on_images_dropped = []
  1246. for points_on_image, keep_mask in zip(points_on_images, keep_masks):
  1247. if len(points_on_image) == 0:
  1248. # other sampler didn't provide any points
  1249. poi_dropped = points_on_image
  1250. else:
  1251. if not np.any(keep_mask):
  1252. # keep at least one point if all were supposed to be
  1253. # dropped
  1254. # TODO this could also be moved into its own point sampler,
  1255. # like AtLeastOnePoint(...)
  1256. idx = (len(points_on_image) - 1) // 2
  1257. keep_mask = np.copy(keep_mask)
  1258. keep_mask[idx] = True
  1259. poi_dropped = points_on_image[keep_mask, :]
  1260. points_on_images_dropped.append(poi_dropped)
  1261. return points_on_images_dropped
  1262. def __repr__(self):
  1263. return "DropoutPointsSampler(%s, %s)" % (self.other_points_sampler,
  1264. self.p_drop)
  1265. def __str__(self):
  1266. return self.__repr__()
  1267. class UniformPointsSampler(IPointsSampler):
  1268. """Sample points uniformly on images.
  1269. This point sampler generates `n_points` points per image. The x- and
  1270. y-coordinates are both sampled from uniform distributions matching the
  1271. respective image width and height.
  1272. Parameters
  1273. ----------
  1274. n_points : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional
  1275. Number of points to sample on each image.
  1276. * If a single ``int``, then that value will always be used.
  1277. * If a ``tuple`` ``(a, b)``, then a value from the discrete
  1278. interval ``[a..b]`` will be sampled per image.
  1279. * If a ``list``, then a random value will be sampled from that
  1280. ``list`` per image.
  1281. * If a ``StochasticParameter``, then that parameter will be
  1282. queried to draw one value per image.
  1283. Examples
  1284. --------
  1285. >>> import imgaug.augmenters as iaa
  1286. >>> sampler = iaa.UniformPointsSampler(500)
  1287. Create a point sampler that generates an array of ``500`` random points for
  1288. each input image. The x- and y-coordinates of each point are sampled
  1289. from uniform distributions.
  1290. """
  1291. def __init__(self, n_points):
  1292. self.n_points = iap.handle_discrete_param(
  1293. n_points, "n_points", value_range=(1, None),
  1294. tuple_to_uniform=True, list_to_choice=True, allow_floats=False)
  1295. def sample_points(self, images, random_state):
  1296. random_state = iarandom.RNG(random_state)
  1297. _verify_sample_points_images(images)
  1298. rss = random_state.duplicate(2)
  1299. n_points_imagewise = self._draw_samples(len(images), rss[0])
  1300. n_points_total = np.sum(n_points_imagewise)
  1301. n_components_total = 2 * n_points_total
  1302. coords_relative = rss[1].uniform(0.0, 1.0, n_components_total)
  1303. coords_relative_xy = coords_relative.reshape(n_points_total, 2)
  1304. return self._convert_relative_coords_to_absolute(
  1305. coords_relative_xy, n_points_imagewise, images)
  1306. def _draw_samples(self, n_augmentables, random_state):
  1307. n_points = self.n_points.draw_samples((n_augmentables,),
  1308. random_state=random_state)
  1309. n_points_clipped = np.clip(n_points, 1, None)
  1310. return n_points_clipped
  1311. @classmethod
  1312. def _convert_relative_coords_to_absolute(cls, coords_rel_xy,
  1313. n_points_imagewise, images):
  1314. coords_absolute = []
  1315. i = 0
  1316. for image, n_points_image in zip(images, n_points_imagewise):
  1317. height, width = image.shape[0:2]
  1318. xx = coords_rel_xy[i:i+n_points_image, 0]
  1319. yy = coords_rel_xy[i:i+n_points_image, 1]
  1320. xx_int = np.clip(np.round(xx * width), 0, width)
  1321. yy_int = np.clip(np.round(yy * height), 0, height)
  1322. coords_absolute.append(np.stack([xx_int, yy_int], axis=-1))
  1323. i += n_points_image
  1324. return coords_absolute
  1325. def __repr__(self):
  1326. return "UniformPointsSampler(%s)" % (self.n_points,)
  1327. def __str__(self):
  1328. return self.__repr__()
  1329. class SubsamplingPointsSampler(IPointsSampler):
  1330. """Ensure that the number of sampled points is below a maximum.
  1331. This point sampler will sample points from another sampler and
  1332. then -- in case more points were generated than an allowed maximum --
  1333. will randomly pick `n_points_max` of these.
  1334. Parameters
  1335. ----------
  1336. other_points_sampler : IPointsSampler
  1337. Another point sampler that is queried to generate a ``list`` of points.
  1338. The dropout operation will be applied to that ``list``.
  1339. n_points_max : int
  1340. Maximum number of allowed points. If `other_points_sampler` generates
  1341. more points than this maximum, a random subset of size `n_points_max`
  1342. will be selected.
  1343. Examples
  1344. --------
  1345. >>> import imgaug.augmenters as iaa
  1346. >>> sampler = iaa.SubsamplingPointsSampler(
  1347. >>> iaa.RelativeRegularGridPointsSampler(0.1, 0.2),
  1348. >>> 50
  1349. >>> )
  1350. Create a points sampler that places ``y*H`` points on the y-axis (with
  1351. ``y`` being ``0.1`` and ``H`` being an image's height) and ``x*W`` on
  1352. the x-axis (analogous). Then, if that number of placed points exceeds
  1353. ``50`` (can easily happen for larger images), a random subset of ``50``
  1354. points will be picked and returned.
  1355. """
  1356. def __init__(self, other_points_sampler, n_points_max):
  1357. assert isinstance(other_points_sampler, IPointsSampler), (
  1358. "Expected to get an instance of IPointsSampler as argument "
  1359. "'other_points_sampler', got type %s." % (
  1360. type(other_points_sampler),))
  1361. self.other_points_sampler = other_points_sampler
  1362. self.n_points_max = np.clip(n_points_max, -1, None)
  1363. if self.n_points_max == 0:
  1364. ia.warn("Got n_points_max=0 in SubsamplingPointsSampler. "
  1365. "This will result in no points ever getting "
  1366. "returned.")
  1367. def sample_points(self, images, random_state):
  1368. random_state = iarandom.RNG(random_state)
  1369. _verify_sample_points_images(images)
  1370. rss = random_state.duplicate(len(images) + 1)
  1371. points_on_images = self.other_points_sampler.sample_points(
  1372. images, rss[-1])
  1373. return [self._subsample(points_on_image, self.n_points_max, rs)
  1374. for points_on_image, rs
  1375. in zip(points_on_images, rss[:-1])]
  1376. @classmethod
  1377. def _subsample(cls, points_on_image, n_points_max, random_state):
  1378. if len(points_on_image) <= n_points_max:
  1379. return points_on_image
  1380. indices = np.arange(len(points_on_image))
  1381. indices_to_keep = random_state.permutation(indices)[0:n_points_max]
  1382. return points_on_image[indices_to_keep]
  1383. def __repr__(self):
  1384. return "SubsamplingPointsSampler(%s, %d)" % (self.other_points_sampler,
  1385. self.n_points_max)
  1386. def __str__(self):
  1387. return self.__repr__()
  1388. # TODO Add points subsampler that drops points close to each other first
  1389. # TODO Add poisson points sampler
  1390. # TODO Add jitter points sampler that moves points around
  1391. # for both see https://codegolf.stackexchange.com/questions/50299/draw-an-image-as-a-voronoi-map/50345#50345