常言道“温故而知新”,写此文章就是对自己目前学习内容的小小的总结与记录。
本文力求用最简洁的语言,详细的代码将此部分内容讲解清楚,但由于博主同样是刚刚接触OpenCV,或许表达上有些瑕疵,还望读者能够指教探讨,大家共同进步。
博主机器配置为:VS2013+opencv2.4.13+Win-64bit。
若本文能给读者带来一点点启示与帮助,我就很开心了。
====================分割线====================
滤波目的:
滤波要求:
滤波器的种类有很多, 本文结合前几节的内容,写了个综合示例,包含6种滤波方法:
盒式滤波、平滑处理1线性滤波之——盒式滤波(方框滤波)
均值滤波、平滑处理2线性滤波之——均值滤波
高斯滤波、平滑处理3线性滤波之——高斯滤波
中值滤波、平滑处理4非线性滤波之——中值滤波
双边滤波、平滑处理5非线性滤波之——双边滤波
导向滤波、平滑处理6——引导滤波/导向滤波(Guided Filter)
其中阈值量可通过滑动条来调节,下面来看看程序是如何实现的。
=======================分割线====================
/*
功能:用滚动条来控制6种滤波方式的参数值。
盒式滤波、均值滤波、高斯滤波、中值滤波、双边滤波、导向滤波。
*/
#include
#include
#include
#include
using namespace std;
using namespace cv;
#define WINDOWNAME "【滤波处理结果窗口】"
//---------------【全局变量声明部分】-------------------------
Mat g_srcIamge, g_dstImage1, g_dstImage2, g_dstImage3, g_dstImage4, g_dstImage5, g_dstImage6;
int g_nBoxFilterValue = 1;//盒式滤波内核值
int g_nMeanBlurValue = 1;//均值滤波内核值
int g_nGaussianBlurValue = 1;//高斯滤波内核值
int g_nMedianBlurValue = 1;//中值滤波内核值
int g_nBilateralFilterValue = 1;//双边滤波内核值
int g_nGuidedFilterValue = 1;//导向滤波内核值
const int g_nMaxVal = 20; //预设滑动条最大值
//--------------【全局函数声明部分】-------------------------
//轨迹条回调函数
static void on_BoxFilter(int, void*);//盒式滤波器
static void on_MeanBlur(int, void*);//均值滤波器
static void on_GaussianBlur(int, void*);//高斯滤波器
static void on_MedianBlur(int, void*);//中值滤波器
static void on_BilateralFilter(int, void*);//双边滤波器
static void on_GuidedFilter(int, void*);//导向滤波器
void guidedFilter(Mat &srcMat, Mat &guidedMat, Mat &dstImage, int radius, double eps);//导向滤波器
//----------------------------【主函数】---------------------------
int main()
{
//------------【1】读取源图像并检查图像是否读取成功------------
g_srcIamge = imread("D:\\OutPutResult\\ImageTest\\boatLong.jpg");
if (!g_srcIamge.data)
{
cout << "读取图片错误,请重新输入正确路径!\n";
system("pause");
return -1;
}
namedWindow("【源图像】", 1);//创建窗口
imshow("【源图像】", g_srcIamge);//显示窗口
//------------【2】在WINDOWNAME窗口上分别创建滤波6个滑动条------------
namedWindow(WINDOWNAME);//创建窗口
createTrackbar("方框滤波", WINDOWNAME, &g_nBoxFilterValue, g_nMaxVal, on_BoxFilter);//创建方框滤波轨迹条
on_BoxFilter(g_nBoxFilterValue, 0); //轨迹条的回调函数
createTrackbar("均值滤波", WINDOWNAME, &g_nMeanBlurValue, g_nMaxVal, on_MeanBlur);//创建均值滤波轨迹条
on_MeanBlur(g_nMeanBlurValue, 0);
createTrackbar("高斯滤波", WINDOWNAME, &g_nGaussianBlurValue, g_nMaxVal, on_GaussianBlur);//创建高斯滤波轨迹条
on_GaussianBlur(g_nGaussianBlurValue, 0);
createTrackbar("中值滤波", WINDOWNAME, &g_nMedianBlurValue, g_nMaxVal, on_MedianBlur);//创建中值滤波轨迹条
on_MedianBlur(g_nMedianBlurValue, 0);
createTrackbar("双边滤波", WINDOWNAME, &g_nBilateralFilterValue, g_nMaxVal, on_BilateralFilter);//创建双边滤波轨迹条
on_BilateralFilter(g_nBilateralFilterValue, 0);
createTrackbar("导向滤波", WINDOWNAME, &g_nGuidedFilterValue, g_nMaxVal, on_GuidedFilter);//创建导向滤波轨迹条
on_GuidedFilter(g_nGuidedFilterValue, 0);
//------------【3】退出程序------------
cout << "\t按下'q'键,退出程序~!\n" << endl;
while (char(waitKey(1)) != 'q'){}
return 0;
}
//----------------------【on_BoxFilter()函数】------------------------
static void on_BoxFilter(int, void*)
{
boxFilter(g_srcIamge, g_dstImage1, -1, Size(g_nBoxFilterValue * 2 + 1, g_nBoxFilterValue * 2 + 1));
cout << "\n当前为【盒式滤波】处理效果,其内核大小为:" << g_nBoxFilterValue * 2 + 1 << endl;
imshow(WINDOWNAME, g_dstImage1);
}
//----------------------【on_MeanBlur()函数】------------------------
static void on_MeanBlur(int, void*)
{
blur(g_srcIamge, g_dstImage2, Size(g_nMeanBlurValue * 2 + 1, g_nMeanBlurValue * 2 + 1), Point(-1, -1));
cout << "\n当前为【均值滤波】处理效果,其内核大小为:" << g_nMeanBlurValue * 2 + 1 << endl;
imshow(WINDOWNAME, g_dstImage2);
}
//----------------------【on_GaussianBlur()函数】------------------------
static void on_GaussianBlur(int, void*)
{
GaussianBlur(g_srcIamge, g_dstImage3, Size(g_nGaussianBlurValue * 2 + 1, g_nGaussianBlurValue * 2 + 1), 0, 0);
cout << "\n当前为【高斯滤波】处理效果,其内核大小为:" << g_nGaussianBlurValue * 2 + 1 << endl;
imshow(WINDOWNAME, g_dstImage3);
}
//----------------------【on_MedianBlur()函数】------------------------
static void on_MedianBlur(int, void*)
{
medianBlur(g_srcIamge, g_dstImage4, g_nMedianBlurValue * 2 + 1);
cout << "\n当前为【中值滤波】处理效果,其内核大小为:" << g_nMedianBlurValue * 2 + 1 << endl;
imshow(WINDOWNAME, g_dstImage4);
}
//----------------------【on_BilateralFilter()函数】------------------------
static void on_BilateralFilter(int, void*)
{
bilateralFilter(g_srcIamge, g_dstImage5, g_nBilateralFilterValue, g_nBilateralFilterValue * 2, g_nBilateralFilterValue / 2);
cout << "\n当前为【双边滤波】处理效果,其内核大小为:" << g_nBilateralFilterValue << endl;
imshow(WINDOWNAME, g_dstImage5);
}
//----------------------【on_GuidedFilter()函数】------------------------
static void on_GuidedFilter(int, void*)
{
vector vSrcImage, vResultImage;
//【1】对源图像进行通道分离,并对每个分通道进行导向滤波操作
split(g_srcIamge, vSrcImage);
for (int i = 0; i < 3; i++)
{
Mat tempImage;
vSrcImage[i].convertTo(tempImage, CV_64FC1, 1.0 / 255.0);//将分通道转换成浮点型数据
Mat cloneImage = tempImage.clone(); //将tempImage复制一份到cloneImage
Mat resultImage;
guidedFilter(tempImage, cloneImage, resultImage, g_nGuidedFilterValue * 2 + 1, 0.01);//对分通道分别进行导向滤波
vResultImage.push_back(resultImage);//将分通道导向滤波后的结果存放到vResultImage中
}
//【2】将分通道导向滤波后结果合并
merge(vResultImage, g_dstImage6);
cout << "\n当前处理为【导向滤波】,其内核大小为:" << g_nGuidedFilterValue * 2 + 1 << endl;
imshow(WINDOWNAME, g_dstImage6);
}
//-------------------【实现导向滤波器函数部分】-------------------------
void guidedFilter(Mat &srcMat, Mat &guidedMat, Mat &dstImage, int radius, double eps)
{
//------------【0】转换源图像信息,将输入扩展为64位浮点型,以便以后做乘法------------
srcMat.convertTo(srcMat, CV_64FC1);
guidedMat.convertTo(guidedMat, CV_64FC1);
//--------------【1】各种均值计算----------------------------------
Mat mean_p, mean_I, mean_Ip, mean_II;
boxFilter(srcMat, mean_p, CV_64FC1, Size(radius, radius));//生成待滤波图像均值mean_p
boxFilter(guidedMat, mean_I, CV_64FC1, Size(radius, radius));//生成导向图像均值mean_I
boxFilter(srcMat.mul(guidedMat), mean_Ip, CV_64FC1, Size(radius, radius));//生成互相关均值mean_Ip
boxFilter(guidedMat.mul(guidedMat), mean_II, CV_64FC1, Size(radius, radius));//生成导向图像自相关均值mean_II
//--------------【2】计算相关系数,计算Ip的协方差cov和I的方差var------------------
Mat cov_Ip = mean_Ip - mean_I.mul(mean_p);
Mat var_I = mean_II - mean_I.mul(mean_I);
//---------------【3】计算参数系数a、b-------------------
Mat a = cov_Ip / (var_I + eps);
Mat b = mean_p - a.mul(mean_I);
//--------------【4】计算系数a、b的均值-----------------
Mat mean_a, mean_b;
boxFilter(a, mean_a, CV_64FC1, Size(radius, radius));
boxFilter(b, mean_b, CV_64FC1, Size(radius, radius));
//---------------【5】生成输出矩阵------------------
dstImage = mean_a.mul(srcMat) + mean_b;
}
========================分割线====================
原始图像窗口,如下图:
盒式滤波/方框滤波操作,如下图:
均值滤波操作,如下图:
高斯滤波操作,如下图:
中值滤波操作,如下图:
双边滤波操作,如下图:
导向滤波操作,如下图:
=====================分割线==================
【滤波处理结果窗口】中显示的数值并非实际内核大小,真正内核大小还请看黑窗口的提示信息。
其中前五种滤波方式:盒式滤波、均值滤波、高斯滤波、中值滤波、双边滤波,OpenCV都已封装在函数里了,我们直接调用即可,而导向滤波,是需要自己编写,然后调用即可。
为了便于观察各种滤波方式的效果优缺点,我对原图进行了处理,左边的图像是添加了椒盐噪声,而右边的图像是添加了高斯噪声,同学们可以通过滑动条调节,试试看每种滤波方式对噪声的处理结果是如何情况。
给图像添加噪声也在前几节文章中讲到,不会的同学可以去看看。
传送门:
图像噪声1——椒盐噪声
图像噪声2——高斯噪声
======================END===================