导读
在图像滤波算法中,导向滤波、双边滤波、最小二乘滤波并称三大保边滤波器,他们是各向异性滤波器。相对于常见的均值滤波、高斯滤波等各向同性滤波器,他们最大的特点是在去除噪声的同时,能最大限度保持边缘不被平滑。本文讲解导向滤波及其应用。
总的来讲,导向滤波就是尽可能让输出图像的梯度和导向图相似,同时让输出图像的灰度(亮度)与输入图像相似,以此来保留边缘并且滤除噪声。
原理推导
我们先看下图:
输入图像,经过引导图像, 滤波得到输出图像, 导向滤波算法中有一个重要假设:即在局部窗口上,导向图和输出图存在局部线性关系:
同时在窗口上, 滤波后的图像和输入图像有如下关系:
这样公式的线性关系保证了如果在每个局部窗口中,如果导向图中存在一个边缘,输出图像将保持边缘不变。同时,滤波结果图要尽可能与输入图像相同以此减小滤波带来的信息损失,该算法的最小二乘表示即:
这是求解最优值的问题,引入一个正则化参数防止过大,得到损失函数:
运用最小二乘法求解极小值,利用极小值处导数为0,求解过程如下:
由上面推到得出:
其中:
将代入计算可得:
上下两边同除以,为窗口像素数量。得到:
最后:
得到上述公式后,可以对每个窗口计算一个,但是,每个像素都被包含在多个窗口中,对每个像素,都能计算出多个,我么将使用多个计算得到的值求平均得到输出值,上述过程描述如下:
其中:
导向滤波的应用
- 保边滤波
当时,该算法成为一个保边滤波器。上述计算公式变化为:
考虑以下两种情况:- Case 1:平坦区域。如果在某个滤波窗口内,该区域是相对平滑的,方差将远远小于。从而。相当于对该区域作均值滤波。
- Case 2:高方差区域。相反,如果该区域是边缘区域,方差很大,将远远大于。从而。相当于在区域保持原有梯度。
以上可以出:为界定平滑区域和边缘区域的阈值。
- 图像去雾
在图像去雾中,导向滤波一般用来细化透射率图像,以原图的灰度图为导向图,以粗投射率图为输入图,能得到非常精细的透射率图像。
当然,导向滤波的应用不止以上两种,网上还有图像融合等应用,本人没有去了解。
导向滤波的实现
导向滤波的代码实现较为简单,我们直接贴出计算流程
快速导向滤波的实现
由于导向滤波效率问题,何凯明博士在2015年,对其做了优化,基本原理是将导向图,输入图都进行下采样计算,然后对进行上采样恢复原始大小,整个算法流程如下:
算法效果
我们演示一下保边滤波效果:
代码
接下来,废话不多说,我们上代码:https://github.com/EthanAndEvan/ImageAlgorithmDraft,为防止github无法访问,我们直接贴上代码:
- 导向滤波
#include <opencv2/opencv.hpp>
//导向滤波
void GuidedFilter(cv::Mat& srcImage, cv::Mat& guidedImage, cv::Mat& outputImage, int filterSize, double eps)
{
try
{
if (srcImage.empty() || guidedImage.empty() || filterSize <= 0 || eps < 0 ||
srcImage.channels() != 1 || guidedImage.channels() != 1)
{
throw "params input error";
}
cv::Mat srcImageP, srcImageI, meanP, meanI, meanIP, meanII, varII, alfa, beta;
srcImage.convertTo(srcImageP, CV_32FC1);
guidedImage.convertTo(srcImageI, CV_32FC1);
cv::boxFilter(srcImageP, meanP, CV_32FC1, cv::Size(filterSize, filterSize));
cv::boxFilter(srcImageI, meanI, CV_32FC1, cv::Size(filterSize, filterSize));
cv::boxFilter(srcImageI.mul(srcImageP), meanIP, CV_32FC1, cv::Size(filterSize, filterSize));
cv::boxFilter(srcImageI.mul(srcImageI), meanII, CV_32FC1, cv::Size(filterSize, filterSize));
varII = meanII - meanI.mul(meanI);
alfa = (meanIP - meanI.mul(meanP)) / (varII + eps);
beta = meanP - alfa.mul(meanI);
cv::boxFilter(alfa, alfa, CV_32FC1, cv::Size(filterSize, filterSize));
cv::boxFilter(beta, beta, CV_32FC1, cv::Size(filterSize, filterSize));
outputImage = (alfa.mul(srcImageI) + beta);
}
catch (cv::Exception& e)
{
throw e;
}
catch (std::exception& e)
{
throw e;
}
}
- 快速导向滤波
#include <opencv2/opencv.hpp>
//快速导向滤波
void FastGuidedFilter(cv::Mat& srcImage, cv::Mat& guidedImage, cv::Mat& outputImage, int filterSize, double eps, int samplingRate)
{
try
{
if (srcImage.empty() || guidedImage.empty() || filterSize <= 0 || eps < 0 ||
srcImage.channels() != 1 || guidedImage.channels() != 1 || samplingRate < 1)
{
throw "params input error";
}
cv::Mat srcImageP, srcImageSubI, srcImageI, meanP, meanI, meanIP, meanII, var, alfa, beta;
cv::resize(srcImage, srcImageP, cv::Size(srcImage.cols / samplingRate, srcImage.rows / samplingRate));
cv::resize(guidedImage, srcImageSubI, cv::Size(srcImage.cols / samplingRate, srcImage.rows / samplingRate));
filterSize = filterSize / samplingRate;
srcImageP.convertTo(srcImageP, CV_32FC1);
guidedImage.convertTo(srcImageI, CV_32FC1);
srcImageSubI.convertTo(srcImageSubI, CV_32FC1);
cv::boxFilter(srcImageP, meanP, CV_32FC1, cv::Size(filterSize, filterSize));
cv::boxFilter(srcImageSubI, meanI, CV_32FC1, cv::Size(filterSize, filterSize));
cv::boxFilter(srcImageSubI.mul(srcImageP), meanIP, CV_32FC1, cv::Size(filterSize, filterSize));
cv::boxFilter(srcImageSubI.mul(srcImageSubI), meanII, CV_32FC1, cv::Size(filterSize, filterSize));
var = meanII - meanI.mul(meanI);
alfa = (meanIP - meanI.mul(meanP)) / (var + eps);
beta = meanP - alfa.mul(meanI);
cv::boxFilter(alfa, alfa, CV_32FC1, cv::Size(filterSize, filterSize));
cv::boxFilter(beta, beta, CV_32FC1, cv::Size(filterSize, filterSize));
cv::resize(alfa, alfa, cv::Size(srcImage.cols, srcImage.rows));
cv::resize(beta, beta, cv::Size(srcImage.cols, srcImage.rows));
outputImage = alfa.mul(srcImageI) + beta;
}
catch (cv::Exception& e)
{
throw e;
}
catch (std::exception& e)
{
throw e;
}
}
参考
[1] 视觉一只白 .《导向滤波的原理及实现》[DB/OL].
[2] lsflll.《导向滤波(Guided Filter)公式详解》[DB/OL]
[3] SongpingWang.《OpenCV—Python 导向滤波》[DB/OL]