PCA人脸识别

来源:互联网 发布:三个数最大公约数算法 编辑:程序博客网 时间:2024/06/09 16:47

本实验基于Matlab,使用的是ORL人脸库。一共40个人,每个人10张照片。
有两种分组方式,第一种:从每个人中选出5张作为训练集并做为验证集,剩下的5张作为测试集;第二种:从40个人中选前30个人作为PCA模型训练集,剩下的10人中每个人的5张作为验证集,剩下5张作为测试集。


1. 第一种分组方式

——分组——
将库里的400张照片分成两组,每个人的任意5张照片(定义train=5)作为PCA模型训练集和验证集,其他的5张作为测试待识别图片。将PCA训练集的所有照片转化为N维(112*92)的向量,并以列向量xi(10304*1)的形式存入矩阵X(10304*200)中,将这200个向量的每个元素加起来求平均值。将其还原成图像的形式可以得到平均脸。这不就是我经常做梦梦到的那货!
平均脸

——求特征脸——
协方差矩阵能同时表现不同维度间的相关性以及各个维度上的方差。对角线上的元素是各个维度上的方差(即能量),其他元素是两两维度间的协方差(即相关性)。PCA的目的就是使各个维度上的方差尽可能大,不同维度间的相关性尽可能小,以使降维同时最大程度保持数据原始特征。
协方差矩阵的维数比较大,采用SVD定理,将矩阵X中心化之后可以很方便的求出协方差矩阵。MATLAB的函数pcacov()可以得到协方差矩阵的主成分PC、特征值latent(降序排列)和每个特征向量在观测量总方差中所占的百分数explained(降序排列)。将explained累加到95,即选取构成95%能量的特征值,这里是前116个,构成映射矩阵。可以看到比起10304维降低了近100倍。将原始图像矩阵(10304*200)和只包含主成分的映射矩阵(200*116)相乘获得特征脸的矩阵(10304*116)。下图是train=5时的前6张特征脸。任意一张人脸都可以由平均脸和特征脸重构得到。
特征脸
这些特征脸超级恐怖的说!!将特征脸矩阵的转置乘X,即得到测试样本在PCA特征空间上降维的表达矩阵W(116*200)。
——人脸识别——
将待识别照片的人脸图像投影到特征脸子空间,可以获得一组坐标系数,这组系数表明了图像在子空间的位置,从而可以作为人脸识别的依据。将待测试照片进行同样的降维操作,然后与降维的训练集匹配,利用近邻法识别,对结果升序排序,打印出排在前三的照片,即为识别结果。下图中每个人的第一张为待识别人脸,后三张为识别的人脸。
这里写图片描述
这里写图片描述
这里写图片描述

——准确率——
在进行分组时对照片进行了重命名,比如train=5时,训练集和测试集各有200张照片,命名顺序依次从001到200。我们将照片名除以5,向上去整即可得到组别。当改变训练集样本数量时有类似的数学关系。
uigetfile命令可以选择要打开的文件,以下程序是选择待测试照片的代码,recognize5为待识别照片存放的文件夹:

[filename,pathname] = uigetfile({'*.*';'*.pgm'},'recognize5/');str = [pathname filename];%合成路径名im = imread(str);

选择待测试照片后,下面代码可以得到照片名中的数字,从而计算出组别:

s = filename(regexp(filename,'\d'));s = str2double(s);No = ceil(s/5);subplot(1,4,1),imshow(im),title(['人脸组别',num2str(No)]);

匹配到训练集中的照片后,进行排序。在计算准确率时我们只考虑匹配出的第一张照片。train5为人脸识别训练集的文件夹。由于sort()函数进行排序后会返回索引,所以组别计算相对简单:

[s_temp,id] = sort(temp1,'ascend');path1 = strcat('train5/',sprintf('%03d',id(i)),'.pgm');imshow(path1),title(['人脸组别',num2str(ceil(id(i)/5))]);

遍历测试集中所有待识别照片,通过比较待识别照片和匹配出的照片的组别,可以计算准确率。新建如下图所示空文件夹,其中’train’文件夹下存放训练样本,’recognize’文件夹下存放测试样本。每个人任意1张照片放于train1中,其他放于recognize1中;每个人任意2张照片放于train2中,其他放于recognize2中;以此类推,从而将不同样本数量的人脸识别训练集得出。当然也可以只建两个文件夹,每次得到准确率后delete所有照片再重新分组命名。

你需要新建的文件夹:
这里写图片描述

得出的准确率曲线:
这里写图片描述
可以看到随训练集样本数量增多,准确率大体上也是增加的,但在300之后有一点下降,原因自己猜。

2. 第二种分组方式

——分组并求协方差矩阵——
将库里的400张照片分成三组,前30个人作为PCA模型训练集;后10个人中每个人的任意5张照片作为验证集,其他的5张作为测试待识别照片。将每一PCA模型训练集的300张照片转化为N维(112*92)的向量,并以列向量xi(10304*1)的形式存入矩阵X(10304*300)中,将这300个向量的每个元素加起来求平均值,得到平均脸。计算每个照片和平均脸的差值。从而容易得出协方差矩阵。
这里写图片描述

——特征脸提取——
通过PCA主成分分析,在能量达95%时,取到特征向量151个,得到映射矩阵(10304*151),由PCA模型训练集照片矩阵和映射矩阵得出特征脸。
这里写图片描述

——人脸识别——
从最后10人中每人选5张,一共50张照片组成验证集。对验证集的照片矩阵进行同样的中心化操作,最后映射到PCA模型训练集的特征脸上,得到PCA特征空间下的表达矩阵W(151*50)。
选择待识别人脸测试集中的一张照片,和验证集的照片进行匹配,得出前三张照片,即为识别结果,每个人第一个照片为待识别照片,后三张为匹配出的照片。
这里写图片描述
这里写图片描述
这里写图片描述
识别效果不错吧。

——准确率——
准确率的计算方法和上一种计算方法类似,只不过计算关系式不同。
将PCA模型训练集的样本大小设为自变量,即样本大小为300、310、320…380(即前30、31、32…38个人,每人10张照片)分别放在’PCA’各文件夹里。验证集取剩下的人中每人5张照片,分别放在’train’各文件夹里。其余作为对待测试照片,分别放于’recognize’各文件夹里。

这里写图片描述

下边是计算准确率的代码,搞不懂为什么这样高亮,凑活看吧

for num = 30:38    %%%%%%%%%%%%%%%%%%%%%%%%     PCA训练   %%%%%%%%%%%%%%%%%%%%%%%%%%%    [PCAimagedata,~,~] = Centralization(['PCA',num2str(num),]);    covMat = PCAimagedata'*PCAimagedata; % 协方差矩阵    % 说明: [PC,latent,explained]=pcacov(X) 通过协方差矩阵 X 进行主成分分析,返回主成分 (PC) 、    % 协方差矩阵 X 的特征值 (latent) 和每个特征向量表征在观测量总方差中所占的百分数    %(explained) 。'     [COEFF, latent, explained] = pcacov(covMat);    % 选择构成 95%能量的特征值    i = 1;    proportion = 0;    while(proportion < 95)        proportion = proportion + explained(i);        i = i+1;    end    k = i - 1;    %得出特征脸    V = PCAimagedata*COEFF(:,1:k);     %%10304*115%    %%%%%%%%%%%%%%%%%%%%%%%%      '验证集训练'''       %%%%%%%%%%%%%%%%%%%%%%%%%%%    [imagedata,img_pj,wtsXL]= Centralization(['train',num2str(num-29)]); %%PCA30对应train1%    W = V'*imagedata;  %  115*50降维    %%%%%%%%%%%%%%%%%%%%%%%%       求准确率      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%'    Accurate_number = 0;    temp2=[];    for Picture_number=1:wtsXL  %%人脸识别训练集和待识别照片数量一样%        picture=imread(['recognize',num2str(num-29),'/',sprintf('%03d',Picture_number),'.pgm']);        picture = double(picture(:));        objectone = V'*(picture - img_pj);        %'        for k = 1:wtsXL            temp2(k) = norm(objectone - W(:,k));        end        [~,id]=sort(temp2,'ascend');          if ceil(Picture_number/5+num) == ceil(id(1)/5+num)  %实际组别都要加上num            Accurate_number = Accurate_number+1;        end     end    Accurate = Accurate_number/wtsXL;    fprintf('######  PCA训练集为%d张时,准确率为:%.2f%%  #####\n',num*10,Accurate*100);    y(num)=Accurate;    x(num)=num;endfigure;plot(x,y,'o-b');axis([30 38 0.7 1])xlabel('PCA模型训练集大小');ylabel('准确率');

Centralization是我自定义的函数:

function [imagedata,img_pj,wts]=Centralization(pathstr)%对输入路径里的照片矩阵中心化,并返回:%imagedata 中心化后的照片库矩阵%imgedata  平均脸%wts 照片库照片数量%###################################%author 王喆%###################################    img_path = dir([pathstr,'\*.pgm']);    img_num = length(img_path);    imagedata = [];    if img_num >0        for j = 1:img_num            img_name = img_path(j).name;            temp = imread(strcat(pathstr,'/', img_name));%112*92            temp = double(temp(:));%10304*1            imagedata = [imagedata, temp];%所有照片的矩阵        end    end    wts = size(imagedata,2);%    % 平均脸和中心化    img_pj = mean(imagedata,2);%    for i = 1:wts        imagedata(:,i) = imagedata(:,i) - img_pj;    endend

识别准确率:

这里写图片描述

这里写图片描述

3. GUI设计

以第一种分组情况为例,设计如下GUI界面
这里写图片描述

这里写图片描述

运行结果找了一个漂亮妹子:
这里写图片描述

全部重新对训练集和测试集分组,再做一次识别,看到这次准确率有所不同:
这里写图片描述


想要源码的孩子可以戳我,这里就不全发了。一是时间紧任务重,二是懒。

原创粉丝点击