OpenCV Python 图像去噪 Image Denoising
【目标】
- 非局部均值去噪算法去除图像中的噪声。
- cv2.fastNlMeansDenoising() , cv2.fastNlMeansDenoisingColored() etc.
【理论】
在前面的章节中,我们已经看到了许多图像平滑技术,如高斯模糊,中值模糊等,它们在一定程度上很好地去除少量的噪声。在这些技术中,我们在像素周围取一个小的邻域,并进行一些操作,如高斯加权平均,值的中值等来替换中心元素。简而言之,一个像素的噪声去除是局部的。
噪声有一个特性。噪声通常被认为是一个均值为零的随机变量。考虑一个有噪声的像素, p = p 0 + n p=p_0+n p=p0+n,其中 p 0 p_0 p0是像素的真实值, n n n是像素中的噪声。您可以从不同的图像中获取大量相同的像素(例如 N N N)并计算它们的平均值。理想情况下,你应该得到 p = p 0 p=p_0 p=p0,因为噪声的均值为零。
您可以通过一个简单的设置自己验证它。将静态相机固定在某个位置几秒钟。这将为您提供大量的帧,或同一场景的大量图像。然后写一段代码来找到视频中所有帧的平均值(这对你来说应该太简单了)。比较最终结果和第一帧。你可以看到噪音减少了。不幸的是,这种简单的方法对相机和场景运动不健壮。通常只有一个噪点图像可用。
想法很简单,我们需要一组相似的图像来平均噪声。考虑图像中的一个小窗口(比如5x5窗口)。同样的补丁很有可能出现在图像中的其他地方。有时在它周围的一个小社区。把这些相似的补丁放在一起并找出它们的平均值怎么样?对于特定的窗口,这是可以的。请看下面的示例图片:
图中的蓝色斑块看起来很相似。绿色的斑块看起来很相似。所以我们取一个像素,在它周围取一个小窗口,在图像中搜索相似的窗口,平均所有的窗口,然后用我们得到的结果替换像素。这种方法是非局部均值去噪。与我们之前看到的模糊技术相比,它需要更多的时间,但它的结果非常好。更多细节和在线演示可以在附加资源的第一个链接中找到。
对于彩色图像,将图像转换为CIELAB色彩空间,然后分别去噪L分量和AB分量。
现在我们将同样的方法应用到视频中。第一个参数是噪声帧的列表。第二个参数imgToDenoiseIndex指定我们需要去噪的帧,为此我们将帧的索引传递到输入列表中。第三个是temporalWindowSize,它指定用于去噪的附近帧的数量。它应该是奇数。在这种情况下,总共使用了temporalWindowSize帧,其中中心帧是要去噪的帧。例如,您传递了一个5帧的列表作为输入。让imgToDenoiseIndex = 2和temporalWindowSize = 3。然后利用帧1、帧2和帧3对帧2进行去噪。
【代码】
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread("assets/die.png")
dst = cv2.fastNlMeansDenoisingColored(img, None, 10, 10, 7, 21)
cv2.imshow("src image", img)
cv2.imshow("denoised image", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
import numpy as np
import cv2
from matplotlib import pyplot as plt
cap = cv2.VideoCapture("assets/vtest.avi")
# 连续读入帧
img = [cap.read()[1] for i in range(5)]
# 转换灰度
gray = [cv2.cvtColor(i, cv2.COLOR_BGR2GRAY) for i in img]
gray = [np.float64(i) for i in gray]
noise = np.random.randn(*gray[1].shape)*10
# 添加噪声到图像中
noisy = [i+noise for i in gray]
# 转换为 uint8
noisy = [np.uint8(np.clip(i, 0, 255)) for i in noisy]
gray_back = [np.uint8(np.clip(i, 0, 255)) for i in gray]
# 参考连续5帧为第3帧降噪
dst = cv2.fastNlMeansDenoisingMulti(noisy, 2, 5, None, 4, 7, 35)
cv2.imshow("gray", gray_back[2])
cv2.imshow("noisy", noisy[2])
cv2.imshow("dst", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
【接口】
- fastNlMeansDenoisingColored
cv2.fastNlMeansDenoisingColored( src[, dst[, h[, hColor[, templateWindowSize[, searchWindowSize]]]]] ) -> dst
修改彩色图像的fastnlmeans降噪函数。
- src: 8位3通道图像
- dst: 输出结果图像
- templateWindowSize: 模板 patch 大小,奇数,推荐为7
- searchWindowSize: 窗口大小(以像素为单位),用于计算给定像素的加权平均。奇数。更大的searchWindowsSize -更长的去噪时间。推荐值21像素
- h: 亮度组件滤光强度调节参数。较大的h值可以完美地去除噪声,但也可以去除图像细节,较小的h值保留了细节,但也保留了一些噪声
- hColor: 和h一样,只是颜色分量不同。对于大多数图像,值等于10将足以消除彩色噪声,不扭曲颜色
该函数将图像转换为CIELAB颜色空间,然后使用fastnlmeans降噪函数对给定h参数的L和AB分量分别进行降噪。
- fastNlMeansDenoisingMulti
cv2.fastNlMeansDenoisingMulti( srcImgs, imgToDenoiseIndex, temporalWindowSize[, dst[, h[, templateWindowSize[, searchWindowSize]]]] ) -> dst
cv2.fastNlMeansDenoisingMulti( srcImgs, imgToDenoiseIndex, temporalWindowSize, h[, dst[, templateWindowSize[, searchWindowSize[, normType]]]] ) -> dst
利用多帧图像进行降噪
- srcImgs: 8位,单通道,二通道,三通道,四通道图像序列
- imgToDenoiseIndex: 需要降噪图像的序号
- temporalWindowSize: 目标图像周围图像个数
- dst: 输出与srcImgs图像大小和类型相同的图像。
- templatewindowsize: 用于计算权重的模板补丁的像素大小。应该是奇数。推荐值7像素
- searchWindowSize: 窗口大小(以像素为单位),用于计算给定像素的加权平均。应该是奇数。线性影响性能:更大的searchWindowsSize -更长的去噪时间。推荐值21像素
- h: 参数调节过滤强度。较大的h值可以完美地去除噪声,但也可以去除图像细节,较小的h值保留了细节,但也保留了一些噪声
【参考】
- OpenCV: Image Denoising
- http://www.ipol.im/pub/art/2011/bcm_nlm/ (It has the details, online demo etc. Highly recommended to visit. Our test image is generated from this link)
- Online course at coursera (First image taken from here)
- Denoising