双边滤波(Bilateral filter)是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的。具有简单、非迭代、局部的特点。
双边滤波器的好处是可以做边缘保存(edge preserving),一般过去用的维纳滤波或者高斯滤波去降噪,都会较明显地模糊边缘,对于高频细节的保护效果并不明显。双边滤波器顾名思义比高斯滤波多了一个高斯方差sigma-d,它是基于空间分布的高斯滤波函数,所以在边缘附近,离的较远的像素不会太多影响到边缘上的像素值,这样就保证了边缘附近像素值的保存。但是由于保存了过多的高频信息,对于彩色图像里的高频噪声,双边滤波器不能够干净的滤掉,只能够对于低频信息进行较好的滤波。
在双边滤波器中,输出像素的值依赖于邻域像素值的加权值组合:
而加权系数w(i,j,k,l)取决于定义域核和值域核的乘积。
其中定义域核表示如下(如图):
定义域滤波对应图示:
值域核表示为:
值域滤波:
两者相乘后,就会产生依赖于数据的双边滤波权重函数:
二、深入——OpenCV源码分析溯源
首先让我们一起领略medianBlur()函数的源码,其于…\opencv\sources\modules\imgproc\src\smooth.cpp的第1653行开始。
//-----------------------------------【medianBlur()函数中文注释版源代码】------------------------- // 代码作用:进行中值滤波操作的函数 // 说明:以下代码为来自于计算机开源视觉库OpenCV的官方源代码 // OpenCV源代码版本:2.4.8 // 源码路径:…\opencv\sources\modules\imgproc\src\smooth.cpp // 源文件中如下代码的起始行数:1653行 // 中文注释by浅墨 //-------------------------------------------------------------------------------------------------------- void cv::medianBlur( InputArray _src0,OutputArray _dst, int ksize ) { //拷贝形参Mat数据到临时变量,用于稍后的操作 Mat src0 = _src0.getMat(); _dst.create( src0.size(), src0.type() ); Mat dst = _dst.getMat(); //处理特定的ksize值的不同情况 if( ksize <= 1 ) { src0.copyTo(dst); return; } CV_Assert( ksize% 2 == 1 ); //若之前有过HAVE_TEGRA_OPTIMIZATION优化选项的定义,则执行宏体中的tegra优化版函数并返回 #ifdef HAVE_TEGRA_OPTIMIZATION if (tegra::medianBlur(src0, dst, ksize)) return; #endif bool useSortNet = ksize == 3 || (ksize == 5 #if !CV_SSE2//若CV_SSE2为假,则执行宏体中语句 && src0.depth() > CV_8U #endif ); //开始正式进行滤波操作 Mat src; if( useSortNet ) { if( dst.data != src0.data ) src = src0; else src0.copyTo(src); //根据不同的位深,给medianBlur_SortNet函数取不同的模板类型值 if( src.depth() == CV_8U ) medianBlur_SortNet<MinMax8u, MinMaxVec8u>( src, dst, ksize ); else if( src.depth() == CV_16U ) medianBlur_SortNet<MinMax16u, MinMaxVec16u>( src, dst, ksize ); else if( src.depth() == CV_16S ) medianBlur_SortNet<MinMax16s, MinMaxVec16s>( src, dst, ksize ); else if( src.depth() == CV_32F ) medianBlur_SortNet<MinMax32f, MinMaxVec32f>( src, dst, ksize ); else CV_Error(CV_StsUnsupportedFormat, ""); return; } else { cv::copyMakeBorder( src0, src, 0, 0, ksize/2, ksize/2, BORDER_REPLICATE); int cn = src0.channels(); CV_Assert( src.depth() == CV_8U && (cn == 1 || cn == 3 || cn ==4) ); double img_size_mp = (double)(src0.total())/(1 << 20); if( ksize <= 3 + (img_size_mp < 1 ? 12 : img_size_mp < 4 ? 6 :2)*(MEDIAN_HAVE_SIMD && checkHardwareSupport(CV_CPU_SSE2) ? 1 : 3)) medianBlur_8u_Om( src, dst, ksize ); else medianBlur_8u_O1( src, dst, ksize ); } }
仔细阅读源码我们可以发现,正式进入滤波操作时,根据图像不同的位深,我们会给medianBlur_SortNet函数模板取不同的模板类型值,或者调用medianBlur_8u_Om或medianBlur_8u_O1来进行操作。