这篇文章主要讲解了openCV对图像的平滑去噪的处理基础

滤波操作

卷积操作,通过像素卷积核对其核内的数值进行相关的数值变换操作

滤波卷积核展示

这里是通过一定的规则,对于一组卷积核内的数值进行变换进行滤波

原始数据:初始数据图片

这张图片好像不是带杂音的图像,读者可以自己去找一找一张带杂讯的图片

均值滤波

openCV中可以使用blur函数对数据进行均值滤波

1
blur = cv2.blur(img, (3, 3))

cv2.blur(image,kernel_size)的两个参数image代表要处理的文件,kernel_size代表在滤波过程中使用的卷积核大小(最好使用奇数大小)

均值滤波实际上是对于卷积核内的数值相加再取算数均值填入卷积核最中间的格子中

方框滤波

和均值率波的计算过程是一样的,不过可以添加正则化处理和大小越界

1
box = cv2.boxFilter(img, -1, (3, 3), normalize=True) # 第二个参数 为颜色通道统一,填入-1即可

如果不加normalize=True或者设置normalize=False那么对于方框滤波就不会取均值而是取和,很容故意发生越界现象,比如下图:

不加正则化后的方框滤波处理

高斯滤波

可理解为根据离卷积核中间的距离可以判断远近,通过高斯概率分布赋予对应方格权值,在进行计算加权均值填入卷积核中间的格子中

1
gaussian = cv2.GaussianBlur(img, (5, 5), 1)

最后的一个参数代表是正态分布的标准差为1

中值滤波

对中间值取kernel中的值的中位数替换

1
median = cv2.medianBlur(img, 5)  #第二个参数为kernel_size,这个5代表(5,5)

tip(图片拼接显示)

利用np.hstack()可以实现多组图像的水平拼接显示,而利用np.vstack()可以实现垂直拼接实现

1
2
3
def figure_splicing(images):
res = np.hstack(images) # res = np.vstack(images)
return res
1
2
res = utils.figure_splicing((img, blur, gaussian, median))
utils.show_image('r', res)

对比结果

四个图像依次是:原图、均值滤波、高斯滤波、中值滤波

过滤操作

腐蚀操作

注意 : 腐蚀操作的对象通常是二值图

类似与上面的滤波操作,腐蚀操作也有一个腐蚀核,对于一个腐蚀核,如果腐蚀核中有黑又白,那么将核中将白点全部涂黑,黑点不变(从图像开始检索,到检索玩这张图为一个迭代)

读入二值图像 利用ret,dst=cv2.threshold(src,thresh,maxval,type) (上一章有讲到)

1
img = cv2.imread('../img/fs.png')

这个项目没用的原因是因为图片本身就是二值图,不需要再根据函数得到二值图

腐蚀操作的代码

1
2
kernel = np.ones((5, 5), np.uint8)
erosion = cv2.erode(img, kernel, iterations=1)

对于腐蚀操作cv2.erode()的三个参数,第一个略过:

  • 第二个:kernel,腐蚀核的大小
  • 第三个:iterations,迭代次数,迭代此时对于结果影像如下图

迭代次数对于结果的影响

对应的代码:

1
2
3
4
5
6
erosion1 = cv2.erode(img, kernel, iterations=1)
erosion2 = cv2.erode(img, kernel, iterations=2)
erosion3 = cv2.erode(img, kernel, iterations=3)

res = utils.figure_splicing((img, erosion1, erosion2, erosion3))
utils.show_image("对比", res)

上述四图依次是原图、迭代1次、迭代2次、迭代3次的结果 ,其中kernel 保持为(5,5)

腐蚀核大小对于结果的影响这里就不在给出,读者若有兴趣可以自行尝试,或者取我的GitHub上查看源码

膨胀操作

膨胀操作是腐蚀操作的逆操作,膨胀核中只要存在白点就要将核中黑点染白,其他于腐蚀操作类似

1
2
kernel = np.ones((5, 5), np.uint8)
dilate = cv2.dilate(img, kernel, iterations=10)

膨胀操作的基本代码,参数于腐蚀操作的参数一样这里就不再介绍

1
2
3
4
5
6
j0 = img = cv2.imread('../img/fs.png')
kernel1 = np.ones((3, 3), np.uint8)
j1 = cv2.erode(j0, kernel1, iterations=10)
j2 = cv2.dilate(j1, kernel1, iterations=10)
res2 = utils.figure_splicing((j0, j1, j2))
utils.show_image("process", res2)

上面的代码这是腐蚀与膨胀的结合产生的结果的代码,下图是最终呈现的效果

开运算

开运算和闭运算

  • 开运算:先腐蚀,再膨胀(结果见上一节的图像)
1
2
3
4
5
6
j0 = img = cv2.imread('../img/fs.png')
kernel1 = np.ones((3, 3), np.uint8)
j1 = cv2.erode(j0, kernel1, iterations=10)
j2 = cv2.dilate(j1, kernel1, iterations=10)
res2 = utils.figure_splicing((j0, j1, j2))
utils.show_image("process", res2)
  • 闭运算:先膨胀,再腐蚀

结果如下:闭运算

代码如下:

1
2
3
4
5
kernel2 = np.ones((3, 3), np.uint8)
ji1 = cv2.dilate(j0, kernel2, iterations=10)
ji2 = cv2.erode(ji1, kernel2, iterations=10)
res3 = utils.figure_splicing((j0, ji1, ji2))
utils.show_image("process", res3)

梯度计算

梯度 = 膨胀 - 腐蚀

代码描述

1
gradient = cv2.morphologyEx(pie, cv2.MORPH_GRADIENT, kernel)
  • pie : 传入图片
  • 第二个参数为计算模式,这里是计算梯度
  • 传入的核大小

我们注意这个函数是对于操作模式的封装其原因为cv2.morphologyEx(src, op, kernel),作用是对于多种形态学的运算,三个参数分别是

  • src传入的图片
  • op进行变化的方式
  • kernel表示方框的大小

op = cv2.MORPH_OPEN 进行开运算,指的是先进行腐蚀操作,再进行膨胀操作

op = cv2.MORPH_CLOSE 进行闭运算, 指的是先进行膨胀操作,再进行腐蚀操作

op的值可以点击这里了解更多

礼帽和黑帽

  • 礼帽=原始输入 - 开运算结果
  • 黑帽=闭运算结果 - 原始输入

对应cv2.morphologyEx(src, op, kernel)中的op值分别是:

  • 礼帽op = cv2.MORPH_TOPHAT
  • 黑帽op= cv2.MORPH_BLACKHAT

对于上述描述的形态学参数的计算我放在了一段代码中,如下段代码:

1
2
3
4
5
6
7
8
9
10
11
12
pie = cv2.imread('../img/fs.png')
pie = cv2.resize(pie, (200, 200))
kernel = np.ones((7, 7), np.uint8)

openNum = cv2.morphologyEx(pie, cv2.MORPH_OPEN, kernel) # 开运算
closeNum = cv2.morphologyEx(pie, cv2.MORPH_CLOSE, kernel) # 闭运算
gradient = cv2.morphologyEx(pie, cv2.MORPH_GRADIENT, kernel) # 梯度
top = cv2.morphologyEx(pie, cv2.MORPH_TOPHAT, kernel) # 礼帽
black = cv2.morphologyEx(pie, cv2.MORPH_BLACKHAT, kernel) # 黑帽

res = utils.figure_splicing((pie, openNum, closeNum, gradient, top, black))
utils.show_image('images result', res)

其运算的结果如下:

结果对比

五张图片依次是,原图、开运算结果、闭云算、梯度、礼帽、黑帽

本篇文章中的代码我已经传入我的Github ,若想查看代码请移步至我的GitHub