我在之前的文章中
讲了OpenCV中的滤波函数以及专门去模糊化的函数fastNlMeansDenoisingColored讲解了OpenCV的入门。这一次,我们详细讲解OpenCV中的滤波函数,这样我们在学会滤波函数之后,既可以按照上一篇文章的内容进行噪音去除,也可以使用滤波函数进行反向操作—模糊。
说起来滤波的话,也算是计算机视觉里的元老了,除了前面的噪音去除以外,其实滤波在之后的各个算法中都会露脸,所以我们这里也就不得不认真讲一讲他了。┑( ̄Д  ̄)┍
OpenCV实现了常见的数种滤波函数,同时也支持用户自己编写滤波函数。OpenCV中的滤波函数如下:
1. 均值滤波
2. 中值滤波
3. 高斯滤波
4. 边缘保留滤波
5. 自定义滤波函数
其中的第一种滤波函数和第二种滤波函数,我们上一次都已经演示过来。滤波器本身是个矩阵,均值滤波是计算的均值,在我们之前的白噪点去除实验中,均值滤波效果并不好,而更适合中值滤波。除此之外还有几种比较特别的滤波函数,比如高斯滤波以及边缘保留滤波。其中高斯滤波也是高斯噪音的最佳去除器。而边缘保留滤波(EPF)则是用来保留边缘的滤波函数如双边滤波,均值迁移等等,由于篇幅原因,且边缘保留滤波相对于其他滤波器有些许特殊,所以我们这次会优先讲一讲其他滤波器,边缘保留滤波将会放在之后讲解。
如果需要测试去噪效果,那么我们自然需要一个自动生成噪音图片的函数,以及模块化的进行去除效果展示的函数。这样可以模块化我们的代码,便于学习和理解,具体代码如下:
def showAndCreateNoiseImage(filePath):
if filePath ==None:
print("[ERROR] Parameter filePath is None!" )
else:
img = cv.imread(filePath)
cv.imshow("original",img)
# 根据图像形状自动添加噪音
for i in range(1000): #生成1000个噪点
a = random.randint(0,img.shape[0]-1)
b = random.randint(0,img.shape[1]-1)
img[a,b] = 255
cv.imshow("noise",img) # 显示噪点图
return img
上面这段代码就是对我们上篇文章中增加随机白噪音的改造,我们传入图片,然后就可以获取结果。专业的角度来说,图片中出现的白色或者黑色噪音都是椒盐噪音(更准确的说——椒盐噪声是指两种噪声,盐噪声(高灰度噪声)、胡椒噪声(低灰度噪声)。同时出现时,在图像上呈现为黑白杂点。),因此使用中值滤波处理比较好。
下面的代码和效果就分别展示了噪音图和中值滤波后的数据。
def blurDemo(image): #均值模糊 去随机噪声有很好的去噪效果
dst = cv.blur(image, (3, 3)) #(1, 15)是垂直方向模糊,(15, 1)是水平方向模糊
cv.namedWindow('blur_demo', cv.WINDOW_NORMAL)
cv.imshow("blur_demo", dst)
return dst
def medianBblurDemo(image): # 中值模糊 对椒盐噪声有很好的去噪效果
dst = cv.medianBlur(image, 3)
cv.namedWindow('median_blur_demo', cv.WINDOW_NORMAL)
cv.imshow("median_blur_demo", dst)
src = showAndCreateNoiseImage(filePath)
medianBblurDemo(src)
从定义上来说:高斯噪声是指它的概率密度函数服从高斯分布(即正态分布)的一类噪声。常见的高斯噪声包括起伏噪声、宇宙噪声、热噪声和散粒噪声等等。对于这类高斯噪音,我们其实使用高斯滤波或者均值滤波都有不错的处理效果。
def clamp(pv):
if pv > 255:
return 255
if pv < 0:
return 0
else:
return pv
def gaussian_noise(image): #加高斯噪声
h, w, c = image.shape
for row in range(h):
for col in range(w):
s = np.random.normal(0, 20, 3)
b = image[row, col, 0] #blue
g = image[row, col, 1] #green
r = image[row, col, 2] #red
image[row, col, 0] = clamp(b + s[0])
image[row, col, 1] = clamp(g + s[1])
image[row, col, 2] = clamp(r + s[2])
cv.namedWindow("noise image", cv.WINDOW_NORMAL)
cv.imshow("noise image", image)
return image
src = cv.imread('/home/fonttian/Data/image/OpenCV/test_lena.jpg')
cv.namedWindow("input_image", cv.WINDOW_NORMAL)
cv.imshow('input_image', src)
src = gaussian_noise(src)
dst = cv.GaussianBlur(src, (5,5), 0) #高斯模糊
cv.namedWindow("Gaussian Blur", cv.WINDOW_NORMAL)
cv.imshow("Gaussian Blur", dst)
同时我们可以再用一次均值滤波器,来完成噪音去除。
上面的图像中,我们为高斯滤波选择的参数为(5,5),均值滤波为(3,3),不同的参数会带来不同的效果。你可以多尝试一下上面的代码(参数必须为奇数)。当我们将数值调大时虽然会获得更好地噪音去除效果,但是图像细节也会保留的更少,参数调小的时候噪音留下会逐渐变多,但是细节也更多。
滤波本质就是使用矩阵对图像进行卷积操作,那么我们自然也可以使用自定义滤波器来实现之前OpenCV中的的各种滤波器。比如其中的均值滤波就是n*n矩阵乘以n分之一(乘以n分之一是为了像素值溢出),而高斯滤波则是一个符合高斯分布的矩阵,也就是中值加权。而中值模糊则顾名思义就是用卷积框对应像素的中值来替代中心像素的值。如果我们想要在OpenCV中使用自定义滤波器,则需要使用cv.filter2D
函数。具体代码如下:
def custom_blur_demo(image): # 用户自定义模糊
kernel = np.ones([5, 5], np.float32)/25 #实现均值模糊,除以25是防止数值溢出
# kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32) # 实现锐化处理,提高图像的对比度,提高立体感,轮廓更加清晰
# kernel = np.array([[1,0], [0,-1]], np.float32) # 实现图像梯度
dst = cv.filter2D(image, -1, kernel)
上面的代码中,我们实现了多种自定义滤波器,比如第一个相当于参数(5,5)的均值滤波,二第二种则是锐化处理,第三种是计算图像梯度。下面第一张图片是使用均值滤波模糊后的结果,然后是第二种和第三种的演示效果。
(3,3)中值滤波后
图像锐化后
图像梯度
通过上面的代码我们可以很明显的看到自定义滤波器的强大,我们不仅能够复刻出OpenCV原有的滤波器,也能制作出新的滤波器。而且其中图像梯度滤波器还做了一件非常有趣的事情,就是绘制出了图像轮廓。不过时间上OpenCV能够做到的还不止这些,比如下一次我们就将会讲解真正的OpenCV图像轮廓有关知识。