(SRC)基于稀疏表示的人脸识别

本文主要记录自己在学习稀疏表示在人脸识别中的应用所遇到的问题作一简单的总结。

1.  问题背景


        信号的稀疏表示并不是新的东西。我们很早就一直在利用这一特性。例如,最简单的JPEG图像压缩算法。原始的图像信号经过DCT变换之后,只有极少数元素是非零的,而大部分元素都等于零或者说接近于零。这就是信号的稀疏性。

        任何模型都有建模的假设条件。压缩感知,正是利用的信号的稀疏性这个假设。对于我们处理的信号,时域上本身就具有稀疏性的信号是很少的。但是,我们总能找到某种变换,使得在某个变换域之后信号具有稀疏性。这种变换是很多的,最常见的就是DCT变换,小波变换,gabor变换等。

        然而,这种正交变换是传统视频图像处理采用的方法。目前所采用的一般不是正交变换。它是基于样本采样的。或者说是通过大量图像数据学习得到的,其结果称作字典,字典中的每一个元素称作原子。相关的学习算法称作字典学习。常见的算法例如K-SVD算法。学习的目标函数是找到所有样本在这些原子的线性组合表示下是稀疏的,即同时估计字典和稀疏表示的系数这两个目标。


       压缩感知和稀疏表示其实是有些不同的。压缩感知的字典是固定的,在压缩感知的术语里面其字典叫做测量矩阵。但压缩感知的恢复算法和稀疏表示是同一个问题。他们都可以归结为带约束条件的L1范数最小化问题。求解这类泛函的优化有很多种方法。早在80年代,统计学中Lasso问题,其实和稀疏分解的优化目标泛函是等价的。而求解统计学中lasso 问题的LARS算法很早就被提出了,故我们还可以通过统计学的LARS算法求解稀疏表示问题。目前很多统计学软件包都自带LARS算法的求解器。


2. 基于稀疏表示的分类 SRC


      人脸的稀疏表示是基于光照模型。即一张人脸图像,可以用数据库中同一个人所有的人脸图像的线性组合表示。而对于数据库中其它人的脸,其线性组合的系数理论上为零。由于数据库中一般有很多个不同的人脸的多张图像,如果把数据库中所有的图像的线性组合来表示这张给定的测试人脸,其系数向量是稀疏的。因为除了这张和同一个人的人脸的图像组合系数不为零外,其它的系数都为零。

       上述模型导出了基于稀疏表示的另外一个很强的假设条件:所有的人脸图像必须是事先严格对齐的。否则,稀疏性很难满足。换言之,对于表情变化,姿态角度变化的人脸都不满足稀疏性这个假设。所以,经典的稀疏脸方法很难用于真实的应用场景。

       稀疏脸很强的地方在于对噪声相当鲁棒,相关文献表明,即使人脸图像被80%的随机噪声干扰,仍然能够得到很高的识别率。稀疏脸另外一个很强的地方在于对于部分遮挡的情况,例如戴围巾,戴眼镜等,仍然能够保持较高的识别性能。上述两点,是其它任何传统的人脸识别方法所不具有的。


3. 稀疏人脸识别的实现问题与算法简介

3.1 问题

        一谈到识别问题,大家都会想到要用机器学习的方法。先进行训练,把训练的结果以模板的形式存储到数据库上;真实应用环境的时候,把测试样本经过特征提取之后,和数据库中的模板进行比对,查询得到一个最相似的类别作为识别结果。往往,机器训练的时间都超级长,几天,几个礼拜乃至几个月,那是常见的事情;识别的时间一般是很小的。典型的例如人脸检测问题。这是可以接受的,因为训练一般都是离线的。


        然而,基于稀疏分解的人脸识别是不需要训练的,或者说训练及其简单。基于稀疏表示的人脸识别,其稀疏表示用的字典直接由训练所用的全部图像构成,而不需要经过字典学习【也有一些改进算法,针对字典进行学习的】。当然,一般是经过简单的特征提取。由于稀疏表示的方法对使用什么特征并不敏感。故而,其训练过程只需要把原始图像数据经过简单的处理之后排列成一个很大的三维矩阵存储到数据库里面就可以了。


        关键的问题在于,当实际环境中来了一张人脸图像之后,去求解这张人脸图像在数据库所有图像上的稀疏表示,这个求解算法,一般比较耗时。尽管有很多的方法被提出,但是对于实时应用问题,依然没法满足。所以,问题的关键还是归结于L1范数最小化问题上来。


       L1范数最小化问题已经有很多种快速求解方法,这里主要包括有梯度投影Gradient Projection,同伦算法,迭代阈值收缩,领域梯度Proximal Gradient,增广拉格朗日方法,这几种方法都比正交匹配追踪算法OMP要高效的多。上述几种快速算法中,采用增广拉格朗日的对偶实现相比其它的快速算法要更好。最近流行的Spit Bregman算法也是不错的选择。

     3.2 算法简介



 

4. 稀疏表示人脸识别的改进算法


         稀疏人脸识别算法要用于实际的系统,需要在两方面加以改进。首先,要突破人脸图像的对齐这一很强的假设。实际环境中的人脸往往是不对齐的,如何处理不对其的人脸是额待解决的问题。其实,是快速高效的优化算法。最后,也是最重要,实际环境中的应用往往训练样本很少。目前,研究人员已经取得了很多可喜的成果,下面分别予以介绍。

4.1 CRC-RLS算法 

          CVPR2011 LeiZhang  Sparse Representatiion or Callaborative Representation: Which helps Face Recognition? 稀疏表示和协同表示,哪一个有助于人脸识别。该文作 者提出了用L2范数代替L1范数求解原问题。这样,能够非常快速的求解问题,实时性没有任何问题。但稀疏性不像原来的L1范数那样强。但作者对分类准则进行了改进,使得其分类性能几乎接近于原始L1范数最小化问题分类性能。为了对比,我把关键性算法列表如下:

                                                                   
                                                               
         SRC算法求解的是方程1的解,而CRC-RLS算法则直接给出了表示系数的最小二乘解。二者另外一个主要的不同点在于计算残差的方式不一样,具体请注意上述方程2和方程10的不同点。后者的计算时间较前者最多情况下加速了1600倍。更多的实现细节可以参考原文。

       

   4.2  RSC算法 

              CVPR2011 Meng Yang,Robost  Sparse Coding for Face Recognition. 鲁棒的稀疏编码算法。该文作者没有直接求解稀疏编码问题,而是求解Lasso问题,因为Lasso问题的解和稀疏编码的解是等价的。在传统的SRC框架下,编码误差使用L2范数来度量的,这也就意味着编码误差满足高斯分布,然而,当人脸图像出现遮挡和噪声污染的情况下,并非如此。在字典学习框架下,这样的字典是有噪声的。该文作者对原始Lasso问题进行改进,求解加权L1范数约束的线性回归问题。Lasso问题描述如下:


                                                      


               加权Lasso问题的目标函数描述如下:

                                                             

            此算法的关键还在于权重系数的确定,文中采用的是logistic函数,而具体的实现则是通过迭代估计学习得到。该方法基于这样一个事实:被遮挡或噪声干扰的像素点赋予较小的权重,而其它像素点的权重相对较大。具体迭代算法采用经典的迭代重加权算法框架,当然内部嵌入的稀疏编码的求解过程。此算法在50%遮挡面积的情况下取得的更好更满意的结果。但是文中没有比较计算时间上的优略而直说和SRC框架差不多。


4.3  RASL算法

          CVPR2010. Yigang Peng.  Robust batch Alignment of Images by Sparse and Low-Rank Decomposition. 这篇文章的作者在这篇文章中讨论的是用矩阵的低秩分解和稀疏表示来对齐人脸的问题。

4.4  RASR算法

       PAMI2011 Wagner. Towards a Practical Face Recognition System:Robust Alignment and Illumination by Sparse Representation.该文的目的和RASL类似。

4.5  MRR算法

            ECCV2012,Meng Yang. Efficient Misalignment-Robust Representation for Real Time Face Recognition.这篇文章又是Meng Yang的大作。这篇文章在充分分析RASR算法的基础上提出了一个高效的快速算法。该文对人脸对齐中求解变换矩阵T分为由粗到细的两个阶段。 这篇文章把稀疏脸应用在实际系统中推进了一大步。具体算法实现本人正在拜读之中。

        对稀疏脸的改进算法其实很多,例如分块SRC算法,表情鲁棒SRC等。但本人认为能够把它推向实际应用的却很少。上述文献是本人认为可圈可点的值得仔细拜读的文献。

5  代码

下面贴出我写的C++代码
做压缩感知的库是Kl1p
用opencv处理137*147大小的图片
降维是用randomfaces
// 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;
}


©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页