本实验基于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;
end
figure;
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;
end
end
识别准确率:
3. GUI设计
以第一种分组情况为例,设计如下GUI界面
运行结果找了一个漂亮妹子:
全部重新对训练集和测试集分组,再做一次识别,看到这次准确率有所不同:
想要源码的孩子可以戳我,这里就不全发了。一是时间紧任务重,二是懒。