前言:总纲查看《计算机视觉学习路》
基本概念
滤波器又称为卷积核,滤波的过程称为卷积,卷积核的大小通常为3X3、5X5、7X7
为什么卷积核一般是奇数?
1.从图中可以看到,卷积后输出特征图的尺寸比输入图片的尺寸小,为了使输出和输入大小一致,通常会在输入图像进行padding操作,填充后,再与奇数卷积核进行卷积,就可以得到与输入尺寸一致的输出。
2.另一方面是保证锚点在中间,防止位置发生偏移
卷积核大小的影响?
卷积核越大,感受野(看到的信息)越多,提取的特征越好,但是计算量也就越大
边界扩充padding
为了保证输入输出的尺寸一致
如下图所示,输入图片为5X5外面填充一圈0后再卷积,输出的也是5X5(虽然在计算层面输入变成7X7了,但是在用户层面输入还是5X5)
低通滤波和高通滤波
低通滤波可以去除噪音或平滑图像
高通滤波可以帮助查找图像的边缘
计算公式
N = (W - F + 2P)/S +1
N:输出图像的大小
W:源图大小
F:卷积核大小
P:扩充尺寸
S:步长,步长就是卷积核移动的步幅,若步长为1,则卷积核每次往后移动一个格子,如下图所示
步长不同,padding的大小也需对应变化才能保证输入输出尺寸一致。
卷积
filter2D(src , ddepth, kernel, anchor , delta , borderType)
src : 源图片
ddepth : 位深,一般设置为 -1 , 即输入输出位深一致
kernel : 卷积核
anchor : 锚点 默认(-1,-1)根据核的内容找到中心点,可以不设
delta : 可以让卷积后的每个元素加一个delta值,默认0
borderType: 边界类型 , 默认不做处理
下面通过一个5*5,元素都为1/25的卷积核对图像进行滤波:可以看到有模糊效果
import cv2
import numpy as np
img = cv2.imread('../img/ex03.jpg')
img = cv2.resize(img,None,fx=0.5,fy=0.5)
kernel = np.ones((5, 5), np.float32) / 25
dst = cv2.filter2D(img, -1, kernel)
cv2.imshow('img',img)
cv2.imshow('dst',dst)
cv2.waitKey(0)
低通滤波
方盒滤波与均值滤波
卷积核如图所示:
参数a的作用:
normalize = true , a = 1/W*H 均值滤波(常用)
normalize = false , a=1 即元素之和
方盒滤波
boxFilter(src , ddepth , ksize , anchor , normalize , borderType)
ksize: 卷积核的大小
均值滤波
blur(src , ksize , anchor , borderType)
由于方盒滤波normalize很少有等于false的情况,所以一般使用blur而不用boxFilter
可以看到和上面那个1/25的矩阵效果一样
import cv2
import numpy as np
img = cv2.imread('../img/ex03.jpg')
img = cv2.resize(img,None,fx=0.5,fy=0.5)
dst = cv2.blur(img,(5,5)) # 卷积核大小为5*5
cv2.imshow('img',img)
cv2.imshow('dst',dst)
cv2.waitKey(0)
高斯滤波
高斯滤波又称钟型滤波,中间元素的权重最高,越往边上走权重越低
GaussianBlur(img , kernel , sigmaX, sigmaY,…)
sigmaX: 最远的x与中心的距离,即沿着X方向,钟型拓展的多远,sigmaY同理。
当sigma不设值时,根据kernel进行模糊
下面对sigmaX分别取1,10,100进行高斯模糊
import cv2
import numpy as np
img = cv2.imread('../img/ex03.jpg')
img = cv2.resize(img,None,fx=0.5,fy=0.5)
dst = cv2.GaussianBlur(img,(5,5),sigmaX=1)
dst10 = cv2.GaussianBlur(img,(5,5),sigmaX=10)
dst100 = cv2.GaussianBlur(img,(5,5),sigmaX=100)
cv2.imshow('img',img)
cv2.imshow('dst',dst)
cv2.imshow('dst10',dst10)
cv2.imshow('dst100',dst100)
cv2.waitKey(0)
中值滤波
每进行一次卷积后,取其中的中位数作为结果
medianBlur(img , ksize)
中值滤波对椒盐噪声效果明显
椒盐噪声也称为脉冲噪声,是图像中经常见到的一种噪声,它是一种随机出现的白点或者黑点,可能是亮的区域有黑色像素或是在暗的区域有白色像素(或是两者皆有)。
import cv2
import numpy as np
img = cv2.imread('../img/jy.png')
dst = cv2.medianBlur(img,5)
cv2.imshow('img',img)
cv2.imshow('dst',dst)
cv2.waitKey(0)
双边滤波
可以保留边缘,同时对边缘内的区域平滑处理。最大的应用就是美颜
bliateralFilter(img , d , sigmaColor , simgaSpace , …)
d : filter的大小,sigma两个参数可以自己调整看看效果
import cv2
import numpy as np
img = cv2.imread('../img/双边.png')
dst = cv2.bilateralFilter(img,7,50,50)
cv2.imshow('img',img)
cv2.imshow('dst',dst)
cv2.waitKey(0)
高通滤波
Sobel(索贝尔)(高斯)
不能同时计算x,y方向的导数
先对x方向求导,再求y方向导,最后相加
Sobel(src , ddepth , dx , dy , ksize = 3)
dx = 1,对x方向求导,dy=1,对y方向求导
import cv2
import numpy as np
img = cv2.imread('../img/ex03.jpg')
img = cv2.resize(img,None,fx=0.5,fy=0.5)
# 对x方向求导
dstx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
# 对y求导
dsty = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)
#合并
dst = dstx + dsty
cv2.imshow('img',img)
cv2.imshow('dstx',dstx)
cv2.imshow('dsty',dsty)
cv2.imshow('dst',dst)
cv2.waitKey(0)
Scharr(沙尔)
与Sobel类似,kernel值不同,只能求x或y方向的边缘,相比于Sobel,能够识别一些细小的线
需要注意的是:当Sobel的 ksize = -1时,就是Scharr
Scharr(src , ddepth , dx , dy , scale = 1, delta = 0)
import cv2
import numpy as np
img = cv2.imread('../img/ex03.jpg')
img = cv2.resize(img, None, fx=0.5, fy=0.5)
# 对x方向求导
dstx = cv2.Scharr(img, cv2.CV_64F, 1, 0)
# 对y求导
dsty = cv2.Scharr(img, cv2.CV_64F, 0, 1)
# 合并
dst = dstx + dsty
cv2.imshow('img', img)
cv2.imshow('dstx', dstx)
cv2.imshow('dsty', dsty)
cv2.imshow('dst', dst)
cv2.waitKey(0)
Laplacian(拉普拉斯)
可以同时求两个方向的边缘
对噪音敏感,一般需要先去噪再调用拉普拉斯
Laplacian(img , ddepth , ksize =1)
import cv2
import numpy as np
img = cv2.imread('../img/ex03.jpg')
img = cv2.resize(img, None, fx=0.5, fy=0.5)
# 对x方向求导
dst = cv2.Laplacian(img, cv2.CV_64F, ksize=5)
cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey(0)
Canny
使用5*5高斯滤波消除噪声
计算图像梯度的方向:0度、45度、90度、135度
在4个方向取局部最大值,认为是边界值
取阈值计算,超过最大值肯定是边缘,低于最小值肯定不是边缘,如果介于中间,看和那条线连续
Canny(img , minVal , maxVal)
minVal:最小值 maxVal 最大值 阈值
import cv2
import numpy as np
img = cv2.imread('../img/ex03.jpg')
img = cv2.resize(img, None, fx=0.5, fy=0.5)
# 对x方向求导
dst = cv2.Canny(img, 100,200)
cv2.imshow('img', img)
cv2.imshow('dst', dst)
cv2.waitKey(0)
想获得更多或者更少的边缘,可以调整阈值的大小