#include "gabor.h" | |
cv::Mat getMyGabor(int width, int height, int U, int V, double Kmax, double f, | |
double sigma, int ktype, const string kernel_name) | |
{ | |
int half_width = width / 2; | |
int half_height = height / 2; | |
double Qu = PI*U/8; | |
double sqsigma = sigma*sigma; | |
double Kv = Kmax/pow(f,V); | |
double postmean = exp(-sqsigma/2); | |
cv::Mat kernel_re(width, height, ktype); | |
cv::Mat kernel_im(width, height, ktype); | |
cv::Mat kernel_mag(width, height, ktype); | |
double tmp1, tmp2, tmp3; | |
for(int j = -half_height; j <= half_height; j++){ | |
for(int i = -half_width; i <= half_width; i++){ | |
tmp1 = exp(-(Kv*Kv*(j*j+i*i))/(2*sqsigma)); | |
tmp2 = cos(Kv*cos(Qu)*i + Kv*sin(Qu)*j) - postmean; | |
tmp3 = sin(Kv*cos(Qu)*i + Kv*sin(Qu)*j); | |
kernel_re.at<float>(j+half_height, i+half_width) = | |
(float)(Kv*Kv*tmp1*tmp2/sqsigma); | |
kernel_im.at<float>(j+half_height, i+half_width) = | |
(float)(Kv*Kv*tmp1*tmp3/sqsigma); | |
} | |
} | |
magnitude(kernel_re, kernel_im, kernel_mag); | |
if(kernel_name.compare("real") == 0) | |
return kernel_re; | |
else if(kernel_name.compare("imag") == 0) | |
return kernel_im; | |
else{ | |
printf("Invalid kernel name!\n"); | |
return kernel_mag; | |
} | |
} | |
void gabor_filter(cv::Mat& img,vector |
|
{ | |
//cv::Mat img input character image | |
const int kernel_size = 7; // should be odd | |
// variables for gabor filter | |
double Kmax = PI/2; | |
double f = sqrt(2.0); | |
double sigma = 2*PI; | |
int U = 0; | |
int V = 0; | |
int GaborH = kernel_size; | |
int GaborW = kernel_size; | |
int UStart = 0, UEnd = 8; | |
int VStart = 1, VEnd = 2; | |
// variables for filter2D | |
cv::Point archor(-1,-1); | |
int ddepth = CV_32F;//CV_64F | |
//double delta = 0; | |
//eight orientation in terms of one frequnecy | |
for(V = VStart; V < VEnd; V++){ | |
for(U = UStart; U < UEnd; U++){ | |
cv::Mat kernel_re, kernel_im; | |
cv::Mat dst_re, dst_im, dst_mag; | |
kernel_re = getMyGabor(GaborW, GaborH, U, V, | |
Kmax, f, sigma, CV_32F, "real"); | |
kernel_im = getMyGabor(GaborW, GaborH, U, V, | |
Kmax, f, sigma, CV_32F, "imag"); | |
// flip kernel | |
// Gabor kernel is symmetric, so do not need flip | |
filter2D(img, dst_re, ddepth, kernel_re); | |
filter2D(img, dst_im, ddepth, kernel_im); | |
dst_mag.create(img.rows, img.cols, CV_32FC1); | |
magnitude(cv::Mat(dst_re),cv::Mat(dst_im), | |
dst_mag); | |
//normalize gabor kernel | |
cv::normalize(dst_mag, dst_mag, 0, 255, CV_MINMAX); | |
dst_mag.convertTo(dst_mag,CV_8U); | |
featureMaps.push_back(dst_mag); | |
kernel_re.release(); | |
kernel_im.release(); | |
dst_re.release(); | |
dst_im.release(); | |
dst_mag.release(); | |
} | |
} | |
} |
在图像处理、模式识别以及计算机视觉等领域中,,Gabor 滤波器得到了广泛的应用。Gabor滤波器是一个用于边缘检测的线性滤波器。Gabor滤波器的频率和方向表示接近人类视觉系统对于频率和方向的表示,并且它们常备用于纹理表示和描述。在空域,一个2维的Gabor滤波器是一个正弦平面波和高斯核函数的乘积,具有在空间域和频率域同时取得最优局部化的特性,与人类生物视觉特性很相似,因此能够很好地描述对应于空间频率(尺度)、空间位置及方向选择性的局部结构信息。。Gabor滤波器是自相似的,也就是说,所有Gabor滤波器都可以从一个母小波经过膨胀和旋转产生。实际应用中,Gabor滤波器可以在频域的不同尺度,不同方向上提取相关特征。
Gabor变换是一种短时傅里叶变换方法,其实质是在傅里叶变换中加入一个窗函数,通过窗函数来实现信号的时频分析。当选取高斯函数作为窗函数时,短时傅里叶变换称为Gabor变换。
二、Gabor滤波器公式化定义
公式中:
λ:正弦函数波长;
θ:Gabor核函数的方向
ψ:相位偏移
σ:高斯函数的标准差
γ: 空间的宽高比(这个没太理解)
常用的偶对称二维Gabor滤波器可表示为:
1. 不同方向下的Gabor滤波器:
图1 不同方向上的滤波器
在实际应用时,可以根据检测对象的方向趋势,选择合适的方向参数进行滤波。如在检测人脸的五官时,可以根据人脸的偏转角度进行滤波,可以使特征点的定位更加准确。
2. 不同频率下的滤波器:
图2 不同频率下的滤波器
从图2可以看出随着的变化,Gabor滤波器中出现了很多宽窄与纹理不同的明暗条纹。当滤波器纹理与图像作用时,滤波器覆盖下的局部纹理频率与滤波器的频率越接近响应就越大,反之越小。
3. 人脸光照之Gabor滤波 试验结果:
在“人脸光照调整之DCT变换”随笔中,原始图像经过DCT变换处理后,并不能完全去除光照在人脸上分布不均的影响,而且人脸的本真信息也难以被全部表达。为此在DCT变换的基础上,用Gabor滤波对其进行再处理,可以达到更好的结果。
图三 基于DCT变换的Gabor滤波
图四 基于DCT变换的Gabor滤波
图三(c)是在(b)图基础上做的Gabor滤波,效果显示已基本完全消除了高曝光对图像的影响。同理,图四(c)的右边脸的光照也被抑制下来。图四(d)是对原始图像直接做Gabor滤波,虽然局部效果较(c)图更清晰,但整体纹理没有(c)图平滑,这样会给后续特征点定位的收敛性带来影响,因此定位效果欠稳定。
这两种方法合在一起使用,时间开销还是挺大的,在人脸识别等实时系统中,需要优化或精简。一般情况下,就单比处理效果和稳定性,Gabor要由于DCT变换。因此,在容许情况下,我们可以只取Gabor对图像进行处理。比如,作者在“眼睛定位”随笔中,就只用Gabor滤波对人脸处理,以提高眼睛定位精度。
下面,作者再贴几张图,看看这两种方法合在一起时,对AAM的帮助。
图五 光照调整对AAM定位的帮助
图五中的(a)图是AAM对原始图像直接定位的结果,(b)图是在去光照后的定位效果。比较两组图像,可以很明显的看到(b)图的定位精度有了大幅度的提高。
参考文献: