前面我们介绍的图像卷积处理无论是均值还是高斯都是属于模糊卷积,它们都有一个共同的特点就是模糊之后图像的边缘信息不复存在,受到了破坏。我们今天介绍的滤波方法有能力通过卷积处理实现图像模糊的同时对图像边缘不会造成破坏,滤波之后的输出完整的保存了图像整体边缘(轮廓)信息,我们称这类滤波算法为边缘保留滤波算法(EPF)。
最常见的边缘保留滤波算法有以下几种
OpenCV中对边缘保留滤波还有一个专门的API
从最经典的高斯双边模糊开始,高斯模糊是考虑图像空间位置对权重的影响,但是它没有考虑图像像素分布对图像卷积输出的影响(像素值差异巨大时,应该将像素剥离出来,不参与计算),双边模糊考虑了像素值分布的影响,对像素值空间分布差异较大的进行保留从而完整的保留了图像的边缘信息。
C++:
bilateralFilter(
InputArray src,
OutputArray dst,
int d, //图像是否放大或缩小,d=0,原图输出
double sigmaColor, //色彩空间,色差差距多少,像素参与计算
double sigmaSpace, //与之前的sigmaX与siamaY相同
int borderType = BORDER_DEFAULT//边缘填充格式
)
Python:
dst = cv.bilateralFilter( src, d, sigmaColor, sigmaSpace, dst,borderType)
图中的图像左右两边分别为灰色和黑色,在进行双边模糊时,中间的分界线像素值差距较大,因此该处的像素信息不参与运算得以保留
均值迁移模糊是图像边缘保留滤波算法中一种,经常用来在对图像进行分水岭分割之前去噪声,可以大幅度提升分水岭分割的效果。
均值迁移模糊的主要思想如下:
就是在图像进行开窗的时候同样,考虑像素值空间范围分布,只有符合分布的像素点才参与计算,计算得到像素均值与空间位置均值,使用新的均值位置作为窗口中心位置继续基于给定像素值空间分布计算均值与均值位置,如此不断迁移中心位置直到不再变化位置(dx=dy=0),但是在实际情况中我们会人为设置一个停止条件比如迁移几次,这样就可以把最后的RGB均值赋值给中心位置。
OpenCV中均值迁移滤波的API函数:
C++:
pyrMeanShiftFiltering(
InputArray src,
OutputArray dst,
double sp,//空间窗口大小,窗口的半径
double sr,//颜色值空间大小
int maxLevel = 1,
TermCriteria termcrit = //停止条件
TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS, 5, 1)
)//迁移5次停止或者两次迁移的dx和dy的差值小于1
Python:
dst=cv.pyrMeanShiftFiltering(src, sp, sr, dst, maxLevel, termcrit)
上图为像素点在二维空间中的分布,圆形空间窗口会在图像上迁移,圆心不断朝向像素点最密集的地方迁移。
dst=cv.pyrMeanShiftFiltering(src,15,30,termcrit=(cv.TERM_CRITERIA_MAX_ITER+cv.TERM_CRITERIA_EPS,5,1))
积分图像是Crow在1984年首次提出,是为了在多尺度透视投影中提高渲染速度,是一种快速计算图像区域和与平方和的算法。其核心思想是对每个图像建立自己的积分图查找表,在图像积分处理计算阶段根据预先建立的积分图查找表,直接查找从而实现对均值卷积线性时间计算,做到了卷积执行的时间与半径窗口大小的无关联。图像积分图在图像特征提取HAAR/SURF、二值图像分析、图像相似相关性NCC计算、图像卷积快速计算等方面均有应用,是图像处理中的经典算法之一。(思想类似于查找表)
图像积分图建立与查找
在积分图像(Integral Image - ii)上任意位置(x, y)处的ii(x, y)表示该点左上角所有像素之和, 其中(x,y)是图像像素点坐标。
OpenCV中的相关API如下:
integral(
InputArray src, // 输入图像
OutputArray sum, // 和表
OutputArray sqsum, // 平方和表
OutputArray tilted, // 瓦块和表
int sdepth = -1, // 和表数据深度常见CV_32S
int sqdepth = -1 // 平方和表数据深度 常见 CV_32F
)
原图大小为W * H,输出积分图大小为(W+1)*(H+1)
s u m ( X , Y ) = ∑ x ≤ X ∑ y ≤ Y i m a g e ( x , y ) sum(X,Y)=\sum^{}_{x \leq X}\sum^{}_{y \leq Y}{image(x,y)} sum(X,Y)=x≤X∑y≤Y∑image(x,y)
s u m ( X , Y ) = ∑ x ≤ X ∑ y ≤ Y ( i m a g e ( x , y ) ) 2 sum(X,Y)=\sum^{}_{x \leq X}\sum^{}_{y \leq Y}{(image(x,y))^2} sum(X,Y)=x≤X∑y≤Y∑(image(x,y))2
高斯双边模糊与mean shift均值模糊两种边缘保留滤波算法,都因为计算量比较大,无法实时实现图像边缘保留滤波,限制了它们的使用场景,OpenCV中还实现了一种快速的边缘保留滤波算法。高斯双边与mean shift均值在计算时候使用五维向量是其计算量大速度慢的根本原因,该算法通过等价变换到低纬维度空间,实现了数据降维与快速计算。
OpenCV API函数为:
C++:
void cv::edgePreservingFilter(
InputArray src,
OutputArray dst,
int flags = 1,
float sigma_s = 60,
float sigma_r = 0.4f
)
Python:
dst = cv.edgePreservingFilter(src, dst, flags, sigma_s, sigma_r)
当sigma_s取值不变时候,sigma_r越大图像滤波效果越明显
当sigma_r取值不变时候,窗口sigma_s越大图像模糊效果越明显
当sgma_r取值很小的时候,窗口sigma_s取值无论如何变化,图像双边滤波效果都不好!
ct(u)求输入I的梯度,对I求导得到图c
dst=cv.edgePreservingFilter(src,sigma_s=100,sigma_r=0.4,flags=cv.RECURS_FILTER)
图像卷积最主要功能有图像模糊、锐化、梯度边缘等,OpenCV除了支持上述的卷积模糊(均值与边缘保留)还支持自定义卷积核,实现自定义的滤波操作。自定义卷积核常见的主要是均值、锐化、梯度等算子。
下面的三个自定义卷积核分别可以实现卷积的均值模糊、锐化、梯度功能。
1,1, 1 0, -1, 0 1,0
1,1, 1 -1, 5, -1 0 -1
1,1, 1 0, -1, 0
OpenCV自定义滤波器API:
void cv::filter2D(
InputArray src,
OutputArray dst,
int ddepth, // 默认-1
InputArray kernel, // 卷积核或者卷积窗口大小
Point anchor = Point(-1,-1),
double delta = 0,
int borderType = BORDER_DEFAULT
)
Python:
dst=cv.filter2D(src, ddepth, kernel, dst, anchor, delta, borderType)
int ddepth//默认-1,表示输入与输出图像类型一致,但是当涉及浮点数计算时候,需要设置为CV_32F。滤波完成之后需要使用convertScaleAbs函数将结果转换为字节类型。
blur_op=np.ones([5,5],np.float32)/25
shape_op=np.array([[0,-1,0],[-1,5,-1],[0,-1,0]],np.float32)
grad_op=np.array([[1,0],[0,-1]],np.float32)
dst1=cv.filter2D(src,-1,blur_op)
dst2=cv.filter2D(src,-1,shape_op)
dst3=cv.filter2D(src,cv.CV_32F,grad_op)
dst3=cv.converScaleAbs(dst3)#imshow显示图像,数据为字节类型