Gabor滤波简介与Opencv中的实现及参数变化实验

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lhanchao/article/details/55006663

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滤波中使用了复数,计算量会比较大,而已经有很多应用只使用实数部分近似得到了不错的结果,所以可能因此这里使用了实数部分。

猜你喜欢

转载自blog.csdn.net/lhanchao/article/details/55006663