看到的大神写到的博客,非常详细,大部分信息整理自以下博客:
1. Gabor滤波器详细介绍:http://mplab.ucsd.edu/tutorials/gabor.pdf
2. Gabor滤波器学习:http://blog.csdn.net/jinshengtao/article/details/17797641
3. 国外大学对Gabor滤波器的参数的讲解:http://matlabserver.cs.rug.nl/edgedetectionweb/web/edgedetection_params.html
4. 备份论文,在校外没法下载:http://www.researchgate.net/publication/252703921_Designing_multiple_Gabor_filters_for_multitexture_image_segmentation
一、Gabor Filter
一维Gabor滤波器
Gabor滤波器是处理一维信号(比如音频)最佳的带通滤波器,一个复杂的Gabor滤波器是一个高斯核函数乘以一个复杂的sin函数(A complex Gabor filter is defined as the product of a Gaussian kernel times a complex sinusoid),比如:
k theta fo是滤波器的参数。我们可以把这个复杂的Gabor滤波器想象成两个相位滤波器分成了一个复杂函数的实部和虚部,
二维Gabor滤波器
但是我要用到的是二维Gabor滤波器,也叫空间Gabor滤波器(The Spatial (2-D) Gabor Filter),但是第一条参考中给出的二维Gabor滤波器讲解太过于。。怎么说。
按照普通的解释来吧,二维Gabor函数的数学表达式:
如何得到的这个公式呢?大神博客(http://blog.csdn.net/yanmy2012/article/details/8090400)中给出的详细求解过程,截图如下:
根据第三条参考文献的解释,关于实部和虚部的说明,只有一句话,实部可以对图像进行平滑滤波,虚部可以用来边缘检测,具体的用法可以可以参考相关论文,不在学校下论文不方便,暂且写这点吧。
下面具体看一下Gabor滤波器的参数说明:
二、Gabor滤波器的应用和适应性
根据第二条参考文献给出的说明,可以总结以下几点:
1. Gabor滤波器可以很好的近似单细胞的感受野细胞(光强刺激下的传递函数),在提取目标的局部空间和频率域信息方面具有良好的特性。
2. 虽然Gabor小波本身不能构成正交基,但在特定参数下可构成紧框架。Gabor小波对于图像的边缘敏感,能够提供良好的方向选择和尺度选择特性,而且对于光照变化不敏感,能够提供对光照变化良好的适应性。-------Gabor小波被广泛应用于视觉信息理解
3. 二维Gabor小波变换是在时频域进行信号分析处理的重要工具,其变换系数有着良好的视觉特性和生物学背景。------因此被广泛应用于图像处理、模式识别等领域。
4. 与传统的傅立叶变换相比,Gabor小波变换具有良好的时频局部化特性。即非常容易地调整Gabor滤波器的方向、基频带宽及中心频率从而能够最好的兼顾信号在时空域和频域中的分辨能力.
5. Gabor小波变换具有多分辨率特性即变焦能力。即采用多通道滤波技术,将一组具有不同时频域特性的Gabor小波应用于图像变换,每个通道都能够得到输入图像的某种局部特性,这样可以根据需要在不同粗细粒度上分析图像。
6. 在特征提取方面,Gabor小波变换与其它方法相比:一方面其处理的数据量较少,能满足系统的实时性要求;另一方面,小波变换对光照变化不敏感,且能容忍一定程度的图像旋转和变形,当采用基于欧氏距离进行识别时,特征模式与待测特征不需要严格的对应,故能提高系统的鲁棒性。
为什么能够有这些应用呢?我们需要深入的理解下Gabor的特性。根据大神博客(http://blog.csdn.net/yanmy2012/article/details/8090400)中的详细解释。在基本视觉皮层里的简单细胞的感受野局限在很小的空域范围内,并且高度结构化。
1. Gabor变换所采用的核(Kernels)与哺乳动物视觉皮层简单细胞2D感受野剖面(Profile)非常相似,具有优良的空间局部性和方向选择性,能够抓住图像局部区域内多个方向的空间频率(尺度)和局部性结构特征。这样,Gabor分解可以看作一个对方向和尺度敏感的有方向性的显微镜。
2. 二维Gabor函数也类似于增强边缘以及峰、谷、脊轮廓等底层图像特征,这相当于增强了被认为是面部关键部件的眼睛、鼻子、嘴巴等信息,同时也增强了诸于黑痣、酒窝、伤疤等局部特征,从而使得在保留总体人脸信息的同时增强局部特性成为可能。它的小波特性说明了Gabor滤波结果是描述图像局部灰度分布的有力工具,因此,可以使用Gabor滤波来抽取图像的纹理信息。
3. 由于Gabor特征具有良好的空间局部性和方向选择性,而且对光照、姿态具有一定的鲁棒性,因此在人脸识别中获得了成功的应用。然而,大部分基于Gabor特征的人脸识别算法中,只应用了Gabor幅值信息,而没有应用相位信息,主要原因是Gabor相位信息随着空间位置呈周期性变化,而幅值的变化相对平滑而稳定,幅值反映了图像的能量谱,Gabor幅值特征通常称为Gabor 能量特征(Gabor Energy Features)。Gabor小波可像放大镜一样放大灰度的变化,人脸的一些关键功能区域(眼睛、鼻子、嘴、眉毛等)的局部特征被强化,从而有利于区分不同的人脸图像。
4. Gabor小波核函数具有与哺育动物大脑皮层简单细胞的二维反射区相同的特性,即具有较强的空间位置和方向选择性,并且能够捕捉对应于空间和频率的局部结构信息;Gabor滤波器对于图像的亮度和对比度变化以及人脸姿态变化具有较强的健壮性,并且它表达的是对人脸识别最为有用的局部特征。Gabor 小波是对高级脊椎动物视觉皮层中的神经元的良好逼近,是时域和频域精确度的一种折中。
三、Gabor滤波器的编程实现
这里我用opencv做的,因为对matlab不是很熟,二是工业应用一般都是C或C++做的,代码给出吧,我用mfc做的界面,之前有好几个版本,验证了几个算法,最后这个就是为了看效果的,比较简单。因为用的opencv3.0,所以显示图像时遇到了麻烦,查找相关资料,解决办法是自己建立CvvImage.h CvvImage.cpp文件,添加到工程中即可。还有一个要说明的是,Gabor滤波器的实现,是用到的网上搜到的Gabor.h Gabor.cpp实现的,只是进行了简单的注释和文档整理。原作者。。。额,sorry,找的资料太多,不知道出处了,见谅啊!
Gabor.h头文件
- #ifndef _GABOR_H
- #define _GABOR_H
- #include
- #include
- #include
-
- #define PI 3.14159
- #define GAMMA 0.5 //The default value of γ,which is the spatial aspect ratio (sigma_x/sigma_y)
- #define RATIO_S2L 0.56 //The default value of σ/λ
- #define THETA 45
- class Gabor
- {
- public:
-
- Gabor(float dLambda, float dTheta, float dRatio_S2L = RATIO_S2L, float dGamma = GAMMA, float dPhi = 0);
-
- ~Gabor();
-
-
- void init(float dLambda, float dTheta, float dPhi, float dGamma = GAMMA);
-
- void init(float dSigma, float dTheta, float dPhi);
-
- void init();
-
-
-
- bool is_kernel(){ return bKernel; }
-
- bool is_init() { return bInit; }
-
- bool is_param() { return bParam; }
-
- CvMat* get_Mat() { return pGaborfilter; }
-
- IplImage* get_NormImage();
-
- IplImage* do_Filter(const IplImage *src);
-
- protected:
- bool bParam;
- bool bKernel;
- bool bInit;
- float Lambda;
- float Theta;
- float Sigma;
- float Gamma;
- float Phi;
- CvSize GaborWindow;
- CvMat *pGaborfilter;
-
- private:
- void create_kernel();
- };
- #endif
Gabor.cpp源文件
- #include "stdafx.h"
- #include
- #include
- #include
- #include
- #include "Gabor.h"
-
-
- Gabor::Gabor(float dLambda, float dTheta, float dRatio_S2L, float dGamma, float dPhi)
- {
- Lambda = dLambda;
- Theta = dTheta;
- Sigma = dLambda*dRatio_S2L;
- Gamma = dGamma;
- Phi = dPhi;
- pGaborfilter = NULL;
- bParam = 1;
- }
-
- Gabor::~Gabor()
- {
- cvReleaseMat(&pGaborfilter);
- }
- void Gabor::init()
- {
- float dtmp;
- int itmp;
- if (is_param() == 0)
- {
- AfxMessageBox("The parameters are not enough!");
- return;
- }
-
-
- dtmp = sqrt(48 * pow(Sigma, 2) + 1);
- itmp = cvRound(dtmp);
- if (itmp % 2 == 0)
- itmp++;
- GaborWindow.height = GaborWindow.width = itmp;
- bInit = 1;
-
- create_kernel();
- }
-
- void Gabor::init(float dSigma, float dTheta, float dPhi)
- {
- float dtmp;
- int itmp;
-
- Sigma = dSigma;
- Theta = dTheta;
- Phi = dPhi;
- Gamma = GAMMA;
- Lambda = Sigma / RATIO_S2L;
- bParam = 1;
-
- dtmp = sqrt(24 * pow(Sigma, 2));
- itmp = cvRound(dtmp);
- if (itmp % 2 == 0)
- itmp++;
- GaborWindow.height = GaborWindow.width = itmp;
- bInit = 1;
-
- create_kernel();
- }
-
- void Gabor::init(float dLambda, float dTheta, float dPhi, float dGamma)
- {
-
- float dtmp;
- int itmp;
-
- Lambda = dLambda;
- Theta = dTheta;
- Phi = dPhi;
- Gamma = dGamma;
- Sigma = Lambda * RATIO_S2L;
- bParam = 1;
-
- dtmp = sqrt(24 * pow(Sigma, 2));
- itmp = cvRound(dtmp);
- if (itmp % 2 == 0)
- itmp++;
- GaborWindow.height = GaborWindow.width = itmp;
- bInit = 1;
-
- create_kernel();
- }
-
-
- void Gabor::create_kernel()
- {
- float tmp1, tmp2, xtmp, ytmp, re;
- int i, j, x, y;
-
- if (is_init() == 0)
- {
- AfxMessageBox("The paremeters haven't been initialed!");
- }
-
- pGaborfilter = cvCreateMat(GaborWindow.height, GaborWindow.width, CV_32FC1);
- for (i = 0; i < GaborWindow.height; i++)
- {
- for (j = 0; j < GaborWindow.width; j++)
- {
- x = j - GaborWindow.width / 2;
- y = i - GaborWindow.height / 2;
-
-
-
-
- xtmp = (float)x*cos(Theta) + (float)y*sin(Theta);
- ytmp = -(float)x*sin(Theta) + (float)y*cos(Theta);
-
- tmp1 = exp(-(pow(xtmp, 2) + pow(ytmp*Gamma, 2)) / (2 * pow(Sigma, 2)));
- tmp2 = cos(2 * PI*xtmp / Lambda + Phi);
-
- re = tmp1*tmp2;
- cvSetReal2D((CvMat*)pGaborfilter, i, j, re);
- }
- }
-
- bKernel = 1;
- }
-
- IplImage* Gabor::get_NormImage()
- {
- if (is_kernel() == 0)
- {
- AfxMessageBox("The filter hasn't bee created!");
- return NULL;
- }
-
- IplImage *pImg = cvCreateImage(GaborWindow, IPL_DEPTH_32F, 1);
- IplImage *pImgU8 = cvCreateImage(GaborWindow, IPL_DEPTH_8U, 1);
- CvMat * pMat = cvCreateMat(GaborWindow.height, GaborWindow.width, CV_32FC1);
-
- cvCopy(pGaborfilter, pImg);
-
-
- cvNormalize((IplImage*)pImg, (IplImage*)pImg, 0, 255, CV_MINMAX, NULL);
-
- cvConvertScaleAbs(pImg, pImgU8, 1, 0);
-
- return pImgU8;
-
- }
-
- IplImage * Gabor::do_Filter(const IplImage *src)
- {
-
- if (is_kernel() == false)
- {
- printf("The Gabor Kernel has not been created!");
- return NULL;
- }
-
- IplImage *pDestImage = cvCreateImage(cvSize(src->width, src->height), IPL_DEPTH_8U, 1);
- IplImage *tmpImg = cvCloneImage(src);
- IplImage *tmpGrayImg = cvCreateImage(cvSize(src->width, src->height), IPL_DEPTH_8U, 1);
-
-
- if (tmpImg->nChannels != 1)
- {
- cvCvtColor(tmpImg, tmpGrayImg, CV_BGR2GRAY);
- }
- else
- {
- cvReleaseImage(&tmpGrayImg);
- tmpGrayImg = tmpImg;
- }
-
- CvMat *pGaborKernel = get_Mat();
-
- cvFilter2D(tmpGrayImg, pDestImage, pGaborKernel, cvPoint((GaborWindow.width - 1) / 2, (GaborWindow.height - 1) / 2));
-
- cvReleaseImage(&tmpImg);
- cvReleaseImage(&tmpGrayImg);
-
- return pDestImage;
- }
对话框Dlg.cpp,这里只给出了实现按钮的实现函数代码,完整的工程代码,看文章最后的附录:
- void CTyreXDlg::OnBnClickedBtndstimg()
- {
-
- UpdateData(TRUE);
-
- CSliderCtrl *pSlidCtrlTheta = (CSliderCtrl*)GetDlgItem(IDC_SLIDER_THETA);
- int tmptheta = pSlidCtrlTheta->GetPos();
-
- CString sValueTheta = "";
- sValueTheta.Format("%d", tmptheta);
- SetDlgItemText(IDC_StaticTheta, sValueTheta);
-
-
- CSliderCtrl *pSlidCtrlLambda = (CSliderCtrl*)GetDlgItem(IDC_SLIDER_LAMBDA);
- int tmplambda = pSlidCtrlLambda->GetPos();
-
- CString sValueLambda = "";
- sValueLambda.Format("%d", tmplambda);
- SetDlgItemText(IDC_StaticLambda, sValueLambda);
-
-
- if (SrcImg == NULL)
- {
- AfxMessageBox("没有可处理图像!!!");
- return;
- }
-
-
-
-
-
- Gabor GaborFilter(tmplambda *0.1, PI*tmptheta / 180, RATIO_S2L, GAMMA, 0);
-
- GaborFilter.init();
-
- IplImage* pGaborNormImg = GaborFilter.get_NormImage();
-
- IplImage* poutGaborimg;
- poutGaborimg = GaborFilter.do_Filter(SrcImg);
-
-
-
- IplImage *pGrayImage;
-
- pGrayImage = cvCreateImage(cvGetSize(poutGaborimg), IPL_DEPTH_8U, 1);
-
- if (poutGaborimg->nChannels != 1)
- cvCvtColor(poutGaborimg, pGrayImage, CV_BGR2GRAY);
- else
- {
- cvReleaseImage(&pGrayImage);
- pGrayImage = poutGaborimg;
- }
-
-
- IplImage *pBinaryImage;
- pBinaryImage = cvCreateImage(cvGetSize(pGrayImage), IPL_DEPTH_8U, 1);
- cvThreshold(pGrayImage, pBinaryImage, 0, 255, CV_THRESH_BINARY);
-
-
- ShowImgFunc(pBinaryImage, IDC_ShowDstImg);
-
-
-
- cvReleaseImage(&DstImg);
- cvReleaseImage(&DstGaborImg);
- cvReleaseImage(&DstBinaryImg);
- }
附录:
1. 上面代码的完整的工程文件,可以到这里下载,不需要积分。我运行成功,但是出现错误,或者有不合理的地方,希望各位看到能告诉我一声,共同进步。
工程文件完整下载地址:http://download.csdn.net/detail/jorg_zhao/8949247