Image Processing 必备(十):Imgaug之使用随机分布作为参数

Date:2020-09-08

官方教程:https://nbviewer.jupyter.org/github/aleju/imgaug-doc/blob/master/notebooks/C01%20-%20Using%20Probability%20Distributions%20as%20Parameters.ipynb

在大多数情况下,采用简单均匀分布的增强就足以产生良好的结果。

在之前的示例中,会在一些增强方法中看到使用随机分别作为参数的情况。例如,Affine(rotate=(-30, 30)),会在(-30, 30)之间生成随机值作为旋转的角度作用于图像上;Affine(rotate=-30)会始终让图像旋转-30度。

而有时,使用不同于均匀分布的概率分布是明智的。例如使用高斯,拉普拉斯,泊松分布等在这种情况下可以使用Affine(rotate=imgaug.parameters.Normal(0, 5.0))生成服从高斯分布的随机数。

生成&&使用随机参数

确定与随机参数的设置:

  • 标量值被解释为非随机/确定性值(imgaug.parameters.Deterministic);

  • 元组的形式(a, b)经常解释为均匀分布imgaug.parameters.Uniform或者imgaug.parameters.DiscreteUniform;

  • 列表被解释为要从中取样的一组值imgaug.parameters.choice

下面的示例中通过Multiply()函数演示了标量、元组和列表构成参数的输出。

 1 import numpy as np
 2 import imgaug as ia
 3 from imgaug import augmenters as iaa
 4 from imgaug import parameters as iap
 5 %matplotlib inline
 6 np.set_printoptions(precision=2, linewidth=125, suppress=False)
 7 ia.seed(1)
 8  9 aug = iaa.Multiply(mul=1)
10 print(aug.mul)
11 12 aug = iaa.Multiply(mul=(0.5, 1.5))
13 print(aug.mul)
14 15 aug = iaa.Multiply(mul=[0.5, 1.0, 1.5])
16 print(aug.mul)
Deterministic(int 1)
Uniform(Deterministic(float 0.50000000), Deterministic(float 1.50000000))
Choice(a=[0.5, 1.0, 1.5], replace=True, p=None)

另外,可以直接提供一个确定的参数Deterministic(1):

1 aug = iaa.Multiply(mul=iap.Deterministic(1))
2 print(aug.mul)
Deterministic(int 1)

所有随机参数提供一个方法:draw_samples(size, [random_state]),可以用于从参数中采样随机值。生成的数组具有size的形状。注意,random_state的容忍度很高,它也接受整数种子。

1 print(iap.Deterministic(1).draw_samples(10))
2 print(iap.Deterministic(1).draw_samples(10, random_state=1))
3 print(iap.Deterministic(1).draw_samples(10, random_state=1))
4 print(iap.Deterministic(1).draw_samples(10, random_state=2))
[1 1 1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1 1 1]

从上述代码运行结果中可以看出,在以1为确定参数中采样10次随机值,输出10个1。但从结果中暂时看不出random_state的作用。

下面代码使用DeterministicList方法,用于生成确定的多值序列。之前Deterministic方法仅适用于一个值。

1 print("no random_state:", iap.DeterministicList([1,2,3]).draw_samples(10))
2 print("random_state=1:", iap.DeterministicList([1,2,3]).draw_samples(10, random_state=1))
3 print("random_state=2:", iap.DeterministicList([1,2,3]).draw_samples(10, random_state=2))
no random_state: [1 2 3 1 2 3 1 2 3 1]
random_state=1: [1 2 3 1 2 3 1 2 3 1]
random_state=2: [1 2 3 1 2 3 1 2 3 1]

但从返回结果中,也没有看出random_state在其中起到的作用!

  • Uniform:均匀分布

1 print(iap.Uniform(0, 1.0).draw_samples(10))
2 print(iap.Uniform(0, 1.0).draw_samples(10, random_state=1))
3 print(iap.Uniform(0, 1.0).draw_samples(10, random_state=1))
4 print(iap.Uniform(0, 1.0).draw_samples(10, random_state=2))
5 print(iap.Uniform(0, 1.0).draw_samples(10, random_state=1))
[0.49 0.21 0.25 0.65 0.18 0.75 0.22 0.09 0.55 0.46]
[0.43 0.76 0.83 0.83 0.88 0.29 0.2 0.79 0.34 0.31]
[0.43 0.76 0.83 0.83 0.88 0.29 0.2 0.79 0.34 0.31]
[0.89 0.09 0.61 0.64 0.33 0.8 0.19 0.1 0.01 0.7 ]
[0.43 0.76 0.83 0.83 0.88 0.29 0.2 0.79 0.34 0.31]

从上述返回结果可以看出, random_state的作用相当于随机种子的作用表示随机状态,同一随机状态会生成相同的随机数值。 设置的随机状态的数值应该就是random_state容忍度的含义吧。

  • Choice: 随机抽取数值

1 print(iap.Choice([0, 0.5, 1.0]).draw_samples(10))
2 print(iap.Choice([0, 0.5, 1.0]).draw_samples(10, random_state=1))
3 print(iap.Choice([0, 0.5, 1.0]).draw_samples(10, random_state=2))
4 print(iap.Choice([0, 0.5, 1.0]).draw_samples(10, random_state=1))
[1.  0.  0.5 1.  0.  0.5 0.5 0.  0.  0. ]
[0.5 1. 0.5 0.5 0.5 0.5 1. 1. 1. 1. ]
[0.5 1. 0. 0. 0. 1. 0. 0. 0.5 0.5]
[0.5 1. 0.5 0.5 0.5 0.5 1. 1. 1. 1. ]
  • Binomial:二项分布

之前的示例在draw_samples()参数中size均使用的标量。实际上, size也可以是一个tuple,用于生成矩阵。

1 print(iap.Binomial(p=0.5).draw_samples(4*4, random_state=1))
2 print(iap.Binomial(p=0.7).draw_samples((4, 4), random_state=1))
[0 0 1 1 1 1 0 0 1 0 0 1 1 0 1 1]
[[1 1 0 0]
[0 0 1 1]
[0 1 1 0]
[1 1 1 1]]
  • 随机参数支持基本的数学运算

可以在随机数的基础上进行加减乘除等数学运算。

1 print((iap.Deterministic(1) + 1).draw_samples(10))
2 print((iap.Deterministic(1) - 1).draw_samples(10))
3 print((iap.Deterministic(1) / 2).draw_samples(10))
4 print((iap.Deterministic(1) * 2).draw_samples(10))
5 print((iap.Deterministic(1) ** 2).draw_samples(10))
[2 2 2 2 2 2 2 2 2 2]
[0 0 0 0 0 0 0 0 0 0]
[0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5]
[2 2 2 2 2 2 2 2 2 2]
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
1 print(iap.Choice([1, 2, 3]).draw_samples(10))
2 print((iap.Choice([1, 2, 3]) * 2).draw_samples(10))
[2 2 2 2 3 2 2 2 2 1]
[6 2 6 6 2 6 6 2 4 2]

离散随机变量

之前介绍的案例中,要么使用连续概率分布(返回浮点值),要么使用混合概率分布(返回各种类型的数据)。 还有三种离散概率分布,返回整型。

  • 离散随机分布DiscreteUniform

  • 二项分布Binomial

  • 泊松分布Poisson

1 print(iap.DiscreteUniform(0, 3).draw_samples(20))
2 print(iap.Binomial(p=0.3).draw_samples(20))
3 print(iap.Poisson(lam=1).draw_samples(20))
[0 3 0 1 0 1 3 3 1 0 3 2 1 1 0 2 1 0 2 1]
[1 0 1 0 1 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0]
[0 1 0 2 2 2 3 0 4 0 2 1 2 1 0 1 1 3 1 3]

生成的随机变量均为离散随机变量。

另外,可以使用Discretize方法将浮点型四舍五入为整型变量。

1 print("Continuous:", iap.Normal(0.0, 1.0).draw_samples(20))
2 print("Discrete:  ", iap.Discretize(iap.Normal(0.0, 1.0)).draw_samples(20))
Continuous: [-0.62  1.05 -0.64 -0.79  0.04  0.48 -1.67 -0.78 -1.06  0.7  -0.22 -0.18  0.01  0.09 -1.67 -0.22 -0.97 -0.51  1.43 -2.42]
Discrete:   [ 0 -1 -1 0 1 1 0 -1 3 2 -1 -2 1 0 -1 0 1 1 -2 1]

Clip 设置上下限

如果需要给随机变量设置上下限,可以使用Clip方法。

1 uniform = iap.Uniform(-2, 2)
2 print(uniform.draw_samples(15))
3 print(iap.Clip(uniform, -1, None).draw_samples(15))
4 print(iap.Clip(uniform, None, 1).draw_samples(15))
5 print(iap.Clip(uniform, -1, 1).draw_samples(15))
[-1.42 -1.21 -1.68 -0.14 -1.8   0.26  1.96  0.92 -0.97  1.    1.39 -0.68 -1.69 -0.27 -1.43]
[-0.51 -0.94 1.45 -0.96 -0.9 -1.   1.12 0.38 0.11 -1.   1.34 -0.45 -0.84 -1.   0.35]
[ 0.1 -0.25 1.   -1.21 -0.1   0.94 -0.64 -1.49 0.27 0.3 -0.45 -0.5 -0.95 -1.94 -1.47]
[ 1.   -0.43 1.   1.   1.   -0.46 -1.   0.79 -0.6 -1.   -1.   1.   -0.43 1.   -1. ]

从返回的结果可以看出,第二行返回结果最小值为-1,第三行返回结果最大值为1,第四行返回结果介于±1之间。

值得注意的是Clip虽然可以将定义的最小/最大值以下/以上的所有值投影到最小/最大值上,剪辑可能导致失真的概率密度。

1 plot = iap.Clip(uniform, -1, 1).draw_distribution_graph()
2 plot_large = ia.imresize_single_image(plot, 2.0)
3 ia.imshow(plot_large)

png

上述代码使用draw_distribution_graph的方法获取Clip返回数值的概率分布。从图中可以看出,±1的概率分布明显高于其他数值。

改变信号

翻转信号是最简易的方式去改变信号的正负:

1 print(iap.Poisson(lam=1).draw_samples(20, random_state=1))
2 print((-1) * iap.Poisson(lam=1).draw_samples(20, random_state=1))
[1 4 0 1 0 2 1 1 2 0 1 1 0 1 1 2 0 0 3 2]
[-1 -4 0 -1 0 -2 -1 -1 -2 0 -1 -1 0 -1 -1 -2 0 0 -3 -2]

可以取绝对值,如果你只想要正数:

1 print(iap.Normal(0, 1.0).draw_samples(15, random_state=1))
2 print(iap.Absolute(iap.Normal(0, 1.0)).draw_samples(15, random_state=1))
[-0.8   0.17  1.66 -0.61 -0.03 -0.33 -0.73  0.41  1.24 -0.97  1.2   0.54 -0.72 -1.08  0.77]
[0.8 0.17 1.66 0.61 0.03 0.33 0.73 0.41 1.24 0.97 1.2 0.54 0.72 1.08 0.77]

RandomSign:以p_positive概率让数值变为正数,以1-positive概率翻转信号。

1 print(iap.Normal(0, 1.0).draw_samples(15, random_state=1))
2 print(iap.RandomSign(iap.Normal(0,1.0), p_positive=0.8).draw_samples(15, random_state=1))
[-0.8   0.17  1.66 -0.61 -0.03 -0.33 -0.73  0.41  1.24 -0.97  1.2   0.54 -0.72 -1.08  0.77]
[ 0.8   0.17  1.66  0.61  0.03  0.33  0.73  0.41  1.24  0.97  1.2   0.54  0.72  1.08 -0.77]
1 rnd_sign = iap.RandomSign(iap.Normal(0, 1.0), p_positive=0.8)
2 print("15 random samples: ", rnd_sign.draw_samples(15))
15 random samples:  [ 0.8   0.17  1.66  0.61  0.03  0.33  0.73  0.41  1.24  0.97  1.2   0.54  0.72  1.08 -0.77]

ForceSign:具有两个参数:

  • 其一,positive用于控制输出值具有的正负号;

  • 其二,mode用于控制符号是否翻转,当mode="invert"时,会给信号乘以-1;当mode="reroll"时,只对符号错误的值重新采样,从而保持概率分布的密度不变(除去正/负的值)。

1 force_sign_invert = iap.ForceSign(iap.Choice([-2, -1, 5, 10]), positive=False, mode="invert")
2 force_sign_reroll = iap.ForceSign(iap.Choice([-2, -1, 5, 10]), positive=False, mode="reroll")
3 print(force_sign_invert.draw_samples(15))
4 print(force_sign_reroll.draw_samples(15))
[ -1  -2  -2  -2  -2  -1  -5  -5  -5 -10  -1 -10 -10  -1  -1]
[ -1  -2  -1  -2  -2  -2  -1  -1  -2 -10  -2  -2  -2  -1  -1]

下面的示例代码将会探讨ForceSign中参数的作用:* "positive" && "mode":("invert"&&"reroll") *

  • positive为True;mode为invert的情况:

1 param = iap.ForceSign(iap.Normal(4, 2.0), positive=True, mode="invert")
2 plot = param.draw_distribution_graph(bins=400)
3 plot_large = ia.imresize_single_image(plot, 2.0)
4 ia.imshow(plot_large)

png

从代码中可以看出,生成的是一个均值为4,方差为2的正太分布图像。但从图中可以看出,[0, 4]的概率分布要大于[4, 8]的概率分布。这是由于[-∞, 0]的数值被乘以-1(使用mode="invert"的效果),变为正数。 同时由于[-4, 0]的概率密度相对较大(类比于[8, 12]),也就是说,概率密度增大的只有[0, 4]这段。

  • positive为true;mode为reroll的情况:

1 param = iap.ForceSign(iap.Normal(4, 2.0), positive=True, mode="reroll")
2 plot = param.draw_distribution_graph(bins=400)
3 plot_large = ia.imresize_single_image(plot, 2.0)
4 ia.imshow(plot_large)

png

当mode使用reroll时,对于生成的以4为均值,2为方差的正态分布。可以看出,输出无负数部分,[0, 4]与[4, 8]几乎具有相同的概率分布,整体上仍服从正态分布

  • positive为False; mode为invert时:

param = iap.ForceSign(iap.Normal(4, 2.0), positive=False, mode="invert")
plot = param.draw_distribution_graph(bins=400)
plot_large = ia.imresize_single_image(plot, 2.0)
ia.imshow(plot_large)

png

可以看出,当mode为invert时,positive的取False得到的概率分布图实际上与取True时是对称的。

  • positive为False;mode为reroll的情况:

1 param = iap.ForceSign(iap.Normal(4, 2.0), positive=False, mode="reroll")
2 plot = param.draw_distribution_graph(bins=400)
3 plot_large = ia.imresize_single_image(plot, 2.0)
4 ia.imshow(plot_large)

png

另外,Positive()和Negative()两个函数相当于是ForceSign(positive=True) 和 ForceSign(positive=False)实现的捷径。

1 positive_invert = iap.Positive(iap.Choice([-2, -1, 5, 10]), mode="invert")
2 positive_reroll = iap.Positive(iap.Choice([-2, -1, 5, 10]), mode="reroll")
3 print(positive_invert.draw_samples(15))
4 print(positive_reroll.draw_samples(15))
[ 5  2  5 10 10  5 10  2  2 10  1  5  1  2  2]
[ 5  5 10  5 10 10  5 10  2 10 10 10  5  5 10]

结合概率分布

imgaug允许组合概率分布。简单来说,可以将随机生成的数值作为某种概率分布的参数;也可以将概率分布产生的数值作为随机列表。下述两个示例将会演示这两种情况。

  • Situation 1:

将Choice随机生成的数值作为Normal正态分布的均值参数。

1 param = iap.Normal(iap.Choice([2, -2]), 1.0)
2 plot = param.draw_distribution_graph()
3 plot_large = ia.imresize_single_image(plot, 2.0)
4 ia.imshow(plot_large)

png

可以看出,生成了双峰的正态分布,一个以-2为均值,一个以2为均值。

  • Situation 2:

将Normal生成的正态分布作为Choice的随机选择的列表。

1 param = iap.Choice([iap.Normal(-1, 1.0), iap.Normal(1, 0.1)])
2 plot = param.draw_distribution_graph()
3 plot_large = ia.imresize_single_image(plot, 2.0)
4 ia.imshow(plot_large)

png

利用随机参数生成椒盐噪声

 1 def apply_coarse_salt_and_pepper(image, p, size_px):
 2     # mask where to replace
 3     mask = iap.Binomial(p)
 4     
 5     # make the mask coarser
 6     mask_coarse = iap.FromLowerResolution(other_param=mask, size_px=size_px)
 7 
 8     # the noise to use as replacements, mostly close to 0.0 and 1.0
 9     replacement = iap.Beta(0.5, 0.5)
10     
11     # project noise to uint8 value range
12     replacement = replacement * 255
13 
14     # replace masked areas with noise
15     return iaa.ReplaceElementwise(
16         mask=mask_coarse,
17         replacement=replacement
18     )(image=image)
19 
20 import imageio 
21 image = imageio.imread("samoye.jpg")
22 image_aug = apply_coarse_salt_and_pepper(image, 0.05, 20)
23 image_resize = ia.imresize_single_image(np.hstack([image, image_aug]), 4.0)
24 ia.imshow(image_resize)

png

整理总结

本节主要介绍了随机参数的使用,包含生成随机变量(连续-离散),限制生成随机变量的上下限,以及改变信号的一些方法,同时,随机变量与概率分布也可以结合使用。

确定参数:

  • 确定参数:Deterministic

  • 确定参数列表:DeterministicList

获取其中的值:draw_samples(size, random_state)

  • size: 从分布中取多少个参数;

  • random_state: 分布随机种子;

获取概率分布:draw_distribution_graph()

分布:

  • uniform 均匀分布

  • Choice 随机抽取

  • Binomial 二项分布

  • Poisson 泊松分布

  • DiscreteUniform 离散均匀分布

  • Normal 正态分布

限制上下限:

  • Clip

改变信号:

  • Absolute 绝对值

  • RandomSign ( p_positive 依概率)

  • ForceSign (positive, mode[invert&&reroll])

概率分布与随机数生成相互套用

原文地址:https://www.cnblogs.com/monologuesmw/p/13691799.html