官网:https://imgaug.readthedocs.io/en/latest/
教程:https://github.com/aleju/imgaug-doc/tree/master/notebooks
到目前为止,所有其他教程都假设只有一个增强序列应用于所有输入。本教程将介绍如何使用多个增强序列,尤其是如何对齐这些序列之间的随机数,这对增强图像、热图、分割图非常有用。
imgaug
提供了热图和分割图增强的方法,但这些方法适用于实况数据,并且仅适用于影响几何形状的增强(如,高斯噪声或dropout are deactivated)。
如果你想要非几何增强,你必须将热图或分割图作为图像处理并通过augment_images()
提供,但是你可能想要使用与用于图像的增强器和参数不同的增强器和参数,仍然得到可比较的结果(例如作物数量应该匹配)。
本教程将展示如何控制随机数生成,从而使用具有对齐随机模式的多个增强序列。
以下示例从标准方案开始,包含一张图像和一张热图:
import numpy as np
import imgaug as ia
%matplotlib inline
ia.seed(1)
# load image + heatmap
image = ia.quokka(size=0.2) # uint8 array
heatmap = ia.quokka_heatmap(size=0.2) # HeatmapsOnImage object, contains a float array
# show image + heatmap
ia.imshow(np.hstack([image, heatmap.draw_on_image(image)[0]]))
# print min/max of value ranges
print("image min: %.2f, max: %.2f" % (np.min(image), np.max(image)))
print("heatmap min: %.2f, max: %.2f" % (np.min(heatmap.get_arr()), np.max(heatmap.get_arr())))
image min: 0.00, max: 255.00
heatmap min: 0.00, max: 1.00
如你所见,图像(0到255)和热图(0.0到1.0)之间的值范围不同。 让我们野蛮地将相同的增强序列应用于图像和热图,同时使用augment_image()
:
import imgaug.augmenters as iaa
# our augmentation sequence: affine rotation, dropout, gaussian noise
augs = iaa.Sequential([
iaa.Affine(rotate=(-45, 45)),
iaa.Dropout(0.2),
iaa.AdditiveGaussianNoise(scale=20)
])
# apply to image + heatmap
augs_det = augs.to_deterministic()
image_aug = augs_det.augment_image(image)
heatmap_aug = augs_det.augment_image(heatmap.get_arr())
# print min/max of value ranges
print("image min: %.2f, max: %.2f" % (np.min(image_aug), np.max(image_aug)))
print("heatmap min: %.2f, max: %.2f" % (np.min(heatmap_aug), np.max(heatmap_aug)))
# show results
ia.imshow(np.hstack([
image_aug,
ia.HeatmapsOnImage(
np.clip(heatmap_aug, 0.0, 1.0),
shape=image_aug.shape
).draw_on_image(image_aug)[0]
]))
image min: 0.00, max: 255.00
heatmap min: -73.75, max: 77.37
热图的价值范围现在与以前相比非常不同。 它从[0.0,1.0]
变为[-73.75,77.37]
。 这是由AdditiveGaussianNoise
引起的,它从N(0, 20)
之后的高斯分布中采样。
如此大的变化对图像来说是不合适的,但对于热图则不然。
下面的示例显示了解决此问题的一种方法。
我们首先将特定于图像的增强序列复制一份用于热图。 然后,仅在热图特定序列中,我们将AdditiveGaussianNoise
的scale参数包装在Multiply(..., 0.001)
中,它将采样的比例值减少1000倍。(图像中的比例被定义为20,我们也可以通过iap.Deterministic(0.02)
指定,并且具有完全相同的结果。)
import imgaug.parameters as iap
# Ensure that all augmenters in 'augs' use their own random number generators,
# instead of using a global (shared) random number generator.
augs = augs.localize_random_state(recursive=True)
# Now copy augs to create an augmentation sequence for heatmaps.
# This also copies all random number generators, which means that the
# augmenters will sample the same random numbers.
augs_heatmap = augs.deepcopy()
# Reduce the scale for heatmaps to `0.001 * scale_for_images`.
# Here, this is the same as
# augs_heatmap[2].value.scale.value = iap.Deterministic(0.2)
# because the scale was defined above as scale=20 and hence was a deterministic value.
# Note that it would be .value.scale.value and not just .scale, because AdditiveGaussianNoise
# returns an instance of AddElementwise, which adds values sampled from .value to images,
# where .value is a gaussian distribution with .value.scale.value.
augs_heatmap[2].value = iap.Multiply(augs_heatmap[2].value, 0.001)
# Augment images and heatmaps.
# We can skip here calling to_deterministic(), as both sequences
# already use the exactly same random number generators.
image_aug = augs.augment_image(image)
heatmap_aug = augs_heatmap.augment_image(heatmap.get_arr())
print("image min: %.2f, max: %.2f" % (np.min(image_aug), np.max(image_aug)))
print("heatmap min: %.2f, max: %.2f" % (np.min(heatmap_aug), np.max(heatmap_aug)))
ia.imshow(np.hstack([
image_aug,
ia.HeatmapsOnImage(
np.clip(heatmap_aug, 0.0, 1.0),
shape=image_aug.shape
).draw_on_image(image_aug)[0]
]))
image min: 0.00, max: 255.00
heatmap min: -0.07, max: 1.05
如你所见,增强热图的值范围现在更合适。 它略微超出[0.0,1.0]
的期望值范围,但可以在通过剪裁增强后处理。
上述模式仍有一个值得注意的缺点:必须找出并保存要替换的参数以及替换它们的值。
然而,更容易的解决方案是基于以标准方式用略微不同的参数实例化两个序列,然后将随机性从一个序列复制到另一个序列。
成功复制的唯一要求是为每个增强器分配唯一的名称。 确保两个序列之间的名称匹配,以便相同的增强器具有相同的名称。 然后将随机状态从一个序列复制到另一个序列就可以了。 例:
# Create image-specific augmentation sequence.
# Give each augmenter its own name.
sequence_images = iaa.Sequential([
iaa.Affine(rotate=(-45, 45), name="affine"),
iaa.Dropout(0.2, name="dropout"),
iaa.AdditiveGaussianNoise(scale=20, name="gauss-noise")
])
# Create heatmap-specific augmentation sequence.
# Make sure that the names are the same for augmenters that are supposed to be aligned!
# Note how the scale of AdditiveGaussianNoise is much lower than in the image-specific sequence.
sequence_heatmaps = iaa.Sequential([
iaa.Affine(rotate=(-45, 45), name="affine"),
iaa.Dropout(0.2, name="dropout"),
iaa.AdditiveGaussianNoise(scale=0.02, name="gauss-noise") # different!
])
# Copy once the random states between the sequences by name.
# As before, first make sure that the source augmentation sequence
# uses its own random states instead of global (shared) random states.
# Now both sequences will follow the same sampling behaviour.
sequence_images = sequence_images.localize_random_state(recursive=True)
sequence_heatmaps_det = sequence_heatmaps.copy_random_state(sequence_images, matching="name")
# We can skip deterministic mode again, because both sequences have the same
# random states anyways.
image_aug = sequence_images.augment_image(image)
heatmap_aug = sequence_heatmaps.augment_image(heatmap.get_arr())
print("image min: %.2f, max: %.2f" % (np.min(image_aug), np.max(image_aug)))
print("heatmap min: %.2f, max: %.2f" % (np.min(heatmap_aug), np.max(heatmap_aug)))
ia.imshow(np.hstack([
image_aug,
ia.HeatmapsOnImage(
np.clip(heatmap_aug, 0.0, 1.0),
shape=image_aug.shape
).draw_on_image(image_aug)[0]
]))
image min: 0.00, max: 255.00
heatmap min: -0.08, max: 1.05
你可能想知道:如果我们只是希望两个序列中的增强器都使用相似的随机状态,那么在实例化这些增强器时我们是否可以定义这些随机状态?然后我们可以确保相同的增强器获得相同的随机状态。是的,这是可能的。每个增强器都有一个random_state
参数。您可以使用此参数来提供numpy.random.RandomState
实例或仅提供种子值。
这使得对齐两个序列十分简单的,如下面的例子所示。
请注意,这次我们不仅要更改增强序列之间的值范围,还要为它们添加不同的增强器。这不会影响剩余增强器的指定随机状态。
请记住,虽然在这里我们只能在序列之间使用增强器的不同位置,因为我们没有在Sequential
中激活random_order
。否则会得不到匹配,因为图像序列序列中的第二个增强器与热图序列中的第二个不同。
ia.seed(1) # to make Snowflakes reproducible
# Image-specific sequence.
sequence_images = iaa.Sequential([
iaa.Affine(rotate=(-45, 45), random_state=1),
iaa.Snowflakes(), # added!
iaa.Dropout(0.2, random_state=2),
iaa.AdditiveGaussianNoise(scale=20, random_state=3)
], random_state=4)
# Heatmap-specific sequence.
# Make sure to use the same random state seeds as above.
sequence_heatmaps = iaa.Sequential([
iaa.Affine(rotate=(-45, 45), random_state=1),
iaa.Dropout(0.2, random_state=2),
iaa.CoarseDropout(0.2, size_px=4, random_state=100), # added!
iaa.AdditiveGaussianNoise(scale=0.02, random_state=3)
], random_state=4)
# We can skip deterministic mode again, because both sequences have the same
# random states anyways.
image_aug = sequence_images.augment_image(image)
heatmap_aug = sequence_heatmaps.augment_image(heatmap.get_arr())
print("image min: %.2f, max: %.2f" % (np.min(image_aug), np.max(image_aug)))
print("heatmap min: %.2f, max: %.2f" % (np.min(heatmap_aug), np.max(heatmap_aug)))
ia.imshow(np.hstack([
image_aug,
ia.HeatmapsOnImage(
np.clip(heatmap_aug, 0.0, 1.0),
shape=image_aug.shape
).draw_on_image(image_aug)[0]
]))