Gabor濾波簡介與Opencv中的實現及引數變化實驗
阿新 • • 發佈:2019-01-03
Gabor濾波是一種非常常見的特徵提取演算法,在人臉識別等領域有著很廣泛的應用,在這裡我主要介紹一下Gabor濾波器的公式及Opencv下的程式碼實現,以及我做的一些引數變化的實驗。
一、Gabor濾波簡介
注意,這裡我介紹的Gabor演算法與在人臉識別中使用的Gabor演算法貌似是不太相同的,具體內容我沒有深入瞭解。
Gabor濾波的公式如下所示:
這裡面的引數:
(1)
(2)
(3)
(4)
(5)
(6)
這裡的引數不瞭解不要緊,後面我將會針對這些引數進行一系列的實驗,直觀的展示出這些引數的作用。
二、Opencv中Gabor核函式的實現
在Opencv中的給出了實現核函式的程式碼,如下所示,這裡我將我的理解作為註釋加入到程式碼中:
//獲取Gabor的核函式,並以Mat的形式返回
//ksize表示核函式視窗大小,sigma,theta,lambd,gamma和psi分別為前面提到的引數
//ktype表示需要獲得的核函式矩陣的資料型別
cv::Mat cv::getGaborKernel( Size ksize, double sigma, double theta,
double lambd, double gamma, double psi, int ktype )
{
//獲取兩個數學遍歷sigma_x和sigma_y,方便後面的計算,沒有特殊含義
double sigma_x = sigma;
double sigma_y = sigma/gamma;
int nstds = 3;//為後面的當ksize出現負數時計算核函式視窗大小提供引數
int xmin, xmax, ymin, ymax;
double c = cos(theta), s = sin(theta);
//當ksize的width和height沒有負數時,取其一半給xmax和ymax
if( ksize.width > 0 )
xmax = ksize.width/2;
else
xmax = cvRound(std::max(fabs(nstds*sigma_x*c), fabs(nstds*sigma_y*s)));
if( ksize.height > 0 )
ymax = ksize.height/2;
else
ymax = cvRound(std::max(fabs(nstds*sigma_x*s), fabs(nstds*sigma_y*c)));
xmin = -xmax;
ymin = -ymax;
CV_Assert( ktype == CV_32F || ktype == CV_64F );
//核函式矩陣,ymax-ymin+1和xmax-xmin+1是為保證核函式矩陣是長和寬都是奇數
Mat kernel(ymax - ymin + 1, xmax - xmin + 1, ktype);
double scale = 1;
//方便計算,可以自己帶入到後面的v的計算公式中會發現Gabor濾波器的實數部分的公式
double ex = -0.5/(sigma_x*sigma_x);
double ey = -0.5/(sigma_y*sigma_y);
double cscale = CV_PI*2/lambd;
for( int y = ymin; y <= ymax; y++ )
for( int x = xmin; x <= xmax; x++ )
{
double xr = x*c + y*s;
double yr = -x*s + y*c;
//計算公式,Opencv這裡獲取的是Gabor濾波器的實數部分
double v = scale*exp(ex*xr*xr + ey*yr*yr)*cos(cscale*xr + psi);
if( ktype == CV_32F )
kernel.at<float>(ymax - y, xmax - x) = (float)v;
else
kernel.at<double>(ymax - y, xmax - x) = v;
}
return kernel;
}
這裡Opencv只是獲得了Gabor的實數部分,為何沒有虛數部分我暫時還不太清楚,如果有人清楚請留言指出,謝謝!
三、Gabor引數實驗
1、
//注意:為了演示方便這裡採用的核函式視窗大小比較大,一般情況下不會採用這麼大的核函式視窗
//另外,如要儲存核函式結果,記得先把影象歸一化到0~255以後再儲存,否則可能只有黑色
int main()
{
Mat kernel1 = getGaborKernel(Size(311, 311), 10, 0, 5, 0.5, 0, CV_32F);
Mat kernel2 = getGaborKernel(Size(311, 311), 10, 0, 10, 0.5, 0, CV_32F);
Mat kernel3 = getGaborKernel(Size(311, 311), 10, 0, 15, 0.5, 0, CV_32F);
Mat kernel4 = getGaborKernel(Size(311, 311), 10, 0, 20, 0.5, 0, CV_32F);
imshow("lambda: 5", kernel1);
imshow("lambda: 10", kernel2);
imshow("lambda: 15", kernel3);
imshow("lambda: 20", kernel4);
waitKey();
return 0;
}
可以看出波長越大,黑白相間的間隔越大
(2)
//注意:角度要轉換為弧度
int main()
{
Mat kernel1 = getGaborKernel(Size(311, 311), 10, 0, 10, 0.5, 0, CV_32F);
Mat kernel2 = getGaborKernel(Size(311, 311), 10, CV_PI * 0.25, 10, 0.5, 0, CV_32F);
Mat kernel3 = getGaborKernel(Size(311, 311), 10, CV_PI * 0.5, 10, 0.5, 0, CV_32F);
Mat kernel4 = getGaborKernel(Size(311, 311), 10, CV_PI * 0.75, 10, 0.5, 0, CV_32F);
imshow("theta: 0", kernel1);
imshow("theta: 45", kernel2);
imshow("theta: 90", kernel3);
imshow("theta: 135", kernel4);
waitKey();
return 0;
}
(3)
當
(4)
int main()
{
Mat kernel1 = getGaborKernel(Size(311, 311), 5, 0, 10, 0.5, 0, CV_32F);
Mat kernel2 = getGaborKernel(Size(311, 311), 10, 0, 10, 0.5, 0, CV_32F);
Mat kernel3 = getGaborKernel(Size(311, 311), 15, 0, 10, 0.5, 0, CV_32F);
Mat kernel4 = getGaborKernel(Size(311, 311), 20, 0, 10, 0.5, 0, CV_32F);
imshow("sigma: 5", kernel1);
imshow("sigma: 10", kernel2);
imshow("sigma: 15", kernel3);
imshow("sigma: 20", kernel4);
waitKey();
return 0;
}
實驗結果如下:
可以看出,隨著
(5)
int main()
{
Mat kernel1 = getGaborKernel(Size(311, 311), 10, 0, 10, 0.5, 0, CV_32F);
Mat kernel2 = getGaborKernel(Size(311, 311), 10, 0, 10, 1.0, 0, CV_32F);
Mat kernel3 = getGaborKernel(Size(311, 311), 10, 0, 10, 1.5, 0, CV_32F);
Mat kernel4 = getGaborKernel(Size(311, 311), 10, 0, 10, 2.0, 0, CV_32F);
imshow("gamma: 0.5", kernel1);
imshow("gamma: 1.0", kernel2);
imshow("gamma: 1.5", kernel3);
imshow("gamma: 2.0", kernel4);
waitKey();
return 0;
}
實驗結果如下:
可以看出,隨著
四、Gabor濾波的使用
再得到Gabor核函式以後,其使用方法就比較簡單了,其實就是使用該核函式視窗對影象進行卷積操作,參考小魏的修行路【影象處理】Gabor濾波器中使用Gabor濾波和影象歸一化的方法。
注:Opencv中Gabor濾波只使用實數部分,同時wiki中Gabor filter中的介紹和實現示例也同樣只使用了實數部分,《基於 Gabor 濾波器的特徵抽取技術》一文中提到,Gabor濾波中使用了複數,計算量會比較大,而已經有很多應用只使用實數部分近似得到了不錯的結果,所以可能因此這裡使用了實數部分。