from abc import abstractmethod
import random
import cv2
# noinspection PyUnresolvedReferences
INTERP_NEAREST = cv2.INTER_NEAREST
# noinspection PyUnresolvedReferences
INTERP_LINEAR = cv2.INTER_LINEAR
# noinspection PyUnresolvedReferences
INTERP_CUBIC = cv2.INTER_CUBIC
# noinspection PyUnresolvedReferences
INTERP_LANCZOS4 = cv2.INTER_LANCZOS4
# noinspection PyUnresolvedReferences
INTERP_AERA = cv2.INTER_AREA
INTERPOLATIONS = (
INTERP_NEAREST,
INTERP_LINEAR,
# INTERP_CUBIC,
# INTERP_LANCZOS4
)
MAX_SUPERSAMPLING = 3
[docs]def human2gamma(human):
"""
Convert some human-understandable value
to a value that is suitable for use in gamma correction.
Values outside of `[-1,1]` are clipped.
:param human:
value to convert to gamma
:return:
value suitable for gamma correction
"""
if human >= 0.999:
return 0.00099999999999988987
elif human <= -0.999:
return 999.99999999999909
gamma = human + 1
if gamma > 1:
gamma = 2 - gamma
else:
gamma = 1 / gamma
return gamma
[docs]class RNG(object):
"""
Abstract base class for augmentation random number generators (RNG).
For further information about the semantics of individual parameters,
have a look at :func:`~crumpets.augmentation.randomize_image`.
"""
@abstractmethod
def __call__(self, image, buffer):
"""
Generate random augmentation values.
:param image:
Image that will be augmented.
:param buffer:
Target buffer for the augmented image.
:return:
Dict with randomly generated augmentation values.
For instance:
.. code-block:: python
{
'angle': 10.7650,
'scale': 1.1139,
'aspect': 1.0063,
'shear': (0.0043, -0.0091),
'shift': (0.9163, 0.1045),
'gamma_gray': 1.2463,
'gamma_color': [0.9335, 1.05214, 0.9256],
'contrast': 0.0597,
'noise': 0.0140,
'blur': 0,
'interp_method': 1,
'hmirror': False,
'vmirror': False
}
"""
pass
[docs]class NoRNG(RNG):
"""
Decidedly non-random RNG that simply returns an empty dict.
Useful for validation runs.
"""
def __call__(self, image, buffer):
return {}
[docs]class MixtureRNG(RNG):
"""
Old crumpets 2-style RNG that uses a mix of base probability,
Gaussian and uniform distributions to generate parameters.
See :func:`~crumpets.augmentation.randomize_image` for more details
on allowed values.
..note:
`*_range` parameters must be 2 numbers `(a,b)`.
They define a uniform distribution `U[a,b]`
..note:
`*_sigma` parameters define the standard deviation
of a Gaussian distribution.
:param prob:
probability that pixel-based augmentations are applied;
each augmentation rolls separately;
does not apply to spatial transforms like scale, rotation, etc.
:param scale_range:
influences the `'scale'` value
:param shift_range:
influences the `'shift'` values
:param noise_range:
influences the `'noise'` value
:param brightness_range:
influences the `'gamma_gray'` value;
brightness is converted to gamma by
:func:`~crumpets.rng.convert_gamma`
:param color_range:
influences the `'gamma_color'` values;
color values are converted to gamma by
:func:`~crumpets.rng.convert_gamma`
:param contrast_range:
influences the `'contrast'` value
:param blur_range:
influences the `'blur'` value;
effective sigma for Gaussian blur is `blur*image_width`
:param rotation_sigma:
influences the `'angle'` value
:param aspect_sigma:
influences the `'aspect'` value
:param interpolations:
list of possible interpolation methods to use;
influences the `'interp_method'` value
:param hmirror:
probability that horizontal mirror is applied;
influences the `'hmirror'` value
:param vmirror:
probability that vertical mirror is applied;
influences the `'vmirror'` value
:param shear_range:
influences the `'shear'` values
"""
def __init__(
self,
prob=1,
scale_range=None,
shift_range=None,
noise_range=None,
brightness_range=None,
color_range=None,
contrast_range=None,
blur_range=None,
rotation_sigma=0,
aspect_sigma=0,
interpolations=None,
hmirror=0,
vmirror=0,
shear_range=None,
):
self.prob = prob
self.scale_range = scale_range
self.shift_range = shift_range
self.noise_range = noise_range
self.brightness_range = brightness_range
self.color_range = color_range
self.contrast_range = contrast_range
self.blur_range = blur_range
self.rotation_sigma = rotation_sigma
self.aspect_sigma = aspect_sigma
self.interpolations = interpolations
self.hmirror = hmirror
self.vmirror = vmirror
self.shear_range = shear_range
def __call__(
self,
image,
buffer,
):
prob = self.prob
if not prob:
return {}
kwargs = dict(
angle=0,
scale=1,
aspect=1,
shear=None,
shift=None,
gamma_gray=None,
gamma_color=None,
contrast=0,
noise=0,
blur=0,
interp_method=0,
hmirror=random.random() < self.hmirror,
vmirror=random.random() < self.vmirror,
)
if self.rotation_sigma:
kwargs['angle'] = random.gauss(0, self.rotation_sigma)
if self.scale_range is not None:
kwargs['scale'] = random.uniform(*self.scale_range)
if self.shear_range is not None:
kwargs['shear'] = tuple(random.uniform(*self.shear_range) for _ in range(2))
if self.shift_range is not None:
kwargs['shift'] = tuple(random.uniform(*self.shift_range) for _ in range(2))
if self.aspect_sigma:
kwargs['aspect'] = random.gauss(1, self.aspect_sigma)
if self.brightness_range is not None and random.random() < prob:
kwargs['gamma_gray'] = human2gamma(random.uniform(*self.brightness_range))
if self.color_range is not None and random.random() < prob:
kwargs['gamma_color'] = [human2gamma(random.uniform(*self.color_range))
for _ in range(3)]
if self.contrast_range is not None and random.random() < prob:
kwargs['contrast'] = random.uniform(*self.contrast_range)
if self.noise_range is not None and random.random() < prob:
kwargs['noise'] = random.uniform(*self.noise_range)
if self.blur_range is not None and random.random() < prob:
kwargs['blur'] = random.uniform(*self.blur_range)
if self.interpolations:
kwargs['interp_method'] = random.choice(self.interpolations)
return kwargs