【从零学习OpenCV 4】双边滤波
重磅干货,第一时间送达
经过几个月的努力,小白终于完成了市面上第一本OpenCV 4入门书籍《OpenCV 4开发详解》。为了更让小伙伴更早的了解最新版的OpenCV 4,小白与出版社沟通,提前在公众号上连载部分内容,请持续关注小白。 |
前面我们介绍的滤波方法都会图像照成模糊,使得边缘信息变弱或者消失,因此需要一种能够对图像边缘信息进行保留的滤波算法,双边滤波就是经典的常用的能够保留图像边缘信息的滤波算法之一。双边滤波是一种综合考虑滤波器内图像空域信息和滤波器内图像像素灰度值相似性的滤波算法,可以实现在保留区域信息的基础上实现对噪声的去除、对局部边缘的平滑。双边滤波对高频率的波动信号起到平滑的作用,同时保留大幅值的信号波动,进而实现对保留图像中边缘信息的作用。双边滤波的示意图如图5-24所示,双边滤波器是两个滤波器的结合,分别考虑空域信息和值域信息,使得滤波器对边缘附近的像素进行滤波时,距离边缘较远的像素值不会对边缘上的像素值影响太多,进而保留了边缘的清晰性。
图5-24 图像双边滤波器原理示意图
双边滤波原理的数学表示如式(5.9)中所示。
其中ω(i,j,k,l)为加权系数,其取值决定于空域滤波器和值域滤波器的乘积,空域滤波器的表示形式如式(5.9)所示,值域表示形式如式(5.10)所示。
两者相乘后,会产生形如式(5.11)所示的依赖于数据的双边滤波器。
OpenCV 4提供了对图像进行双边滤波操作的bilateralFilter()函数,该函数的函数原型在代码清单5-20中给出。
代码清单5-20 bilateralFilter()函数原型
void cv::bilateralFilter(InputArray src,
OutputArray dst,
int d,
double sigmaColor,
double sigmaSpace,
int borderType = BORDER_DEFAULT
)
src:待双边滤波图像,图像数据类型为必须是CV_8U、CV_32F和CV_64F三者之一,并且通道数必须为单通道或者三通道。 dst:双边滤波后的图像,尺寸和数据类型与输入图像src相同。 d:滤波过程中每个像素邻域的直径,如果这个值是非正数,则由第五个参数sigmaSpace计算得到。 sigmaColor:颜色空间滤波器的标准差值,这个参数越大表明该像素领域内有越多的颜色被混合到一起,产生较大的半相等颜色区域。 sigmaSpace:空间坐标中滤波器的标准差值,这个参数越大表明越远的像素会相互影响,从而使更大领域中有足够相似的颜色获取相同的颜色。当第三个参数d大于0时,邻域范围由d确定,当第三个参数小于等于0时,邻域范围正比于这个参数的数值。 borderType:像素外推法选择标志,取值范围在表3-5中给出,默认参数为BORDER_DEFAULT,表示不包含边界值倒序填充。
该函数可以对图像进行双边滤波处理,在减少噪声的同时保持边缘的清晰。该函数第一个参数是待进行双边滤波的图像,该函数要求只能输入单通道的灰度图和三通道的彩色图像,并且对于图像的数据类型也有严格的要求,必须是CV_8U、CV_32F和CV_64F三者之一。函数第三个参数是滤波器的直径,当滤波器的直径大于5时,函数的运行速度会变慢,因此如果需要在实时系统中使用该函数,建议将滤波器的半径设置为5,对于离线处理含有大量噪声的滤波图像时,可以将滤波器的半径设为9,当滤波器半径为非正数的时候,会根据空间滤波器的标准差计算滤波器的直径。函数第四个和第五个参数是两个滤波器的标准差值,为了简单起见可以将两个参数设置成相同的数值,当他们小于10时,滤波器对图像的滤波作用较弱,当他们大于150时滤波效果会非常的强烈,使图像看起来具有卡通的效果。该函数运行时间比其他滤波方法时间要长,因此在实际工程中使用的时候,选择合适的参数十分重要。另外比较有趣的现象是,使用双边滤波会具有美颜效果。
为了了解双边函数bilateralFilter()的使用方法,在代码清单5-21中给出了利用双边滤波函数bilateralFilter()对含有人脸的图像进行滤波的示例程序,滤波结果在图5-25、图5-26给出。通过结果可以知道,滤波器的直径对于滤波效果具有重要的影响,滤波器直径越大,滤波效果越明显;同时当滤波器半径相同时,标准差值越大,滤波效果越明显。另外通过结果也可以看出双边滤波确实能对人脸起到美颜的效果。
代码清单5-21 myBilateralFilter.cpp人脸图像双边滤波
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
//读取两张含有人脸的图像
Mat img1 = imread("img1.png", IMREAD_ANYCOLOR);
Mat img2 = imread("img2.png", IMREAD_ANYCOLOR);
if (img1.empty()||img2.empty())
{
cout << "请确认图像文件名称是否正确" << endl;
return -1;
}
Mat result1, result2, result3, result4;
//验证不同滤波器直径的滤波效果
bilateralFilter(img1, result1, 9, 50, 25 / 2);
bilateralFilter(img1, result2, 25, 50, 25 / 2);
//验证不同标准差值的滤波效果
bilateralFilter(img2, result3, 9, 9, 9);
bilateralFilter(img2, result4, 9, 200, 200);
//显示原图
imshow("img1", img1);
imshow("img2", img2);
//不同直径滤波结果
imshow("result1", result1);
imshow("result2", result2);
//不同标准差值滤波结果
imshow("result3 ", result3);
imshow("result4", result4);
waitKey(0);
return 0;
}
图5-25 myBilateralFilter.cpp程序中不同滤波器直径滤波结果
图5-26 myBilateralFilter.cpp程序中不同标准差值滤波结果