Newer
Older
Lucia D. Hradecka
committed
return f'Flip(axes={self.axes}, always_apply={self.always_apply}, p={self.p})'
class RandomFlip(DualTransform):
"""Flip input around a set of axes randomly chosen from the input list of axis combinations.
Args:
axes_to_choose (List[int], Tuple[int], or None, optional): List of axis indices from which some are randomly
chosen. Recognised axis symbols are ``1`` for Z, ``2`` for Y, and ``3`` for X. The image will be
flipped around the chosen axes.
If ``None``, a random subset of spatial axes is chosen, corresponding to inputting
Defaults to ``None``.
always_apply (bool, optional): Always apply this transformation in composition.
Defaults to ``False``.
p (float, optional): Chance of applying this transformation in composition.
Defaults to ``0.5``.
Targets:
image, mask, float mask, key points, bounding boxes
"""
def __init__(self, axes_to_choose: Union[None, List[int], Tuple[int]] = None, always_apply=False, p=0.5):
super().__init__(always_apply, p)
self.axes = axes_to_choose
def apply(self, img, **params):
return np.flip(img, params["axes"])
def apply_to_mask(self, mask, **params):
# Mask has no dimension channel
return np.flip(mask, params["axes"] - 1) # params["axes"] is a np.ndarray
def apply_to_keypoints(self, keypoints, keep_all=False, **params):
return F.flip_keypoints(keypoints,
axes=params['axes'],
img_shape=params['img_shape'])
def get_params(self, targets, **data):
if self.axes == []:
axes = np.asarray(self.axes)
else:
# Use all spatial axes if not specified otherwise:
to_choose = [1, 2, 3] if self.axes is None else self.axes
# Randomly choose some axes from the given list:
axes = sample(population=to_choose, k=randint(0, len(to_choose)))
Lucia D. Hradecka
committed
# Get image shape (needed for keypoints):
img_shape = get_spatial_shape_from_image(data, targets)
return {"axes": axes,
"img_shape": img_shape}
Lucia D. Hradecka
committed
return f'RandomFlip(axes_to_choose={self.axes}, always_apply={self.always_apply}, p={self.p})'
class CenterCrop(DualTransform):
"""Crops the central region of the input of given size.
Unlike ``CenterCrop`` from `Albumentations`, this transform pads the input in dimensions
where the input is smaller than the ``shape`` with ``numpy.pad``. The ``border_mode``, ``ival`` and ``mval``
arguments are forwarded to ``numpy.pad`` if padding is necessary. More details at:
https://numpy.org/doc/stable/reference/generated/numpy.pad.html.
Args:
shape (Tuple[int]): The desired shape of input.
border_mode (str, optional): Values outside image domain are filled according to this mode.
Defaults to ``'reflect'``.
ival (float | Sequence, optional): Values of `image` voxels outside of the `image` domain.
Only applied when ``border_mode = 'constant'`` or ``border_mode = 'linear_ramp'``.
Defaults to ``(0, 0)``.
mval (float | Sequence, optional): Values of `mask` voxels outside of the `mask` domain.
Only applied when ``border_mode = 'constant'`` or ``border_mode = 'linear_ramp'``.
ignore_index (float | None, optional): If a float, then transformation of `mask` is done with
``border_mode = 'constant'`` and ``mval = ignore_index``.
If ``None``, this argument is ignored.
Defaults to ``None``.
always_apply (bool, optional): Always apply this transformation in composition.
Defaults to ``False``.
p (float, optional): Chance of applying this transformation in composition.
image, mask, float mask, key points, bounding boxes
Lucia D. Hradecka
committed
def __init__(self, shape: TypeSpatialShape, border_mode: str = "reflect",
ival: Union[Sequence[float], float] = (0, 0),
mval: Union[Sequence[float], float] = (0, 0), ignore_index: Union[float, None] = None,
always_apply: bool = False, p: float = 1.0):
Lucia D. Hradecka
committed
self.output_shape = np.asarray(shape, dtype=np.intc) # TODO: make it len 3 and type tuple
self.border_mode = border_mode
self.mask_mode = border_mode
self.mask_mode = "constant"
self.mval = ignore_index
def apply(self, img, **params):
return F.crop(img,
crop_shape=self.output_shape,
crop_position=params['crop_position'],
pad_dims=params['pad_dims'],
border_mode=self.mask_mode, cval=self.mval, mask=False)
return F.crop(mask,
crop_shape=self.output_shape,
crop_position=params['crop_position'],
pad_dims=params['pad_dims'],
border_mode=self.mask_mode, cval=self.mval, mask=True)
def apply_to_keypoints(self, keypoints, keep_all=False, **params):
return F.crop_keypoints(keypoints,
crop_shape=self.output_shape,
crop_position=params['crop_position'],
pad_dims=params['pad_dims'],
keep_all=keep_all)
Lucia D. Hradecka
committed
def get_params(self, targets, **data):
Lucia D. Hradecka
committed
# Get crop coordinates:
# 1. Original image shape
img_spatial_shape = get_spatial_shape_from_image(data, targets)
# 2. Position of the corner closest to the image origin when cropping from the center of the image
position: TypeSpatialCoordinate = (img_spatial_shape - self.output_shape) // 2
position = np.maximum(position, 0).astype(int)
Lucia D. Hradecka
committed
# 3. Padding size if necessary
pad_dims = F.get_pad_dims(img_spatial_shape, self.output_shape)
return {'crop_position': position,
'pad_dims': pad_dims}
Lucia D. Hradecka
committed
return f'CenterCrop(shape={self.output_shape}, border_mode={self.border_mode}, ival={self.ival}, ' \
f'mval={self.mval}, always_apply={self.always_apply}, p={self.p})'
class RandomCrop(DualTransform):
"""Randomly crops a region of given size from the input.
Unlike ``RandomCrop`` from `Albumentations`, this transform pads the input in dimensions
where the input is smaller than the ``shape`` with ``numpy.pad``. The ``border_mode``, ``ival`` and ``mval``
arguments are forwarded to ``numpy.pad`` if padding is necessary. More details at:
https://numpy.org/doc/stable/reference/generated/numpy.pad.html.
shape (Tuple[int]): The desired shape of input.
Must be ``[Z, Y, X]``.
border_mode (str, optional): Values outside image domain are filled according to this mode.
Defaults to ``'reflect'``.
ival (float | Sequence, optional): Values of `image` voxels outside of the `image` domain.
Only applied when ``border_mode = 'constant'`` or ``border_mode = 'linear_ramp'``.
Defaults to ``(0, 0)``.
mval (float | Sequence, optional): Values of `mask` voxels outside of the `mask` domain.
Only applied when ``border_mode = 'constant'`` or ``border_mode = 'linear_ramp'``.
Defaults to ``(0, 0)``.
ignore_index (float | None, optional): If a float, then transformation of `mask` is done with
``border_mode = 'constant'`` and ``mval = ignore_index``.
If ``None``, this argument is ignored.
Defaults to ``None``.
always_apply (bool, optional): Always apply this transformation in composition.
Defaults to ``False``.
p (float, optional): Chance of applying this transformation in composition.
Defaults to ``1``.
image, mask, float mask, key points, bounding boxes
def __init__(self, shape: TypeSpatialShape, border_mode: str = "reflect", ival: Union[Sequence[float], float] = (0, 0),
mval: Union[Sequence[float], float] = (0, 0), ignore_index: Union[float, None] = None,
always_apply: bool = False, p: float = 1.0):
self.output_shape = np.asarray(shape, dtype=np.intc)
self.border_mode = border_mode
self.mask_mode = border_mode
self.ival = ival
self.mval = mval
if not (ignore_index is None):
self.mask_mode = "constant"
self.mval = ignore_index
def apply(self, img, **params):
return F.crop(img,
crop_shape=self.output_shape,
crop_position=params['crop_position'],
pad_dims=params['pad_dims'],
border_mode=self.mask_mode, cval=self.mval, mask=False)
def apply_to_mask(self, mask, **params):
return F.crop(mask,
crop_shape=self.output_shape,
crop_position=params['crop_position'],
pad_dims=params['pad_dims'],
border_mode=self.mask_mode, cval=self.mval, mask=True)
def apply_to_keypoints(self, keypoints, keep_all=False, **params):
return F.crop_keypoints(keypoints,
crop_shape=self.output_shape,
crop_position=params['crop_position'],
pad_dims=params['pad_dims'],
keep_all=keep_all)
def get_params(self, targets, **data):
Lucia D. Hradecka
committed
# Get crop coordinates:
# 1. Original image shape
img_spatial_shape = get_spatial_shape_from_image(data, targets)
# 2. Position of the corner closest to the image origin, positioned randomly so that the whole crop is
# within the image domain if possible
ranges: TypeSpatialShape = np.maximum(img_spatial_shape - self.output_shape, 0)
position = randint(0, ranges)
Lucia D. Hradecka
committed
# 3. Padding size if necessary
pad_dims = F.get_pad_dims(img_spatial_shape, self.output_shape)
Lucia D. Hradecka
committed
return {'crop_position': position,
'pad_dims': pad_dims}
Lucia D. Hradecka
committed
return f'RandomCrop(shape={self.output_shape}, border_mode={self.border_mode}, ival={self.ival}, ' \
f'mval={self.mval}, always_apply={self.always_apply}, p={self.p})'
class Pad(DualTransform):
"""Pads the input.
Internally, the ``numpy.pad`` function is used. The ``border_mode``, ``ival`` and ``mval``
arguments are forwarded to it. More details at:
https://numpy.org/doc/stable/reference/generated/numpy.pad.html.
Args:
pad_size (int | Tuple[int]): Number of pixels padded to the edges of each axis.
Must be either of: ``P``, ``(P1, P2)``, or ``(P_Z1, P_Z2, P_Y1, P_Y2, P_X1, P_X2)``.
If an integer, it is equivalent to ``(P, P, P, P, P, P)``.
If a tuple of two numbers, it is equivalent to ``(P1, P2, P1, P2, P1, P2)``.
Otherwise, it must specify padding for all spatial dimensions.
The unspecified dimensions (C and T) are not affected.
border_mode (str, optional): Values outside image domain are filled according to this mode.
Defaults to ``'constant'``.
ival (float | Sequence, optional): Values of `image` voxels outside of the `image` domain.
Only applied when ``border_mode = 'constant'`` or ``border_mode = 'linear_ramp'``.
Defaults to ``0``.
mval (float | Sequence, optional): Values of `mask` voxels outside of the `mask` domain.
Only applied when ``border_mode = 'constant'`` or ``border_mode = 'linear_ramp'``.
Defaults to ``0``.
ignore_index (float | None, optional): If a float, then transformation of `mask` is done with
``border_mode = 'constant'`` and ``mval = ignore_index``.
If ``None``, this argument is ignored.
Defaults to ``None``.
always_apply (bool, optional): Always apply this transformation in composition.
p (float, optional): Chance of applying this transformation in composition.
image, mask, float mask, key points, bounding boxes
def __init__(self, pad_size: Union[int, TypePairInt, TypeSextetInt],
border_mode: str = 'constant', ival: Union[float, Sequence] = 0, mval: Union[float, Sequence] = 0,
ignore_index: Union[float, None] = None, always_apply: bool = True, p: float = 1):
self.pad_size: TypeSextetInt = parse_pads(pad_size)
self.border_mode = border_mode
self.mask_mode = border_mode
self.ival = ival
self.mval = mval
if not (ignore_index is None):
self.mask_mode = "constant"
self.mval = ignore_index
def apply(self, img, **params):
return F.pad_pixels(img, self.pad_size, self.border_mode, self.ival)
def apply_to_mask(self, mask, **params):
return F.pad_pixels(mask, self.pad_size, self.mask_mode, self.mval, True)
def apply_to_keypoints(self, keypoints, **params):
return F.pad_keypoints(keypoints, self.pad_size)
Lucia D. Hradecka
committed
return f'Pad(pad_size={self.pad_size}, border_mode={self.border_mode}, ival={self.ival}, mval={self.mval}, ' \
f'always_apply={self.always_apply}, p={self.p})'
##########################################################################################
# #
# INTENSITY-BASED TRANSFORMATIONS (LOCAL) #
# #
##########################################################################################
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
class GaussianBlur(ImageOnlyTransform):
"""Performs Gaussian blurring of the image. In case of a multi-channel image, individual channels are blured separately.
Internally, the ``scipy.ndimage.gaussian_filter`` function is used. The ``border_mode`` and ``cval``
arguments are forwarded to it. More details at:
https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.gaussian_filter.html.
Args:
sigma (float, Tuple(float), List[Tuple(float) | float] , optional): Gaussian sigma.
Must be either of: ``S``, ``(S_Z, S_Y, S_X)``, ``(S_Z, S_Y, S_X, S_T)``, ``[S_1, S_2, ..., S_C]``,
``[(S_Z1, S_Y1, S_X1), (S_Z2, S_Y2, S_X2), ..., (S_ZC, S_YC, S_XC)]``, or
``[(S_Z1, S_Y1, S_X1, S_T1), (S_Z2, S_Y2, S_X2, S_T2), ..., (S_ZC, S_YC, S_XC, S_TC)]``.
If a float, the spatial dimensions are blurred with the same strength (equivalent to ``(S, S, S)``).
If a tuple, the sigmas for spatial dimensions and possibly the time dimension must be specified.
If a list, sigmas for each channel must be specified either as a single number or as a tuple.
Defaults to ``0.8``.
border_mode (str, optional): Values outside image domain are filled according to this mode.
Defaults to ``'reflect'``.
cval (float, optional): Value to fill past edges of image. Only applied when ``border_mode = 'constant'``.
Defaults to ``0``.
always_apply (bool, optional): Always apply this transformation in composition.
Defaults to ``False``.
p (float, optional): Chance of applying this transformation in composition.
Defaults to ``0.5``.
Targets:
image
"""
Lucia D. Hradecka
committed
def __init__(self, sigma: Union[float, tuple, List[Union[tuple, float]]] = 0.8,
border_mode: str = "reflect", cval: float = 0,
always_apply: bool = False, p: float = 0.5):
super().__init__(always_apply, p)
self.sigma = sigma
self.border_mode = border_mode
self.cval = cval
def apply(self, img, **params):
return F.gaussian_blur(img, self.sigma, self.border_mode, self.cval)
Lucia D. Hradecka
committed
def __repr__(self):
return f"GaussianBlur(sigma={self.sigma}, border_mode={self.border_mode}, cval={self.cval}, " \
f"always_apply={self.always_apply}, p={self.p})"
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
class RandomGaussianBlur(ImageOnlyTransform):
"""Performs Gaussian blur on the image with a random strength blurring.
In case of a multi-channel image, individual channels are blured separately.
Behaves similarly to GaussianBlur. The Gaussian sigma is randomly drawn from
the interval [min_sigma, s] for the respective s from ``max_sigma`` for each channel and dimension.
Internally, the ``scipy.ndimage.gaussian_filter`` function is used. The ``border_mode`` and ``cval``
arguments are forwarded to it. More details at:
https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.gaussian_filter.html.
Args:
max_sigma (float, Tuple(float), List[Tuple(float) | float] , optional): Maximum Gaussian sigma.
Must be either of: ``S``, ``(S_Z, S_Y, S_X)``, ``(S_Z, S_Y, S_X, S_T)``, ``[S_1, S_2, ..., S_C]``,
``[(S_Z1, S_Y1, S_X1), (S_Z2, S_Y2, S_X2), ..., (S_ZC, S_YC, S_XC)]``, or
``[(S_Z1, S_Y1, S_X1, S_T1), (S_Z2, S_Y2, S_X2, S_T2), ..., (S_ZC, S_YC, S_XC, S_TC)]``.
If a float, the spatial dimensions are blurred equivalently (equivalent to ``(S, S, S)``).
If a tuple, the sigmas for spatial dimensions and possibly the time dimension must be specified.
If a list, sigmas for each channel must be specified either as a single number or as a tuple.
Defaults to ``0.8``.
min_sigma (float, optional): Minimum Gaussian sigma for all channels and dimensions.
Defaults to ``0``.
border_mode (str, optional): Values outside image domain are filled according to this mode.
Defaults to ``'reflect'``.
cval (float, optional): Value to fill past edges of image. Only applied when ``border_mode = 'constant'``.
Defaults to ``0``.
always_apply (bool, optional): Always apply this transformation in composition.
Defaults to ``False``.
p (float, optional): Chance of applying this transformation in composition.
Defaults to ``0.5``.
Targets:
image
"""
def __init__(self, max_sigma: Union[float, tuple, List[Union[float, tuple]]] = 0.8,
min_sigma: float = 0, border_mode: str = "reflect", cval: float = 0,
always_apply: bool = False, p: float = 0.5):
super().__init__(always_apply, p)
self.max_sigma = max_sigma # parse_coefs(max_sigma, d4=True)
self.min_sigma = min_sigma
self.border_mode = border_mode
self.cval = cval
def apply(self, img, **params):
return F.gaussian_blur(img, params["sigma"], self.border_mode, self.cval)
Lucia D. Hradecka
committed
def get_params(self, targets, **data):
Lucia D. Hradecka
committed
if isinstance(self.max_sigma, (float, int, tuple)):
# Randomly choose a single sigma for all axes and channels OR a sigma for each axis (except the C axis)
sigma = get_sigma_axiswise(self.min_sigma, self.max_sigma)
Lucia D. Hradecka
committed
# max_sigma is list --> randomly choose sigmas for each channel
sigma = [get_sigma_axiswise(self.min_sigma, channel) for channel in self.max_sigma]
Lucia D. Hradecka
committed
def __repr__(self):
return f"RandomGaussianBlur(max_sigma={self.max_sigma}, min_sigma={self.min_sigma}, " \
f"border_mode={self.border_mode}, cval={self.cval}, always_apply={self.always_apply}, p={self.p})"
class RemoveBackgroundGaussian(ImageOnlyTransform):
"""
Removes background by subtracting a blurred image from the original image.
The background image is created using Gaussian blurring. In case of a multi-channel image, individual channels
are blured separately.
Internally, the ``scipy.ndimage.gaussian_filter`` function is used. The ``border_mode`` and ``cval``
arguments are forwarded to it. More details at:
https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.gaussian_filter.html.
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
Args:
sigma (float, Tuple(float), List[Tuple(float) | float] , optional): Gaussian sigma.
Must be either of: ``S``, ``(S_Z, S_Y, S_X)``, ``(S_Z, S_Y, S_X, S_T)``, ``[S_1, S_2, ..., S_C]``,
``[(S_Z1, S_Y1, S_X1), (S_Z2, S_Y2, S_X2), ..., (S_ZC, S_YC, S_XC)]``, or
``[(S_Z1, S_Y1, S_X1, S_T1), (S_Z2, S_Y2, S_X2, S_T2), ..., (S_ZC, S_YC, S_XC, S_TC)]``.
If a float, the spatial dimensions are blurred with the same strength (equivalent to ``(S, S, S)``).
If a tuple, the sigmas for spatial dimensions and possibly the time dimension must be specified.
If a list, sigmas for each channel must be specified either as a single number or as a tuple.
Defaults to ``10``.
mode (str, optional): How to compute the background and remove it. Possible values:
``'default'`` (subtract blurred image from the input image),
``'bright_objects'`` (subtract the point-wise minimum of (blurred image, input image) from the input image),
``'dark_objects'`` (subtract the input image from the point-wise maximum of (blurred image, input image)).
Defaults to ``'default'``.
border_mode (str, optional): Values outside image domain are filled according to this mode.
Defaults to ``'reflect'``.
cval (float, optional): Value to fill past edges of image. Only applied when ``border_mode = 'constant'``.
Defaults to ``0``.
always_apply (bool, optional): Always apply this transformation in composition.
Defaults to ``True``.
p (float, optional): Chance of applying this transformation in composition.
Defaults to ``1.0``.
Targets:
image
def __init__(self, sigma: Union[float, tuple, List[Union[tuple, float]]] = 10, mode: str = 'default',
border_mode: str = "reflect", cval: float = 0,
always_apply: bool = True, p: float = 1.0):
self.sigma = sigma
self.mode = mode
self.border_mode = border_mode
self.cval = cval
def apply(self, img, **params):
background = F.gaussian_blur(img, self.sigma, self.border_mode, self.cval)
if self.mode == 'bright_objects':
return img - np.minimum(background, img)
if self.mode == 'dark_objects':
return np.maximum(background, img) - img
return img - background
Lucia D. Hradecka
committed
return f'RemoveBackgroundGaussian(sigma={self.sigma}, mode={self.mode}, border_mode={self.border_mode}, ' \
f'cval={self.cval}, always_apply={self.always_apply}, p={self.p})'
##########################################################################################
# #
# INTENSITY-BASED TRANSFORMATIONS (POINT) #
# #
##########################################################################################
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
class RandomBrightnessContrast(ImageOnlyTransform):
"""Randomly change brightness and contrast of the input image.
Unlike ``RandomBrightnessContrast`` from `Albumentations`, this transform is using the
formula :math:`f(a) = (c+1) * a + b`, where :math:`c` is contrast and :math:`b` is brightness.
Args:
brightness_limit ((float, float) | float, optional): Interval from which the change in brightness is
randomly drawn. If the change in brightness is 0, the brightness will not change.
Must be either of: ``B``, ``(B1, B2)``.
If a float, the interval will be ``(-B, B)``.
Defaults to ``0.2``.
contrast_limit ((float, float) | float, optional): Interval from which the change in contrast is
randomly drawn. If the change in contrast is 1, the contrast will not change.
Must be either of: ``C``, ``(C1, C2)``.
If a float, the interval will be ``(-C, C)``.
Defaults to ``0.2``.
always_apply (bool, optional): Always apply this transformation in composition.
Defaults to ``False``.
p (float, optional): Chance of applying this transformation in composition.
Defaults to ``0.5``.
Targets:
image
"""
def __init__(self, brightness_limit: Union[float, TypePairFloat] = 0.2,
contrast_limit: Union[float, TypePairFloat] = 0.2,
always_apply: bool = False, p: float = 0.5):
super().__init__(always_apply, p)
self.brightness_limit = to_tuple(brightness_limit)
self.contrast_limit = to_tuple(contrast_limit)
def apply(self, img, **params):
return F.brightness_contrast_adjust(img, params['alpha'], params['beta'])
Lucia D. Hradecka
committed
def get_params(self, targets, **data):
Lucia D. Hradecka
committed
# Get transformation parameters:
"alpha": 1.0 + uniform(self.contrast_limit[0], self.contrast_limit[1]),
"beta": 0.0 + uniform(self.brightness_limit[0], self.brightness_limit[1]),
Lucia D. Hradecka
committed
return f'RandomBrightnessContrast(brightness_limit={self.brightness_limit}, ' \
f'contrast_limit={self.contrast_limit}, always_apply={self.always_apply}, p={self.p})'
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
class RandomGamma(ImageOnlyTransform):
"""Performs the gamma transformation with a randomly chosen gamma. If image values (in any channel) are outside
the [0,1] interval, this transformation is not performed.
Args:
gamma_limit (Tuple(float), optional): Interval from which gamma is selected.
Defaults to ``(0.8, 1.2)``.
always_apply (bool, optional): Always apply this transformation in composition.
Defaults to ``False``.
p (float, optional): Chance of applying this transformation in composition.
Defaults to ``0.5``.
Targets:
image
"""
def __init__(self, gamma_limit: TypePairFloat = (0.8, 1.2),
always_apply: bool = False, p: float = 0.5):
super().__init__(always_apply, p)
self.gamma_limit = gamma_limit
def apply(self, img, gamma=1, **params):
return F.gamma_transform(img, gamma=gamma)
def get_params(self, targets, **data):
return {"gamma": uniform(self.gamma_limit[0], self.gamma_limit[1])}
Lucia D. Hradecka
committed
return f'RandomGamma(gamma_limit={self.gamma_limit}, always_apply={self.always_apply}, p={self.p})'
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
class HistogramEqualization(ImageOnlyTransform):
"""Performs equalization of histogram. The equalization is done channel-wise, meaning that each channel is equalized
separately.
**Warning! Images are normalized over both spatial and temporal domains together. The output is in the range [0, 1].**
Args:
bins (int, optional): Number of bins for image histogram.
Defaults to ``256``.
always_apply (bool, optional): Always apply this transformation in composition.
Defaults to ``False``.
p (float, optional): Chance of applying this transformation in composition.
Defaults to ``1``.
Targets:
image
"""
def __init__(self, bins: int = 256, always_apply: bool = False, p: float = 1):
super().__init__(always_apply, p)
self.bins = bins
def apply(self, img, **params):
return F.histogram_equalization(img, self.bins)
Lucia D. Hradecka
committed
def __repr__(self):
return f'HistogramEqualization(bins={self.bins}, always_apply={self.always_apply}, p={self.p})'
# TODO potential upgrade : different sigmas for different channels
class GaussianNoise(ImageOnlyTransform):
"""Adds Gaussian noise to the image. The noise is drawn from normal distribution with given parameters.
var_limit (tuple, optional): Variance of normal distribution is randomly chosen from this interval.
Defaults to ``(0.001, 0.1)``.
mean (float, optional): Mean of normal distribution.
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
Defaults to ``0``.
always_apply (bool, optional): Always apply this transformation in composition.
Defaults to ``False``.
p (float, optional): Chance of applying this transformation in composition.
Defaults to ``0.5``.
Targets:
image
"""
def __init__(self, var_limit: TypePairFloat = (0.001, 0.1), mean: float = 0,
always_apply: bool = False, p: float = 0.5):
super().__init__(always_apply, p)
self.var_limit = var_limit
self.mean = mean
def apply(self, img, **params):
return F.gaussian_noise(img, sigma=params['sigma'], mean=self.mean)
def get_params(self, targets, **params):
Lucia D. Hradecka
committed
# Choose noise standard deviation randomly (noise mean is given deterministically)
var = uniform(self.var_limit[0], self.var_limit[1])
sigma = var ** 0.5
return {"sigma": sigma}
Lucia D. Hradecka
committed
return f'GaussianNoise(var_limit={self.var_limit}, mean={self.mean}, ' \
f'always_apply={self.always_apply}, p={self.p})'
class PoissonNoise(ImageOnlyTransform):
"""Adds Poisson noise to the image.
Args:
peak_limit (tuple): Range to sample the expected intensity of Poisson noise.
Defaults to ``(0.1, 0.5)``.
always_apply (bool, optional): Always apply this transformation in composition.
Defaults to ``False``.
p (float, optional): Chance of applying this transformation in composition.
def __init__(self, peak_limit: TypePairFloat = (0.1, 0.5),
always_apply: bool = False, p: float = 0.5):
super().__init__(always_apply, p)
self.peak_limit = peak_limit
return F.poisson_noise(img, peak=params['peak'])
def get_params(self, targets, **params):
peak = uniform(self.peak_limit[0], self.peak_limit[1])
return {"peak": peak}
Lucia D. Hradecka
committed
return f'PoissonNoise(peak_limit={self.peak_limit}, always_apply={self.always_apply}, p={self.p})'
class Normalize(ImageOnlyTransform):
"""Change image mean and standard deviation to the given values (channel-wise).
Args:
mean (float | List[float], optional): The desired channel-wise means.
Must be either of: ``M`` (for single-channel images),
``[M_1, M_2, ..., M_C]`` (for multi-channel images).
Defaults to ``0``.
std (float | List[float], optional): The desired channel-wise standard deviations.
Must be either of: ``S`` (for single-channel images),
``[S_1, S_2, ..., S_C]`` (for multi-channel images).
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
Defaults to ``1``.
always_apply (bool, optional): Always apply this transformation in composition.
Defaults to ``True``.
p (float, optional): Chance of applying this transformation in composition.
Defaults to ``1``.
Targets:
image
"""
def __init__(self, mean: Union[float, List[float]] = 0, std: Union[float, List[float]] = 1,
always_apply: bool = True, p: float = 1.0):
super().__init__(always_apply, p)
self.mean = mean
self.std = std
def apply(self, img, **params):
return F.normalize(img, self.mean, self.std)
def __repr__(self):
Lucia D. Hradecka
committed
return f'Normalize(mean={self.mean}, std={self.std}, always_apply={self.always_apply}, p={self.p})'
# TODO create checks (mean, std, got good shape, and etc.), what if given list but only one channel, and reverse.
class NormalizeMeanStd(ImageOnlyTransform):
"""Normalize image values to have mean 0 and standard deviation 1, given channel-wise means and standard deviations.
Lucia D. Hradecka
committed
For a single-channel image, the normalization is applied by the formula: :math:`img = (img - mean) / std`.
If the image contains more channels, then the formula is used for each channel separately.
Lucia D. Hradecka
committed
It is recommended to input dataset-wide means and standard deviations.
Lucia D. Hradecka
committed
Args:
mean (float | List[float]): Channel-wise image mean.
Lucia D. Hradecka
committed
Must be either of: ``M`` (for single-channel images),
``(M_1, M_2, ..., M_C)`` (for multi-channel images).
std (float | List[float]): Channel-wise image standard deviation.
Lucia D. Hradecka
committed
Must be either of: ``S`` (for single-channel images),
``(S_1, S_2, ..., S_C)`` (for multi-channel images).
Lucia D. Hradecka
committed
always_apply (bool, optional): Always apply this transformation in composition.
Defaults to ``True``.
p (float, optional): Chance of applying this transformation in composition.
Defaults to ``1``.
Targets:
Lucia D. Hradecka
committed
"""
def __init__(self, mean: Union[tuple, float], std: Union[tuple, float],
Lucia D. Hradecka
committed
always_apply: bool = True, p: float = 1.0):
super().__init__(always_apply, p)
self.mean: np.ndarray = np.array(mean, dtype=np.float32)
self.std: np.ndarray = np.array(std, dtype=np.float32)
assert self.mean.shape == self.std.shape
Lucia D. Hradecka
committed
# Compute the formula denominator once as it is computationally expensive:
self.denominator = np.reciprocal(self.std, dtype=np.float32)
Lucia D. Hradecka
committed
def apply(self, image, **params):
return F.normalize_mean_std(image, self.mean, self.denominator)
Lucia D. Hradecka
committed
def __repr__(self):
Lucia D. Hradecka
committed
return f'NormalizeMeanStd(mean={self.mean}, std={self.std}, always_apply={self.always_apply}, p={self.p})'
Lucia D. Hradecka
committed
##########################################################################################
# #
# OTHER TRANSFORMATIONS #
# #
##########################################################################################
Lucia D. Hradecka
committed
class Contiguous(DualTransform):
"""Transform the image data to a contiguous array.
Args:
always_apply (bool, optional): Always apply this transformation in composition.
Defaults to ``True``.
p (float, optional): Chance of applying this transformation in composition.
Defaults to ``1``.
Targets:
"""
def __init__(self, always_apply: bool = True, p: float = 1.0):
super().__init__(always_apply, p)
def apply(self, image, **params):
return np.ascontiguousarray(image)
def apply_to_mask(self, mask, **params):
return np.ascontiguousarray(mask)
def __repr__(self):
Lucia D. Hradecka
committed
return f'Contiguous(always_apply={self.always_apply}, p={self.p})'
class StandardizeDatatype(DualTransform):
"""Change image and float_mask datatype to ``np.float32`` without changing intensities.
Change mask datatype to ``np.int32``.
Args:
always_apply (bool, optional): Always apply this transformation in composition.
Defaults to ``True``.
p (float, optional): Chance of applying this transformation in composition.
Defaults to ``1``.
Targets:
"""
def __init__(self, always_apply: bool = True, p: float = 1.0):
super().__init__(always_apply, p)
def apply(self, image, **params):
return image.astype(np.float32)
def apply_to_mask(self, mask, **params):
return mask.astype(np.int32)
def apply_to_float_mask(self, mask, **params):
return mask.astype(np.float32)
def __repr__(self):
Lucia D. Hradecka
committed
return f'Float(always_apply={self.always_apply}, p={self.p})'