Gabor滤波器学习
本文的目的是用C实现生成Gabor模版,并对图像卷积。并简单提一下,Gabor滤波器在纹理特征提取上的应用。
一、什么是Gabor函数(以下内容含部分翻译自维基百科)
在图像处理中,Gabor函数是一个用于边缘提取的线性滤波器。Gabor滤波器的频率和方向表达同人类视觉系统类似。研究发现,Gabor滤波器十分适合纹理表达和分离。在空间域中,一个二维Gabor滤波器是一个由正弦平面波调制的高斯核函数。
还有,生物学实验发现,Gabor滤波器可以很好地近似单细胞的感受野函数(光强刺激下的传递函数),什么视皮层内的超柱,bla...bla,总之是这方面仿生的数学模型。
另外,网上有一种说法,gabor分为实部和虚部,用实部进行滤波后图像会平滑;虚部滤波后用来检测边缘。【来自百度知道某个大神的回答】,我查了文献,发现的确有人用Gabor的奇函数部分做边缘提取(《基于Gabor滤波器的边缘检测算法》 无线电工程 2000年第3卷第30期)。另外,从我的实验结果也有类似的发现。暂且认为这个对的吧。
Gabor滤波器的脉冲响应,可以定义为一个正弦波(对于二维Gabor滤波器是正弦平面波)乘以高斯函数。由于乘法卷积性质,Gabor滤波器的脉冲响应的傅立叶变换是其调和函数的傅立叶变换和高斯函数傅立叶变换的卷积。该滤波器由实部和虚部组成,二者相互正交。一组不同频率不同方向的Gabor函数数组对于图像特征提取非常有用。
下面给出二维Gabor函数的数学表达:
复数表达:
实数部分:
虚数部分:
其中:
和
下面介绍公式中各个参数的含义,及参数如何配置问题【都从老外那翻译来的】:
波长(λ):它的值以像素为单位指定,通常大于等于2.但不能大于输入图像尺寸的五分之一。
方向(θ):这个参数指定了Gabor函数并行条纹的方向,它的取值为0到360度
相位偏移(φ):它的取值范围为-180度到180度。其中,0he180度分别对应中心对称的center-on函数和center-off函数,而-90度和90度对应反对称函数。
长宽比(γ):空间纵横比,决定了Gabor函数形状(support,我翻译为形状)的椭圆率(ellipticity)。当γ= 1时,形状是圆的。当γ< 1时,形状随着平行条纹方向而拉长。通常该值为0.5
带宽(b):Gabor滤波器的半响应空间频率带宽b和σ/ λ的比率有关,其中σ表示Gabor函数的高斯因子的标准差,如下:
σ的值不能直接设置,它仅随着带宽b变化。带宽值必须是正实数,通常为1,此时,标准差和波长的关系为:σ= 0.56 λ。带宽越小,标准差越大,Gabor形状越大,可见平行兴奋和抑制区条纹数量越多。
下面给出,不同参数配置下的Gabor核函数效果图,大小均100*100:
a.波长对比组【方向为:0,相位偏移量为:0,纵横比率为:0.5,带宽为:1,下图波长分别为5,10,15】
b.方向对比组【波长为:10,相位偏移量为:0,空间纵横比为:0.5,带宽为:1,方向分别为:0,45,90】
c.相位偏移量对比组【波长为:10,方向为:0,空间纵横比:0.5,带宽:1,相位偏移量分别为:0,180,-90,90】
d.空间纵横比对比组【波长:10,相位偏移量:0,方向:0,带宽:1,空间纵横比分别为:0.5,1】
e.带宽对比组【波长:10,方向:0,相位偏移量:0,空间纵横比:0.5,带宽分别为:0.5,1,2】
二、gabor函数实现:
matlab版本,我从pudn上找来的,但他的gabor函数,我没怎么看明白:
gabor函数:
1 function gabor_k = compute(x,y,f0,theta)
2 r = 1; g = 1;
3 x1 = x*cos(theta) + y*sin(theta);
4 y1 = -x*sin(theta) + y*cos(theta);
5 gabor_k = f0^2/(pi*r*g)*exp(-(f0^2*x1^2/r^2+f0^2*y1^2/g^2))*exp(i*2*pi*f0*x1);
1 %绘制一个Gabor滤波器的空域和频域函数图
2 clear;
3 x = 0;
4 theta = 0;
5 f0 = 0.2;
6 for i = linspace(-15,15,50)
7 x = x + 1;
8 y = 0;
9 for j = linspace(-15,15,50)
10 y = y + 1;
11 z(y,x)=compute(i,j,f0,theta);
12 end
13 end
14 x = linspace(-15,15,50);
15 y = linspace(-15,15,50);
16 surf(x,y,real(z))
17 title('Gabor filter:real component');
18 xlabel('x');
19 ylabel('y');
20 zlabel('z');
21 figure(2);
22 surf(x,y,imag(z))
23 title('Gabor filter:imaginary component');
24 xlabel('x');
25 ylabel('y');
26 zlabel('z');
27
28 Z = fft2(z);
29 u = linspace(-0.5,0.5,50);
30 v = linspace(-0.5,0.5,50);
31 figure(3);
32 surf(u,v,abs(fftshift(Z)))
33 title('Gabor filter:frequency component');
34 xlabel('u');
35 ylabel('v');
36 zlabel('Z');
运行结果:
1 %4个方向的Gabo滤波器通过图像显示
2 clear;
3 x = 0;
4 theta = pi*3/4;%用弧度0,pi/4,pi/2,pi*3/4
5 f0 = 0.2;
6 for i = linspace(-15,15,50)
7 x = x + 1;
8 y = 0;
9 for j = linspace(-15,15,50)
10 y = y + 1;
11 z(y,x)=compute(i,j,f0,theta);
12 end
13 end
14 z_real = real(z);
15 m = min(z_real(:));
16 z_real = z_real+abs(m);
17 M = max(z_real(:));
18 imshow(1/M*z_real);
19 figure(2)
20 z_imag = imag(z);
21 m = min(z_imag(:));
22 z_imag = z_imag+abs(m);
23 M = max(z_imag(:));
24 imshow(1/M*z_imag);
运行效果:
实数部分:
虚数部分:
%4个方向的Gabor滤波器对lena进行滤波
clear;
I = imread('.\pic\lena.bmp');
f0 = 0.2;
count = ;
for theta = [,pi/,pi/,pi*/];%用弧度0,pi/,pi/,pi*/
count = count + ;
x = ;
for i = linspace(-,,)
x = x + ;
y = ;
for j = linspace(-,,)
y = y + ;
z(y,x)=compute(i,j,f0,theta);
end
end
figure(count);
filtered = filter2(z,I,'valid');
f = abs(filtered);
imshow(f/max(f(:)))
end
运行效果:
好吧,不管他了。大概感受一下吧。由于我没看明白他的gabor函数怎么定义的,参数设置也不一样,实验结果很不相同,我希望我是对的,天地良心呐!!我只能按照维基百科给出的函数,编写了以下C代码:
// my_gabor.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include<iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "math.h"
#define PI 3.1415926
#define N 4
using namespace std;
using namespace cv; void m_filer(double *src,int height,int width,double *mask_rel,double *mask_img,int mW,int mH,int k)
{
IplImage *tmp;
double a,b,c;
char res[]; //保存的图像名称 tmp = cvCreateImage(cvSize(width,height),IPL_DEPTH_8U,); for (int i = ;i < height;i++)
{
for (int j = ;j < width;j++)
{
a = 0.0;
b = 0.0;
c = 0.0;
//去掉靠近边界的行列,无法滤波,超出范围
if (i > int(mH/) && i < height - int(mH/) && j > int(mW) && j < width - int(mW/))
{
for (int m = ;m < mH;m++)
{
for(int n = ;n < mW;n++)
{
//printf("%f\n",src[(i+m-int(mH/2))*width+(j+n-int(mW))]);
a += src[(i+m-int(mH/))*width+(j+n-int(mW))]*mask_rel[m*mW+n];
b += src[(i+m-int(mH/))*width+(j+n-int(mW))]*mask_img[m*mW+n];
//printf("%f,%f\n",a,b);
}
}
}
c = sqrt(a*a+b*b);
c /= mW*mH;
tmp->imageData[i*width+j] = (unsigned char)c;
}
}
sprintf(res,"result%d.jpg",k);
cvSaveImage(res,tmp);
cvReleaseImage(&tmp);
} int _tmain(int argc, _TCHAR* argv[])
{
IplImage *src;
double *rel,*img,*src_data,xtmp,ytmp,tmp1,tmp2,tmp3,re,im;
double Theta,sigma,Gamma,Lambda,Phi; //公式中的5个参数
int gabor_height,gabor_width,x,y; src = cvLoadImage("test.jpg",CV_LOAD_IMAGE_GRAYSCALE);
gabor_height = ;
gabor_width = ;
Gamma = 1.0;
Lambda = 10.0;
sigma = ;
Phi = ; rel = (double *)malloc(sizeof(double)*gabor_width*gabor_height);//实数部分
img = (double *)malloc(sizeof(double)*gabor_width*gabor_height);//虚数部分
src_data = (double *)malloc(sizeof(double)*src->widthStep*src->height); //图像数据 for (int i=;i<src->height;i++)
{
for (int j=;j<src->widthStep;j++)
{
src_data[i*src->widthStep+j]=(unsigned char)src->imageData[i*src->widthStep+j];
//printf("%f\n",src_data[i*src->widthStep+j]);
}
} //构造gabor函数
for (int k = ;k < N;k++) //定义N方向
{
Theta = PI*((double)k/N);
for (int i = ;i < gabor_height;i++) //定义模版大小
{
for (int j = ;j < gabor_width;j++)
{
x = j - gabor_width/;
y = i - gabor_height/; xtmp = (double)x*cos(Theta) + (double)y*sin(Theta);
ytmp = (double)y*cos(Theta) - (double)x*sin(Theta); tmp1 = exp(-(pow(xtmp,)+pow(ytmp*Gamma,))/(*pow(sigma,)));
tmp2 = cos(*PI*xtmp/Lambda + Phi);
tmp3 = sin(*PI*xtmp/Lambda + Phi); re = tmp1*tmp2;
im = tmp1*tmp3; rel[i*gabor_width+j] = re;
img[i*gabor_width+j] = im;
//printf("%f,%f\n",re,im);
}
}
//用不同方向的GABOR函数对图像滤波并保存图片
m_filer(src_data,src->height,src->width,rel,img,,,k);
} free(rel);free(img);free(src_data);
return ;
}
运行效果:
大概就这样凑活吧。我这边实数部分和虚数部分的处理是采用求模的方式。有问题的,请广大人民群众提出来啊。
三、用gabor提取纹理特征的思路【抄别人的论文】
Gabor滤波方法的主要思想是:不同纹理一般具有不同的中心频率及带宽,根据这些频率和带宽可以设计一组Gabor滤波器对纹理图像进行滤波,每个Gabor滤波器只允许与其频率相对应的纹理顺利通过,而使其他纹理的能量受到抑制,从各滤波器的输出结果中分析和提取纹理特征,用于之后的分类或分割任务。Gabor滤波器提取纹理特征主要包括两个过程:①设计滤波器(例如函数、数目、方向和间隔);②从滤波器的输出结果中提取有效纹理特征集。Gabor滤波器是带通滤波器,它的单位冲激响应函数(Gabor函数)是高斯函数与复指数函的乘积。它是达到时频测不准关系下界的函数,具有最好地兼顾信号在时频域的分辨能力。
实现步骤:
(1)将输入图像分为3×3(9块)和4×4(16块)的图像块;
(2)建立Gabor滤波器组:选择4个尺度,6个方向,这样组成了24个Gabor滤波器;
(3)Gabor滤波器组与每个图像块在空域卷积,每个图像块可以得到24个滤波器输出,这些输出是图像块大小的图像,如果直接将其作为特征向量,特征空间的维数会很大,所以需要“浓缩”;
(4)每个图像块经过Gabor滤波器组的24个输出,要“浓缩”(文中提到“average filter responses within the block”我的理解是取灰度均值)为一个24×1的列向量作为该图像块的纹理特征。查阅相关文献,发现也可以用方差。
利用一幅真实图像,按照文献原文所说,利用4scales*6orientations的Gabor滤波器组进行纹理特征提取,可以有效获得图像纹理信息。其中,单独拿出某组相同scale的结果,展示如下所示。【别人的实验结果,也没给代码,我也没去做】
From:http://blog.csdn.net/jinshengtao/article/details/17797641
Gabor滤波器学习的更多相关文章
- Gabor变换、Gabor滤波器
D.Gabor 1946年提出 窗口Fourier变换,为了由信号的Fourier变换提取局部信息,引入了时间局部化的窗函数. 由于窗口Fourier变换只依赖于部分时间的信号,所以,现在窗口Four ...
- python实现gabor滤波器提取纹理特征 提取指静脉纹理特征 指静脉切割代码
参考博客:https://blog.csdn.net/xue_wenyuan/article/details/51533953 https://blog.csdn.net/jinshengtao/ar ...
- Gabor滤波器的理解
搬以前写的博客[2014-02-28 20:03] 关于Gabor滤波器是如何提取出特征点,这个过程真是煎熬.看各种文章,结合百度.文章内部的分析才有一点点明白. Gabor滤波器究竟是什么? 很 ...
- 图像算法五:【图像小波变换】多分辨率重构、Gabor滤波器、Haar小波
原 https://blog.csdn.net/alwaystry/article/details/52756051 图像算法五:[图像小波变换]多分辨率重构.Gabor滤波器.Haar小波 2018 ...
- gabor滤波器
https://blog.csdn.net/u013709270/article/details/49642397 https://github.com/xuewenyuan/Gabor_Visual ...
- matlab实现gabor滤波器的几种方式
转自:http://blog.csdn.net/watkinsong/article/details/7882443 方式一: function result = gaborKernel2d( lam ...
- matlab中实现Gabor滤波器
1.spatialgabor.m描述gabor函数 % SPATIALGABOR - applies single oriented gabor filter to an image%% Usage: ...
- matlab gabor 滤波器
0. gabor 基本原理 1. matlab 内置对 gabor 的支持 gabor:Create Gabor filter or Gabor filter bank g = gabor(wavel ...
- 学习OpenCV——Gabor函数的应用
原文:http://blog.csdn.net/yao_zhuang/article/details/2532279 下载cvgabor.cpp和cvgabor.h到你的C/C++工程目录下 注:在我 ...
随机推荐
- Bestcoder HDU5059 Help him 字符串处理
Help him Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total S ...
- onvif规范的实现:onvif开发常用调试方法 和常见的segmentation fault错误
在前几篇中,虽然已经实现了rtsp视频流的对接,但是还要做的工作还非常多,onvif本来就是一个覆盖面非常广的一个协议,每一个功能都要填充大量的函数.而且稍不注意就会出现segmentation fa ...
- mysql跟踪提交的SQL语句
http://www.cnblogs.com/wuyifu/p/3328024.html
- JavaScript模块化-require.js
http://www.cnblogs.com/duanhuajian/archive/2013/01/04/2844151.html 原文:http://www.ruanyifeng.com/blog ...
- mvc 客户端验证
@model MvcApplication1.Models.ViewClass @{ ViewBag.Title = "View2"; } @******引用这两个js实现客户端的 ...
- JavaScript的对象——灵活与危险
转:http://www.cnblogs.com/tolg/p/4873000.html 没有哪种数据结构比JavaScript的对象更简单灵活了.作为一个弱动态类型语言,JavaScript对对象的 ...
- stl的集合set——安迪的第一个字典(摘)
set就是数学上的集合——每个元素最多只出现一次,和sort一样,自定义类型也可以构造set,但同样必须定义“小于”运算符 以下代码测试set中无重复元素 #include<iostream&g ...
- Android 瘦身攻略
http://www.jayfeng.com/2015/12/29/APK%E7%98%A6%E8%BA%AB%E5%AE%9E%E8%B7%B5/
- [汇编语言]-第五章[bx]和loop指令
1- [bx]和内存单元的描述 [0]表示内存单元, 他的偏移地址为0 mov ax,[0] 将一个内存单元的内容送入到ax.这个内存单元的长度为2字节(字单元),存放一个字,偏移地址为0,段地址在d ...
- Scrapy:python3下的第一次运行测试
1,引言 <Scrapy的架构初探>一文讲解了Scrapy的架构,本文就实际来安装运行一下Scrapy爬虫.本文以官网的tutorial作为例子,完整的代码可以在github上下载. 2, ...