skimage中random_noise使用的一点问题

利用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()
归一化16bit array输入
归一化16bit转换到[0, 255]区间输入

如果换成加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()
归一化16bit array输入
归一化16bit转换到[0, 255]区间输入

问题解决:
函数说明中表示‘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较大,因此值较小。

λ对结果的影响

你可能感兴趣的:(skimage中random_noise使用的一点问题)