可参考:http://blog.csdn.net/jinshengtao/article/details/17797641
http://www.cnblogs.com/emouse/p/3611256.html
http://blog.csdn.net/yutianzuijin/article/details/10823985
基于Gabor+PCA+SVM的性别识别(1): http://www.cnblogs.com/xiaoming123abc/p/5078411.html
基于Gabor+PCA+SVM的性别识别(3): http://www.cnblogs.com/xiaoming123abc/p/5079116.html
这里只简单说一下自己的理解:
一维Gabor小波的实质是一个带通滤波器,具有频率选择性。
对于二维Gabor小波,它不仅具有频率选择性,还有频率方向的选择性;这时,可以理解Gabor是个空域的模板,匹配与模板频率大小和频率方向相同的成分。也就是说,图像中的频率大小和频率方向与该Gabor的频率大小和频率方向相同,则经过Gabor滤波后,响应会比较大,这样就把特征提取出来了。
二维Gabor小波是由二维Gabor滤波器函数 G(ω,θ)(Gabor滤波器有很多参数,为了方便理解,这里只写了两个)通过尺度伸缩和旋转生成的一组滤波器,其参数的选择通常在频率空间进行考虑。为了对一幅图像的整个频域进行采样,可以采用具有多个中心频率和方向的Gabor滤波器组来描述图像。参数 ω,θ的不同选择分别体现了二维Gabor小波在频率和方向空间的采样方式。
整个频率空间可以是0到无穷大的任意值,由于一幅图像实际的频率分布是有限的范围,所以,对于图像的局部特征来说,参数 ω只能在一个很小的范围内选取。由于图像的纹理是随机分布的, θ的取值范围为0到2*pi ,考虑到Gabor滤波器的对称性, 的实际取值范围为0到pi 。本文采用5个中心频率和8个方向组成40个Gabor滤波器。
40个Gabor滤波器,由于每一个Gabor滤波器都由实部和虚部组成。所以一共有80个Gabor模板。
Gabor小波提取特征的过程就是拿着这些Gabor模板与图像卷积,得出40个实部响应图和40个实部响应图。实部与虚部合并,形成40个总响应图(18X21),最后把每一个响应图(18X21)拉直,既18X21的矩阵变成1X378的向量(这只是本人用的方法,其他方法也应该可以)。这样每幅人脸(18X21)的特征个数(40X378),运用PCA对(40X378)降维。
PCA
PCA(Principal Component Analysis)是一种常用的数据分析方法。PCA通过线性变换将原始数据变换为一组各维度线性无关的表示,可用于提取数据的主要特征分量,常用于高维数据的降维。
PCA的算法步骤:
设有m条n维数据。
1) 将原始数据按列组成n行m列矩阵X;
2) 将X的每一行(代表一个属性字段)进行零均值化,即减去这一行的均值;
3) 求出协方差矩阵(样本协方差矩阵) ;求出协方差矩阵的特征值及对应的特征向量;
4) 将特征向量按对应特征值大小从上到下按行排列成矩阵,取前k行组成矩阵P;
5) Y=PX即为降维到k维后的数据。
本程序实现了Gabor特征提取,PCA降维,SVM分类。由于每一幅图像要与80个Gabor模板卷积。man样本与woman样本一共600多个。可能需要运行一段时间,最后得到一个SVM分类器。
main.cpp
#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>
#include <opencv2/ml/ml.hpp>
#include "GaborFR.h"
using namespace std;
using namespace cv;
#define manNO 409 //man样本个数
#define womanNO 287 //woman样本个数
int iSize=20;// Gabor的scale
int main()
{
int DescriptorDim=200;
string ImgName;//图片名(绝对路径)
ifstream finMan("man.txt");//man样本图片的文件名列表
ifstream finWoman("woman.txt");//woman样本图片的文件名列表
Mat ROI;
Mat Gabor_feature;
Mat PCA_feature;
Mat sampleFeatureMat;//所有训练样本的特征向量组成的矩阵,行数等于所有样本的个数,列数等于Gabor描述子维数
Mat sampleLabelMat; //训练样本的类别向量,行数等于所有样本的个数,列数等于1;1表示man,-1表示woman
Vector<Mat> GaborReal;
Vector<Mat> GaborImag;
//生成Gabor 模板
for(int i=0;i<8;i++)
{
for(int j=0;j<5;j++)
{
Mat M1= GaborFR::getRealGaborKernel(Size(iSize,iSize),
2*CV_PI,
i*CV_PI/8+CV_PI/2,
j,
1);
Mat M2 = GaborFR::getImagGaborKernel(Size(iSize,iSize),
2*CV_PI,
i*CV_PI/8+CV_PI/2,
j,
1);
GaborReal.push_back(M1);
GaborImag.push_back(M2);
}
}
//依次读取man样本图片
for(int num=0; num<manNO && getline(finMan,ImgName); num++)
{
cout<<"处理:"<<ImgName<<endl;
ImgName = "D:\\Mycode\\mantrain_cut\\" + ImgName+".png";
ROI= imread(ImgName,0);//读取图片
if(ROI.data ==0)
{
printf("[error] 没有图片\n");
return -5;
}
//Gabor变换提取特征*************************
for(int i=0;i<8;i++)
{
for(int j=0;j<5;j++)
{
Mat outR,outI,M_Magnitude;
GaborFR::getFilterRealImagPart(ROI,GaborReal[i*5+j],GaborImag[i*5+j],outR,outI);
M_Magnitude=GaborFR::getMagnitude(outR,outI);
//cartToPolar( outR, outI, M_Magnitude, Angle, false ); //计算幅值和相角
normalize(M_Magnitude,M_Magnitude,0,1,CV_MINMAX,CV_32F);
Mat line= M_Magnitude.reshape(0,1);
Gabor_feature.push_back(line);
}
}
//PCA降维************************************************************
PCA pca(Gabor_feature, cv::Mat(), CV_PCA_DATA_AS_ROW,5);
Mat dst=pca.project(Gabor_feature) ;
Mat line =dst.reshape(0,1);
//处理第一个样本时初始化特征向量矩阵和类别矩阵,因为只有知道了特征向量的维数才能初始化特征向量矩阵
if( 0 == num )
{
//初始化所有训练样本的特征向量组成的矩阵,行数等于所有样本的个数,列数等于Gabor降维后描述子维数sampleFeatureMat
sampleFeatureMat = Mat::zeros(manNO+womanNO, DescriptorDim, CV_32FC1);
//初始化训练样本的类别向量,行数等于所有样本的个数,列数等于1;1表示有人,0表示无人
sampleLabelMat = Mat::zeros(manNO+womanNO, 1, CV_32FC1);
}
//将计算好的Gabor降维后描述子复制到样本特征矩阵sampleFeatureMat
for(int i=0; i<DescriptorDim; i++)
sampleFeatureMat.at<float>(num,i) = line.at<float>(0,i);//第num个样本的特征向量中的第i个元素
sampleLabelMat.at<float>(num,0) = 1;//man样本类别为1
}
//依次读取woman样本图片,生成Gabor描述子并降维
for(int num=0; num<womanNO && getline(finWoman,ImgName); num++)
{
cout<<"处理:"<<ImgName<<endl;
ImgName = "D:\\Mycode\\womantrain_cut\\" + ImgName+".png";
ROI= imread(ImgName,0);//读取图片
if(ROI.data ==0)
{
printf("[error] 没有图片\n");
return -5;
}
//Gabor变换提取特征*************************
for(int i=0;i<8;i++)
{
for(int j=0;j<5;j++)
{
Mat outR,outI,M_Magnitude;
GaborFR::getFilterRealImagPart(ROI,GaborReal[i*5+j],GaborImag[i*5+j],outR,outI);
M_Magnitude=GaborFR::getMagnitude(outR,outI);
//cartToPolar( outR, outI, M_Magnitude, Angle, false ); //计算幅值和相角
normalize(M_Magnitude,M_Magnitude,0,1,CV_MINMAX,CV_32F);
Mat line= M_Magnitude.reshape(0,1);
Gabor_feature.push_back(line);
}
}
//PCA降维************************************************************
PCA pca( Gabor_feature, cv::Mat(), CV_PCA_DATA_AS_ROW,5);
Mat dst=pca.project(Gabor_feature) ;
Mat line =dst.reshape(0,1);
for(int i=0; i<DescriptorDim; i++)
sampleFeatureMat.at<float>(num+manNO,i) = line.at<float>(0,i);//第PosSamNO+num个样本的特征向量中的第i个元素
sampleLabelMat.at<float>(num+manNO,0) = -1;//woman样本类别为-1
}
//训练SVM分类器
//迭代终止条件,当迭代满1000次或误差小于FLT_EPSILON时停止迭代
CvSVM svm; //SVM分类器
CvTermCriteria criteria = cvTermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 1000, FLT_EPSILON);
//SVM参数:SVM类型为C_SVC;线性核函数;松弛因子C=0.01
CvSVMParams param(CvSVM::C_SVC, CvSVM::LINEAR, 0, 1, 0, 0.01, 0, 0, 0, criteria);
cout<<"开始训练SVM分类器"<<endl;
svm.train(sampleFeatureMat, sampleLabelMat, Mat(), Mat(), param);//训练分类器
cout<<"训练完成"<<endl;
svm.save("SVM_PCA.xml");//将训练好的SVM模型保存为xml文件
waitKey();//注意:imshow之后必须加waitKey,否则无法显示图像
system("pause");
}
GarborFR.hpp
#include "opencv2\opencv.hpp"
#include <vector>
using namespace std;
using namespace cv;
class GaborFR
{
public:
GaborFR();
static Mat getImagGaborKernel(Size ksize, double sigma, double theta,
double nu,double gamma=1, int ktype= CV_32F);
static Mat getRealGaborKernel( Size ksize, double sigma, double theta,
double nu,double gamma=1, int ktype= CV_32F);
static Mat getPhase(Mat &real,Mat &imag);
static Mat getMagnitude(Mat &real,Mat &imag);
static void getFilterRealImagPart(Mat& src,Mat& real,Mat& imag,Mat &outReal,Mat &outImag);
static Mat getFilterRealPart(Mat& src,Mat& real);
static Mat getFilterImagPart(Mat& src,Mat& imag);
/*
void Init(Size ksize=Size(19,19), double sigma=2*CV_PI,
double gamma=1, int ktype=CV_32FC1);
*/
private:
//vector<Mat> gaborRealKernels;
//vector<Mat> gaborImagKernels;
bool isInited;
};
Gabor.cpp
//#include "StdAfx.h"
#include "GaborFR.h"
GaborFR::GaborFR()
{
isInited = false;
}
/*
void GaborFR::Init(Size ksize, double sigma,double gamma, int ktype)
{
gaborRealKernels.clear();
gaborImagKernels.clear();
double mu[8]={0,1,2,3,4,5,6,7};
double nu[5]={0,1,2,3,4};
int i,j;
for(i=0;i<5;i++)
{
for(j=0;j<8;j++)
{
gaborRealKernels.push_back(getRealGaborKernel(ksize,sigma,mu[j]*CV_PI/8,nu[i],gamma,ktype));
gaborImagKernels.push_back(getImagGaborKernel(ksize,sigma,mu[j]*CV_PI/8,nu[i],gamma,ktype));
}
}
isInited = true;
}
*/
Mat GaborFR::getImagGaborKernel(Size ksize,
double sigma,
double theta,
double nu,
double gamma,
int ktype)
{
double sigma_x = sigma;
double sigma_y = sigma/gamma;
int nstds = 3;
double kmax = CV_PI/2;
double f = cv::sqrt(2.0);
int xmin, xmax, ymin, ymax;
double c = cos(theta), s = sin(theta);
if( ksize.width > 0 )
{
xmax = ksize.width/2;
}
else//这个和matlab中的结果一样,默认都是19 !
{
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 );
float* pFloat;
double* pDouble;
Mat kernel(ymax - ymin + 1, xmax - xmin + 1, ktype); //初始化gabor的尺寸
double k = kmax/pow(f,nu);
double scaleReal= k*k/sigma_x/sigma_y;
for( int y = ymin; y <= ymax; y++ )
{
if( ktype == CV_32F )
{
pFloat = kernel.ptr<float>(ymax-y);
}
else
{
pDouble = kernel.ptr<double>(ymax-y);
}
for( int x = xmin; x <= xmax; x++ )
{
double xr = x*c + y*s;
double v = scaleReal*exp(-(x*x+y*y)*scaleReal/2);
double temp=sin(k*xr);
v = temp*v;
if( ktype == CV_32F )
{
pFloat[xmax - x]= (float)v;
}
else
{
pDouble[xmax - x] = v;
}
}
}
return kernel;
}
//sigma一般为2*pi
Mat GaborFR::getRealGaborKernel( Size ksize,
double sigma,
double theta,
double nu,
double gamma,
int ktype)
{
double sigma_x = sigma;
double sigma_y = sigma/gamma;
int nstds = 3;
double kmax = CV_PI/2;
double f = cv::sqrt(2.0);
int xmin, xmax, ymin, ymax;
double c = cos(theta), s = sin(theta);
if( ksize.width > 0 )
{
xmax = ksize.width/2;
}
else//这个和matlab中的结果一样,默认都是19 !
{
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 );
float* pFloat;
double* pDouble;
Mat kernel(ymax - ymin + 1, xmax - xmin + 1, ktype);
double k = kmax/pow(f,nu);
double exy = sigma_x*sigma_y/2;
double scaleReal= k*k/sigma_x/sigma_y;
int x,y;
for( y = ymin; y <= ymax; y++ )
{
if( ktype == CV_32F )
{
pFloat = kernel.ptr<float>(ymax-y);
}
else
{
pDouble = kernel.ptr<double>(ymax-y);
}
for( x = xmin; x <= xmax; x++ )
{
double xr = x*c + y*s;
double v = scaleReal*exp(-(x*x+y*y)*scaleReal/2);
double temp=cos(k*xr) - exp(-exy);
v = temp*v;
if( ktype == CV_32F )
{
pFloat[xmax - x]= (float)v;
}
else
{
pDouble[xmax - x] = v;
}
}
}
return kernel;
}
Mat GaborFR::getMagnitude(Mat &real,Mat &imag)
{
CV_Assert(real.type()==imag.type());
CV_Assert(real.size()==imag.size());
int ktype=real.type();
int row = real.rows,col = real.cols;
int i,j;
float* pFloat,*pFloatR,*pFloatI;
double* pDouble,*pDoubleR,*pDoubleI;
Mat kernel(row, col, real.type());
for(i=0;i<row;i++)
{
if( ktype == CV_32FC1 )
{
pFloat = kernel.ptr<float>(i);
pFloatR= real.ptr<float>(i);
pFloatI= imag.ptr<float>(i);
}
else
{
pDouble = kernel.ptr<double>(i);
pDoubleR= real.ptr<double>(i);
pDoubleI= imag.ptr<double>(i);
}
for(j=0;j<col;j++)
{
if( ktype == CV_32FC1 )
{
pFloat[j]= sqrt(pFloatI[j]*pFloatI[j]+pFloatR[j]*pFloatR[j]);
}
else
{
pDouble[j] = sqrt(pDoubleI[j]*pDoubleI[j]+pDoubleR[j]*pDoubleR[j]);
}
}
}
return kernel;
}
Mat GaborFR::getPhase(Mat &real,Mat &imag)
{
CV_Assert(real.type()==imag.type());
CV_Assert(real.size()==imag.size());
int ktype=real.type();
int row = real.rows,col = real.cols;
int i,j;
float* pFloat,*pFloatR,*pFloatI;
double* pDouble,*pDoubleR,*pDoubleI;
Mat kernel(row, col, real.type());
for(i=0;i<row;i++)
{
if( ktype == CV_32FC1 )
{
pFloat = kernel.ptr<float>(i);
pFloatR= real.ptr<float>(i);
pFloatI= imag.ptr<float>(i);
}
else
{
pDouble = kernel.ptr<double>(i);
pDoubleR= real.ptr<double>(i);
pDoubleI= imag.ptr<double>(i);
}
for(j=0;j<col;j++)
{
if( ktype == CV_32FC1 )
{
// if(pFloatI[j]/(pFloatR[j]+pFloatI[j]) > 0.99)
// {
// pFloat[j]=CV_PI/2;
// }
// else
// {
// pFloat[j] = atan(pFloatI[j]/pFloatR[j]);
pFloat[j] = asin(pFloatI[j]/sqrt(pFloatR[j]*pFloatR[j]+pFloatI[j]*pFloatI[j]));
/* }*/
// pFloat[j] = atan2(pFloatI[j],pFloatR[j]);
}//CV_32F
else
{
if(pDoubleI[j]/(pDoubleR[j]+pDoubleI[j]) > 0.99)
{
pDouble[j]=CV_PI/2;
}
else
{
pDouble[j] = atan(pDoubleI[j]/pDoubleR[j]);
}
// pDouble[j]=atan2(pDoubleI[j],pDoubleR[j]);
}//CV_64F
}
}
return kernel;
}
Mat GaborFR::getFilterRealPart(Mat& src,Mat& real)
{
//CV_Assert(real.type()==src.type());
Mat dst;
Mat kernel;
flip(real,kernel,-1);//中心镜面
// filter2D(src,dst,CV_32F,kernel,Point(-1,-1),0,BORDER_CONSTANT);
filter2D(src,dst,CV_32F,kernel,Point(-1,-1),0,BORDER_REPLICATE);
return dst;
}
Mat GaborFR::getFilterImagPart(Mat& src,Mat& imag)
{
//CV_Assert(imag.type()==src.type());
Mat dst;
Mat kernel;
flip(imag,kernel,-1);//中心镜面
// filter2D(src,dst,CV_32F,kernel,Point(-1,-1),0,BORDER_CONSTANT);
filter2D(src,dst,CV_32F,kernel,Point(-1,-1),0,BORDER_REPLICATE);
return dst;
}
void GaborFR::getFilterRealImagPart(Mat& src,Mat& real,Mat& imag,Mat &outReal,Mat &outImag)
{
outReal=getFilterRealPart(src,real);
outImag=getFilterImagPart(src,imag);
}
本人能力有限,错误在所难免。敬请赐教
程序下载:http://download.csdn.net/detail/u012507022/9378491
(VS2010+opencv2.4.11)