- 平滑技术(滤波技术)可以抑制或者消除图像噪声,常用的平滑处理算法包括二维离散卷积的高斯平滑、均值平滑,基于统计学方法的中值平滑,具备保持边缘作用的平滑算法的双边滤波、导向滤波等。
1. 高斯平滑
-
高斯卷积核的构建及分离性
假设构造宽(列数)为 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∈Ng 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-一维水平、垂直方向高斯卷积核的标准差
- 当平滑窗口较小时,对标准差的变化不是很敏感,得到的高斯平滑效果差别不大;当平滑窗口较大时,对标准差的变化很敏感,得到的高斯平滑效果差别较大。
2.均值平滑
- 均值卷积核的构建及分离性
高为H、宽为W的均值卷积算子,令所有元素均为 1 W ∗ H \frac{1}{W*H} W∗H1即可。
m e a n K e r n e l H X W = 1 H ∗ W [ 1 ] H X W meanKernel_{HXW}=\frac{1}{H*W}[1]_{HXW} meanKernelHXW=H∗W1[1]HXW
其中W、H均为奇数,锚点位置在 ( H − 1 2 , w − 1 2 ) (\frac{H-1}{2},\frac{w-1}{2}) (2H−1,2w−1)
均值平滑算子是可分离卷积核,图像中每一个位置的邻域的平均值作为该位置的输出值。 - 快速均值平滑
R行C列的图像矩阵I的积分Integral由以下定义计算:
I n t e g r a l ( r , c ) = ∑ i = 0 i = r ∑ i = 0 j = c I ( i , j ) , 0 ≤ r < R , 0 ≤ c < C Integral(r,c)=\sum_{i=0}^{i=r}\sum_{i=0}^{j=c}I(i,j),0 \leq r <R,0 \leq c <C Integral(r,c)=∑i=0i=r∑i=0j=cI(i,j),0≤r<R,0≤c<C
即任意一个位置的积分等于该位置左上角所有值的和。
//图像的积分
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)
//这两个函数作用相同
3.中值平滑
- 中值平滑是一种邻域运算,对邻域中的像素点按灰度值进行排序,然后选择该组的中值作为输出灰度值。
- 假设输入图像I,高为H、宽为C,对于图像中任意位置 ( r , c ) , 0 ≤ r < R , 0 ≤ c < C (r,c),0 \leq r <R,0 \leq c <C (r,c),0≤r<R,0≤c<C,取以(r,c)为中心、宽W高H的邻域,其中W、H均为奇数,对邻域中的像素点灰度值排序,然后取中值,作为输出图像O的(r,c)处的灰度值。、
- 中值滤波最主要的能力是去除椒盐噪声。椒盐噪声是指图像传输系统中由于解码误差等原因,导致图像出现孤立的白点或黑点。
medianBlur(InputArray src, OutputArray dst, int ksize)
//ksize-若为大于1的奇数,则窗口大小为ksizeXksize
4.双边滤波
- 双边滤波是根据每个位置的邻域,对该位置构建不同的权重模板,详细过程如下:
第一步:构建 w i n H × w i n W winH\times winW winH×winW的空间距离权重模板, w i n H 和 w i n W winH和winW winH和winW均为奇数。
c l o s e n e s s W e i g h t ( h , w ) = e x p ( − ( h − w i n H − 1 2 ) 2 + ( w − w i n W − 1 2 ) 2 2 σ 1 2 ) closenessWeight(h,w)=exp(-\frac{(h-\frac{winH-1}{2})^2+(w-\frac{winW-1}{2})^2}{2\sigma_1^2}) closenessWeight(h,w)=exp(−2σ12(h−2winH−1)2+(w−2winW−1)2)
其中, 0 ≤ h < w i n H , 0 ≤ w < w i n W 0 \leq h<winH,0 \leq w <winW 0≤h<winH,0≤w<winW,且每个位置空间距离权重模板相同。
第二步:构建 w i n H × w i n W winH\times winW winH×winW的相似性权重模板,是通过(r,c)处的值与其邻域值的差值的指数衡量的。
s i m i l a r i t y W e i g h t ( h , w ) = e x p ( − ∣ ∣ I ( r , c ) − I ( r + ( h − w i n H − 1 2 ) , c + ( w − w i n W − 1 2 ) ) ∣ ∣ 2 2 σ 2 2 ) similarityWeight(h,w)=exp(-\frac{||I(r,c)-I(r+(h-\frac{winH-1}{2}),c +(w-\frac{winW-1}{2}))||^2}{2\sigma_2^2}) similarityWeight(h,w)=exp(−2σ22∣∣I(r,c)−I(r+(h−2winH−1),c+(w−2winW−1))∣∣2)
其中, 0 ≤ h < w i n H , 0 ≤ w < w i n W 0 \leq h<winH,0 \leq w <winW 0≤h<winH,0≤w<winW,每个位置相似性权重模板不一样。
第三步:将 c l o s e n e s s W e i g h t 和 s i m i l a r i t y W e i g h t closenessWeight和similarityWeight closenessWeight和similarityWeight 对应位置点乘,进行归一化,即可得到该位置的权重模板。将得到的权重模板和该位置邻域对应位置相乘,然后求和就得到该位置输出值。 - 与高斯平滑、均值平滑处理相比,双边滤波可以保持图像中目标的边缘。
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 )
5.联合双边滤波
- 联合双边滤波与双边滤波类似,具体过程如下:
第一步:对每个位置的邻域构建空间距离权重模板,与双边滤波一样。
第二步:构建相似性权重模板,与双边滤波唯一的不同。双边滤波是根据原图,对于每一个位置,通过该位置和其邻域灰度值的差的指数来估计相似性;而联合双边滤波是首先对原图进行高斯平滑,根据平滑结果,用当前位置及其邻域的值的差来估计相似性权重模板。
第三步:将空间距离权重模板和相似性权重模板对应位置点乘,进行归一化,即可得到该位置的权重模板。将得到的权重模板和该位置邻域对应位置相乘,然后求和就得到该位置输出值。
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;
}
6.导向滤波
-
导向滤波不依赖于权重模板保持边缘,不仅在平滑图像的基础上有良好的保边作用,而且在细节增强等方面有良好的表现,执行时间也比双边滤波快很多。
-
导向滤波伪码如下:
输入:导向图象 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;
}