1. 高斯濾波原理
根據數學知識,一維高斯函數可以描述為:
在圖像處理中,選定X方向上長度為3的窗口,令δ=1,中心坐標為1,由上述公式,其卷積核(Xa,X,Xb)可以如下計算:
Xa = exp(-1*(0-1)(0-1)/(2*1*1))= 0.606530659712633
X = exp(-1*(1-1)(1-1)/(2*1*1))= 1
Xb = exp(-1*(2-1)(2-1)/(2*1*1))= 0.606530659712633
可以看到計算過程沒有用到常數部分,是因為需要歸一化,常數部分可以省略:
Sum = Xa + X + Xb = 2.2130613194252668
Xa = Xa/Sum = 0.274068619061197
X = X/Sum = 0.451862761877606
Xb = Xb/Sum = 0.274068619061197
通過OpenCV驗證下上述結果是否正確,OpenCV可以通過函數getGaussianKernel()來實現計算高斯核,運行如下代碼,可以發現兩者的計算結果是一致的。
Mat kernelX = getGaussianKernel(3, 1);
cout << kernelX << endl;
2. 圖像二維卷積
上述的推導過程都是一維的,那么二維情況下的卷積核怎么計算呢,其實很簡單,轉置並相乘就可以了:
Mat kernelX = getGaussianKernel(3, 1);
cout << kernelX << endl;
Mat kernelY = getGaussianKernel(3, 1);
Mat G = kernelX * kernelY.t();
cout << G << endl << endl << endl;
運行結果:
在得到卷積核之后,將其放到圖像中進行二維卷積,對於原圖像中的一個像素P(x,y),有如下卷積過程:
將窗口覆蓋的對應位置的像素值相乘后相加,即可得到新圖像對應位置的像素值Q(x,y)。當對圖像所有的像素值都這樣做時,就可以得到濾波后的圖像。由於一般情況下總是順序去卷積的,從左至右,由上而下,所以這個過程就是卷積核的滑動。
當滑動到邊界的時候,就會產生一個問題,就是卷積核對應的位置沒有像素值。這時可以將邊界像素值舍棄(卷積),或者自動填充為0(濾波)。
3. 具體實現
在OpenCV中,可以直接使用GaussianBlur()函數實現高斯濾波,但是為了驗證和學習高斯濾波算法,也可以自己構建高斯卷積核,使用濾波函數filter2D()進行濾波。其具體實現如下:
#include <iostream>
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
//從文件中讀取成灰度圖像
const char* imagename = "C:\\Data\\imgDemo\\lena.jpg";
Mat img = imread(imagename, IMREAD_GRAYSCALE);
if (img.empty())
{
fprintf(stderr, "Can not load image %s\n", imagename);
return -1;
}
//直接高斯濾波
Mat dst1;
GaussianBlur(img, dst1, Size(3, 3), 1, 1);
//自定義高斯濾波器
Mat kernelX = getGaussianKernel(3, 1);
Mat kernelY = getGaussianKernel(3, 1);
Mat G = kernelX * kernelY.t();
Mat dst2;
filter2D(img, dst2, -1, G);
//比較兩者的結果
Mat c;
compare(dst1, dst2, c, CMP_EQ);
//
imshow("原始", img);
imshow("高斯濾波1", dst1);
imshow("高斯濾波2", dst2);
imshow("比較結果", c);
waitKey();
return 0;
}
可以看到這里分別用GaussianBlur()和filter2D()進行了高斯濾波,並通過compare()函數進行比較。運行結果如下所示,兩者的濾波結果基本一致,說明構建的卷積核是正確的。
4. 參考資料
1.OpenCV實現二維高斯核GaussianKernel
2.opencv3.2.0圖像處理之高斯濾波GaussianBlur API函數
3.OpenCV高斯濾波器詳解及代碼實現