前言
本节简单介绍滤波的概念和OpenCV中主要的滤波API,不涉及相关算法。
目录
一、滤波
1.什么是滤波?
一幅图像通过滤波器得到另一幅图像,同时在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像处理中不可缺少的一项操作。其中滤波器又被称为卷积核,滤波过程被称为卷积。下面这幅图就是一幅图卷积前后的对比,明显变得锐化。
下面的图片是一个简化的卷积过程:采用一个3x3的卷积核,与图片中的3x3的数据相乘,然后遍历整张图像。经过卷积后图片的尺寸必定会减小(卷积核>1),如想保持原来的大小,要进行对应的处理。
2.卷积的基本概念
- 卷积核的大小:
卷积核一般为奇数,3x3, 5x5, 7x7;一方面是增加padding的原因,另一方面是为了保证锚点在中间,防止位置发生偏移
卷积核大小的影响:在深度学习中,卷积核越大,看到的信息(感受野)越多,提取的特征越好,但同时计算量也就越大。
-
锚点:
如下图中中心41就称为锚点,简单来说就是正中心,主要为了防止信息偏差。
-
边界扩充:
边界扩充就是为了解决卷积之后,输出尺寸变小的问题。
计算公式:
N = ( W − F + 2 P ) / S + 1 N = (W-F+2P)/S+1 N=(W−F+2P)/S+1
N:输出图像大小
W:源图像大小;F:卷积核大小;P:扩充尺寸
S:步长大小 -
步长:卷积核每次移动的长度
低通滤波可以去除噪声或平滑图像
高通滤波可以帮助查找图像的边缘
二、图像卷积
1.图像卷积API
filter2D()
声明:void filter2D( InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor = Point(-1,-1), double delta = 0, int borderType = BORDER_DEFAULT );
参数:
src:需要滤波的图像
ddepth:图像卷积之后的位深,-1表示与原图一致
kernel:卷积核
anchor:锚点,默认中心点
delta:每次卷积之后+delta
borderType:边界的类型
代码案例:
import cv2
import numpy as np
cat = cv2.imread('./picture/cat.jpg')
kernel = np.ones((5,5), np.float32) / 25 # 平均滤波
dst = cv2.filter2D(cat, -1, kernel)
cv2.imshow('dst', dst)
cv2.imshow('cat', cat)
cv2.waitKey(0)
2.方盒滤波与均值滤波
方盒滤波卷积核:
参数a的作用:
normalize = true, a= 1 / W x H (平均滤波)
normalize = false, a= 1
boxFilter()
声明:void boxFilter( InputArray src, OutputArray dst, int ddepth, Size ksize, Point anchor = Point(-1,-1), bool normalize = true, int borderType = BORDER_DEFAULT );
参数:
src:需要滤波的图像
ddepth:图像卷积之后的位深,-1表示与原图一致
ksize:卷积核大小
anchor:锚点,默认中心点
normalize:如上
borderType:边界的类型
blur()
声明:void blur( InputArray src, OutputArray dst, Size ksize, Point anchor = Point(-1,-1), int borderType = BORDER_DEFAULT );
参数:
src:需要滤波的图像
ksize:卷积核大小
anchor:锚点,默认中心点
borderType:边界的类型
3.高斯滤波
高通滤波可以帮助查找图像的边缘
高斯权重:
从上面两图可以看出,高斯滤波呈现出中间权重高,边缘权重低。
GaussianBlur()
声明:void GaussianBlur( InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, int borderType = BORDER_DEFAULT );
参数:
src:需要滤波的图像
ksize:卷积核大小,
sigmaX:X方向上的高斯核标准差
sigmaY:Y方向上的高斯核标准差
如果两个sigma都为零,则根据ksize计算
代码案例:
dst = cv2.GaussianBlur(cat, (5, 5), sigmaX = 1)
主要用来解决高斯噪音。
4.中值滤波
中值滤波对胡椒噪音效果明显
medianBlur()
声明:void medianBlur( InputArray src, OutputArray dst, int ksize );
参数:
src:需要滤波的图像
ksize:卷积核大小,整数
代码示例:
dst = cv2.medianBlur(hujiao, 7)
结果展示:
经过中值滤波后图像中的黑色噪点去掉了,但是图片的边缘也变得模糊了。
5.双边滤波
可以保留边缘,同时可以对边缘内的区域进行平滑处理。主要的作用是进行美颜。
双边滤波原理:
bilateralFilter()
声明:void bilateralFilter( InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType = BORDER_DEFAULT );
参数:
src:需要滤波的图像
d:滤波期间使用的每个像素邻域的直径。如果为非正,它是从sigmaSpace计算的。
sigmaColor:
sigmaSpace:
代码案例:
dst = cv2.bilateralFilter(lian, 7, 20, 50)
结果展示:
可以明显看出经过滤波后的图片脸部皮肤更光滑,肤色更均匀,同时眉毛,帽子的边缘并没有模糊化。
三、高通滤波
1.Sobel(索贝尔)(高斯)
内部使用高斯滤波首先对图像进行滤波,然后才进行一阶导提取图像边缘,因此对噪音适应性很强。
Sobel和下面的Scharr求边缘线是都只能求一个方向上的边缘,最终要将横轴与纵轴的边缘相加,才能得到完整的边缘图像。
Sobel()
声明:void Sobel( InputArray src, OutputArray dst, int ddepth, int dx, int dy, int ksize = 3, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT );
参数:
src:需要滤波的图像
ddepth:位深
dx:等于1,检测Y轴边缘
dy:等于1,检测X 轴边缘
ksize:卷积核大小
scale:缩放
代码案例:
d1 = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize = 5)
d2 = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize = 5)
d = cv2.add(d1, d2)
2.Scharr(沙尔)
Scharr不可以改变大小,他是一个3x3的卷积核,当Sobel的ksize参数设为-1时,就是Scharr。但Scharr可以检测出更细的边缘线。
Sobel()
声明:void Scharr( InputArray src, OutputArray dst, int ddepth, int dx, int dy, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT );
参数:
src:需要滤波的图像
ddepth:位深
dx:等于1,检测Y轴边缘
dy:等于1,检测X 轴边缘
3.Laplacian(拉普拉斯)
Laplacian可以一次求出图像的完整边缘,但由于内部没有进行降噪,因此对噪声敏感。
Sobel()
声明:void Laplacian( InputArray src, OutputArray dst, int ddepth, int ksize = 1, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT );
参数:
src:需要滤波的图像
ddepth:位深
ksize:卷积核大小
4.Canny
Canny效果好的原因:
- 使用5x5高斯滤波消除噪声
- 计算图像梯度方向(0°/45°/90°/135°)
- 取局部极大值
- 阈值计算
当值大于maxval是一定是边缘,小于minval时一定不是边缘,而当处于maxval和minval之间则需要看与之前的边缘是否连续,不连续就不是边缘。
Canny()
声明:void Canny( InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize = 3, bool L2gradient = false );
参数:
image:需要滤波的图像
threshold1:阈值下限
threshold2:阈值上限
代码案例:
import cv2
import numpy as np
img = cv2.imread('./picture/lian.jpeg')
fimg = cv2.GaussianBlur(img, (5, 5), sigmaX = 1)
dst1 = cv2.Laplacian(fimg, cv2.CV_64F)
dst2 = cv2.Canny(img, 50, 150)
cv2.imshow('img', img)
cv2.imshow('Laplacian', dst1)
cv2.imshow('Canny', dst2)
cv2.waitKey(0)
结果展示:
上图可以看出:即使Laplacian方法经过滤波处理,但Canny方法的边缘提取效果仍然要好得多。