利用skimage.util.random_noise函数,我们可以很方便的实现对图像添加高斯,泊松,椒盐噪声等常见噪声。下面是函数的使用帮助:
random_noise(image, mode='gaussian', seed=None, clip=True, **kwargs)
Function to add random noise of various types to a floating-point image.
Parameters
----------
image : ndarray
Input image data. Will be converted to float.
mode : str, optional
One of the following strings, selecting the type of noise to add:
- 'gaussian' Gaussian-distributed additive noise.
- 'localvar' Gaussian-distributed additive noise, with specified
local variance at each point of `image`.
- 'poisson' Poisson-distributed noise generated from the data.
- 'salt' Replaces random pixels with 1.
- 'pepper' Replaces random pixels with 0 (for unsigned images) or
-1 (for signed images).
- 's&p' Replaces random pixels with either 1 or `low_val`, where
`low_val` is 0 for unsigned images or -1 for signed
images.
- 'speckle' Multiplicative noise using out = image + n*image, where
n is uniform noise with specified mean & variance.
seed : int, optional
If provided, this will set the random seed before generating noise,
for valid pseudo-random comparisons.
clip : bool, optional
If True (default), the output will be clipped after noise applied
for modes `'speckle'`, `'poisson'`, and `'gaussian'`. This is
needed to maintain the proper image data range. If False, clipping
is not applied, and the output may extend beyond the range [-1, 1].
mean : float, optional
Mean of random distribution. Used in 'gaussian' and 'speckle'.
Default : 0.
var : float, optional
Variance of random distribution. Used in 'gaussian' and 'speckle'.
Note: variance = (standard deviation) ** 2. Default : 0.01
local_vars : ndarray, optional
Array of positive floats, same shape as `image`, defining the local
variance at every image point. Used in 'localvar'.
amount : float, optional
Proportion of image pixels to replace with noise on range [0, 1].
Used in 'salt', 'pepper', and 'salt & pepper'. Default : 0.05
salt_vs_pepper : float, optional
Proportion of salt vs. pepper noise for 's&p' on range [0, 1].
Higher values represent more salt. Default : 0.5 (equal amounts)
Returns
-------
out : ndarray
Output floating-point image data on range [0, 1] or [-1, 1] if the
input `image` was unsigned or signed, respectively.
由上述说明可见,函数的输入是一个ndarray image。
但是在将16bit图像归一化作为输入加泊松噪声时,发现函数输出观察不到噪声。而换用8bit图像,用[0, 255]区间图像和归一化后图像作为输入,输出都可观察到明显噪声。
将16bit图像归一化后再转换到[0, 255]区间输入,则可观察到明显噪声。
I = imageio.imread('16bit.tif')/(2**16-1)
I_conv = (255*I).astype(np.uint8) #np.array
I_p = util.random_noise(I,mode='poisson')
I_conv_p = util.random_noise(I_conv,mode='poisson')
plt.imshow(I_p)
plt.show()
plt.imshow(I_conv_p)
plt.show()
如果换成加gaussian,椒盐,speckle噪声等其他与图像信息无关的噪声,则输入图像比特数没有影响
I = imageio.imread('16bit.tif')/(2**16-1)
I_conv = (255*I).astype(np.uint8) #np.array
I_p = util.random_noise(I,mode='gaussian')
I_conv_p = util.random_noise(I_conv,mode='gaussian')
plt.imshow(I_p)
plt.show()
plt.imshow(I_conv_p)
plt.show()
问题解决:
函数说明中表示‘poisson’噪声是和图像数据相关: Poisson-distributed noise generated from the data. 因此我去scikit-image: Image processing in Python
搜索了该段的源码,位于master/skimage/util/noise.py,poisson噪声部分定义如下:
def img_as_float(image, force_copy=False):
"""Convert an image to floating point format.
This function is similar to `img_as_float64`, but will not convert
lower-precision floating point arrays to `float64`.
Notes
-----
The range of a floating point image is [0.0, 1.0] or [-1.0, 1.0] when
converting from unsigned or signed datatypes, respectively.
If the input image has a float type, intensity values are not modified
and can be outside the ranges [0.0, 1.0] or [-1.0, 1.0].
"""
return _convert(image, np.floating, force_copy)
## ----------------------- util.random_noise -----------------------------
# Detect if a signed image was input
if image.min() < 0:
low_clip = -1.
else:
low_clip = 0.
image = img_as_float(image)
## ...
elif mode == 'poisson':
# Determine unique values in image & calculate the next power of two
vals = len(np.unique(image))
vals = 2 ** np.ceil(np.log2(vals))
# Ensure image is exclusively positive
if low_clip == -1.:
old_max = image.max()
image = (image + 1.) / (old_max + 1.)
# Generating noise for each unique value in image.
out = np.random.poisson(image * vals) / float(vals)
# Return image to original range if input was signed
if low_clip == -1.:
out = out * (old_max + 1.) - 1.
## ...
if clip:
out = np.clip(out, low_clip, 1.0)
return out
对于16 bit图像 val=65536,对于8 bit图像,val=256(max)。
绘制np.random.poisson和lamda关系图:
import scipy.stats as stats
import matplotlib.pyplot as plt
plt.plot(np.arange(256),stats.poisson.pmf(np.arange(256),mu=10))
plt.plot(np.arange(256),stats.poisson.pmf(np.arange(256),mu=50))
plt.plot(np.arange(256),stats.poisson.pmf(np.arange(256),mu=100))
plt.plot(np.arange(256),stats.poisson.pmf(np.arange(256),mu=256))
#LEGEND 图例
plt.xlim(0,256)
plt.legend(["$λ=10$", "$λ=50$", "$λ=100$", "$λ=256$"])
plt.show()
对于归一化后的16 bit图像输入,我们使用val=256,输出图像则可看到明显的噪声。因此是由于val值过大导致np.random.poisson(lamda) 的lamda较大,因此值较小。