在進行圖像目標識別與跟蹤時,攝像機所採集的圖像,在成像、數字化以及傳輸過程中,難免會受到各種各樣噪聲的干擾,圖像的質量往往會出現不盡人意的退化,影響了圖像的視覺效果。通常這些噪聲干擾使得圖像退化,表現爲圖像模糊,特徵淹沒,這會對圖像分析產生不利,使所獲得的圖像質量較低。對這樣的圖像直接進行目標的識別與跟蹤是比較困難的。抑制使圖像退化的各種干擾信號、增強圖像中的有用信號,以及將觀測到的不同圖像在同一約束條件下進行校正處理就顯得非常重要。本文主要對圖像預處理過程中的濾波方法進行總結,闡述濾波原理,在VC下基於OpenCV實現各種算法,並給出各種濾波的效果。
1、高斯濾波
高斯濾波是一種線性平滑濾波,適用於濾除高斯白噪聲,已廣泛應用於圖像處理的預處理階段。按照本人的理解,對圖像進行高斯濾波就是對圖像中的每個點的像素值計算,計算的準則是,由該點本身灰度值以及其鄰域內的其他像素灰度值加權平均所得,而加權平均的權係數由二維離散高斯函數採樣並歸一化後所得。具體的高斯函數及其離散化參考《高斯圖像濾波原理及其編程離散化實現方法》一文。以下給出圖像高斯濾波的實現代碼。
- void GuassFilter(CvMat *pGrayMat, CvMat* pFilterMat, int nWidth, int nHeight, double dSigma)
- {
- ////////////////////////參數說明///////////////////////////////////
- //pGrayMat:待處理圖像數組
- //pFilterMat:保存高斯濾波結果
- //nWidth:圖像寬度
- //nHeight:圖像高度
- //dSigma:高斯濾波參數,方差
- int nWidowSize = (int)(1+2*ceil(3*dSigma)); //定義濾波窗口的大小
- int nCenter = (nWidowSize)/2; //定義濾波窗口中心的索引
- //生成二維的高斯濾波係數
- double* pdKernal = new double[nWidowSize*nWidowSize]; //定義一維高斯核數組
- double dSum = 0.0; //求和,進行歸一化
- /////////////二維高斯函數公式//////////////////////
- // x*x+y*y ///////
- // -1*-------------- ///////
- // 1 2*Sigma*Sigma ///////
- // ---------------- e ///////
- // 2*pi*Sigma*Sigma ///////
- ///////////////////////////////////////////////////
- for(int i=0; i<nWidowSize; i++)
- {
- for(int j=0; j<nWidowSize; j++)
- {
- int nDis_x = i-nCenter;
- int nDis_y = j-nCenter;
- pdKernal[i+j*nWidowSize]=exp(-(1/2)*(nDis_x*nDis_x+nDis_y*nDis_y)
- /(dSigma*dSigma))/(2*3.1415926*dSigma*dSigma);
- dSum += pdKernal[i+j*nWidowSize];
- }
- }
- //進行歸一化
- for(i=0; i<nWidowSize; i++)
- {
- for(int j=0; j<nWidowSize; j++)
- {
- pdKernal[i+j*nWidowSize] /= dSum;
- }
- }
- for(i=0; i<nHeight; i++)
- {
- for(int j=0; j<nWidth; j++)
- {
- double dFilter=0.0;
- double dSum = 0.0;
- for(int x=(-nCenter); x<=nCenter; x++) //行
- {
- for(int y=(-nCenter); y<=nCenter; y++) //列
- {
- if( (j+x)>=0 && (j+x)<nWidth && (i+y)>=0 && (i+y)<nHeight) //判斷邊緣
- {
- double ImageData = cvmGet(pGrayMat ,i+y, j+x);
- dFilter += ImageData * pdKernal[(y+nCenter)*nWidowSize+(x+nCenter)];
- dSum += pdKernal[(y+nCenter)*nWidowSize+(x+nCenter)];
- }
- }
- }
- cvmSet(pFilterMat, i, j, dFilter/dSum);
- }
- }
- delete[]pdKernal;
- }
2、均值濾波
均值濾波也稱爲線性濾波,其採用的主要方法爲鄰域平均法。線性濾波的基本原理是用均值代替原圖像中的各個像素值,即對待處理的當前像素點(x,y),選擇一個模板,該模板由其近鄰的若干像素組成,求模板中所有像素的均值,再把該均值賦予當前像素點(x,y),作爲處理後圖像在該點上的灰度值g(x,y),即g(x,y)=1/m ∑f(x,y), m爲該模板中包含當前像素在內的像素總個數。這樣的方法可以平滑圖像,速度快,算法簡單。但是無法去掉噪聲,這能微弱的減弱它。其實現代碼如下。
- void MeanFilter(CvMat *pGrayMat, CvMat *pFilterMat, int nWidth, int nHeight, int nWindows)
- {
- ////////////////////////參數說明///////////////////////////////////
- //pGrayMat:待處理圖像數組
- //pFilterMat:保存高斯濾波結果
- //nWidth:圖像寬度
- //nHeight:圖像高度
- //nWindows:濾波窗口大小
- if((nWindows%2) == 0)
- {
- MessageBox("此函數必須設置鄰域爲奇數矩陣");
- return;
- }
- int nNumData = nWindows/2;
- if((nWindows>nHeight) && (nWindows>nWidth))
- {
- MessageBox("濾波窗口超出許可範圍,請檢查!");
- return;
- }
- for(int i=nNumData; i<(nHeight-nNumData); i++)
- {
- for(int j=nNumData; j<(nWidth-nNumData); j++)
- {
- double dData = 0.0;
- int nNum = 0;
- for(int m=-nNumData; m<=nNumData; m++)
- {
- for(int n=-nNumData; n<=nNumData; n++)
- {
- if((m!=0) && (n!=0))
- {
- dData += cvmGet(pGrayMat ,i+m, j+n);
- nNum++;
- }
- }
- }
- dData /= nNum;
- cvmSet(pFilterMat, i, j, dData);
- }
- }
- }
3、中值濾波
中值濾波法是一種非線性平滑技術,它將每一像素點的灰度值設置爲該點某鄰域窗口內的所有像素點灰度值的中值。其實現過程爲:
1)通過從圖像中的某個採樣窗口取出奇數個數據進行排序
2)用排序後的中值作爲當前像素點的灰度值
在圖像處理中,中值濾波常用來保護邊緣信息,是經典的平滑噪聲的方法,該方法法對消除椒鹽噪音非常有效,在光學測量條紋圖象的相位分析處理方法中有特殊作用,但在條紋中心分析方法中作用不大。其實現代碼如下:
- Void MedianFilter(CvMat *pGrayMat, CvMat *pFilterMat, int nWidth, int nHeight, int nWindows)
- {
- ////////////////////////參數說明///////////////////////////////////
- //pGrayMat:待處理圖像數組
- //pFilterMat:保存高斯濾波結果
- //nWidth:圖像寬度
- //nHeight:圖像高度
- //nWindows:濾波窗口大小
- if((nWindows%2) == 0)
- {
- MessageBox("此函數必須設置鄰域爲奇數矩陣");
- return;
- }
- int nNumData = nWindows/2;
- unsigned char* nData = new unsigned char[nWindows*nWindows-1]; //保存鄰域中的數據
- if((nWindows>nHeight) && (nWindows>nWidth))
- {
- MessageBox("濾波窗口超出許可範圍,請檢查!");
- return;
- }
- for(int i=nNumData; i<(nHeight-nNumData); i++)
- {
- for(int j=nNumData; j<(nWidth-nNumData); j++)
- {
- int nIndex = 0;
- for(int m=-nNumData; m<=nNumData; m++)
- {
- for(int n=-nNumData; n<=nNumData; n++)
- {
- if((m!=0) && (n!=0))
- nData[nIndex++] = (unsigned char)cvmGet(pGrayMat, i+m, j+n);
- }
- }
- InsertSort(nData, nIndex); //排序
- unsigned char nMedium = 0;
- if(nIndex%2==0)
- nMedium = (unsigned char)((nData[(nIndex-1)/2] + nData[(nIndex+1)/2])/2);
- else
- nMedium = nData[nIndex/2];
- cvmSet(pFilterMat, i, j, nMedium);
- }
- }
- delete[]nData;
- }
以上代碼中用到了排序函數InsertSort(unsigned char a[], int count),關於具體的代碼實現以及其他排序算法,請參考博文《數組排序方法及C實現的總結》。
4、雙邊濾波(Bilateral filter)
雙邊濾波是一種可以保邊去噪的濾波器。之所以可以達到這樣的效果,是因爲該濾波器是由兩個函數構成,一個函數是由幾何空間距離決定濾波器係數,另外一個由像素差決定濾波器係數。
在前面幾種講述的濾波方法中,像素點的灰度值均是由該點鄰域內其他點的灰度值決定的,比如高斯濾波和均值濾波都可看作是加權平均,中值濾波取的是鄰域灰度中值。雙邊濾波則不但考慮鄰域範圍內點的灰度值,同樣考慮這些點距離中心點的幾何距離,這樣可以得到濾波後的點的灰度值表達公式爲:
其中k爲歸一化係數,其表達式爲:
h和x分別爲濾波後和濾波前對應點的灰度值;
c表示中心點與其鄰域內點的空間相似度;
s表示中心點與其鄰域內點的灰度相似度。
在實現過程中,c和s函數均可用高斯函數實現,即其定義如下:
可根據以上原理實現雙邊濾波如下。
- Void BilateralFilter(CvMat *pGrayMat, CvMat *pFilterMat, int nWidth, int nHeight, double dSigma1, double dSigma2, int nWindows)
- {
- ////////////////////////參數說明///////////////////////////////////
- //pGrayMat:待處理圖像數組
- //pFilterMat:保存高斯濾波結果
- //nWidth:圖像寬度
- //nHeight:圖像高度
- //dSigma1、dSigma2:分別爲幾何與灰度相關高斯函數的方差
- double* dDistance = new double[nWindows*nWindows]; //計算距離中間點的幾何距離
- double* dGrayDiff = new double[nWindows*nWindows]; //定義中心點到當前點的灰度差
- for(int i=0; i<nWindows*nWindows; i++)
- {
- int nNumX = i/nWindows;
- int nNumY = i%nWindows;
- dDistance[i] = ((nWindows-1)/2-nNumX)*((nWindows-1)/2-nNumX)+((nWindows-1)/2-nNumY)*((nWindows-1)/2-nNumY);
- dDistance[i] = exp(-0.5*dDistance[i]/dSigma1/dSigma1); //C參數
- }
- //以下求解灰度值的差
- for(i=0; i<nHeight; i++)
- {
- for(int j=0; j<nWidth; j++)
- {
- double dGray = cvmGet(pGrayMat, i, j); //當前點的灰度值
- double dData = 0.0;
- double dTotal = 0.0; //用於進行歸一化
- for(int m=0; m<nWindows*nWindows; m++)
- {
- int nNumX = m/nWindows; //行索引
- int nNumY = m%nWindows; //列索引
- int nX = i-(nWindows-1)/2+nNumX;
- int nY = j-(nWindows-1)/2+nNumY;
- if( (nY>=0) && (nY<nWidth) && (nX>=0) && (nX<nHeight))
- {
- double dGray1 = cvmGet(pGrayMat, nX, nY);
- dGrayDiff[m] = fabs(dGray-dGray1);
- dGrayDiff[m] = exp(-0.5*dGrayDiff[m]*dGrayDiff[m]/dSigma2/dSigma2); //S參數
- if(m!=4)
- {
- dData += dGray1 * dGrayDiff[m] * dDistance[m]; //C和S參數綜合
- dTotal += dGrayDiff[m]*dDistance[m]; //加權係數求和,進行歸一化
- }
- }
- }
- dData /=dTotal;
- cvmSet(pFilterMat, i, j, dData);
- }
- }
- delete[]dDistance;
- delete[]dGrayDiff;
- }
5、結果對比
以下對幾種濾波方法的結果進行展示對比。
1)待處理的彩色圖片
2)灰度化後的圖像(灰度化實現詳見《圖像灰度化方法總結及其VC實現》)
3)高斯濾波(Sigma=0.4)
4)均值濾波(濾波窗口=3)
5)中值濾波結果(濾波窗口=3)
6)雙邊濾波結果(濾波窗口=3,距離Sigma=10, 灰度Sigma=300)
根據以上幾幅圖,可以看出高斯濾波和均值濾波模糊了邊界,而中值濾波和雙邊濾波則能夠較好的保存圖像的邊界信息。
另外,作者最近正在思考每種方法中所對應參數的設置策略,能否有根據圖像某種特性而變化的自適應的參數,目前所有的參數都是根據經驗進行設置的,因爲整個過程並沒有反饋信息。因此,後續還需要研究各種不同參數的設置方法,以最大限度的增大信噪比,同時還能夠爲目標識別提供良好的邊緣信息。