基於PCA和SVM的人臉識別
程式中採用的資料集是ORL人臉庫,該人臉庫共有400副人臉影象,40人,每人10幅,大小為112*92畫素,同一個人的表情,姿勢有少許變化。
程式的流程主要分為三部分,資料的預處理(PCA降維和規格化),資料的訓練階段,資料的識別階段
資料的預處理的流程圖如下:
資料的訓練流程圖如下:
識別流程:
下面貼上一些matlab的實現程式碼:
資料預處理主要是兩個函式,ReadFaces和scaling,第一個函式是將訓練影象存成一個200*10304的矩陣,第二個是對資料進行規格化,具體程式碼如下:
function [imgRow,imgCol,FaceContainer,faceLabel] = ReadFaces(nFacesPerson,nPerson,bTest)
%nFacesPersonn-----每個人需要讀入的樣本數,預設為5
%nPerson ------需要讀入的人數,預設為全部四十個人
%bTest ------bool型引數。預設為0,表示讀入樣本前五張;1:表示後五張
%輸出: FaceContainer------向量化人臉容器,nPerson*10304的二維矩陣,每行對應一個人臉向量
if nargin==0 %預設值
nFacesPerson = 5;
nPerson = 40;
bTest = 0;
elseif nargin<3
bTest = 0;
end
img=imread('PCA_face/data/ORL/s1_1.bmp') %為計算尺寸先讀一張
[imgRow,imgCol]=size(img);
FaceContainer = zeros(nFacesPerson*nPerson,imgRow*imgCol);
facelabel = zeros(nFacesPerson*nPerson,1);
%讀入訓練資料
for i=1:nPerson %不同的人
i1=mod(i,10);
i0=char(i/10);
strPath='PCA_face/data/ORL/s';
if(i0~=0)
strPath=strcat(strPath,'0'+i0);
end
strPath=strcat(strPath,'0'+i1);
strPath=strcat(strPath,'_');
tempStrPath=strPath;
for j=1:nFacesPerson %每一個人的前五張
strPath=tempStrPath;
if bTest==0
strPath=strcat(strPath,'0'+j);
else
strPath=strcat(strPath,num2str(5+j));
end
strPath = strcat(strPath,'.bmp');
img=imread(strPath);
%把讀入的影象按列儲存為行向量放入向量化人臉容器FaceContainer的對應行中
FaceContainer((i-1)*nFacesPerson+j,:)= img(:)';
faceLabel((i-1)*nFacesPerson+j) = i;
end
end
%儲存人臉樣本矩陣
save('PCA_face/Mat/FaceMat.mat','FaceContainer');
function [ SVFM, lowVec,upVec ] = scaling( VecFeaMat,bTest,lRealBVec,uRealBVec)
% Input: VecFeaMat --- 需要scaling的 m*n 維資料矩陣,每行一個樣本特徵向量,列數為維數
% bTest --- =1:說明是對於測試樣本進行scaling,此時必須提供 lRealBVec 和 uRealBVec
% 的值,此二值應該是在對訓練樣本 scaling 時得到的
% =0:預設值,對訓練樣本進行 scaling
% lRealBVec --- n維向量,對訓練樣本 scaling 時得到的各維的實際下限資訊
% uRealBVec --- n維向量,對訓練樣本 scaling 時得到的各維的實際上限資訊
%
% output: SVFM --- VecFeaMat的 scaling 版本
% upVec --- 各維特徵的上限(只在對訓練樣本scaling時有意義,bTest = 0)
% lowVec --- 各維特徵的下限(只在對訓練樣本scaling時有意義,bTest = 0)
if nargin<2
bTest=0;
end
lTargB=-1;
uTargB=1;
[m n] = size(VecFeaMat);
if bTest
if nargin<4
error('to do scaling on test,param must 4');
end
if nargout>1
error('when do scaling ,only one output is supported');
end
for iCol = 1:n
if lRealBVec(iCol)==uRealBVec(iCol)
SVFM(:,iCol) = uRealBVec(iCol);
SVFM(:,iCol) = 0;
else
SVFM(:,iCol) = lTargB + ( VecFeaMat(:,iCol) - lRealBVec(iCol) ) / ( uRealBVec(iCol) - lRealBVec(iCol) ) * ( uTargB - lTargB );
end
end
else %bTest = 0
upVec = zeros(1,n);
lowVec= zeros(1,n);
for iCol = 1:n
lowVec(iCol) = min( VecFeaMat(:,iCol) );
upVec(iCol) = max( VecFeaMat(:,iCol) );
if lowVec(iCol) == upVec(iCol)
SVFM(:,iCol) = upVec(iCol);
SVFM(:,iCol) = 0;
else
SVFM(:,iCol) = lTargB + ( VecFeaMat(:,iCol) - lowVec(iCol) ) / ( upVec(iCol) - lowVec(iCol) ) * ( uTargB - lTargB );
end
end
end
end
訓練階段的函式是train,程式碼如下:
function train()
%整個訓練過程包括讀入影象,PCA降維以及多類SVM訓練,各個階段的處理結果分別儲存至檔案:
% 將PCA變換矩陣W儲存至 PCA_face\Mat\PCA.mat
% 將scaling的各維上下界資訊儲存至 PCA_face\Mat\scaling.mat
% 將PCA降維並且scaling後的資料儲存至 PCA_face\Mat\trainData.mat
% 將多類SVM的訓練資訊儲存至 PCA_face\Mat\multiSVMTrain.mat
global imgRow;
global imgCol;
global W
display('');
display('');
display('訓練開始.....');
nPerson = 40;
nFacesPerson = 5;
nSplPerClass=zeros(1,nPerson);
display('讀入人臉資料');
[ imgRow, imgCol, FaceContainer, faceLabel] = ReadFaces(nFacesPerson, nPerson);
save('PCA_face\Mat\FaceMat.mat','FaceContainer');
display('..................');
nFaces = size(FaceContainer, 1);%樣本人臉數目
display('PCA降維...');
[pcaFaces, W] = fastPCA(FaceContainer, 20);
%pcaFaces是200*20的矩陣,每一行代表一張主成分臉
%W是分離變換矩陣, 10304*20的矩陣
visualize_pc(W);
display('............');
X=pcaFaces;
[X,A0,B0] = scaling(X);
save('PCA_face\Mat\scaling.mat','A0','B0');
%儲存scaling的資料至trainData.mat
TrainData = X;
trainLabel = faceLabel;
save('PCA_face\Mat\trainData.mat','TrainData','trainLabel');
display('.........儲存scaling的資料至trainData.mat..........');
for iPerson = 1:nPerson
nSplPerClass(iPerson) = sum((trainLabel == iPerson));
end
multiSVMStruct = multiSVMTrain (TrainData, nSplPerClass, nPerson, Inf, 1);
display('正在儲存訓練結果.....');
save('PCA_face\Mat\multiSVMTrain.mat','multiSVMStruct');
display('訓練結束.................');
end
識別階段的函式是
function class = SVMClassify(TestFace, multiSVMStruct)
%class ------識別出的類別
%TestFace------測試影象轉換的行向量經過降維後的1*20的行向量,並經過規定化到-1~+1之間
%multiSVMStruct結構體陣列,儲存了兩兩分類的svm結構體資訊
if nargin<2
t = dir('PCA_face\Mat\multiSVMTrain.mat');
if length(t) == 0
error('沒有找到訓練結果');
end
load('PCA_face\Mat\multiSVMTrain.mat');
end
%nClass = multiSVMStruct.nClass;
nClass=40;
%CASVMStruct = multiSVMStruct.CASVMStruct;
CASVMStruct = multiSVMStruct;
%%%%%投票策略解決多類問題
m = size(TestFace, 1);
Voting = zeros(m,nClass);
for iIndex = 1:nClass-1
for jIndex = iIndex+1:nClass
classes = svmclassify(CASVMStruct{iIndex}{jIndex},TestFace);
%voting
Voting(:,iIndex) = Voting(:,iIndex) + (classes==1);
Voting(:,jIndex) = Voting(:,jIndex) + (classes==0);
end
end
%decision by voting
[vecMaxValue, class] = max(Voting, [ ] , 2);
end