高斯卷积核的构建及分离性
假设构造宽(列数)为 W W W、高(行数)为 H H H的高斯卷积算子 g a u s s K e r n e l H X W gaussKernel_{HXW} gaussKernelHXW, H 、 W H、W H、W均为奇数,锚点位置在中心,步骤如下:
第一步:计算高斯矩阵
g a u s s M a t r i x H X W = [ g a u s s ( r , c , σ ) ] 0 ≤ r ≤ H − 1 , 0 ≤ c ≤ W − 1 , r , c ∈ N gaussMatrix_{HXW}=[gauss(r,c,\sigma)]_{0\leq r\leq H-1,0\leq c\leq W-1,r,c\in N} gaussMatrixHXW=[gauss(r,c,σ)]0≤r≤H−1,0≤c≤W−1,r,c∈N
g a u s s ( r , c , σ ) = 1 2 π σ 2 e − ( r − H − 1 2 ) 2 + ( c − W − 1 2 ) 2 2 σ 2 gauss(r,c,\sigma)=\frac{1}{2\pi \sigma ^2}e^{-\frac{(r-\frac{H-1}{2})^2+(c-\frac{W-1}{2})^2}{2\sigma^2}} gauss(r,c,σ)=2πσ21e−2σ2(r−2H−1)2+(c−2W−1)2
第二步:计算高斯矩阵的和
s u m ( g a u s s M a t r i x H X W ) sum(gaussMatrix_{HXW}) sum(gaussMatrixHXW)
第三步:高斯矩阵归一化
g a u s s K e r n e l H X W = g a u s s M a t r i x H X W s u m ( g a u s s M a t r i x H X W ) gaussKernel_{HXW}=\frac{gaussMatrix_{HXW}}{sum(gaussMatrix_{HXW})} gaussKernelHXW=sum(gaussMatrixHXW)gaussMatrixHXW
高斯卷积算子是可分离卷积核
e − ( r − H − 1 2 ) 2 + ( c − W − 1 2 ) 2 2 σ 2 = e − ( r − H − 1 2 ) 2 2 σ 2 ∗ e − ( c − W − 1 2 ) 2 2 σ 2 e^{-\frac{(r-\frac{H-1}{2})^2+(c-\frac{W-1}{2})^2}{2\sigma^2}}=e^{-\frac{(r-\frac{H-1}{2})^2}{2\sigma^2}}*e^{-\frac{(c-\frac{W-1}{2})^2}{2\sigma^2}} e−2σ2(r−2H−1)2+(c−2W−1)2=e−2σ2(r−2H−1)2∗e−2σ2(c−2W−1)2
因此高斯卷积核可以分离为一维水平方向的高斯核和一维垂直方向的高斯核。
//构建一维垂直方向的高斯核
Mat getGaussKernel(int ksize, double sigma, int ktype=CV_32F)
//ksize-一维垂直方向高斯核行数,正奇数;sigma-标准差
//对垂直方向上的高斯核进行转置即可获得一维水平方向的高斯核
void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT)
//ksize-高斯卷积核的大小,宽、高均为奇数
//sigmaX、sigmaY-一维水平、垂直方向高斯卷积核的标准差
//图像的积分
void integral(InputArray src, OutputArray sum, int sdepth=-1)
//src-输入HxW的矩阵,数据类型CV_8U,CV_32F,CV_64F
//sum-输出矩阵,大小为(H+1)X(W+1)
//sdepth-输出矩阵的数据类型,CV_32S,CV_32F,CV_64FCV_32S
//快速均值平滑
void boxFilter(InputArray src, OutputArray dst, int ddepth, Size ksize, Point anchor=Point(-1,-1), bool normalize=true, int borderType=BORDER_DEFAULT)
//ksize-平滑窗口的尺寸,normalize-是否归一化
void blur(InputArray src, OutputArray dst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT)
//这两个函数作用相同
medianBlur(InputArray src, OutputArray dst, int ksize)
//ksize-若为大于1的奇数,则窗口大小为ksizeXksize
void bilateralFilter(InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT )
//d-表示在过滤过程中每个像素邻域的直径范围。如果这个值是非正数,则函数会从第五个参数sigmaSpace计算该值
//sigmaColor-颜色空间过滤器的sigma值,这个参数的值越大,表明该像素邻域内有越宽广的颜色会被混合到一起,产生较大的半相等颜色区域。
//sigmaSpace-坐标空间中滤波器的sigma值,如果该值较大,则意味着颜色相近的较远的像素将相互影响,从而使更大的区域中足够相似的颜色获取相同的颜色。当d>0时,d指定了邻域大小且与sigmaSpace无关,否则d正比于sigmaSpace.
adaptiveBilateralFilter( InputArray _src, OutputArray _dst, Size ksize, double sigmaSpace, double maxSigmaColor,Point anchor, int borderType )
Mat jointBLF(Mat I, Size size, float sigma_g, float sigma_d, int borderType=BORDER_DEFAULT)
{
//构建空间距离权重模板
Mat closeWeight = getClosenessWeight(sigma_g, size);
//对I进行高斯平滑
Mat Ig;
GaussianBlur(I, Ig, size, sigma_g);
//模板中心
int cH = (size.height - 1) / 2;
int cW = (size.width - 1) / 2;
//对原图和高斯平滑结果进行边界填充
Mat Ip,Igp;
copyMakeBorder(I, Ip, cH, cH, cw, cW, borderType);
copyMakeBorder(Ig, Igp, cH, cH, cw, cW, borderType);
//联合双边滤波
int rows = I.rows;
int cols = I.cols;
int i = 0, j = 0;
Mat jblf = Mat::zeros(I.size(), CV_64FC1);
for(int r = cH; r < cH + rows; c++)
{
for(int c = cW; c < cW + cols; c++)
{
//当前位置的值
double pixel = Igp.at<double>(r,c);
//截取当前位置邻域
Mat region = Igp(Rect(c-cW,r-cH,size.width,size.height));
//当前位置相似性权重模板
Mat similarityWeight;
pow(region - pixel, 2.0, similarityWeight);
cv::exp(-0.5*similarityWeight / pow(sigma_d, 2.0), similarityWeight);
//相似性权重模板和空间距离权重模板点乘
Mat weight = closeWeight.mul(similarityWeight);
//权重模板归一化
weight = weight / cv::sum(weight)[0];
//权重模板和邻域对应位置相乘求和
Mat Iregion = Ip(Rect(c-cW,r-cH,size.width,size.height));
jblf.at<double>(i,j) = sum(Iregion.mul(weight))[0];
j += 1;
}
j = 0;
i += 1;
}
return jblf;
}
导向滤波不依赖于权重模板保持边缘,不仅在平滑图像的基础上有良好的保边作用,而且在细节增强等方面有良好的表现,执行时间也比双边滤波快很多。
导向滤波伪码如下:
输入:导向图象 I I I,滤波输入图像 p p p,均值平滑窗口半径 r r r,正则化参数 ϵ \epsilon ϵ。利用导向滤波进行图像平滑处理时,通常令 p = I p=I p=I。
输出:导向滤波结果 q q q
m e a n I = f m e a n ( I , r ) mean_I=f_{mean}(I,r) meanI=fmean(I,r)
m e a n p = f m e a n ( p , r ) mean_p=f_{mean}(p,r) meanp=fmean(p,r)
c o r r I = f m e a n ( I . ∗ I , r ) corr_I=f_{mean}(I.*I,r) corrI=fmean(I.∗I,r)
c o r r I p = f m e a n ( I . ∗ p ) corr_{Ip}=f_{mean}(I.*p) corrIp=fmean(I.∗p)
v a r I = c o r r I − m e a n I . ∗ m e a n I var_I=corr_I-mean_I.*mean_I varI=corrI−meanI.∗meanI
c o v I p = c o r r I p − m e a n I . ∗ m e a n p cov_{Ip}=corr_{Ip}-mean_I.*mean_p covIp=corrIp−meanI.∗meanp
a = c o v I p . / ( v a r I + ϵ ) a=cov_{Ip}./(var_I+\epsilon) a=covIp./(varI+ϵ)
b = m e a n p − a . ∗ m e a n I b=mean_p-a.*mean_I b=meanp−a.∗meanI
m e a n a = f m e a n ( a , r ) mean_a=f_{mean}(a,r) meana=fmean(a,r)
m e a n b = f m e a n ( b , r ) mean_b=f_{mean}(b,r) meanb=fmean(b,r)
q = m e a n a . ∗ I + m e a n b q=mean_a.*I+mean_b q=meana.∗I+meanb
其中, f m e a n f_{mean} fmean代表均值平滑,.*代表两个图像矩阵对应值相乘;./代表两个图像矩阵对应值相除。 I 和 q I和q I和q均是归一化的图像矩阵, q q q也是灰度值范围为[0,1]的图像矩阵。
通过先缩小图像,再放大图像,可以加速导向滤波的执行效率。
Mat guideFilter(Mat I, Mat p, int r, float eps, float s)
{
//输入图像的宽和高
int rows = I.rows;
int cols = I.cols;
//缩小图像
Mat small_I, small_p;
Size smallSize(int(round(s*cols)), int(round(s*rows)));
resize(I, small_I, smallSize, 0, 0, INTER_CUBIC);
resize(p, small_p, smallSize, 0, 0, INTER_CUBIC);
//缩放均值平滑的窗口半径
int small_r = int(round(r*s));
Size winSize(2 * small_r + 1, 2 * small_r + 1);
//均值平滑
Mat mean_small_I, mean_small_p;
boxFilter(small_I, mean_small_I, CV_64FC1, winSize);
boxFilter(small_p, mean_small_p, CV_64FC1, winSize);
//small_I.*small_p的均值平滑
Mat small_Ip = small_I.mul(small_p);
Mat mean_small_Ip;
boxFilter(small_Ip, mean_small_Ip, CV_64FC1, winSize);
//协方差
Mat cov_small_Ip = mean_small_Ip - mean_small_I.mul(mean_small_p);
//均值平滑
Mat mean_small_II;
boxFilter(small_I.mul(small_I), mean_small_II, CV_64FC1, winSize);
//方差
Mat var_small_I = mean_small_II - small_I.mul(small_I);
Mat small_a = cov_small_Ip / (var_small_I + eps);
Mat small_b = mean_small_p - small_a.mul(mean_small_I);
//对small_a和small_b进行均值平滑
Mat mean_small_a, mean_small_b;
boxFilter(small_a, mean_small_a, CV_64FC1, winSize);
boxFilter(small_b, mean_small_b, CV_64FC1, winSize);
//放大
Mat mean_a, mean_b;
resize(mean_small_a, mean_a, I.size(), 0, 0, INTER_LINEAR);
resize(mean_small_b, mean_b, I.size(), 0, 0, INTER_LINEAR);
Mat q = mean_a.mul(I) + mean_b;
return q;
}