性别识别(中)

基于上一篇博文提取的人脸图像,本文运用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

  1. #include <iostream>
  2. #include <fstream>
  3. #include <opencv2/opencv.hpp>
  4. #include <opencv2/ml/ml.hpp>
  5. #include "GaborFR.h"
  6. using namespace std;
  7. using namespace cv;
  8. #define manNO 409 //man样本个数
  9. #define womanNO 287 //woman样本个数
  10. int iSize=20;// Gabor的scale
  11. int main()
  12. {
  13. int DescriptorDim=200;
  14. string ImgName;//图片名(绝对路径)
  15. ifstream finMan("man.txt");//man样本图片的文件名列表
  16. ifstream finWoman("woman.txt");//woman样本图片的文件名列表
  17. Mat ROI;
  18. Mat Gabor_feature;
  19. Mat PCA_feature;
  20. Mat sampleFeatureMat;//所有训练样本的特征向量组成的矩阵,行数等于所有样本的个数,列数等于Gabor描述子维数
  21. Mat sampleLabelMat; //训练样本的类别向量,行数等于所有样本的个数,列数等于1;1表示man,-1表示woman
  22. Vector<Mat> GaborReal;
  23. Vector<Mat> GaborImag;
  24. //生成Gabor 模板
  25. for(int i=0;i<8;i++)
  26. {
  27. for(int j=0;j<5;j++)
  28. {
  29. Mat M1= GaborFR::getRealGaborKernel(Size(iSize,iSize),
  30. 2*CV_PI,
  31. i*CV_PI/8+CV_PI/2,
  32. j,
  33. 1);
  34. Mat M2 = GaborFR::getImagGaborKernel(Size(iSize,iSize),
  35. 2*CV_PI,
  36. i*CV_PI/8+CV_PI/2,
  37. j,
  38. 1);
  39. GaborReal.push_back(M1);
  40. GaborImag.push_back(M2);
  41. }
  42. }
  43. //依次读取man样本图片
  44. for(int num=0; num<manNO && getline(finMan,ImgName); num++)
  45. {
  46. cout<<"处理:"<<ImgName<<endl;
  47. ImgName = "D:\\Mycode\\mantrain_cut\\" + ImgName+".png";
  48. ROI= imread(ImgName,0);//读取图片
  49. if(ROI.data ==0)
  50. {
  51. printf("[error] 没有图片\n");
  52. return -5;
  53. }
  54. //Gabor变换提取特征*************************
  55. for(int i=0;i<8;i++)
  56. {
  57. for(int j=0;j<5;j++)
  58. {
  59. Mat outR,outI,M_Magnitude;
  60. GaborFR::getFilterRealImagPart(ROI,GaborReal[i*5+j],GaborImag[i*5+j],outR,outI);
  61. M_Magnitude=GaborFR::getMagnitude(outR,outI);
  62. //cartToPolar( outR, outI, M_Magnitude, Angle, false ); //计算幅值和相角
  63. normalize(M_Magnitude,M_Magnitude,0,1,CV_MINMAX,CV_32F);
  64. Mat line= M_Magnitude.reshape(0,1);
  65. Gabor_feature.push_back(line);
  66. }
  67. }
  68. //PCA降维************************************************************
  69. PCA pca(Gabor_feature, cv::Mat(), CV_PCA_DATA_AS_ROW,5);
  70. Mat dst=pca.project(Gabor_feature) ;
  71. Mat line =dst.reshape(0,1);
  72. //处理第一个样本时初始化特征向量矩阵和类别矩阵,因为只有知道了特征向量的维数才能初始化特征向量矩阵
  73. if( 0 == num )
  74. {
  75. //初始化所有训练样本的特征向量组成的矩阵,行数等于所有样本的个数,列数等于Gabor降维后描述子维数sampleFeatureMat
  76. sampleFeatureMat = Mat::zeros(manNO+womanNO, DescriptorDim, CV_32FC1);
  77. //初始化训练样本的类别向量,行数等于所有样本的个数,列数等于1;1表示有人,0表示无人
  78. sampleLabelMat = Mat::zeros(manNO+womanNO, 1, CV_32FC1);
  79. }
  80. //将计算好的Gabor降维后描述子复制到样本特征矩阵sampleFeatureMat
  81. for(int i=0; i<DescriptorDim; i++)
  82. sampleFeatureMat.at<float>(num,i) = line.at<float>(0,i);//第num个样本的特征向量中的第i个元素
  83. sampleLabelMat.at<float>(num,0) = 1;//man样本类别为1
  84. }
  85. //依次读取woman样本图片,生成Gabor描述子并降维
  86. for(int num=0; num<womanNO && getline(finWoman,ImgName); num++)
  87. {
  88. cout<<"处理:"<<ImgName<<endl;
  89. ImgName = "D:\\Mycode\\womantrain_cut\\" + ImgName+".png";
  90. ROI= imread(ImgName,0);//读取图片
  91. if(ROI.data ==0)
  92. {
  93. printf("[error] 没有图片\n");
  94. return -5;
  95. }
  96. //Gabor变换提取特征*************************
  97. for(int i=0;i<8;i++)
  98. {
  99. for(int j=0;j<5;j++)
  100. {
  101. Mat outR,outI,M_Magnitude;
  102. GaborFR::getFilterRealImagPart(ROI,GaborReal[i*5+j],GaborImag[i*5+j],outR,outI);
  103. M_Magnitude=GaborFR::getMagnitude(outR,outI);
  104. //cartToPolar( outR, outI, M_Magnitude, Angle, false ); //计算幅值和相角
  105. normalize(M_Magnitude,M_Magnitude,0,1,CV_MINMAX,CV_32F);
  106. Mat line= M_Magnitude.reshape(0,1);
  107. Gabor_feature.push_back(line);
  108. }
  109. }
  110. //PCA降维************************************************************
  111. PCA pca( Gabor_feature, cv::Mat(), CV_PCA_DATA_AS_ROW,5);
  112. Mat dst=pca.project(Gabor_feature) ;
  113. Mat line =dst.reshape(0,1);
  114. for(int i=0; i<DescriptorDim; i++)
  115. sampleFeatureMat.at<float>(num+manNO,i) = line.at<float>(0,i);//第PosSamNO+num个样本的特征向量中的第i个元素
  116. sampleLabelMat.at<float>(num+manNO,0) = -1;//woman样本类别为-1
  117. }
  118. //训练SVM分类器
  119. //迭代终止条件,当迭代满1000次或误差小于FLT_EPSILON时停止迭代
  120. CvSVM svm; //SVM分类器
  121. CvTermCriteria criteria = cvTermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 1000, FLT_EPSILON);
  122. //SVM参数:SVM类型为C_SVC;线性核函数;松弛因子C=0.01
  123. CvSVMParams param(CvSVM::C_SVC, CvSVM::LINEAR, 0, 1, 0, 0.01, 0, 0, 0, criteria);
  124. cout<<"开始训练SVM分类器"<<endl;
  125. svm.train(sampleFeatureMat, sampleLabelMat, Mat(), Mat(), param);//训练分类器
  126. cout<<"训练完成"<<endl;
  127. svm.save("SVM_PCA.xml");//将训练好的SVM模型保存为xml文件
  128. waitKey();//注意:imshow之后必须加waitKey,否则无法显示图像
  129. system("pause");
  130. }

 GarborFR.hpp 

  1. #include "opencv2\opencv.hpp"
  2. #include <vector>
  3. using namespace std;
  4. using namespace cv;
  5. class GaborFR
  6. {
  7. public:
  8. GaborFR();
  9. static Mat getImagGaborKernel(Size ksize, double sigma, double theta,
  10. double nu,double gamma=1, int ktype= CV_32F);
  11. static Mat getRealGaborKernel( Size ksize, double sigma, double theta,
  12. double nu,double gamma=1, int ktype= CV_32F);
  13. static Mat getPhase(Mat &real,Mat &imag);
  14. static Mat getMagnitude(Mat &real,Mat &imag);
  15. static void getFilterRealImagPart(Mat& src,Mat& real,Mat& imag,Mat &outReal,Mat &outImag);
  16. static Mat getFilterRealPart(Mat& src,Mat& real);
  17. static Mat getFilterImagPart(Mat& src,Mat& imag);
  18. /*
  19. void Init(Size ksize=Size(19,19), double sigma=2*CV_PI,
  20. double gamma=1, int ktype=CV_32FC1);
  21. */
  22. private:
  23. //vector<Mat> gaborRealKernels;
  24. //vector<Mat> gaborImagKernels;
  25. bool isInited;
  26. };

  Gabor.cpp

  1. //#include "StdAfx.h"
  2. #include "GaborFR.h"
  3. GaborFR::GaborFR()
  4. {
  5. isInited = false;
  6. }
  7. /*
  8. void GaborFR::Init(Size ksize, double sigma,double gamma, int ktype)
  9. {
  10. gaborRealKernels.clear();
  11. gaborImagKernels.clear();
  12. double mu[8]={0,1,2,3,4,5,6,7};
  13. double nu[5]={0,1,2,3,4};
  14. int i,j;
  15. for(i=0;i<5;i++)
  16. {
  17. for(j=0;j<8;j++)
  18. {
  19. gaborRealKernels.push_back(getRealGaborKernel(ksize,sigma,mu[j]*CV_PI/8,nu[i],gamma,ktype));
  20. gaborImagKernels.push_back(getImagGaborKernel(ksize,sigma,mu[j]*CV_PI/8,nu[i],gamma,ktype));
  21. }
  22. }
  23. isInited = true;
  24. }
  25. */
  26. Mat GaborFR::getImagGaborKernel(Size ksize,
  27. double sigma,
  28. double theta,
  29. double nu,
  30. double gamma,
  31. int ktype)
  32. {
  33. double sigma_x = sigma;
  34. double sigma_y = sigma/gamma;
  35. int nstds = 3;
  36. double kmax = CV_PI/2;
  37. double f = cv::sqrt(2.0);
  38. int xmin, xmax, ymin, ymax;
  39. double c = cos(theta), s = sin(theta);
  40. if( ksize.width > 0 )
  41. {
  42. xmax = ksize.width/2;
  43. }
  44. else//这个和matlab中的结果一样,默认都是19 !
  45. {
  46. xmax = cvRound(std::max(fabs(nstds*sigma_x*c), fabs(nstds*sigma_y*s)));
  47. }
  48. if( ksize.height > 0 )
  49. {
  50. ymax = ksize.height/2;
  51. }
  52. else
  53. {
  54. ymax = cvRound(std::max(fabs(nstds*sigma_x*s), fabs(nstds*sigma_y*c)));
  55. }
  56. xmin = -xmax;
  57. ymin = -ymax;
  58. CV_Assert( ktype == CV_32F || ktype == CV_64F );
  59. float* pFloat;
  60. double* pDouble;
  61. Mat kernel(ymax - ymin + 1, xmax - xmin + 1, ktype); //初始化gabor的尺寸
  62. double k = kmax/pow(f,nu);
  63. double scaleReal= k*k/sigma_x/sigma_y;
  64. for( int y = ymin; y <= ymax; y++ )
  65. {
  66. if( ktype == CV_32F )
  67. {
  68. pFloat = kernel.ptr<float>(ymax-y);
  69. }
  70. else
  71. {
  72. pDouble = kernel.ptr<double>(ymax-y);
  73. }
  74. for( int x = xmin; x <= xmax; x++ )
  75. {
  76. double xr = x*c + y*s;
  77. double v = scaleReal*exp(-(x*x+y*y)*scaleReal/2);
  78. double temp=sin(k*xr);
  79. v = temp*v;
  80. if( ktype == CV_32F )
  81. {
  82. pFloat[xmax - x]= (float)v;
  83. }
  84. else
  85. {
  86. pDouble[xmax - x] = v;
  87. }
  88. }
  89. }
  90. return kernel;
  91. }
  92. //sigma一般为2*pi
  93. Mat GaborFR::getRealGaborKernel( Size ksize,
  94. double sigma,
  95. double theta,
  96. double nu,
  97. double gamma,
  98. int ktype)
  99. {
  100. double sigma_x = sigma;
  101. double sigma_y = sigma/gamma;
  102. int nstds = 3;
  103. double kmax = CV_PI/2;
  104. double f = cv::sqrt(2.0);
  105. int xmin, xmax, ymin, ymax;
  106. double c = cos(theta), s = sin(theta);
  107. if( ksize.width > 0 )
  108. {
  109. xmax = ksize.width/2;
  110. }
  111. else//这个和matlab中的结果一样,默认都是19 !
  112. {
  113. xmax = cvRound(std::max(fabs(nstds*sigma_x*c), fabs(nstds*sigma_y*s)));
  114. }
  115. if( ksize.height > 0 )
  116. ymax = ksize.height/2;
  117. else
  118. ymax = cvRound(std::max(fabs(nstds*sigma_x*s), fabs(nstds*sigma_y*c)));
  119. xmin = -xmax;
  120. ymin = -ymax;
  121. CV_Assert( ktype == CV_32F || ktype == CV_64F );
  122. float* pFloat;
  123. double* pDouble;
  124. Mat kernel(ymax - ymin + 1, xmax - xmin + 1, ktype);
  125. double k = kmax/pow(f,nu);
  126. double exy = sigma_x*sigma_y/2;
  127. double scaleReal= k*k/sigma_x/sigma_y;
  128. int x,y;
  129. for( y = ymin; y <= ymax; y++ )
  130. {
  131. if( ktype == CV_32F )
  132. {
  133. pFloat = kernel.ptr<float>(ymax-y);
  134. }
  135. else
  136. {
  137. pDouble = kernel.ptr<double>(ymax-y);
  138. }
  139. for( x = xmin; x <= xmax; x++ )
  140. {
  141. double xr = x*c + y*s;
  142. double v = scaleReal*exp(-(x*x+y*y)*scaleReal/2);
  143. double temp=cos(k*xr) - exp(-exy);
  144. v = temp*v;
  145. if( ktype == CV_32F )
  146. {
  147. pFloat[xmax - x]= (float)v;
  148. }
  149. else
  150. {
  151. pDouble[xmax - x] = v;
  152. }
  153. }
  154. }
  155. return kernel;
  156. }
  157. Mat GaborFR::getMagnitude(Mat &real,Mat &imag)
  158. {
  159. CV_Assert(real.type()==imag.type());
  160. CV_Assert(real.size()==imag.size());
  161. int ktype=real.type();
  162. int row = real.rows,col = real.cols;
  163. int i,j;
  164. float* pFloat,*pFloatR,*pFloatI;
  165. double* pDouble,*pDoubleR,*pDoubleI;
  166. Mat kernel(row, col, real.type());
  167. for(i=0;i<row;i++)
  168. {
  169. if( ktype == CV_32FC1 )
  170. {
  171. pFloat = kernel.ptr<float>(i);
  172. pFloatR= real.ptr<float>(i);
  173. pFloatI= imag.ptr<float>(i);
  174. }
  175. else
  176. {
  177. pDouble = kernel.ptr<double>(i);
  178. pDoubleR= real.ptr<double>(i);
  179. pDoubleI= imag.ptr<double>(i);
  180. }
  181. for(j=0;j<col;j++)
  182. {
  183. if( ktype == CV_32FC1 )
  184. {
  185. pFloat[j]= sqrt(pFloatI[j]*pFloatI[j]+pFloatR[j]*pFloatR[j]);
  186. }
  187. else
  188. {
  189. pDouble[j] = sqrt(pDoubleI[j]*pDoubleI[j]+pDoubleR[j]*pDoubleR[j]);
  190. }
  191. }
  192. }
  193. return kernel;
  194. }
  195. Mat GaborFR::getPhase(Mat &real,Mat &imag)
  196. {
  197. CV_Assert(real.type()==imag.type());
  198. CV_Assert(real.size()==imag.size());
  199. int ktype=real.type();
  200. int row = real.rows,col = real.cols;
  201. int i,j;
  202. float* pFloat,*pFloatR,*pFloatI;
  203. double* pDouble,*pDoubleR,*pDoubleI;
  204. Mat kernel(row, col, real.type());
  205. for(i=0;i<row;i++)
  206. {
  207. if( ktype == CV_32FC1 )
  208. {
  209. pFloat = kernel.ptr<float>(i);
  210. pFloatR= real.ptr<float>(i);
  211. pFloatI= imag.ptr<float>(i);
  212. }
  213. else
  214. {
  215. pDouble = kernel.ptr<double>(i);
  216. pDoubleR= real.ptr<double>(i);
  217. pDoubleI= imag.ptr<double>(i);
  218. }
  219. for(j=0;j<col;j++)
  220. {
  221. if( ktype == CV_32FC1 )
  222. {
  223. // if(pFloatI[j]/(pFloatR[j]+pFloatI[j]) > 0.99)
  224. // {
  225. // pFloat[j]=CV_PI/2;
  226. // }
  227. // else
  228. // {
  229. // pFloat[j] = atan(pFloatI[j]/pFloatR[j]);
  230. pFloat[j] = asin(pFloatI[j]/sqrt(pFloatR[j]*pFloatR[j]+pFloatI[j]*pFloatI[j]));
  231. /* }*/
  232. // pFloat[j] = atan2(pFloatI[j],pFloatR[j]);
  233. }//CV_32F
  234. else
  235. {
  236. if(pDoubleI[j]/(pDoubleR[j]+pDoubleI[j]) > 0.99)
  237. {
  238. pDouble[j]=CV_PI/2;
  239. }
  240. else
  241. {
  242. pDouble[j] = atan(pDoubleI[j]/pDoubleR[j]);
  243. }
  244. // pDouble[j]=atan2(pDoubleI[j],pDoubleR[j]);
  245. }//CV_64F
  246. }
  247. }
  248. return kernel;
  249. }
  250. Mat GaborFR::getFilterRealPart(Mat& src,Mat& real)
  251. {
  252. //CV_Assert(real.type()==src.type());
  253. Mat dst;
  254. Mat kernel;
  255. flip(real,kernel,-1);//中心镜面
  256. // filter2D(src,dst,CV_32F,kernel,Point(-1,-1),0,BORDER_CONSTANT);
  257. filter2D(src,dst,CV_32F,kernel,Point(-1,-1),0,BORDER_REPLICATE);
  258. return dst;
  259. }
  260. Mat GaborFR::getFilterImagPart(Mat& src,Mat& imag)
  261. {
  262. //CV_Assert(imag.type()==src.type());
  263. Mat dst;
  264. Mat kernel;
  265. flip(imag,kernel,-1);//中心镜面
  266. // filter2D(src,dst,CV_32F,kernel,Point(-1,-1),0,BORDER_CONSTANT);
  267. filter2D(src,dst,CV_32F,kernel,Point(-1,-1),0,BORDER_REPLICATE);
  268. return dst;
  269. }
  270. void GaborFR::getFilterRealImagPart(Mat& src,Mat& real,Mat& imag,Mat &outReal,Mat &outImag)
  271. {
  272. outReal=getFilterRealPart(src,real);
  273. outImag=getFilterImagPart(src,imag);
  274. }

  本人能力有限,错误在所难免。敬请赐教

转自:https://blog.****.net/u012507022/article/details/50979003