基于上一篇博文提取的人脸图像,本文运用Gabor小波对人脸图像提取特征,再运用PCA对其降维处理,最后用SVM训练一个性别分类器。这里不再深入介绍Gabor小波和PCA的原理。很多博文讲解的很详细。
可参考: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)