本文主要记录自己在学习稀疏表示在人脸识别中的应用所遇到的问题作一简单的总结。
信号的稀疏表示并不是新的东西。我们很早就一直在利用这一特性。例如,最简单的JPEG图像压缩算法。原始的图像信号经过DCT变换之后,只有极少数元素是非零的,而大部分元素都等于零或者说接近于零。这就是信号的稀疏性。
任何模型都有建模的假设条件。压缩感知,正是利用的信号的稀疏性这个假设。对于我们处理的信号,时域上本身就具有稀疏性的信号是很少的。但是,我们总能找到某种变换,使得在某个变换域之后信号具有稀疏性。这种变换是很多的,最常见的就是DCT变换,小波变换,gabor变换等。
然而,这种正交变换是传统视频图像处理采用的方法。目前所采用的一般不是正交变换。它是基于样本采样的。或者说是通过大量图像数据学习得到的,其结果称作字典,字典中的每一个元素称作原子。相关的学习算法称作字典学习。常见的算法例如K-SVD算法。学习的目标函数是找到所有样本在这些原子的线性组合表示下是稀疏的,即同时估计字典和稀疏表示的系数这两个目标。
压缩感知和稀疏表示其实是有些不同的。压缩感知的字典是固定的,在压缩感知的术语里面其字典叫做测量矩阵。但压缩感知的恢复算法和稀疏表示是同一个问题。他们都可以归结为带约束条件的L1范数最小化问题。求解这类泛函的优化有很多种方法。早在80年代,统计学中Lasso问题,其实和稀疏分解的优化目标泛函是等价的。而求解统计学中lasso 问题的LARS算法很早就被提出了,故我们还可以通过统计学的LARS算法求解稀疏表示问题。目前很多统计学软件包都自带LARS算法的求解器。
人脸的稀疏表示是基于光照模型。即一张人脸图像,可以用数据库中同一个人所有的人脸图像的线性组合表示。而对于数据库中其它人的脸,其线性组合的系数理论上为零。由于数据库中一般有很多个不同的人脸的多张图像,如果把数据库中所有的图像的线性组合来表示这张给定的测试人脸,其系数向量是稀疏的。因为除了这张和同一个人的人脸的图像组合系数不为零外,其它的系数都为零。
上述模型导出了基于稀疏表示的另外一个很强的假设条件:所有的人脸图像必须是事先严格对齐的。否则,稀疏性很难满足。换言之,对于表情变化,姿态角度变化的人脸都不满足稀疏性这个假设。所以,经典的稀疏脸方法很难用于真实的应用场景。
稀疏脸很强的地方在于对噪声相当鲁棒,相关文献表明,即使人脸图像被80%的随机噪声干扰,仍然能够得到很高的识别率。稀疏脸另外一个很强的地方在于对于部分遮挡的情况,例如戴围巾,戴眼镜等,仍然能够保持较高的识别性能。上述两点,是其它任何传统的人脸识别方法所不具有的。
一谈到识别问题,大家都会想到要用机器学习的方法。先进行训练,把训练的结果以模板的形式存储到数据库上;真实应用环境的时候,把测试样本经过特征提取之后,和数据库中的模板进行比对,查询得到一个最相似的类别作为识别结果。往往,机器训练的时间都超级长,几天,几个礼拜乃至几个月,那是常见的事情;识别的时间一般是很小的。典型的例如人脸检测问题。这是可以接受的,因为训练一般都是离线的。
然而,基于稀疏分解的人脸识别是不需要训练的,或者说训练及其简单。基于稀疏表示的人脸识别,其稀疏表示用的字典直接由训练所用的全部图像构成,而不需要经过字典学习【也有一些改进算法,针对字典进行学习的】。当然,一般是经过简单的特征提取。由于稀疏表示的方法对使用什么特征并不敏感。故而,其训练过程只需要把原始图像数据经过简单的处理之后排列成一个很大的三维矩阵存储到数据库里面就可以了。
关键的问题在于,当实际环境中来了一张人脸图像之后,去求解这张人脸图像在数据库所有图像上的稀疏表示,这个求解算法,一般比较耗时。尽管有很多的方法被提出,但是对于实时应用问题,依然没法满足。所以,问题的关键还是归结于L1范数最小化问题上来。
L1范数最小化问题已经有很多种快速求解方法,这里主要包括有梯度投影Gradient Projection,同伦算法,迭代阈值收缩,领域梯度Proximal Gradient,增广拉格朗日方法,这几种方法都比正交匹配追踪算法OMP要高效的多。上述几种快速算法中,采用增广拉格朗日的对偶实现相比其它的快速算法要更好。最近流行的Spit Bregman算法也是不错的选择。
CVPR2011 Meng Yang,Robost Sparse Coding for Face Recognition. 鲁棒的稀疏编码算法。该文作者没有直接求解稀疏编码问题,而是求解Lasso问题,因为Lasso问题的解和稀疏编码的解是等价的。在传统的SRC框架下,编码误差使用L2范数来度量的,这也就意味着编码误差满足高斯分布,然而,当人脸图像出现遮挡和噪声污染的情况下,并非如此。在字典学习框架下,这样的字典是有噪声的。该文作者对原始Lasso问题进行改进,求解加权L1范数约束的线性回归问题。Lasso问题描述如下:
加权Lasso问题的目标函数描述如下:
此算法的关键还在于权重系数的确定,文中采用的是logistic函数,而具体的实现则是通过迭代估计学习得到。该方法基于这样一个事实:被遮挡或噪声干扰的像素点赋予较小的权重,而其它像素点的权重相对较大。具体迭代算法采用经典的迭代重加权算法框架,当然内部嵌入的稀疏编码的求解过程。此算法在50%遮挡面积的情况下取得的更好更满意的结果。但是文中没有比较计算时间上的优略而直说和SRC框架差不多。
// FaceRecognize.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include "opencv2/opencv.hpp" using namespace std; using namespace cv; using namespace kl1p; int _tmain(int argc, _TCHAR* argv[]) { Mat imageArray[150],image,image1; imageArray[0] = imread("database\\001\\01.jpg");//001 imageArray[1] = imread("database\\001\\02.jpg"); imageArray[2] = imread("database\\001\\03.jpg"); imageArray[3] = imread("database\\001\\04.jpg"); imageArray[4] = imread("database\\001\\05.jpg"); imageArray[5] = imread("database\\001\\06.jpg"); imageArray[6] = imread("database\\001\\07.jpg"); imageArray[7] = imread("database\\001\\08.jpg"); imageArray[8] = imread("database\\001\\09.jpg"); imageArray[9] = imread("database\\001\\10.jpg"); imageArray[10] = imread("database\\002\\01.jpg");//002 imageArray[11] = imread("database\\002\\02.jpg"); imageArray[12] = imread("database\\002\\03.jpg"); imageArray[13] = imread("database\\002\\04.jpg"); imageArray[14] = imread("database\\002\\05.jpg"); imageArray[15] = imread("database\\002\\06.jpg"); imageArray[16] = imread("database\\002\\07.jpg"); imageArray[17] = imread("database\\002\\08.jpg"); imageArray[18] = imread("database\\002\\09.jpg"); imageArray[19] = imread("database\\002\\10.jpg"); imageArray[20] = imread("database\\003\\01.jpg");//003 imageArray[21] = imread("database\\003\\02.jpg"); imageArray[22] = imread("database\\003\\03.jpg"); imageArray[23] = imread("database\\003\\04.jpg"); imageArray[24] = imread("database\\003\\05.jpg"); imageArray[25] = imread("database\\003\\06.jpg"); imageArray[26] = imread("database\\003\\07.jpg"); imageArray[27] = imread("database\\003\\08.jpg"); imageArray[28] = imread("database\\003\\09.jpg"); imageArray[29] = imread("database\\003\\10.jpg"); imageArray[30] = imread("database\\004\\01.jpg");//004 imageArray[31] = imread("database\\004\\02.jpg"); imageArray[32] = imread("database\\004\\03.jpg"); imageArray[33] = imread("database\\004\\04.jpg"); imageArray[34] = imread("database\\004\\05.jpg"); imageArray[35] = imread("database\\004\\06.jpg"); imageArray[36] = imread("database\\004\\07.jpg"); imageArray[37] = imread("database\\004\\08.jpg"); imageArray[38] = imread("database\\004\\09.jpg"); imageArray[39] = imread("database\\004\\10.jpg"); imageArray[40] = imread("database\\005\\01.jpg");//005 imageArray[41] = imread("database\\005\\02.jpg"); imageArray[42] = imread("database\\005\\03.jpg"); imageArray[43] = imread("database\\005\\04.jpg"); imageArray[44] = imread("database\\005\\05.jpg"); imageArray[45] = imread("database\\005\\06.jpg"); imageArray[46] = imread("database\\005\\07.jpg"); imageArray[47] = imread("database\\005\\08.jpg"); imageArray[48] = imread("database\\005\\09.jpg"); imageArray[49] = imread("database\\005\\10.jpg"); imageArray[50] = imread("database\\006\\01.jpg");//006 imageArray[51] = imread("database\\006\\02.jpg"); imageArray[52] = imread("database\\006\\03.jpg"); imageArray[53] = imread("database\\006\\04.jpg"); imageArray[54] = imread("database\\006\\05.jpg"); imageArray[55] = imread("database\\006\\06.jpg"); imageArray[56] = imread("database\\006\\07.jpg"); imageArray[57] = imread("database\\006\\08.jpg"); imageArray[58] = imread("database\\006\\09.jpg"); imageArray[59] = imread("database\\006\\10.jpg"); imageArray[60] = imread("database\\007\\01.jpg");//007 imageArray[61] = imread("database\\007\\02.jpg"); imageArray[62] = imread("database\\007\\03.jpg"); imageArray[63] = imread("database\\007\\04.jpg"); imageArray[64] = imread("database\\007\\05.jpg"); imageArray[65] = imread("database\\007\\06.jpg"); imageArray[66] = imread("database\\007\\07.jpg"); imageArray[67] = imread("database\\007\\08.jpg"); imageArray[68] = imread("database\\007\\09.jpg"); imageArray[69] = imread("database\\007\\10.jpg"); imageArray[70] = imread("database\\008\\01.jpg");//008 imageArray[71] = imread("database\\008\\02.jpg"); imageArray[72] = imread("database\\008\\03.jpg"); imageArray[73] = imread("database\\008\\04.jpg"); imageArray[74] = imread("database\\008\\05.jpg"); imageArray[75] = imread("database\\008\\06.jpg"); imageArray[76] = imread("database\\008\\07.jpg"); imageArray[77] = imread("database\\008\\08.jpg"); imageArray[78] = imread("database\\008\\09.jpg"); imageArray[79] = imread("database\\008\\10.jpg"); imageArray[80] = imread("database\\009\\01.jpg");//009 imageArray[81] = imread("database\\009\\02.jpg"); imageArray[82] = imread("database\\009\\03.jpg"); imageArray[83] = imread("database\\009\\04.jpg"); imageArray[84] = imread("database\\009\\05.jpg"); imageArray[85] = imread("database\\009\\06.jpg"); imageArray[86] = imread("database\\009\\07.jpg"); imageArray[87] = imread("database\\009\\08.jpg"); imageArray[88] = imread("database\\009\\09.jpg"); imageArray[89] = imread("database\\009\\10.jpg"); imageArray[90] = imread("database\\010\\01.jpg");//010 imageArray[91] = imread("database\\010\\02.jpg"); imageArray[92] = imread("database\\010\\03.jpg"); imageArray[93] = imread("database\\010\\04.jpg"); imageArray[94] = imread("database\\010\\05.jpg"); imageArray[95] = imread("database\\010\\06.jpg"); imageArray[96] = imread("database\\010\\07.jpg"); imageArray[97] = imread("database\\010\\08.jpg"); imageArray[98] = imread("database\\010\\09.jpg"); imageArray[99] = imread("database\\010\\10.jpg"); imageArray[100] = imread("database\\011\\01.jpg");//011 imageArray[101] = imread("database\\011\\02.jpg"); imageArray[102] = imread("database\\011\\03.jpg"); imageArray[103] = imread("database\\011\\04.jpg"); imageArray[104] = imread("database\\011\\05.jpg"); imageArray[105] = imread("database\\011\\06.jpg"); imageArray[106] = imread("database\\011\\07.jpg"); imageArray[107] = imread("database\\011\\08.jpg"); imageArray[108] = imread("database\\011\\09.jpg"); imageArray[109] = imread("database\\011\\10.jpg"); imageArray[110] = imread("database\\012\\01.jpg");//012 imageArray[111] = imread("database\\012\\02.jpg"); imageArray[112] = imread("database\\012\\03.jpg"); imageArray[113] = imread("database\\012\\04.jpg"); imageArray[114] = imread("database\\012\\05.jpg"); imageArray[115] = imread("database\\012\\06.jpg"); imageArray[116] = imread("database\\012\\07.jpg"); imageArray[117] = imread("database\\012\\08.jpg"); imageArray[118] = imread("database\\012\\09.jpg"); imageArray[119] = imread("database\\012\\10.jpg"); imageArray[120] = imread("database\\013\\01.jpg");//013 imageArray[121] = imread("database\\013\\02.jpg"); imageArray[122] = imread("database\\013\\03.jpg"); imageArray[123] = imread("database\\013\\04.jpg"); imageArray[124] = imread("database\\013\\05.jpg"); imageArray[125] = imread("database\\013\\06.jpg"); imageArray[126] = imread("database\\013\\07.jpg"); imageArray[127] = imread("database\\013\\08.jpg"); imageArray[128] = imread("database\\013\\09.jpg"); imageArray[129] = imread("database\\013\\10.jpg"); imageArray[130] = imread("database\\014\\01.jpg");//014 imageArray[131] = imread("database\\014\\02.jpg"); imageArray[132] = imread("database\\014\\03.jpg"); imageArray[133] = imread("database\\014\\04.jpg"); imageArray[134] = imread("database\\014\\05.jpg"); imageArray[135] = imread("database\\014\\06.jpg"); imageArray[136] = imread("database\\014\\07.jpg"); imageArray[137] = imread("database\\014\\08.jpg"); imageArray[138] = imread("database\\014\\09.jpg"); imageArray[139] = imread("database\\014\\10.jpg"); imageArray[140] = imread("database\\015\\01.jpg");//015 imageArray[141] = imread("database\\015\\02.jpg"); imageArray[142] = imread("database\\015\\03.jpg"); imageArray[143] = imread("database\\015\\04.jpg"); imageArray[144] = imread("database\\015\\05.jpg"); imageArray[145] = imread("database\\015\\06.jpg"); imageArray[146] = imread("database\\015\\07.jpg"); imageArray[147] = imread("database\\015\\08.jpg"); imageArray[148] = imread("database\\015\\09.jpg"); imageArray[149] = imread("database\\015\\10.jpg"); int i,j,k,g; double sum; arma::Mat<klab::DoubleReal> A(20139,150); arma::Mat<klab::DoubleReal> A1(500,150); arma::Col<klab::DoubleReal> Y(20139); arma::Col<klab::DoubleReal> Y1(500); arma::Col<klab::DoubleReal> W(650); arma::Mat<klab::DoubleReal> R(500,20139); arma::Mat<klab::DoubleReal> B(500,650); arma::Col<klab::DoubleReal> x1(150); arma::Col<klab::DoubleReal> e1(500); fstream f("R.txt",ios::in); for(i=0;i<500;i++) { for(j=0;j<20139;j++) f>>R(i,j); } f.close(); for(g=0;g<150;g++) //赋值B矩阵 { image = imageArray[g]; if(image.channels()==3) { //若是多通道彩色图,则把图片转换为单通道灰色图 cvtColor(image,image1,CV_BGR2GRAY); if(image1.channels()==1) image1.copyTo(image); } sum=0; for(i=0;i<image.cols;i++){ for(j=0;j<image.rows;j++){ sum = sum + image.at<uchar>(j,i)*image.at<uchar>(j,i); } } sum=sqrt(sum); k=0; for(i=0;i<image.cols;i++){ for(j=0;j<image.rows;j++){ A(k++,g)=image.at<uchar>(j,i)/sum; } } } A1=R*A;//A1(500,150) R(500,20139) A(20139,150) for(i=0;i<150;i++) { sum=0; for(j=0;j<500;j++) { sum=sum+A1(j,i)*A1(j,i); } sum=sqrt(sum); for(j=0;j<500;j++) { A1(j,i)=A1(j,i)/sum; } } for(i=0;i<500;i++) { for(j=0;j<150;j++) B(i,j)=A1(i,j); for(;j<650;j++) { if(j==i+500) B(i,j)=1; B(i,j)=0; } }//B(500,650) Mat imageTest[15]; imageTest[0] = imread("database\\test\\1.jpg"); imageTest[1] = imread("database\\test\\2.jpg"); imageTest[2] = imread("database\\test\\3.jpg"); imageTest[3] = imread("database\\test\\4.jpg"); imageTest[4] = imread("database\\test\\5.jpg"); imageTest[5] = imread("database\\test\\6.jpg"); imageTest[6] = imread("database\\test\\7.jpg"); imageTest[7] = imread("database\\test\\8.jpg"); imageTest[8] = imread("database\\test\\9.jpg"); imageTest[9] = imread("database\\test\\10.jpg"); imageTest[10] = imread("database\\test\\11.jpg"); imageTest[11] = imread("database\\test\\12.jpg"); imageTest[12] = imread("database\\test\\13.jpg"); imageTest[13] = imread("database\\test\\14.jpg"); imageTest[14] = imread("database\\test\\15.jpg"); int o; for(o=0;o<15;o++){ k=0; image = imageTest[o]; //赋值Y矩阵 //image = imread("database\\001\\02.jpg"); if(image.channels()==3) { //若是多通道彩色图,则把图片转换为单通道灰色图 Mat image1; cvtColor(image,image1,CV_BGR2GRAY); if(image1.channels()==1) image1.copyTo(image); } sum=0; for(i=0;i<image.cols;i++){ for(j=0;j<image.rows;j++){ sum = sum + image.at<uchar>(j,i)*image.at<uchar>(j,i); } } sum=sqrt(sum); for(i=0;i<image.cols;i++){ for(j=0;j<image.rows;j++){ Y(k++)=image.at<uchar>(j,i)/sum; } } Y1=R*Y;//Y1(500,1) R(500,20139) Y(20139,1) sum=0; for(j=0;j<500;j++) { sum=sum+Y1(j,0)*Y1(j,0); } sum=sqrt(sum); for(j=0;j<500;j++) { Y1(j,0)=Y1(j,0)/sum; } kl1p::TMatrixOperator<klab::DoubleReal> * matrix = new kl1p::TMatrixOperator<klab::DoubleReal>(B); klab::TSmartPointer<kl1p::TOperator<klab::DoubleReal, klab::DoubleReal> > * B1 =new klab::TSmartPointer<kl1p::TOperator<klab::DoubleReal, klab::DoubleReal> >(matrix); klab::DoubleReal tolerance = 1e1; // Tolerance of the solution. kl1p::TBasisPursuitSolver<klab::DoubleReal> bp(tolerance); bp.solve(Y1, *B1, W); for(i=0;i<150;i++) x1(i)=W(i); for(;i<650;i++) e1(i-150)=W(i); Y1=Y1-e1; double r[15]; arma::Col<klab::DoubleReal> l(150); arma::Col<klab::DoubleReal> l1(500); for(i=0;i<15;i++) { sum=0; for(j=0;j<150;j++) l(j)=0; for(j=i*10;j<i*10+10;j++) l(j)=x1(j); l1=Y1-A1*l; for(j=0;j<500;j++) sum=sum+l1(j)*l1(j); sum=sqrt(sum); r[i]=sum; //cout<<sum<<endl; } double min=r[0]; int min_num=0; for(i=0;i<15;i++) { if(r[i]<min) { min=r[i]; min_num=i; } } cout<<"第"<<o+1<<"张图片与第"<<min_num+1<<"人的脸匹配"<<endl; double sci,sum1=0,sum2=0,sum1_max=0; for(i=0;i<15;i++) { sum1=0; for(j=0;j<10;j++) sum1=sum1+abs(x1(i*10+j)); if(sum1>sum1_max) sum1_max=sum1; } for(i=0;i<150;i++) sum2=sum2+abs(x1(i)); sci=(sum1_max*15/sum2-1)/14; cout<<"SCI为"<<sci<<endl; } getchar(); return 0; }