魔幻濾鏡Gabor Filter 原理與實現(python & C++)

Gabor Filter

以下定義翻譯引用自維基百科:

在圖像處理領域,以Dennis Gabor命名的Gabor濾波器,是一種用於紋理分析的線性濾波器,即它主要分析的是,圖像在某一特定區域的特定方向上是否有特定的頻率內容。當代許多視覺科學家認爲,Gabor濾波器的頻率和方向的表達與人類的視覺系統很相似,儘管並沒有實驗性證據和函數原理能證明這一觀點。研究發現,Gabor濾波器特別適合於紋理表示和辨別。在空間域中,2D Gabor濾波器是由正弦平面波調製的高斯核函數。

其脈衝響應定義爲定義爲一個正弦波(對於二維Gabor濾波器是平面波)乘以高斯函數。因爲乘法卷積性質(卷積定理),由於乘法卷積性質,Gabor濾波器的脈衝響應的傅立葉變換是調和函數的傅立葉變換和高斯函數傅立葉變換的卷積。該濾波器具有表示正交方向的實部和虛部。兩個分量可以構成複數或單獨使用。

  • 複數形式
    這裏寫圖片描述
  • 實部
    這裏寫圖片描述

  • 虛部
    這裏寫圖片描述

其中
這裏寫圖片描述

如果你瞭解傅立葉變換的基礎,那麼你應該知道,圖像可以視爲各個方向的一系列不同頻率的正弦波疊加而成。變換中的“像素”表示波的強度,“像素”的位置表示波的頻率和方向。 在實踐中,人們只想選擇特定頻率和特定方向的特定波。Gabor變換是許多所謂的帶通濾波器之一,它允許你“切割”傅立葉變換,只隔離特定的信息。另一個重要信息是,每個傅立葉“像素”是一個複數值(包含實部和虛部)。

二維Gabor核函數由一個高斯包絡函數和一個餘弦函數相乘得出,其中θ,ϕ,γ,λ,σ爲參數。

這裏寫圖片描述

A complex Gabor filter is defined as the product of a Gaussian kernel times a complex sinusoid.

Gaussian envelope mentiond in this method determines the width of the Gaussian kernel.

Paper— Fingerprint Image Enhancement: Algorithm and Performance Evaluation

需要注意的是,(x,y)是原始座標,(x’,y’)是旋轉後的座標。在(x,y)座標系中,Gabor只能沿x軸或y軸拉伸,但不能對角拉伸。但是上述最後兩個公式允許Gabor能夠在(x’,y’)座標系中沿任何方向被拉伸(由θ定義)。

Python實現

維基百科給出了Gabor函數的Python實現。放在下面供參考。

import numpy as np

def gabor_fn(sigma, theta, Lambda, psi, gamma):
    sigma_x = sigma
    sigma_y = float(sigma) / gamma

    # ------這部分內容是爲了確定卷積核的大小------
    # Bounding box
    nstds = 3 # Number of standard deviation sigma
    xmax = max(abs(nstds * sigma_x * np.cos(theta)), abs(nstds * sigma_y * np.sin(theta)))
    xmax = np.ceil(max(1, xmax))
    ymax = max(abs(nstds * sigma_x * np.sin(theta)), abs(nstds * sigma_y * np.cos(theta)))
    ymax = np.ceil(max(1, ymax))
    xmin = -xmax
    ymin = -ymax
    (y, x) = np.meshgrid(np.arange(ymin, ymax + 1), np.arange(xmin, xmax + 1))
    # ------這部分內容是爲了確定卷積核的大小------

    # Rotation 
    x_theta = x * np.cos(theta) + y * np.sin(theta)
    y_theta = -x * np.sin(theta) + y * np.cos(theta)

    # ------這部分正是上面的公式(1)------
    gb = np.exp(-.5 * (x_theta ** 2 / sigma_x ** 2 + y_theta ** 2 / sigma_y ** 2)) * np.cos(2 * np.pi / Lambda * x_theta + psi)
    return gb

Gabor函數的OpenCV實現

跟其他濾波器(高斯,Sobel)等一樣,需要先確定一個卷積核,然後用次卷積核通過滑窗法對輸入圖像進行卷積運算,得到輸出圖像。Gabor函數的OpenCV實現getGaborKernel函數,正是爲了得到這個卷積核的。爲在OpenCV中的getGaborKernel函數裏需要傳入的參數除了上述5個外,還需要傳入卷積核的大小。

cv::Mat getGaborKernel(Size ksize, double sigma, double theta, double lambd, double gamma, double psi=CV_PI*0.5, int ktype=CV_64F );
cv2.getGaborKernel(ksize, sigma, theta, lambda, gamma, psi, ktype)

各參數含義

ksize:返回的濾波器的大小,即Gabor核的大小。如果ksize=(a,b),那麼Gabor核的大小即爲axb像素。與許多其他的卷積核一樣,這個尺寸一般取奇數而且爲正方形。

sigmaσ是Gabor過濾器中實用的高斯函數的標準差。

thetaθ是Gabor函數的平行條紋的法線方向。有效值爲從0~360度的實數。

lambdaλ是上述方程中正弦因子的波長。有效值應大於2,以像素爲單位。

gammaγ是空間寬高比,表示Gabor濾波器的橢圓度

psiψ是相位偏移。有效值爲-180度~180度,0度和180度對應的方程與原點對稱,-90度和90度的方程分別於原點成中心對稱,可兩項直角座標系中的餘弦函數。

ktype:像素類型。

OpenCV的Gabor源碼

源碼在opencv\sources\modules\imgproc\src\gabor.cpp。仔細看上下兩種實現的話,可以明顯看出OpenCV的實現參考了維基百科的Python實現。只不過在加了一個確定卷積和大小的參數而已。而且在這個參數爲(0,0)的時候,採用的正是維基百科中的方法。

cv::Mat cv::getGaborKernel( Size ksize, double sigma, double theta,
                            double lambd, double gamma, double psi, int ktype )
{
    double sigma_x = sigma;
    double sigma_y = sigma/gamma;
    int nstds = 3;
    int xmin, xmax, ymin, ymax;
    double c = cos(theta), s = sin(theta);

    // ------這部分內容是爲了確定卷積核的大小------
    // ------可以看到,如果ksize不爲0,則取ksize的一半------
    if( ksize.width > 0 )
        xmax = ksize.width/2;
    // ------如果ksize爲0,則按照維基百科的方式來取值------
    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 );

    Mat kernel(ymax - ymin + 1, xmax - xmin + 1, ktype);
    double scale = 1;
    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;

            // ------此處正是上面的公式(1)------
            double v = scale*std::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;
}

參考

  1. https://en.wikipedia.org/wiki/Gabor_filter
  2. https://dsp.stackexchange.com/questions/14714/understanding-the-gabor-filter-function
  3. https://math.stackexchange.com/questions/259877/value-of-x-y-in-computing-gabor-filter-function
  4. https://cvtuts.wordpress.com/2014/04/27/gabor-filters-a-practical-overview/
  5. http://blog.csdn.net/xufangpang/article/details/6426244
  6. https://math.stackexchange.com/questions/177977/how-to-intuitively-interpret-gabor-lambda-param

公衆號CVPy,分享不僅限於OpenCV和Python的有意思的內容。

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章