理论
在早先的章节里,我们看到很多图像平滑技术如高斯模糊,Median模糊等,它们在移除数量小的噪音时在某种程度上比较好用。在这些技术里,我们取像素周围的一小部分邻居,做一些类似于高斯平均权重,中值等替换掉中间的元素。简单说,移除一个像素的噪音是基于本地邻居的。
噪音有一个属性,噪音一般被认为是具有零平均值的随机变量。假设一个像素噪音,p = p0 + n, 其中p0是像素的真实值,n是那个像素的噪音。你可以从不同图像取大量的同一个像素(N)并计算他们的平均值,理想情况下,你应该得到p=p0,因为均值是0.
你可以自己通过一个简单例子验证一下。保持一个静止的摄像机对准一个位置多呆几秒,这会给你很多帧,或者是对一个场景的很多图像。然后写一些代码来找到视频里所有帧的平均值。比较最终的结果和第一帧。你可以看到噪点被去掉了。不幸的是这个简单的方法对于摄像机和场景的运动来说就不健壮了。而且经常你也只有一个噪音图像可用。
所以思路很简单,我们需要一套类似的图像来平均去掉噪点,假设图像上一个小窗口(比如5x5的窗口)。很有可能在图像里的某处还有一个相同的块。有时候是在它附近的邻居。用这样类似的块来做他们的平均会怎么样呢?对于这个特定的窗口,看下面的例子:
图像里蓝色的块看上去一样,绿色块看上去也类似,所以我们取一个像素,取它周围的一个小窗口,找到图像里和它类似的窗口,平均所有的窗口,然后用结果来替换掉这个像素。这哥方法就是非局部均值去噪。它要比其他我们之前介绍过的模糊技术要花更多时间。但是结果要更好。
对于彩色图像,图像先要转换成CIELAB颜色空间然后再分成L去噪和AB部分。
OpenCV里的图像去噪
OpenCV提供了这个技术的四个变形:
1.cv2.fastNlMeansDenoising() - 对于一个灰度图像的
2.cv2.fastNlMeansDenoisingColored() - 对于彩色图像的
3.cv2.fastNlMeansDenoisingMulti() - 对于短时间内拍摄的一序列图像的(灰度图像)
4.cv2.fastNlMeansDenoisingColoredMulti() - 和上面一眼个,不过是彩色图像。
通用参数如下:
·h: 决定过滤器强度的参数。更高的h值能够更好去噪,但是会去掉更多图像细节(10就ok)
·hForColorComponents: 和h一样,不过只是针对彩色图像的(一般和h一样)
·templateWindowSize:应该是奇数(推荐7)
·searchWindowSize:应该是奇数(推荐21)
我们会演示几个例子。
1.cv2.fastNlMeansDenoisingColored()
上面说过这事去除彩色图像噪点的(噪点应该是符合高斯分布的)
import numpy as np
import cv2
from matplotlib import pyplot as pltimg = cv2.imread('die.png')
dst = cv2.fastNlMeansDenoisingColored(img,None,10,10,7,21)
plt.subplot(121),plt.imshow(img)
plt.subplot(122),plt.imshow(dst)
plt.show()
下面是一个放大的结果。我的输入图像是一个σ=25的高斯噪点
2. cv2.fastNlMeansDenoisingMulti()
现在我们把方法应用到视频上,第一个参数是带噪点的帧的列表。第二个参数imgToDenoiseIndex 指定我们需要降哪一帧的噪,应该写输入列表里的帧的索引。第三个参数temporalWindowSize指定多少个周围的帧用来去噪。应该是奇数。这种情况下,temporalWindowSize数量的帧被用来去噪,其中中间帧是要去噪的。比如你传入了5帧的列表作为输入,imgToDenoiseIndex = 2, temporalWindowSize = 3.那么帧1,帧2, 帧3倍用来给帧2去噪,看下面的例子:
import numpy as np
import cv2
from matplotlib import pyplot as pltcap = cv2.VideoCapture('vtest.avi')
# create a list of first 5 frames
img = [cap.read()[1] for i in xrange(5)]# convert all to grayscale
gray = [cv2.cvtColor(i, cv2.COLOR_BGR2GRAY) for i in img]# convert all to float64
gray = [np.float64(i) for i in gray]# create a noise of variance 25
noise = np.random.randn(*gray[1].shape)*10# Add this noise to images
noisy = [i+noise for i in gray]# Convert back to uint8
noisy = [np.uint8(np.clip(i,0,255)) for i in noisy]# Denoise 3rd frame considering all the 5 frames
dst = cv2.fastNlMeansDenoisingMulti(noisy, 2, 5, None, 4, 7, 35)plt.subplot(131),plt.imshow(gray[2],'gray')
plt.subplot(132),plt.imshow(noisy[2],'gray')
plt.subplot(133),plt.imshow(dst,'gray')
plt.show()
它会花掉很多时间计算,上面的结果里,第一张图是原始帧,第二张图是噪点图,第三章是去噪的。