1. 程式人生 > >Gabor濾波簡介與Opencv中的實現及引數變化實驗

Gabor濾波簡介與Opencv中的實現及引數變化實驗

Gabor濾波是一種非常常見的特徵提取演算法,在人臉識別等領域有著很廣泛的應用,在這裡我主要介紹一下Gabor濾波器的公式及Opencv下的程式碼實現,以及我做的一些引數變化的實驗。

一、Gabor濾波簡介

注意,這裡我介紹的Gabor演算法與在人臉識別中使用的Gabor演算法貌似是不太相同的,具體內容我沒有深入瞭解。
Gabor濾波的公式如下所示:

g(x,y;λ,θ,ψ,σ,γ)=exp(x2+γ2y22σ2)exp(i(2πxλ+ψ))其中實數部分為:g(x,y;λ,θ,ψ,σ,γ)=exp(x2+γ2y22σ2)cos(2πxλ+ψ)虛數部分為:g(x,y;λ,θ,
ψ,σ,γ)=exp(x2+γ2y22σ2)sin(2πxλ+ψ)
其中x=xcosθ+ysinθy=xsinθ+ycosθ
這裡面的引數:
(1)x,y分別表示畫素座標位置;
(2)λ表示濾波的波長;
(3)θ表示Gabor核函式影象的傾斜角度;
(4)ψ表示相位偏移量,取值範圍是-180~180;
(5)σ表示高斯函式的標準差;
(6)γ表示長寬比,決定這Gabor核函式影象的橢圓率。
這裡的引數不瞭解不要緊,後面我將會針對這些引數進行一系列的實驗,直觀的展示出這些引數的作用。

二、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)ψ的變化:

這裡寫圖片描述

ψ為0時以白條為中心,當ψ為180時,以黑條為中心
(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濾波中使用了複數,計算量會比較大,而已經有很多應用只使用實數部分近似得到了不錯的結果,所以可能因此這裡使用了實數部分。