引导滤波的opencv实现以及解释
2017-11-21 16:38
513 查看
记录学习引导滤波的笔记。
一、滤波器的作用
1、数字图像信号的频率分布?
回答:信号或者图像的能量大部分在中低频,少部分有用信号在高频段被噪声淹没(噪声都是高频信号)。因此设计滤波器能降低高频成分幅度就能减弱噪声影响。
2、为什么进行图像滤波?
回答:适应图像处理要求,消除图像数字化时所混入的噪声;提取对象特征作为图像识别的特征模式。
3、如何理解滤波器?
回答:把滤波器想象成一个包含加权系数的窗口,当时用滤波器平滑图像时,相当于把窗口放大图像上,透过窗口看图像。
4、滤波器实现的结果是怎样的?
回答:对图像做平滑或者滤波后图像变得更模糊
二、滤波器分类
2.1、线性滤波器
低通滤波器:允许低频通过
高通滤波器:允许高频通过
带通滤波器:允许一定频率通过
带阻滤波器:阻止一定频率通过
全通滤波器:允许所有频率通过,只改变相位
2.2、非线性滤波器
中值滤波器:像素点邻域灰度值的中值代替该像素点的灰度值。
双边滤波器:基于空间分布的高斯滤波函数,比高斯滤波多一个sigma-d的高斯方差。
三、OpenCV提供的滤波器
3.1、线性滤波器
方框滤波:boxFilter
这个函数平滑图像使用如下内核:
其中:
也就是说:选择了归一化,输出结果就是求均值。
均值滤波:blur
这个函数使用如下内核:
注意:调用函数 blur(src, dst, ksize, anchor, borderType) 相当于调用 boxFilter(src, dst, src.type(), anchor, true, borderType) .
高斯滤波:GaussianBlur
注意:对于sigmaX和sigmaY的具体怎么设置,需要查阅相关资料。官方api推荐。
3.2、非线性滤波器
中值滤波:medianBlur
双边滤波:bilateralFilter
四、引导滤波算法原理(公式罗列更加贴切)
假设滤波器输出q(i,j)与引导图I(i,j)之间存在如下关系:
其中:
和
位对于固定窗口
内的线性因子。然后通过输入图像p(i,j)和输出图像q(i,j)之间的差异最小化来确定这两个线性因子,成本函数如下:
通过使得成本最小来确定
和
,ε为防止
过大的正则化参数。上述方程解为:
结论如下:当窗口处于平坦区域时,图像的局部方差小,则
趋于0,
趋于均值,相当于对图像进行了均值滤波;当窗口处于边缘时,局部方差较大,则则
趋于1,
趋于0,滤波器输出相当于原图。这样就可以很好的保护边缘。
注意:一般地,图像中间,像素跳跃变化不大,趋于稳定,方差小;图像边缘,像素跳跃变化大,不稳定,方差大。
五、OpenCV完成引导滤波编写
本案例是可以通过人工手算验证OpenCV的算法是否达到我们所需要的要求,所以直接处理图像数据是不合适的。但是只是简单的处理一个很小的矩阵也是不合适的。通过网络资源的搜寻,采用下面二维数组:
并且,我还为了方便验证和计算,把引导图和输入图像都用这个数据代替。float数组转Mat代码和显示代码如下:
结果截图如下:
证明Mat数据构造没有问题,可以进行下一步,求解这个矩阵的均值,代码如下:
结果截图如下:
由第一幅图圈出的9个数和第二幅图圈出的一个数,经过计算可以得出,在误差允许范围内,值均值关系。
验证成功。进行下一步,求出原矩阵的平方矩阵,代码如下:
结果截图如下:
对比图1和图3的圈住的9个数,确定是平方关系。验证成功,进行下一步,求均值矩阵的平方矩阵,代码如下:
结果截图如下:
图2 和图4是平方关系,验证成功,进行下一步,求方差,方差公式如下:
由这个公式,求平方矩阵的均值矩阵代码如下:
结果截图如下:
现在得到了平方均值和均值平方,求方差,代码如下:
结果如下:
在这里,我们去ε=500,求得到分母,代码如下:
结果如下:
根据上诉公式,求解
和
。
求解代码如下:
结果截图:
有了mean_a和mean_b,求出输出图像,代码如下:
结果截图如下:
可以看出,经过这一番变化,这个矩阵的内容发生变化,也是按照我们需要的方向变化的。每一步都不能错,每一步都是验证成功才进行下一步。
具体对于图像的细节增强,需要调整内核大小和ε值,我这里是固定的3*3和500,是为了方便计算。
整体代码如下:
总算是把这个过程搞明白了,引导滤波也算是弯沉了第一步。
问题:
为什么在OpenCV里面号称保边的boxFilter滤波器,在3*3的内核尺寸下,4边上的像素还是发生了改变?是如何变化的?
需要考究一下。
一、滤波器的作用
1、数字图像信号的频率分布?
回答:信号或者图像的能量大部分在中低频,少部分有用信号在高频段被噪声淹没(噪声都是高频信号)。因此设计滤波器能降低高频成分幅度就能减弱噪声影响。
2、为什么进行图像滤波?
回答:适应图像处理要求,消除图像数字化时所混入的噪声;提取对象特征作为图像识别的特征模式。
3、如何理解滤波器?
回答:把滤波器想象成一个包含加权系数的窗口,当时用滤波器平滑图像时,相当于把窗口放大图像上,透过窗口看图像。
4、滤波器实现的结果是怎样的?
回答:对图像做平滑或者滤波后图像变得更模糊
二、滤波器分类
2.1、线性滤波器
低通滤波器:允许低频通过
高通滤波器:允许高频通过
带通滤波器:允许一定频率通过
带阻滤波器:阻止一定频率通过
全通滤波器:允许所有频率通过,只改变相位
2.2、非线性滤波器
中值滤波器:像素点邻域灰度值的中值代替该像素点的灰度值。
双边滤波器:基于空间分布的高斯滤波函数,比高斯滤波多一个sigma-d的高斯方差。
三、OpenCV提供的滤波器
3.1、线性滤波器
方框滤波:boxFilter
C++: void boxFilter(InputArray src, OutputArray dst, int ddepth, Size ksize, Point anchor=Point(-1,-1), bool normalize=true, int borderType=BORDER_DEFAULT )
参数解释: src – 输入图像. dst – 输出图像(和输入图像有着相同的类型和尺寸). ddepth – 输出图像的深度 (-1代表使用原图深度). ksize – 内核大小. anchor – 被模糊的点; 默认值Point(-1,-1) 表示内核中心. normalize – 内核是否归一化,默认true. borderType – 默认值BORDER_DEFAULT,推断图像外部像素的某种边界模式.
这个函数平滑图像使用如下内核:
其中:
也就是说:选择了归一化,输出结果就是求均值。
均值滤波:blur
void blur(InputArray src, OutputArray dst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
参数解释: src – 输入图像 图像深度应该是:CV_8U, CV_16U, CV_16S, CV_32F or CV_64F. dst – 输出图像 ksize – 内核尺寸 anchor – 被模糊的点; 默认值Point(-1,-1) 表示内核中心. borderType – 默认值BORDER_DEFAULT,推断图像外部像素的某种边界模式.
这个函数使用如下内核:
注意:调用函数 blur(src, dst, ksize, anchor, borderType) 相当于调用 boxFilter(src, dst, src.type(), anchor, true, borderType) .
高斯滤波:GaussianBlur
void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT )
参数解释: src – 输入图像 图像深度应该是:CV_8U, CV_16U, CV_16S, CV_32F or CV_64F. dst – 输出图像. ksize – 内核尺寸. sigmaX – 高斯核函数在X方向上的标准偏差. sigmaY – 高斯核函数在Y方向上的标准偏差. borderType – 默认值BORDER_DEFAULT,推断图像外部像素的某种边界模式.
注意:对于sigmaX和sigmaY的具体怎么设置,需要查阅相关资料。官方api推荐。
3.2、非线性滤波器
中值滤波:medianBlur
void medianBlur(InputArray src, OutputArray dst, int ksize)
参数解释: src – 输入1-,3-,或者4-通道图像;当内核尺寸是3或者5,图像深度必须是:CV_8U,CV_16U,或者CV_32F,对于更大 的内核尺寸,深度只能是CV_8U. dst – 输出图像 ksize – 内核尺寸,必须是大于1的奇数
双边滤波:bilateralFilter
void bilateralFilter(InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT )
参数解释: src – 输入8位或者浮点类型的1-channel或者3-channel图像. dst – 输出图像. d – 过滤过程每个像素邻域的直径. sigmaColor – 颜色滤波器的sigma值. sigmaSpace – 坐标空间的sigma值.
四、引导滤波算法原理(公式罗列更加贴切)
假设滤波器输出q(i,j)与引导图I(i,j)之间存在如下关系:
其中:
和
位对于固定窗口
内的线性因子。然后通过输入图像p(i,j)和输出图像q(i,j)之间的差异最小化来确定这两个线性因子,成本函数如下:
通过使得成本最小来确定
和
,ε为防止
过大的正则化参数。上述方程解为:
结论如下:当窗口处于平坦区域时,图像的局部方差小,则
趋于0,
趋于均值,相当于对图像进行了均值滤波;当窗口处于边缘时,局部方差较大,则则
趋于1,
趋于0,滤波器输出相当于原图。这样就可以很好的保护边缘。
注意:一般地,图像中间,像素跳跃变化不大,趋于稳定,方差小;图像边缘,像素跳跃变化大,不稳定,方差大。
五、OpenCV完成引导滤波编写
本案例是可以通过人工手算验证OpenCV的算法是否达到我们所需要的要求,所以直接处理图像数据是不合适的。但是只是简单的处理一个很小的矩阵也是不合适的。通过网络资源的搜寻,采用下面二维数组:
float matrix[8][8] = { { 45.0f, 60.0f, 98.0f, 127.0f, 132.0f, 133.0f, 137.0f, 133.0f }, { 46.0f, 65.0f, 98.0f, 123.0f, 126.0f, 128.0f, 131.0f, 133.0f }, { 47.0f, 65.0f, 96.0f, 115.0f, 119.0f, 123.0f, 135.0f, 137.0f }, { 47.0f, 63.0f, 91.0f, 107.0f, 113.0f, 122.0f, 138.0f, 134.0f }, { 50.0f, 59.0f, 80.0f, 97.0f, 110.0f, 123.0f, 133.0f, 134.0f }, { 49.0f, 53.0f, 68.0f, 83.0f, 97.0f, 113.0f, 128.0f, 133.0f }, { 50.0f, 50.0f, 58.0f, 70.0f, 84.0f, 102.0f, 116.0f, 126.0f }, { 50.0f, 50.0f, 52.0f, 58.0f, 69.0f, 86.0f, 101.0f, 120.0f } };
并且,我还为了方便验证和计算,把引导图和输入图像都用这个数据代替。float数组转Mat代码和显示代码如下:
//原矩阵 Mat mat(Size(8, 8), CV_32F, matrix); cout <<"mat:\n"<< mat << endl;
结果截图如下:
证明Mat数据构造没有问题,可以进行下一步,求解这个矩阵的均值,代码如下:
//原矩阵的均值矩阵 Mat mean; boxFilter(mat, mean, -1, Size(3, 3)); cout << "mean:\n" << mean << endl;
结果截图如下:
由第一幅图圈出的9个数和第二幅图圈出的一个数,经过计算可以得出,在误差允许范围内,值均值关系。
验证成功。进行下一步,求出原矩阵的平方矩阵,代码如下:
//原矩阵的平方矩阵 Mat mat2 = mat.mul(mat); cout << "mat2:\n" << mat2 << endl;
结果截图如下:
对比图1和图3的圈住的9个数,确定是平方关系。验证成功,进行下一步,求均值矩阵的平方矩阵,代码如下:
//均值矩阵的平方矩阵 Mat mean2 = mean.mul(mean); cout << "mean2:\n" << mean2 << endl;
结果截图如下:
图2 和图4是平方关系,验证成功,进行下一步,求方差,方差公式如下:
由这个公式,求平方矩阵的均值矩阵代码如下:
//平方矩阵的均值矩阵 Mat mat2mean; boxFilter(mat2, mat2mean, -1, Size(3, 3)); cout << "mat2mean:\n" << mat2mean << endl;
结果截图如下:
现在得到了平方均值和均值平方,求方差,代码如下:
//平方均值减去均值平方等于方差 Mat variance = mat2mean - mean2; cout << "variance:\n" << variance << endl;
结果如下:
在这里,我们去ε=500,求得到分母,代码如下:
Mat variance_epislon = variance + 500; cout << "\nvariance_epislon:\n" << variance_epislon << endl;
结果如下:
根据上诉公式,求解
和
。
求解代码如下:
Mat a; divide(variance, variance_epislon, a); cout << "\na:\n" << a << endl; Mat b = (1 - a)*mean; cout << "\nb:\n" << b << endl; Mat mean_a; boxFilter(a, mean_a, CV_32F, Size(3, 3)); cout << "\nmean_a:\n" << mean_a << endl; Mat mean_b; boxFilter(b, mean_b, CV_32F, Size(3, 3)); cout << "\nmean_b:\n" << mean_b << endl;
结果截图:
有了mean_a和mean_b,求出输出图像,代码如下:
Mat tmp = mean_a * mat + mean_b; cout << "\ntmp:\n" << tmp << endl; Mat output; //归一化 normalize(tmp, output, 1.0, 0.0, NORM_MINMAX); //转成8位 output.convertTo(output, CV_8UC1, 255); cout << "\noutput:\n" << output << endl;
结果截图如下:
可以看出,经过这一番变化,这个矩阵的内容发生变化,也是按照我们需要的方向变化的。每一步都不能错,每一步都是验证成功才进行下一步。
具体对于图像的细节增强,需要调整内核大小和ε值,我这里是固定的3*3和500,是为了方便计算。
整体代码如下:
int main() { float matrix[8][8] = { { 45.0f, 60.0f, 98.0f, 127.0f, 132.0f, 133.0f, 137.0f, 133.0f }, { 46.0f, 65.0f, 98.0f, 123.0f, 126.0f, 128.0f, 131.0f, 133.0f }, { 47.0f, 65.0f, 96.0f, 115.0f, 119.0f, 123.0f, 135.0f, 137.0f }, { 47.0f, 63.0f, 91.0f, 107.0f, 113.0f, 122.0f, 138.0f, 134.0f }, { 50.0f, 59.0f, 80.0f, 97.0f, 110.0f, 123.0f, 133.0f, 134.0f }, { 49.0f, 53.0f, 68.0f, 83.0f, 97.0f, 113.0f, 128.0f, 133.0f }, { 50.0f, 50.0f, 58.0f, 70.0f, 84.0f, 102.0f, 116.0f, 126.0f }, { 50.0f, 50.0f, 52.0f, 58.0f, 69.0f, 86.0f, 101.0f, 120.0f } }; //原矩阵 Mat mat(Size(8, 8), CV_32F, matrix); cout <<"mat:\n"<< mat << endl; //原矩阵的均值矩阵 Mat mean; boxFilter(mat, mean, -1, Size(3, 3)); cout << "mean:\n" << mean << endl; //均值矩阵的平方矩阵 Mat mean2 = mean.mul(mean); cout << "mean2:\n" << mean2 << endl; //原矩阵的平方矩阵 Mat mat2 = mat.mul(mat); cout << "mat2:\n" << mat2 << endl; //平方矩阵的均值矩阵 Mat mat2mean; boxFilter(mat2, mat2mean, -1, Size(3, 3)); cout << "mat2mean:\n" << mat2mean << endl; //平方均值减去均值平方等于方差 Mat variance = mat2mean - mean2; cout << "variance:\n" << variance << endl; Mat variance_epislon = variance + 500; cout << "\nvariance_epislon:\n" << variance_epislon << endl; Mat a; divide(variance, variance_epislon, a); cout << "\na:\n" << a << endl; Mat b = (1 - a)*mean; cout << "\nb:\n" << b << endl; Mat mean_a; boxFilter(a, mean_a, CV_32F, Size(3, 3)); cout << "\nmean_a:\n" << mean_a << endl; Mat mean_b; boxFilter(b, mean_b, CV_32F, Size(3, 3)); cout << "\nmean_b:\n" << mean_b << endl; Mat tmp = mean_a * mat + mean_b; cout << "\ntmp:\n" << tmp << endl; Mat output; normalize(tmp, output, 1.0, 0.0, NORM_MINMAX); output.convertTo(output, CV_8UC1, 255); cout << "\noutput:\n" << output << endl; cin.get(); return 0; }
总算是把这个过程搞明白了,引导滤波也算是弯沉了第一步。
问题:
为什么在OpenCV里面号称保边的boxFilter滤波器,在3*3的内核尺寸下,4边上的像素还是发生了改变?是如何变化的?
需要考究一下。
相关文章推荐
- OpenCV导向滤波(引导滤波)实现(Guided Filter)代码,以及使用颜色先验算法去雾
- 引导图滤波(Guided Image Filtering)原理以及OpenCV实现
- OpenCV导向滤波(引导滤波)实现(Guided Filter)代码,以及使用颜色先验算法去雾
- OpenCV导向滤波(引导滤波)实现(Guided Filter)代码,以及使用颜色先验算法去雾
- OpenCV导向滤波(引导滤波)实现(Guided Filter)代码,以及使用颜色先验算法去雾
- OpenCV导向滤波(引导滤波)实现(Guided Filter)代码,以及使用颜色先验算法去雾
- 引导滤波的opencv实现
- OpenCv3.0架构的详细解释以及新增新功能的说明(当然OpenCv3.2.0中的很多新功能更加强大,比如CNN,DNN的实现)
- opencv 中关于BOW模型的实现以及相关的函数解释
- opencv 中关于BOW模型的实现以及相关的函数解释
- 引导滤波的OpenCV实现
- opencv3.0 图像滤波方式代码实现
- opencv 实现导向滤波
- 张正友相机标定Opencv实现以及标定流程&&标定结果评价&&图像矫正流程解析(附标定程序和棋盘图)
- 【对转载进行了部分解释】CRoutingZone的实现机制 以及 process()中一些函数的意义
- QT +openCV 实现摄像头采集以及拍照功能
- 双目相机标定以及立体测距原理及OpenCV实现
- openCV实现图像的轮廓检测以及外接矩形
- opencv 同态滤波实现 homofilter
- Struts2的执行流程解释以及源码分析(以登录 和自动登录实现 为例)