基于PCA和SVM的人脸识别

本文所用的是ORL人脸库,由英国剑桥实验室拍摄,共有40人,每人不同角度不同表情拍摄了10张,所以共有400个样本数据,图片尺寸为112*92,格式为pgm。本文将每人的前5张作为训练集,后5张作为测试集。ORL人脸库可在该网址下载https://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html

一、读取人脸数据(向量化人脸容器)

function [faceContainer,label]=ReadFace(n_persons,flag)
%   当flag为0时,表示读取训练集,flag为1时,表示读取测试集
%   n_persons为多少人,label是人脸的标签  
%   faceContainer是一个向量化人脸容器,即将每一张图片转成一行向量放入其每行中

%先随便读入一张图片,得到其大小
I = imread('ORL\orl_faces\s1\1.pgm');
[M,N] = size(I);

label=zeros(n_persons*5,1);
faceContainer=zeros(n_persons*5,M*N);
for i=1:n_persons
    %函数num2str(i)说明:将数字转化为字符
    facepath=strcat('ORL\orl_faces\s',num2str(i),'\');  %路径因不同情况而定
    temppath=facepath;
    for j=1:5
        facepath=temppath;
        if flag==0      
            facepath=strcat(facepath,num2str(j));
        else
            facepath=strcat(facepath,num2str(j+5));
        end
        label((i-1)*5+j)=i;
        facepath=strcat(facepath,'.pgm');    
        img=imread(facepath);
        faceContainer((i-1)*5+j,:)=img(:)';
    end
end
save('ORL\faceContainer','faceContainer');%保存读取的数据

二、PCA降维(该算法在我的上一篇博客主成分分析(PCA)中有讲解)

        读取的数据是1\times10304的向量,把每一个像素点当做一维特征,故每张图片有10304维,现对其进行降维。

%A为样本矩阵,将其降至k维后的矩阵为pcaA,V为主成分分量
function [pcaA ,V]=fastPCA(A,k)

[m,n]=size(A);
%样本均值,计算各列的均值
meanVec=mean(A); 
 
%计算协方差矩阵的转置 covMatT
%样本矩阵中心化,每一维度减去该维度的均值,使得每一维度的均值为0
%repmat:Replicate Matrix复制和平铺矩阵
Z= ( A-repmat(meanVec,m,1)  );  
                                
covMatT =Z*Z'; %快速PCA
%计算covMatT的前k个特征值和特征向量
[V, ~]=eigs(covMatT,k);  %V为m*k, k个特征向量
 
%得到协方差矩阵(covMatT')的特征向量
V=Z'*V;
 
%特征向量归一化为得到单位特征向量
for i=1:k
    V(:,i)=V(:,i)/norm(V(:,i));  %norm 为范数,默认为2范数(各分量的平方和 再开根号)
end
 
%投影降维至k维
pcaA=Z*V;
 
%保存变换矩阵V和平均矩阵meanVec
save('ORL/PCA.mat','V','meanVec');

三、数据归一化

     特征数据归一化 ,因为对于不同的特征,如果不归一化是不具有比较性的,两者不在一个量级上,比如说A体重70kg,身高1.75,B体重69kg,身高1.50,C体重65kg,身高1.74,SVM是基于距离算的,所以会把A和B看成相似的,而实际上A和C比较相似。

    一般归一化到[-1,1],即lTarget = -1,uTarget = 1。

function [ scaledface] = scaling( faceMat,lTarget,uTarget )  
%faceMat需要进行规范化的图像数据,  
[m,n]=size(faceMat);  
scaledface=zeros(m,n); 

upVec=zeros(1,n); %所有数据中每个特征的最大值
lowVec=zeros(1,n);%所有数据中每个特征的最小值

 for i=1:n
     lowVec(i)=min( faceMat(:,i) );
     upVec(i)=max( faceMat(:,i) );   
     scaledface(:,i)=(faceMat(:,i) - lowVec(i) )/( upVec(i)- lowVec(i))*(uTarget-lTarget)+lTarget;   
 end
 save('ORL/scaling.mat','upVec','lowVec');


四、对样本进行训练(SVM模型)

     我是利用libsvm工具箱,特别简单方便(由台湾林智仁开发),首先下载libsvm工具箱,下载地址https://www.csie.ntu.edu.tw/~cjlin/libsvm/,进入页面后,点击下图圈的zip文件下载。

 

下载解压后,会有c++,python,matlab不同的文件夹即libsvm针对不同语言封装的不同接口。我用的是matlab,首先打开matlab软件,依次点击主页->设置路径->添加并包含子文件夹,将刚才解压的matlab文件夹添加进来,最后一步复制解压后的matlab文件夹的路径到matlab编辑器路径,在命令行窗口输入make然后回车等待编译完成即大功告成。

%第一个参数为标签,第二个为训练数据,第三个是个命令集合,-t表示核函数,-c为惩罚系数,-v为交叉验证数
%-t为0时线性核,1多项式核,2径向基函数(高斯),3sigmod核函数
model = svmtrain(label,scaledface,'-t 0 -c 1');

五、对测试集进行预测

%输出的三个参数分别为预测的标签,准确率,评估值(非分类问题用着),
%输入为测试数据的标签(这个可与可无,如果没有,那么预测的准确率accuracy就没有意义了,
%如果有,那么就可以通过这个值与预测出来的那个类型值相比较得出准确率accuracy,
%但是要说明一点的是,无论这个值有没有,在使用的时候都得加上,即使没有,也要随便加上一个类型值,
%反正你也不管它对不对,这是函数使用所规定的的

[predict_label,accuracy,prob_estimates]=svmpredict(label,scaledface,model);

注意测试数据降维是在训练集的特征向量中降维,即testDataPca = (testData - meanVec)*V,测试数据归一化也是在训练集中进行的。

测试数据归一化:

function [ testscaledface] = testscaling( faceMat,lTargB,uTargB )   
% lowvec原来图像数据中的最小值  
% upvec原来图像数据中的最大值  
[m,n] = size(faceMat);
testscaledface = zeros(m,n);
load ORL/scaling.mat
 for i=1:n
     testscaledface(:,i)=(faceMat(:,i) - lowVec(i) )/( upVec(i)- lowVec(i))*(uTargB-lTargB)+lTargB;   
 end

主函数:


disp('读取训练数据...')
disp('......') 
[train_faceContainer,train_label] = ReadFace(41,0);

disp('训练数据PCA降维...') 
disp('......') 
[pcaA ,V]=fastPCA(train_faceContainer,20);

disp('训练数据归一化...');
disp('.........') 
[ scaledface] = scaling( pcaA,-1,1 );

disp('SVM样本训练...')  
model = svmtrain(train_label,scaledface,'-t 0 ');

disp('读取测试数据...')  
[test_faceContainer,test_label]=ReadFace(40,1);

disp('测试数据pca降维...') 
disp('.......') 
load 'ORL/PCA.mat'
testData = (test_faceContainer - repmat(meanVec,200,1)) * V;

disp('测试数据归一化...')  
disp('.......')  
scaled_testData = testscaling( testData,-1,1);

disp('SVM样本分类预测...')
disp('......')  
[predict_label,accuracy,prob_estimates]=svmpredict(test_label,scaled_testData,model);

补充一:显示主成分脸

function visualize(V)  
%显示主成分脸(变换空间中的投影向量,即单位特征向量),这里显示前20个主成分脸,即将原始数据降至20维
figure  
img=zeros(112,92);  
for i=1:20  
    img(:)=V(:,i);  
    subplot(4,5,i);  
    imshow(img,[])  
end  

补充二:基于主成分分量的人脸重建

%k为重建至多少维
function rebuid(y,k)

%导入平均矩阵meanVec和主成分向量V
load ORL/PCA.mat

temp = meanVec;
for i = 1:k    
    xi = (y - meanVec) * V(:,i);%某人脸y在第i维的投影值
    yi =  xi * V(:,i)';%某人脸y在第i维的向量值
    temp = temp + yi ;%对该人脸投影到所有维的向量进行一个矢量相加,得到该人脸向量的一个近似值
end

%显示重建人脸
I = zeros(112,92);
I(:) = temp';
imshow(I,[]);

以第一个人脸为例,分别基于前50,100,150,200维重建后的人脸如下,可见原来的10304维人脸用150维左右就可重建出来:

补充三:实时识别

首先自拍10张自己的脸,转换成pgm格式,尺寸归一化为112*92,在ORL人脸库中新建文件夹s41,将这10张图片放里面,现在对这41个人脸重新进行训练,得到训练好的模型model。

%改变图像尺寸和格式并保存

%将自己的人脸照片先放在桌面
temp = 'C:\Users\Administrator\Desktop\';

for i = 1:10
    path1 = strcat(temp,num2str(i),'.jpg');
    I = imread(path1);
    I = rgb2gray(I);
    I = imresize(I,[112,92]);%改变图像尺寸

    %先保存在D盘下,然后去D盘全部剪切到ORL人脸库中
    path2 = strcat('D:\',num2str(i),'.pgm');
    imwrite(I,path2);  %保存成pgm格式
end

打开matlab应用程序里的imageAcquision,可以预览开始捕捉停止捕捉,设置帧数等,其右下角会对应生成代码,复制出来直接用即可。

%初始化这部分代码可用imageAcquision应用程序生成:

%创建视频对象
vid = videoinput('winvideo', 1, 'YUY2_160x120');
%设置属性值,持续不断获取图像
vid.FramesPerTrigger = Inf;
%打开摄像头
start(vid);

[faceContainer,label]=ReadFace(41,0);
[pcaA ,V]=fastPCA(faceContainer,20);
[ scaledface] = scaling( pcaA,-1,1 ); 
model = svmtrain(label,scaledface,'-t 0 ');
load ORL\PCA.mat
container = zeros(1,112*92);%一定要和训练时一样先初始化定义一个人脸容器,使其数据类型一样,而不能直接转化成double型

while 1
    frame = getsnapshot(vid);%抓取图像
    I = ycbcr2rgb(frame);%ycbcr是色彩空间的一种,由于我的计算机获取图像是这种格式所以先转换为rgb再转换为gray
    I = rgb2gray(I);
    I = imresize(I,[112,92]);%尺寸归一化
    imshow(I);
    container(1,:) = I(:)';
    faceData = (container - meanVec)*V;%在训练数据的特征向量中降维
    faceData = testscaling( faceData,-1,1 );%测试数据归一化
    [predict_label,accuracy,prob_estimates]=svmpredict(41,faceData,model);
    if predict_label == 41
        disp('识别正确:童小彬')
    else
        disp('识别错误')
    end
    
    if strcmp(get(gcf,'SelectionType'),'alt')%右键鼠标事件
        break;
    end
    
end
stop(vid);%关闭摄像头

实时识别还是挺准的,现截取某一时刻的结果如下图所示:

猜你喜欢

转载自blog.csdn.net/qq_24946843/article/details/81775701