每一幅圖像都包含某種程度的噪聲,噪聲可以理解為由一種或者多種原因造成的灰 度值的隨機(jī)變化,如由光子通量的隨機(jī)性造成的噪聲等,在大多數(shù)情況下,通過(guò)平滑技術(shù)(也常稱為濾波技術(shù))進(jìn)行抑制或者去除, 其中具備保持邊緣(Edge Preserving)作用的平滑技術(shù)得到了更多的關(guān)注。常用的平滑處理算法包括基于二維離散卷積的高斯平滑、均值平滑,基于統(tǒng)計(jì)學(xué)方法的中值平滑,具備保持邊緣作用的平滑算法的雙邊濾波、導(dǎo)向?yàn)V波等。
5.1、二維離散卷積
I與K的二維離散卷積的計(jì)算步驟如下。
第一步: 將K 逆時(shí)針?lè)D(zhuǎn)180°。
第二步: Kflip沿著I 按照先行后列的順序移動(dòng), 每移動(dòng)到一個(gè)固定位置, 對(duì)應(yīng)位置就相乘, 然后求和。
1、full卷積
顯然,高為H1、寬為W1的矩陣I與高為H2、寬為W2的卷積核K 的full卷積結(jié)果是一 個(gè)高為H1+H2-1、寬為W1+W2-1的矩陣,一般H2 ≤H1,W2 ≤W1。
2、valid卷積
從full卷積的計(jì)算過(guò)程可知, 如果Kflip靠近I 的邊界, 那么就會(huì)有部分延伸到I之外而導(dǎo)致訪問(wèn)到未定義的值, 忽略邊界,只是考慮I能完全覆蓋Kflip內(nèi)的值的情況, 該過(guò)程稱為valid卷積。
當(dāng)然, 只有當(dāng)H2≤H1且W2≤W1時(shí)才會(huì)存在 valid卷積 。

3、same卷積
為了使得到的卷積結(jié)果和原圖像的高、寬相等,所以通常在計(jì)算過(guò)程中給Kflip指定 一個(gè)“錨點(diǎn)”, 然后將“錨點(diǎn)”循環(huán)移至圖像矩陣的(r, c) 處, 其中0≤r< H1, 0≤c<W1,接下來(lái)對(duì)應(yīng)位置的元素逐個(gè)相乘,最后對(duì)所有的積進(jìn)行求和作為輸出圖像矩陣在 (r, c) 處的輸出值。這個(gè)卷積過(guò)程稱為same卷積,

大部分時(shí)候,為了更方便地指定卷積核的錨點(diǎn),通常卷積核的寬、高為奇數(shù),那么可以簡(jiǎn)單地令中心點(diǎn)為錨點(diǎn)的位置。same卷積是full卷積的一部分,而如果valid卷積存在,那么valid卷積是same卷積的一部分。
4、邊界擴(kuò)充
對(duì)于full卷積和same卷積,矩陣I 邊界處的值由于缺乏完整的鄰接值,因此卷積運(yùn)算 在這些區(qū)域需要特殊處理,方法是進(jìn)行邊界擴(kuò)充,有如下幾種常用方式。
- (1) 在矩陣I 邊界外填充常數(shù), 通常進(jìn)行的是0擴(kuò)充。
- (2) 通過(guò)重復(fù)I 邊界處的行和列, 對(duì)輸入矩陣進(jìn)行擴(kuò)充, 使卷積在邊界處可計(jì)算。
- (3) 卷繞輸入矩陣, 即矩陣的平鋪。
- (4) 以矩陣邊界為中心, 令矩陣外某位置上未定義的灰度值等于圖像內(nèi)其鏡像位置 的灰度值, 這種處理方式會(huì)令結(jié)果產(chǎn)生最小程度的干擾。(最常用)
利用上述不同的邊界擴(kuò)充方式得到的same卷積只是在距離矩陣上、下、左、右四個(gè)邊界小于卷積核半徑的區(qū)域內(nèi)值會(huì)不同,所以只要在用卷積運(yùn)算進(jìn)行圖像處理時(shí),圖像的重要信息不要落在距離邊界小于卷積核半徑的區(qū)域內(nèi)就行。
copyMakeBorder(inputArray src,OutputArray dst,int top,int bottom,int left,int right,int borderType,
const Scalar& calue=Scalar())
5、卷積運(yùn)算
void conv2D(InputArray src, InputArray kernel, OutputArray dst, int ddepth, Point anchor , int borderType)
{
//step1:卷積核逆時(shí)針?lè)D(zhuǎn)180°
Mat kernelFlip;
flip(kernel, kernelFlip, -1);
//step:卷積運(yùn)算
filter2D(src, dst, ddepth, kernelFlip, anchor, 0.0, borderType);
}
可分離卷積核(速度快)
如果一個(gè)卷積核至少由兩個(gè)尺寸比它小的卷積核full卷積而成,并且在計(jì)算過(guò)程中在所有邊界處均進(jìn)行擴(kuò)充零的操作,且滿足

其中kerneli的尺寸均比Kernel小,1≤i≤n,則稱該卷積核是可分離的。
在圖像處理中經(jīng)常使用這樣的卷積核,它可以分離為一維水平方向和一維垂直方向上的卷積核。
(1)full卷積性質(zhì)
如果卷積核Kernel是可分離的, 且Kernel=kernel1★kernel2, 則有:

(2)same卷積性質(zhì)

void sepConv2D_Y_X(InputArray src, OutputArray src_kerY_kerX, int ddepth, InputArray kernelY, InputArray kernelX, Point anchor , int borderType )
{
//輸入矩陣與垂直方向上的卷積核的卷積
Mat src_kerY;
conv2D(src, kernelY, src_kerY, ddepth, anchor, borderType);
//上面得到的卷積結(jié)果,接著和水平方向上的卷積核卷積
conv2D(src_kerY, kernelX, src_kerY_kerX, ddepth, anchor, borderType);
}
5.2、高斯平滑
- 第一步: 計(jì)算高斯矩陣。

其中

- 第二步: 計(jì)算高斯矩陣的和。

- 第三步: 高斯矩陣除以其本身的和, 即歸一化, 得到的便是高斯卷積算子。

其中,根據(jù)可分離卷積的性質(zhì),有

Mat gaussBlur(const Mat &image, Size winSize, float sigma, int ddepth , Point anchor , int borderType )
{
//卷積核的寬、高均為奇數(shù)
//CV_ASSERT(winSize.width % 2 == 1 && winSize.height % 2 == 1);
//構(gòu)建垂直方向上的高斯卷積算子
Mat gK_y = getGaussianKernel(sigma, winSize.width, CV_64F);
//構(gòu)建水平方向上的高斯卷積算子
Mat gK_x= getGaussianKernel(sigma, winSize.height, CV_64F);
gK_x = gK_x.t();//轉(zhuǎn)置
//分離的高斯卷積
Mat blurImage;
sepConv2D_Y_X(image, blurImage, ddepth, gK_y, gK_x, Point(-1, -1), borderType);
return blurImage;
}
理解了上述高斯平滑的過(guò)程, 就可以明白OpenCV實(shí)現(xiàn)的高斯平滑函數(shù):
void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
double sigmaX, double sigmaY = 0,
int borderType = BORDER_DEFAULT );
從參數(shù)的設(shè)置可以看出, GaussianBlur也是通過(guò)分離的高斯卷積核實(shí)現(xiàn)的,也可以令水平方向和垂直方向上的標(biāo)準(zhǔn)差不相同,但是一般會(huì)取相同的標(biāo)準(zhǔn)差。 當(dāng)平滑窗口比較小時(shí), 對(duì)標(biāo)準(zhǔn)差的變化不是很敏感, 得到的高斯平滑效果差別不大; 相反,當(dāng)平滑窗口 較大時(shí),對(duì)標(biāo)準(zhǔn)差的變化很敏感, 得到的高斯平滑效果差別較大 。
5.3、均值平滑
5.3.1、均值卷積核

利用卷積核 的分離性和卷積的結(jié)合律,雖然減少了運(yùn)算量,但是隨著卷積核窗口的增加,計(jì)算量仍會(huì)繼續(xù)增大,可以利用圖像的積分,實(shí)現(xiàn)時(shí)間復(fù)雜度為O(1)的快速均值平滑。
5.3.2、快速均值平滑

即任意一個(gè)位置的積分等于該位置左上角所有值的和。 利用矩陣的積分,可以計(jì)算出矩陣中任意矩形區(qū)域的和。

void integral(InputArray src,OutputArray sum,int sdepth=-1)
void boxFilter(InputArray src,OutputArray dst,int ddepth,Size ksize,Point anchor=Point(-1,-1),
bool normalize=true,int bordreType=BORDER_DEFAULT)
void blur(InputArray src,OutputArray dst,Size ksize,Point anchor=Point(-1,-1),int borderType=BORDER_DEFAULT)
5.4、中值平滑
中值濾波最重要的能力是去除椒鹽噪聲。椒鹽噪聲是指在圖像傳輸系統(tǒng)中由于解碼誤差等原因,導(dǎo)致圖像中出現(xiàn)孤立的白點(diǎn)或者黑點(diǎn)。
Mat medianSmooth(const Mat& Input, Size size, int borderType )
{
//輸入圖像應(yīng)該為八位的灰度圖
int height = size.height;
int width = size.width;
//窗口的高、寬均為奇數(shù),一般設(shè)置兩者都相同
//對(duì)原圖矩陣進(jìn)行邊界擴(kuò)充
int h = (height - 1) / 2;
int w = (width - 1) / 2;
Mat Ip;
copyMakeBorder(Input, Ip, h, h, w, w, borderType);
//輸入圖像的高、寬
int rows = Input.rows;
int cols = Input.cols;
Mat medianI(Input.size(), CV_8UC1);
int i = 0, j = 0;
//中數(shù)的位置
int index = (height*width - 1) / 2;
for (int r = h; r < h + rows; r++)
{
for (int c = w; c < w + cols; c++)
{
//取以當(dāng)前位置為中心、大小為size的鄰域
Mat region = Input(Rect(c - w, r - h, width, height)).clone();
//將該鄰域轉(zhuǎn)換成行矩陣
cv::sort(region, region, CV_SORT_EVERY_ROW);//注:不能直接使用sort,分不清是cv的
//取中數(shù)
uchar mValue = region.at<uchar>(0, index);
medianI.at<uchar>(i, j) = mValue;
j++;
}
i++;
j = 0;
}
return medianI;
}
一般來(lái)說(shuō),如果圖像中出現(xiàn)較亮或者較暗的物體,若其大小小于中值平滑的窗口半徑,那么它們基本上會(huì)被濾掉,而較大的目標(biāo)則幾乎會(huì)原封不動(dòng)地保存下來(lái)。
中值平滑需要對(duì)鄰域中的所有像素點(diǎn)按灰度值排序, 一般比卷積運(yùn)算要慢。
在OpenCV中同樣通過(guò)定義函數(shù):
medianBlur(InputArray src,OutputArray dst,int ksize)
此外, 中值平滑只是排序統(tǒng)計(jì)平滑中的一種, 如果將取鄰域的中值變?yōu)槿∴徲蛑械?最小值或者最大值, 顯然會(huì)使圖像變暗或者變亮。 這類方法就是后面要介紹的形態(tài)學(xué) 處理的基礎(chǔ)。
高斯平滑、均值平滑在去除圖像噪聲時(shí),會(huì)使圖像的邊緣信息變得模糊,接下來(lái)就 介紹在圖像平滑處理過(guò)程中可以保持邊緣的平滑算法: 雙邊濾波和導(dǎo)向?yàn)V波。
5.5、雙邊濾波
雙邊濾波是根據(jù)每個(gè)位置的鄰域, 對(duì)該位置構(gòu)建不同的權(quán)重模板。 詳細(xì)過(guò)程如下:
- 第一步, 構(gòu)建winH×winW的空間距離權(quán)重模板, 與構(gòu)建高斯卷積核的過(guò)程類似, winH 和winW均為奇數(shù)。

其中0≤h<winH, 0≤w<winW, 且每個(gè)位置的空間距離權(quán)重模板是相同的。
- 第二步, 構(gòu)建winH×winW的相似性權(quán)重模板, 是通過(guò)(r, c) 處的值與其鄰域值的差值的指數(shù)衡量的。

其中0≤h<winH, 0≤w<winW, 顯然每個(gè)位置的相似性權(quán)重模板是不一樣的。
- 第三步, 將
closenessWeight和similarityWeight的對(duì)應(yīng)位置相乘(即點(diǎn)乘),然后進(jìn)行歸一化,便可得到該位置的權(quán)重模板。將所得到的權(quán)重模板和該位置鄰域的對(duì)應(yīng)位置相乘,然后求和就得到該位置的輸出值, 和卷積運(yùn)算的第二步操作類似。
void bilateralFilter( InputArray src, OutputArray dst, int d,
double sigmaColor, double sigmaSpace,
int borderType = BORDER_DEFAULT );
5.6、聯(lián)合雙邊濾波
- 第一步,對(duì)每個(gè)位置的鄰域構(gòu)建空間距離權(quán)重模板。與雙邊濾波構(gòu)建空間距離權(quán)重模板一樣。
- 第二步,構(gòu)建相似性權(quán)重模板。這是與雙邊濾波唯一的不同之處,雙邊濾波是根據(jù)原圖,對(duì)于每一個(gè)位置, 通過(guò)該位置和其鄰域的灰度值的差的指數(shù)來(lái)估計(jì)相似性;而聯(lián)合雙邊濾波是首先對(duì)原圖進(jìn)行高斯平滑,根據(jù)平滑的結(jié)果,用當(dāng)前位置及其鄰域的值的差來(lái)估計(jì)相似性權(quán)重模板。
- 第三步,空間距離權(quán)重模板和相似性權(quán)重模板點(diǎn)乘,然后歸一化,作為最后的權(quán)重模板。
- 第四步將權(quán)重模板與原圖(注意不是高斯平滑的結(jié)果)在該位置的鄰域?qū)?yīng)位置積 的和作為輸出值。
整個(gè)過(guò)程只在第二步計(jì)算相似性權(quán)重模板時(shí)和雙邊濾波不同, 但是對(duì)圖像平滑的效果, 特別是對(duì)紋理圖像來(lái)說(shuō), 卻有很大的不同。
擴(kuò)展
循環(huán)引導(dǎo)濾波 是一種迭代的方法, 本質(zhì)上是一種多次迭代的聯(lián)合雙邊濾波, 只是每次計(jì)算相似性權(quán)重 模板的依據(jù)不一樣——利用本次計(jì)算的聯(lián)合雙邊濾波結(jié)果作為下一次聯(lián)合雙邊濾波計(jì)算 相似性權(quán)重模板的依據(jù)。
5.7、導(dǎo)向?yàn)V波
導(dǎo)向?yàn)V波在平滑圖像的基礎(chǔ)上,有良好的保邊作用, 而且在細(xì)節(jié)增強(qiáng)等方面都有良好的表現(xiàn),在執(zhí)行時(shí)間上也比雙邊濾波快很多。

