正确地为图像添加高斯噪声 - python

开门见山,直接使用 skimage 库为图像添加高斯噪声是很简单的:

import skimage

origin = skimage.io.imread("./lena.png")
noisy = skimage.util.random_noise(origin, mode='gaussian', var=0.01)

但是如果不用库函数而自己实现的话,有几个问题是值得注意的。

彩图 or 灰度图

读取图片时,图片可能是有三通道的RGB图片,也有可能是单通道的灰度图,甚至四通道的RGBA图。

通道数不同会影响图像数据的 shape ,例如: (256, 256, 3) 、(256, 256)

很多人按照MATLAB的习惯,使用 np.random.randn 来生成高斯噪声,则需要根据通道数调整参数。

# RGB
noise = sigma * np.random.randn(256, 256, 3)
# GRAY
noise = sigma * np.random.randn(256, 256)

为了通用的处理,最好使用 np.random.normal 生成高斯噪声。

noise = np.random.normal(mean, var ** 0.5, image.shape)

前两个参数分别为 均值和标准差,第三个参数为生成数据的 shape,直接将图像本身shape输入进去,更加优雅。

uint8 or float64

一般遇到的图像都是8bit的,用skimage或opencv读取后会发现数据类型是uint8的ndarray,取值范围是 [0, 255]

如果手贱直接在整型数据上添加高斯噪声,如:

image = io.imread("lena.png")
noise = np.random.normal(0, 10, image.shape)
noisy = image + noise

你会发现 plt.imshow 出来的是一片空白,或者有零星几个噪点。

以一个像素为例分析原因:

  • 图像本身是[0, 255]的整数:[226 137 125]

  • 生成的噪声是浮点数:[-2.92864248 4.04786763 12.23436435]

  • 相加后最后的数据:[223.07135752 141.04786763 137.23436435]

matplotlib 的 imshow 要求输入是 (0-1 float or 0-255 int),所以上述不伦不类的数据是无法正确显示的(只显示了恰好落在0-1之间的像素)。

int or float

在很多应用中,为了方便计算,都会将图像数据转换为浮点数,float64,取值范围为 [0, 1]

为了转换数据类型,最简单的方式是直接除以255:

image = io.imread("./lena.png")
print(image.dtype)					# uint8

image = image/255
print(image.dtype)					# float64

更稳妥的做法,可以使用skimage的img_to_float()

image = img_as_float(image)

这样再添加高斯噪声就可以正确显示。

方差 or 标准差

高斯噪声符合一个均值为0,方差为 σ 2 \sigma^2 σ2 的高斯分布。

均值为0,是保证图像的亮度不会有变化,而方差大小则决定了高斯噪声的强度。

方差/标准差越大,噪声越强。

这里有一点点初中数学的细节,就是在[0, 1]区间内, y = x y=\sqrt{x} y=x y = x y=x y=x 要大。

sqrtx

所以,设置方差为0.1,噪声要比设置标准差为0.1大不少。注意不要用混了就可以。

sigma

是否截断(clip)

由于需要把噪声叠加到原图像中,因此叠加后的数据值就可能超出对应数据类型的取值范围

如果用matplotlib显示超出范围的彩色图像,则可能遇到以下提示:

Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).

matplotlib自动将图片做了截断。

而不知为何,matplotlib并不会自动对灰度图进行截断,例如:

clip

叠加噪声之后,图片数据的最小值和最大值分别为 -0.32 和 1.25,这明显超过了[0, 1]的范围。

这样显示出的图片是不正确的(中间图像),更像是重新将图像缩放到了[0, 1]范围内,就像将色阶向外扩了一样,对比度也下降了。

使用 np.clip,将图像截断到 [0, 1]之间,如右图所示,图像明显正常了很多。

总结

完整的代码如下:

from skimage import io, img_as_float
import numpy as np

mean = 0
var = 0.01

image = io.imread("./lena.png")

image = img_as_float(image)
noise = np.random.normal(mean, var**0.5, image.shape)
noisy = image + noise
noisy = np.clip(noisy, 0.0, 1.0)

当然,上述问题在 skimage.util.random_noise 中都已解决,工程中可以直接使用。

import skimage

origin = skimage.io.imread("./lena.png")
noisy = skimage.util.random_noise(origin, mode='gaussian', var=0.01)

推荐学习skimage的源码

参考

https://zhuanlan.zhihu.com/p/50820267

https://blog.csdn.net/u013044310/article/details/80739623

https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.imshow.html

https://github.com/scikit-image/scikit-image/blob/v0.17.2/skimage/util/noise.py#L8

  • 44
    点赞
  • 199
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Python是一种功能强大的编程语言,用于图像处理和数字信号处理,使用Python添加噪声是一个相对简单的过程。高斯噪声是一种广泛使用的噪声类型,可用于模拟自然环境中的噪声。 以下是添加高斯噪声的过程: 1.导入所需库——numpy和opencv 2.读取图像——用OpenCV的imread函数或其他适当函数来读取图像 3.创建高斯分布——用numpy的random函数生成高斯分布 4.将高斯噪声添加图像 5.显示和保存图像——用OpenCV的imshow函数显示图像,用imwrite函数保存图像。 具体步骤如下: import cv2 import numpy as np # 读取图像 img = cv2.imread('image.jpg') # 添加高斯噪声 mean = 0 var = 1 sigma = var ** 0.5 gauss = np.random.normal(mean, sigma, img.shape) gauss = gauss.reshape(img.shape[0], img.shape[1], img.shape[2]).astype('uint8') img_gauss = cv2.add(img, gauss) # 显示噪声图像 cv2.imshow('Gaussian Noise', gauss) # 显示添加噪声后的图像 cv2.imshow('Gaussian Noise Applied', img_gauss) # 保存添加噪声后的图像 cv2.imwrite('image_gauss.jpg', img_gauss) # 等待按键退出窗口 cv2.waitKey(0) cv2.destroyAllWindows() 在此示例中,高斯噪声具有平均值为0,方差为1,然后将其重塑为与原始图像相同的大小和数据类型,并将其添加到原始图像中。这生成了添加高斯噪声后的图像。最后,该程序将噪声图像添加噪声后的图像显示在OpenCV窗口中,并将添加噪声后的图像保存到磁盘上。 综上所述,使用Python添加高斯噪声是相对容易的,需要的只是导入需要的库和几行简单的代码即可完成。添加噪声可用于许多实际应用中,例如图像增强和图像识别。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值