引导滤波
导向图滤波(Guided Filter)是一种能使视频平滑化的非线性滤波器,通过一张引导图G,对目标图像P(输入图像)进行滤波处理,使得最后的输出图像大体上与目标图像P相似,但是纹理部分与引导图G相似。应用有两个:保边图像平滑,抠图。
引导滤波是由何凯明等人于2010年发表在ECCV的文章《Guided Image Filtering》中提出的,后续于2013年发表了改进算法快速引导滤波的实现。它与双边滤波最大的相似之处,就是同样具有保持边缘特性。该模型认为,某函数上一点与其邻近部分的点成线性关系,一个复杂的函数就可以用很多局部的线性函数来表示,当需要求该函数上某一点的值时,只需计算所有包含该点的线性函数的值并做平均即可。这种模型,在表示非解析函数上,非常有用。
在滤波效果上,引导滤波和双边滤波差不多,在一些细节上,引导滤波较好。引导滤波最大的优势在于,可以写出时间复杂度与窗口大小无关的算法,因此在使用大窗口处理图片时,其效率更高。
引导图像I,原图P,公式如下:
引导滤波算法,目前opencv没有提供API,伪代码如下:
实现这种算法的关键思想是盒式滤波(box filter),而且必须是通过积分图来实现的盒式滤波,否则不可能与窗口大小无关,好在OpenCV的boxFilter函数满足这个要求。与均值滤波不同的是,方框滤波不会计算像素的均值。在均值滤波中,滤波结果的像素值是任意一个点的邻域平均值,等于各邻域像素值之和除以邻域面积。而在方框滤波中,可以自由选择是否对均值滤波的结果进行归一化,即可以自由选择滤波结果是邻域像素值之和的平均值,还是邻域像素值之和。
CV_EXPORTS_WvoidboxFilter(InputArraysrc,OutputArraydst, intddepth, Sizeksize, Pointanchor=Point(-1,-1), boolnormalize=true, intborderType=BORDER_DEFAULT)
C++实现:
//// GUIDEDFILTER O(1) time implementation of guided filter.// -guidance image : I(should be a gray - scale / single channel image)// -filtering input image : p(should be a gray - scale / single channel image)// -local window radius : r// -regularization parameter : eps/cv::MatGuidedFilter(cv::Mat&I, cv::Mat&p, intr, doubleeps){ intwsize=2*r+1; //数据类型转换I.convertTo(I, CV_64F, 1.0/255.0); p.convertTo(p, CV_64F, 1.0/255.0); //meanI=fmean(I)cv::Matmean_I; cv::boxFilter(I, mean_I, -1, cv::Size(wsize, wsize), cv::Point(-1, -1), true, cv::BORDER_REFLECT);//盒子滤波//meanP=fmean(P)cv::Matmean_p; cv::boxFilter(p, mean_p, -1, cv::Size(wsize, wsize), cv::Point(-1, -1), true, cv::BORDER_REFLECT);//盒子滤波//corrI=fmean(I.*I)cv::Matmean_II; mean_II=I.mul(I); cv::boxFilter(mean_II, mean_II, -1, cv::Size(wsize, wsize), cv::Point(-1, -1), true, cv::BORDER_REFLECT);//盒子滤波//corrIp=fmean(I.*p)cv::Matmean_Ip; mean_Ip=I.mul(p); cv::boxFilter(mean_Ip, mean_Ip, -1, cv::Size(wsize, wsize), cv::Point(-1, -1), true, cv::BORDER_REFLECT);//盒子滤波//varI=corrI-meanI.*meanIcv::Matvar_I, mean_mul_I; mean_mul_I=mean_I.mul(mean_I); cv::subtract(mean_II, mean_mul_I, var_I); //covIp=corrIp-meanI.*meanpcv::Matcov_Ip; cv::subtract(mean_Ip, mean_I.mul(mean_p), cov_Ip); //a=conIp./(varI+eps)//b=meanp-a.*meanIcv::Mata, b; cv::divide(cov_Ip, (var_I+eps),a); cv::subtract(mean_p, a.mul(mean_I), b); //meana=fmean(a)//meanb=fmean(b)cv::Matmean_a, mean_b; cv::boxFilter(a, mean_a, -1, cv::Size(wsize, wsize), cv::Point(-1, -1), true, cv::BORDER_REFLECT);//盒子滤波cv::boxFilter(b, mean_b, -1, cv::Size(wsize, wsize), cv::Point(-1, -1), true, cv::BORDER_REFLECT);//盒子滤波//q=meana.*I+meanbcv::Matq; q=mean_a.mul(I) +mean_b; //数据类型转换I.convertTo(I, CV_8U, 255); p.convertTo(p, CV_8U, 255); q.convertTo(q, CV_8U, 255); returnq; } intmain(){ cv::Matsrc=cv::imread("I:\\Learning-and-Practice\\2019Change\\Image process algorithm\\Img\\woman.jpg"); if (src.empty()){ return-1; } //if (src.channels() > 1) // cv::cvtColor(src, src, CV_RGB2GRAY);//自编GuidedFilter测试doublet2= (double)cv::getTickCount(); //测时间cv::Matdst1, src_input, I; src.copyTo(src_input); if (src.channels() >1) cv::cvtColor(src, I, CV_RGB2GRAY); //若引导图为彩色图,则转为灰度图std::vector<cv::Mat>p,q; if (src.channels() >1){ //输入为彩色图cv::split(src_input, p); for (inti=0; i<src.channels(); ++i){ dst1=GuidedFilter(I, p[i], 9, 0.1*0.1); q.push_back(dst1); } cv::merge(q, dst1); } else{ //输入为灰度图src.copyTo(I); dst1=GuidedFilter(I, src_input, 9, 0.1*0.1); } t2= (double)cv::getTickCount() -t2; doubletime2= (t2*1000.) / ((double)cv::getTickFrequency()); std::cout<<"MyGuidedFilter_process="<<time2<<" ms. "<<std::endl<<std::endl; cv::namedWindow("GuidedImg", CV_WINDOW_NORMAL); cv::imshow("GuidedImg", I); cv::namedWindow("src", CV_WINDOW_NORMAL); cv::imshow("src", src); cv::namedWindow("GuidedFilter_box", CV_WINDOW_NORMAL); cv::imshow("GuidedFilter_box", dst1); cv::waitKey(0); }