找了很久关于这方面的文献,介绍的都比较浅
求助领导,给了我一篇文章:
Gabor Based Kernel Partial-Least-Squares Discrimination Features for Face Recognition.pdf
看起来不是很麻烦,晚上回去实现一下,随后,上代码
// gabor.cpp
//
/********************************************************************
created: 2012/10/08
created: 08:10:2012 14:52
author: SHI Juanfeng
purpose: gabor wavelet and extract gabor feature
*********************************************************************/
/// 转发请注明出处:http://shijuanfeng.blogbus.com/logs/222803606.html
#include "stdafx.h"
#pragma once
#ifdef DEBUG
#pragma comment( lib,"opencv_contrib240d.lib" )
#pragma comment( lib,"opencv_core240d.lib" )
#pragma comment( lib,"opencv_highgui240d.lib" )
#pragma comment( lib,"opencv_imgproc240d.lib" )
#else
#pragma comment( lib,"opencv_contrib240.lib" )
#pragma comment( lib,"opencv_core240.lib" )
#pragma comment( lib,"opencv_highgui240.lib" )
#pragma comment( lib,"opencv_imgproc240.lib" )
#endif
#include <opencv2\opencv.hpp>
using namespace cv;
#include <vector>
/**
* \brief GaborWavelet 的参数
*
* f^2
* ψ(f,θ,γ,η) = ------------- exp( - (f^2*Xt^2/γ^2 + f^2*Yt^2/η^2) ) exp( j*2pai*f*Xt )
* pai*γ*η
* xt = x cos θ + y sin θ,
* yt = −x sin θ + y cos θ
*
* ψg,h(x, y) = ψ(fg,θh,γ,η),
* fg = fmax/(sqrtf(2))^g
* θh = h*π/8
* g,h由scales, orientations 决定, 例如 scales=5, orientations=8时,g={0, . . . , 4},h=h ∈ {0, . . . , 7}
*
* γ、η、fmax是预先设好的参数
**/
struct GaborWaveletParam
{
float gamma; ///< 如上公式, ,默认 sqrtf(2)
float etah; ///< 如上公式,默认 sqrtf(2)
float fmax; ///< 如上公式,默认 0.25
int scales ; ///< 尺度的数目
int orientations; ///< 方向的数目
int width; ///< 生成的小波窗口宽
int height; ///< 生成的小波窗口高
};
/**
* \brief 输出固定尺度固定方向的Gabor小波
* f^2
* ψ(f,θ,γ,η) = ------------- exp( - (f^2*Xt^2/γ^2 + f^2*Yt^2/η^2) ) exp( j*2pai*f*Xt )
* pai*γ*η
* xt = x cos θ + y sin θ,
* yt = −x sin θ + y cos θ
* \param[in] width 小波窗口宽
* \param[in] height 小波窗口高
* \param[in] f 参数
* \param[in] sita 参数
* \param[in] gamma 参数
* \param[in] etah 参数
* \param[in][out] KernelReal Gabor小波实部
* \param[in][out] KernelImg Gabor小波虚部
**/
void TwoDimGaborwavelet( int width, int height, float f, float sita, float gamma, float etah, Mat &KernelReal, Mat &KernelImg );
/**
* \brief 生成多尺度、多方向的Gabor小波
* \param[in] param 参数,详见结构体说明
*/
void GaborWaveletsMultiscalesAndMultiOrientations( GaborWaveletParam ¶m );
/**
* \brief 提取多尺度、多方向的Gabor特征
* 1. 原图像卷积Gabor 小波 Og,h(x, y) = I(x, y) ∗ ψg,h(x, y),
* 2. 在多个尺度上进行下采样
* 3. 归一化到0均值1方差
* 4. 将各尺度变成行向量连接起来,最终的特征为scales*orientations*ImgWidth*ImgHeight/rou长的行向量
*
* \param[in] param 参数,详见结构体说明
* \param[in] src 原图像
* \param[in] rou 下采样的比例
* \param[in] gaborFeatures 求得的gabor特征
**/
void GaborFeaturesMultiscalesAndMultiOrientations( GaborWaveletParam ¶m, const Mat &src, float rou, vector<float> &gaborFeatures );
//////////////////////////////////////////////////////////////////////////
/// 实现
/// 输出固定尺度固定方向的Gabor小波
void TwoDimGaborwavelet( int width, int height, float f, float sita, float gamma, float etah, Mat &KernelReal, Mat &KernelImg )
{
for ( int x=-width/2; x<width/2; x++)
{
for ( int y=-height/2; y<height/2; y++ )
{
float xt = x * cos(sita) + y * sin(sita);
float yt = -x * sin(sita) + y * cos(sita);
float fai = f*f / (CV_PI*gamma*etah) * exp( - f*f* ( xt*xt/(gamma*gamma) + yt*yt/(etah*etah) ) ) ;
KernelReal.at<float>(x+width/2, y+height/2) = fai * cos( 2*CV_PI*f*xt);
KernelImg.at<float>(x+width/2, y+height/2) = fai * sin( 2*CV_PI*f*xt);
}
}
}
/// 生成多尺度、多方向的Gabor小波
void GaborWaveletsMultiscalesAndMultiOrientations( GaborWaveletParam ¶m )
{
int cnt = 1;
Mat KernelReal = Mat::zeros( param.width, param.height, CV_32F );
Mat KernelImg = Mat::zeros( param.width, param.height, CV_32F );
for ( int g=0; g<param.scales; g++ )
{
for ( int h=0; h<param.orientations; h++ )
{
float f = param.fmax/pow( sqrtf(2.0), g );
float sita = h * CV_PI / 8;
TwoDimGaborwavelet( param.width, param.height, f, sita, param.gamma, param.etah, KernelReal, KernelImg );
normalize( KernelReal, KernelReal, 0, 255, NORM_MINMAX );
#if 1 ///< 这里我为了测试加入了保存图像的过程,实际使用应将该部分注释,此外,我只保存了实部用以观察
char buf[MAX_PATH] = {0};
itoa( cnt, buf, 10 );
string name = "gaborWaveletKernelReal" ;
name += buf;
name += ".jpg";
IplImage gaborWaveletImg = IplImage(KernelReal);
cvSaveImage( name.c_str(), &gaborWaveletImg );
cnt++;
#endif
}
}
}
/// 提取多尺度、多方向的Gabor特征
void GaborFeaturesMultiscalesAndMultiOrientations( GaborWaveletParam ¶m, const Mat &src, float rou, vector<float> &gaborFeatures )
{
int cnt = 1;
Mat KernelReal = Mat::zeros( param.width, param.height, CV_32F );
Mat KernelImg = Mat::zeros( param.width, param.height, CV_32F );
Mat faceReal;
Mat faceImg;
Mat faceMag = Mat::zeros( src.rows, src.cols, CV_32F );
Mat gaborImg = Mat::zeros( src.rows/rou, src.cols/rou, CV_32F );
for ( int g=0; g<param.scales; g++ )
{
for ( int h=0; h<param.orientations; h++ )
{
float f = param.fmax/pow( sqrtf(2.0), g );
float sita = h * CV_PI / 8;
TwoDimGaborwavelet( param.width, param.height, f, sita, param.gamma, param.etah, KernelReal, KernelImg );
/// 卷积
flip( KernelReal, KernelReal, -1 );
filter2D( src, faceReal, -1, KernelReal, Point( -1, -1 ), 0, BORDER_REPLICATE );
filter2D( src, faceImg, -1, KernelImg, Point( -1, -1 ), 0, BORDER_REPLICATE );
/// 幅值
faceReal = cv::Mat_<float>(faceReal);
faceImg = cv::Mat_<float>(faceImg);
magnitude( faceReal, faceImg, faceMag );
#if 1 ///< 这里我为了测试加入了保存图像的过程,实际使用应将该部分注释调
normalize( faceMag, faceMag, 0, 255, NORM_MINMAX );
char buf[MAX_PATH] = {0};
itoa( cnt, buf, 10 );
string name = "gaborFeaturesMag" ;
name += buf;
name += ".jpg";
IplImage gaborWaveletImg = IplImage(faceMag);
cvSaveImage( name.c_str(), &gaborWaveletImg );
cnt++;
#endif
/// 下采样到要求的尺寸
resize( faceMag, gaborImg, gaborImg.size() );
/// 归一化到0均值1方差
Scalar m;
Scalar stddev;
meanStdDev( gaborImg, m, stddev );
gaborImg = (gaborImg-m[0])/stddev[0];
/// 得到特征
for ( int i=0; i<gaborImg.rows; i++ )
{
for ( int j=0; j<gaborImg.cols; j++ )
{
gaborFeatures.push_back( gaborImg.at<float>(i,j) );
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////
/// 测试
int _tmain(int argc, _TCHAR* argv[])
{
/// Gabor 小波
GaborWaveletParam param;
param.gamma = sqrtf(2.0);
param.etah = sqrtf(2.0);
param.fmax = 0.25;
param.scales = 5;
param.orientations = 8;
param. width=64; ///< 为了看的清楚,我将窗口尺寸设置较大,实际一般用不到这么大的尺寸
param.height=64; ///< 为了看的清楚,我将窗口尺寸设置较大,实际一般用不到这么大的尺寸
GaborWaveletsMultiscalesAndMultiOrientations( param );
//////////////////////////////////////////////////////////////////////////
/// 提取图像的Gabor特征
/* 1. 原图像卷积Gabor 小波 Og,h(x, y) = I(x, y) ∗ ψg,h(x, y),
2. 在多个尺度上进行下采样
3. 归一化到0均值1方差
4. 将各尺度变成行向量连接起来,最终的特征为scales*orientations*ImgWidth*ImgHeight/rou长的行向量
*/
char name[] = "E:\\database\\CMU\\CMUPie_illum_Cropped_128x128\\04000\\27_02.jpg";
IplImage *img = cvLoadImage( name, CV_LOAD_IMAGE_GRAYSCALE );
Mat src( img, 0 );
/// 实际使用的窗口尺寸, 若高斯核参数为sigma,窗口一般选择为6*sigma;二维时,亦然
param. width = 6 * param.gamma;
param.height=6 * param.etah;
/// 下采样
float rou = 4;
/// 求得的Gabor特征,将各尺度变成行向量连接起来,长度为 scales*orientations*ImgWidth*ImgHeight/rou
vector<float> gaborFeatures;
GaborFeaturesMultiscalesAndMultiOrientations( param, src, rou, gaborFeatures );
return 0;
}
cpp原文件下载地址:http://www.kuaipan.cn/file/id_71521745328144392.htm
二维gabor小波是弄明白了,一维呢?公式是啥啊???继续探究
/**
* \brief 输出一维固定尺度固定方向的Gabor小波
* f^2
* ψ(f,θ,γ) = ------------- exp( - f^2*Xt^2/γ^2 ) exp( j*pai*f*Xt )
* pai*γ
* xt = x cos θ + y sin θ,
* \param[in] width 小波窗口宽
* \param[in] f 参数
* \param[in] gamma 参数
* \param[in] sita 参数
* \param[in] gamma 参数
* \param[in][out] KernelReal Gabor小波实部
* \param[in][out] KernelImg Gabor小波虚部
**/
void GaborWavelet( int width, float f, float gamma, float sita, Mat &KernelReal, Mat &KernelImg )
{
for ( int x=-width/2; x<width/2; x++)
{
float xt = x * cos(sita);
float fai = f*f / ( sqrtf(CV_PI)*gamma) * exp( - f*f*xt*xt/(gamma*gamma) ) ;
KernelReal.at<float>(x+width/2, 0 ) = fai * cos( CV_PI*f*xt);
KernelImg.at<float>(x+width/2,0 ) = fai * sin( CV_PI*f*xt);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int width = 4;
Mat KernelReal = Mat::zeros( width, 1, CV_32F );
Mat KernelImg = Mat::zeros( width, 1, CV_32F );
GaborWavelet( width, 0.25, 1, 6./CV_PI, KernelReal, KernelImg );
/// 归一化到sqrtf(2)均值1方差
Scalar m;
Scalar stddev;
meanStdDev( KernelReal, m, stddev );
KernelReal = sqrtf(2.) * (KernelReal-m[0])/stddev[0];
meanStdDev( KernelImg, m, stddev );
KernelImg = sqrtf(2.) * (KernelImg-m[0])/stddev[0];
/// 幅值
Mat KernelMag = Mat::zeros( width, 1, CV_32F );
KernelReal = cv::Mat_<float>(KernelReal);
KernelImg = cv::Mat_<float>(KernelImg);
magnitude( KernelReal, KernelImg, KernelMag );
for ( int i=0; i<width; i++ )
{
cout << KernelReal.at<float>(i,0) << " ";
}
cout << endl;
for ( int i=0; i<width; i++ )
{
cout << KernelImg.at<float>(i,0) << " ";
}
cout << endl;
for ( int i=0; i<width; i++ )
{
cout << KernelMag.at<float>(i,0) << " ";
}
cout << endl;
return 0;
}
源自作者:http://shijuanfeng.blogbus.com/logs/222803606.html