性别识别(中)
基于上一篇博文提取的人脸图像,本文运用Gabor小波对人脸图像提取特征,再运用PCA对其降维处理,最后用SVM训练一个性别分类器。这里不再深入介绍Gabor小波和PCA的原理。很多博文讲解的很详细。
可参考:http://blog.****.net/jinshengtao/article/details/17797641
http://www.cnblogs.com/emouse/p/3611256.html
http://blog.****.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);
-
}
本人能力有限,错误在所难免。敬请赐教
转自:https://blog.****.net/u012507022/article/details/50979003