源码上传到了我的github上,大家可以去免费下载https://github.com/tongxiaobin?tab=repositories
qq:1344184686
本文所用的是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)中有所讲解)
读取的数据是110304的向量,把每一个像素点当做一维特征,故每张图片有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);%关闭摄像头
实时识别还是挺准的,现截取某一时刻的结果如下图所示: