1. 程式人生 > >基於PCA和SVM人臉識別之二.MATLAB實現

基於PCA和SVM人臉識別之二.MATLAB實現

此文章中MATLAB實現均根據《數字影象處理與機器視覺----Visual c++ 與MATLAB實現》一書,我所獲得的基礎知識也大多源於此書,感謝!

                          

下面將我根據教程建立的工程以及敲擊的程式碼塊一一奉上,供日後參閱。

建立以專門資料夾FaceRec,地址E:\matlab_2016b_app\bin\FaceRec。

1.  E:\matlab_2016b_app\bin\FaceRec\Data 記憶體放ORL檔案。

2.  E:\matlab_2016b_app\bin\FaceRec\exportLibSVM 記憶體放export.m檔案,以及由export( )函式生成的txt檔案。

function export(strMat,strLibSVM)
%{
name: export()函式
fuction:用於從matlab匯出libSVM能夠使用的格式(.txt)
        此工程要用libSVM的引數選擇工具 grid.py,首先需要把資料集 格式化為 grid.py所要求的形式(文字檔案)
Input:strMat:原始檔名(包括路徑),此檔案中包含訓練資料(traindata)和類標籤(trainlabel),該檔案可在訓練SVM的過程中生成。
              註釋:MAT-檔案, mat資料格式是matlab的資料儲存的標準格式。mat檔案是標準的 二進位制檔案,還可以ASCII碼形式儲存和載入。
       strLibSVM:目標檔名(包含路徑),'.txt'檔案,預設為‘traindata.txt’
%}


%預設引數設定
if nargin<1
    strMat ='E:/matlab_2016b_app/bin/FaceRec/Mat/trainData.mat';
    strLibSVM ='E:/matlab_2016b_app/bin/FaceRec/exportLibSVM/trainData.txt';
elseif nargin<2
    strLibSVM ='E:/matlab_2016b_app/bin/FaceRec/exportLibSVM/trainData.txt'
end


[fid,fMsg]= fopen(strLibSVM,'w');   %建立輸出檔案目標,‘w’為寫入,若檔案不存在,則建立此新檔案
if fid==-1
    disp(fMsg);
    return;
end


strNewLine=[13,10];% 換行。  回車鍵(軟回車)的ASCII碼值為13,換行符(硬回車)的值為10; 在windows中換行需要‘軟回車+硬回車’!
strBlack=' '; %ASCII碼為32(空格)


load(strMat);  %load 開啟mat檔案.例:load('filename','X','Y','Z') 載入filename檔案中的X Y Z變數到工作區間中
[nSamp,nDim]=size(TrainData);  %nSamp 行數(即為樣本個數);nDim(列數),即為樣本類數。


for iSamp=1:nSamp
    fwrite(fid,num2str (trainLabel(iSamp)),'char' );%寫入一個標籤(以字串形式寫入)
    for iDim=1:nDim
        fwrite(fid,strBlack,'char');%fwrite(fid,32,'char');
        fwrite(fid,[num2str(iDim) ':'],'char');  %寫入列號
        fwrite(fid,num2str(TrainData(iSamp,iDim) ),'char'); %寫入該位置 的值
    end
    fwrite(fid,strNewLine,'char'); %換行 fwrite(fid,[13,10],'char');
end


fclose(fid);

    

3.  E:\matlab_2016b_app\bin\FaceRec\Kernel記憶體放 徑向基核函式 kfun_rbf.m檔案。

function K=kfun_rbf(U,V,gamma)
%{
name: kfun_rbf(函式)
function: kernel_function-> rbf徑向基核函式
          radial basis function: exp(-gamma*|u-v|^2)
Input: U:公式中的X矩陣(m1* n1)
       V:公式中的Y矩陣(m2 *n2)
       gamma; 引數γ(伽馬)
Output:K:返回m1*m2維矩陣




SVM 怎樣能得到好的結果
1. 對資料做歸一化(simple scaling)
2. 應用 RBF kernel
3. 用cross-validation和grid-search 得到最優的c和g
4. 用得到的最優c和g訓練訓練資料
5. 測試
%}


[m1 n1]=size(U); % m1*n1  %%%%%%%%%%%%%%%%%%%%%%%%%
[m2 n2]=size(V); % m2*n2


K=zeros(m1,m2);  % m1*m2矩陣


for ii=1:m1
    for jj=1:m2
        K(ii,jj)= exp( -gamma * norm(U(ii,:)-V(jj,:) )^2 ); %% rbf函式公式 K(x,y)=exp( -gamma * (x-y)^2 );
    end
end


4.  E:\matlab_2016b_app\bin\FaceRec\Mat記憶體放 .mat檔案,.mat檔案中儲存有用變數。

5.  E:\matlab_2016b_app\bin\FaceRec\PCA 記憶體放 fastPCA.m檔案

function [pcaA V]=fastPCA(A,k)
%{
name: fastPCA()快速PCA函式
function: 將輸入樣本矩陣降維(k維)
Input:A---樣本矩陣,每行為1個樣本
      k---降維至k維
Output:pcaA---降維後的k維矩陣
       V---主成分分量
%}

% A的大小
[m,d]=size(A);

%樣本均值
meanVec=mean(A);    %計算各列的均值

%計算協方差矩陣的轉置 covMatT
Z= ( A-repmat(meanVec,m,1)  );   %樣本矩陣中心化,每一維度減去該維度的均值,使得每一維度的均值為0
                                 %repmat:Replicate Matrix複製和平鋪矩陣
covMatT =Z*Z';       %covMatV :(m*d)*(d*m)=m*m

%計算covMatT的前k個本徵值和本徵向量
[V D]=eigs(covMatT,k);  %V為m*k, k個本徵向量; D為k*k,對角線上每一個元素為本徵值

%得到協方差矩陣(covMatT')的本徵向量
V=Z'*V;

%本徵向量歸一化為 單位本徵向量
for i=1:k
    V(:,i)=V(:,i)/norm(V(:,1));  %norm 為範數,預設為2範數(各分量的平方和 再開根號)
end

%線性變換(投影)降維至k維
pcaA=Z*V;

%儲存變換矩陣V和變換原點 meanVec
save('Mat/PCA.mat','V','meanVec');


6.  E:\matlab_2016b_app\bin\FaceRec\SVM 記憶體放SVM訓練和分類函式 multiSVMTrain.m 和 multiSVMClassify.m檔案

6.1 多類問題訓練函式 multiSVMTrain

function multiSVMStruct =multiSVMTrain(TrainData,nSamp_PerClass,nClass,C,gamma)
%{
name:multiSVMTrain()函式
function:採用1對1投票 策略 將SVM推廣至多類問題的訓練過程,將多類SVM訓練結果儲存至multiSVMStruct中
Input:TrainData--- 訓練資料,每行資料代表一個人臉
      nClass---類別數(即 列數)
      nSamp_PerClass--每一類中的樣本數,即 nSamp_PerClass(i)=20,表示第i類有20個數據。
      C---錯誤代價係數,預設為Inf. %Inf為infinite的前三個字母,無窮大的意思。
      gamma---徑向基函式rbf的gamma引數,預設為1
Output:multiSVMStruct--- 一個包含多類SVM訓練結果的結構體
                           multiSVMStruct.nClass =nClass;
                           multiSVMStruct.CASVMStruct=CASVMStruct; %共有n*(n-1)/2個CASVMStruct
%}

%預設引數設定
if nargin<4
    C=Inf;
    gamma=1;
elseif nargin<5
    gamma=1;
end

%開始訓練,需要計算每兩類間的分類超平面,共(nClass-1)*nClass/2個
for ii=1:nClass-1     %注意!  1-> (nClass-1)
%     clear X;     % X:行數為兩類(ii類別和jj類別)的樣本數之和;列數等於TrainData的列數         %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%     startPosII=  sum(nSamp_PerClass(1:ii-1)  ) +1;
%     endPosII= startPosII + nSamp_PerClass(ii) -1;
%     X(1:nSamp_PerClass(ii) ,:)=TrainData( startPosII:endPosII,:);
    
    for jj=ii+1:nClass
        clear X;     % X:行數為兩類(ii類別和jj類別)的樣本數之和;列數等於TrainData的列數    
        clear Y;
        startPosII=  sum(nSamp_PerClass(1:ii-1)  ) +1;
        endPosII= startPosII + nSamp_PerClass(ii) -1;
        X(1:nSamp_PerClass(ii) ,:)=TrainData( startPosII:endPosII,:);
        
        startPosjj=  sum(nSamp_PerClass(1:jj-1)  ) +1;
        endPosjj= startPosjj + nSamp_PerClass(jj) -1;
        X(nSamp_PerClass(ii)+1 :nSamp_PerClass(ii)+nSamp_PerClass(jj), : )=TrainData(startPosjj:endPosjj,:);
        
        %設定兩兩分類時的標籤 (列向量,維數等於兩類(ii類別和jj類別)的樣本數之和)
        Y=ones(nSamp_PerClass(ii)+nSamp_PerClass(jj), 1);
        Y(nSamp_PerClass(ii)+1:nSamp_PerClass(ii)+nSamp_PerClass(jj),1)=0;
        
        %第ii個類別和第jj個類別的兩兩分類時的分類器結構資訊   %% X的行數等於Y的行數
        CASVMStruct{ii}{jj}=svmtrain(X,Y,'Kernel_Function',@(X,Y) kfun_rbf(X,Y,gamma),'boxconstraint',C );
        %%%% ‘{ }’大括號,用於cell型陣列的分配或引用。比如 A(2,1) = {[1 2 3; 4 5 6]}, 
        %%%%  cell 為 細胞型資料結構(cell),)可以使不同型別和不同維數的陣列可以共存,細胞型陣列實際上可以認為是一種以任意形式的陣列為分量的多維陣列。
    end
end

%已學得的分類結果
multiSVMStruct.nClass =nClass;
multiSVMStruct.CASVMStruct=CASVMStruct;

%儲存引數
save('Mat/params.mat','C','gamma');   %儲存指定變數(‘C’,'gamma')到指定資料夾.

        
    


6.2 多類問題分類函式 multiSVMClassify

function class =multiSVMClassify(TestFace,multiSVMStruct)
%{
name: multiSVMClassify()多類問題分類函式
function: 採用1對1投票策略將SVM推廣至多類問題的分類問題
Input:TestFace---測試樣本集,m*n維,每行一個樣本
      multiSVMStruct---多類SVM的訓練結果,由函式multiSVMTrain()返回,預設從‘Mat/multiSVMTrain.mat’檔案中讀取
Output:class---m*1維矩陣,對應TestFace的類標籤
%}




%讀入訓練結果
if nargin<2
    t=dir('Mat/multiSVMTrain.mat');
    if length(t)==0
        error('沒有找到訓練結果,請在分類前先進行訓練');
    end
    load('Mat/multiSVMTrain.mat');
end


nClass=multiSVMStruct.nClass;    %讀入類別數
CASVMStruct =multiSVMStruct.CASVMStruct;%%讀入兩兩類之間的分類器資訊


%%%%%%%%%%%%%%%%%%%%%%%%投票策略解決多類問題%%%%%%%%%%%%%%%%%%%%
m=size (TestFace,1);
Voting =zeros(m,nClass);  %m個測試樣本,每個樣本有nPerson個類別的投票箱
for i=1:nClass-1
    for j=i+1:nClass
        classes = svmclassify (CASVMStruct{i}{j} ,TestFace);
        
        %投票
        Voting(:,i)=Voting(:,i) +(classes ==1);
        Voting(:,j)=Voting(:,j) +(classes==0);
    end
end
%根據結果做最後決定
[vecMaxVal, class]= max(Voting,[],2);
% fMsg=sprintf('TestFace對應的類別是%d',class);
% msgbox(fMsg);


7.  E:\matlab_2016b_app\bin\FaceRec內嗨存放有 classify.m

% classify.m

function nClass =classify(newFacePath)
%{
function:整個分類(識別)過程
Input:newFacePath---待識別影象的儲存路徑
Output:nClass---識別出的類別標號
%}

disp(' ');
disp(' ');
disp('識別開始... ');

%讀入相關訓練結果
disp('載入訓練引數...');%%%%%%%%%%%%%%%%%%%%%%%%%%%
load('Mat/PCA.mat');
load('Mat/scaling.mat');
load('Mat/trainData.mat');
load('Mat/multiSVMTrain.mat');
disp('載入訓練引數完畢....');

% PCA變維
disp('PCA變維....');
xNewFace=ReadOneFace(newFacePath);%讀入一個測試樣本
xNewFace=double(xNewFace);
%xNewFace=xNewFace* W;           %經過PCA變換降維  W
xNewFace=(xNewFace-meanVec)*V;  %經過PCA變換降維
xNewFace=scaling(xNewFace,1,A0,B0);
disp('PCA變維完畢....');


disp('身份識別中......');
nClass=multiSVMClassify(xNewFace);
disp('身份識別結束...');

8.編寫介面程式FR_GUI.m
% FR_GUI.m
global h_axes1;
global h_axes2;
h_f=figure('name','基於PCA和SVM的人臉識別系統');

%設定 C 和gamma 按鈕
h_textC= uicontrol(h_f,'style','text','unit','normalized','string','C=','position',[0.05 0.7 0.1 0.06] );
h_editC= uicontrol(h_f,'style','edit','unit','normalized','position',[0.05 0.6 0.1 0.06], 'callback','C=str2num(get(h_editC,''string''))' );

h_textGamma= uicontrol(h_f,'style','text','unit','normalized','string','gamma=','position',[0.05 0.5 0.1 0.06] );
h_editGamma= uicontrol(h_f,'style','edit','unit','normalized','position',[0.05 0.4 0.1 0.06], 'callback','gamma=str2num(get(h_editGamma,''string''))' );


%取得C和gamma的當前值,即最近一次訓練所使用的值
t=dir('Mat/params.mat');
if(length(t) ==0)  %沒有找到檔案
    C=Inf;
    gamma=1;
else 
    load('Mat/params.mat');
end

set(h_editC,'string',num2str(C) );
set(h_editGamma,'string',num2str(gamma) );


% axes()函式就是axes的位置,左下寬高,單位是和整個figure寬高的比例
h_axes1=axes('parent',h_f,'position',[0.25 0.23 0.32 0.6],'visible','off');
h_axes2=axes('parent',h_f,'position',[0.62 0.23 0.32 0.6],'visible','off');
h_btnOpen=uicontrol(h_f,'style','push','string','開啟','unit','normalized','position',[0.32 0.1 0.18 0.1],'callback','GUIOpenFaceImage' );
h_btnRecg=uicontrol(h_f,'style','push','string','識別','unit','normalized','position',[0.67 0.1 0.18 0.1],'callback','GUIRecgFaceImage' );
h_btnTrain=uicontrol(h_f,'style','push','string','訓練','unit','normalized','position',[0.32 0.83 0.18 0.1],'callback','train(C,gamma)' );
h_btnTest=uicontrol(h_f,'style','push','string','測試','unit','normalized','position',[0.67 0.83 0.18 0.1],'callback','test' );


9.開啟按鈕 的背後函式 GUIOpenFaceImage.m
%GUIOpenFaceImage.m
global filepath;
[filename,pathname]= uigetfile({'*.pgm; *.jpg;*.tif', '(*.pgm),(*.jpg),(*.tif)'; '*.*','All Files(*.*)'}, ...
                               'Select a face image to be recognized' );
 if filename ~=0  % ~= 不等於
     filepath =[pathname,filename];
     axes(h_axes1);    % axes()函式就是axes的位置,左下寬高,單位是和整個figure寬高的比例
                       % h_axes 為全域性變數 
     imshow(imread(filepath));
 end

10. 識別按鈕的背後函式 GUIRecgFaceImage.m
%GUIRecgFaceImage.m

nClass= classify(filepath);  %filepath 為全域性變數 
                             % classify()函式為人為編寫的,不是系統帶的
msgbox(['所屬類別為: ',num2str(nClass) ]);
axes(h_axes2);
f=imread([ 'Data/ORL/S',num2str(nClass), '/1.pgm'] );%開啟該人的第一幅影象
imshow( f);

11.讀入多個人臉資料樣本 ReadFaces.m
function [imgRow,imgCol,FaceContainer,faceLabel] = ReadFaces (nFacesPerPerson,nPerson,bTest)
%{
name: ReadFaces()讀取人臉影象
function: 將人臉影象(每張112*92畫素點)轉換為向量形式(10304*1 向量),進而組成樣本矩陣(nPerson *10304矩陣)
Input:nFacesPerPerson---每個人需要讀入的人臉影象樣本數目(預設為5張(訓練或測試) )
      nPerson---需要讀入的人數(預設為40人)
      bTest---bool型引數,預設為0(表示讀入訓練樣本,即每人的前5張);如果為1,表示讀入測試樣本(後5張)
Output:FaceContainer---向量化後的人臉容器,nPerson * 10304的2維矩陣,每行對應一個人臉向量
%}


%預設值設定
if nargin==0 %default value
    nFacesPerPerson =5;
    nPerson=40;
    bTest=0;
elseif nargin<3
    bTest=0;     % 讀入訓練樣本
end

% 為計算尺寸先讀入一張,計算出大小
img=imread('Data/ORL/S1/1.pgm'); %S1表示第一個人,1.pgm表示(此人的)第一張影象
[imgRow,imgCol]=size(img);


%轉換目標容器 
FaceContainer =zeros(nFacesPerPerson *nPerson,imgRow*imgCol); %(200*10304)
faceLabel =zeros(nFacesPerPerson*nPerson,1);                 %(200*1)

%讀入訓練資料
for i=1:nPerson
    i0=char(i/10);  % 表示第i0個人的影象(一個人有10張),0->i-1;  i0=int(i/10); 
    i1=mod(i,10);   %取模,表示第i0個人的第i1張影象
    strPath='Data/ORL/S';%影象路徑
    if (i0~=0)      %%%%%%%%但此處理只能最大是99個人,若為100人,則此法不能寫出S100!!
        strPath =strcat(strPath,'0'+i0);%用於s後為多位數時的處理, 例 'Data/ORL/S11'
    end
    strPath =strcat(strPath,'0'+i1);  %字串連線  變為 'Data/ORL/Si1'
    strPath =strcat(strPath,'/');   %'Data/ORL/Si1 / '
    
    tempStrPath=strPath;%暫存影象路徑,因為後邊要區分訓練樣本(前五張)和測試樣本(後五張)
    for j=1:nFacesPerPerson
        strPath=tempStrPath;
        
        if bTest==0  %讀入訓練資料
            strPath=strcat(strPath,'0'+j); %1->5    %'Data/ORL/Si1 /j '
        else 
            strPath=strcat(strPath,num2str(j+5) );  %6->10  % 等價於j+5+'0'
        end
        
        strPath=strcat(strPath,'.pgm');
        img=imread(strPath);
        
        %把讀入的影象按列 儲存為行向量 放入向量化人臉容器 faceContainer 的對應行中
        FaceContainer ((i-1)*nFacesPerPerson +j,:)= img(:)' ; % 按列 儲存的 行向量(最終)
                                                              % img(:)的返回值為按列儲存的一維列向量,即原先的112*92矩陣,變為10304*1的列向量
                                                              % img(:)'轉置後為1*10304的行向量                                           
        faceLabel((i-1)*nFacesPerPerson +j)=i;
        
    end
end

%儲存人臉樣本矩陣
%save('Mat/FaceMat.mat','FaceContainer');
    


12. 讀入當個人臉樣本 ReadOneFace.m
function face=ReadOneFace(dir)
%{
function:根據指定目錄只讀入一張人臉圖片
Input;dir---指定目錄
Output:face---返回行向量向量,人臉圖片按列儲存在行向量裡

%}

img=imread(dir);
[imgRow imgCol]=size(img);  %112*92

face=zeros(1,imgRow*imgCol);
face=img(:)';

13. 資料規格化 scaling.m

function [SVFM,lowVec,upVec]= scaling(VecFeaMat,bTest, lRealBVec,uRealBVec)
%{
name:  scaling()函式;
function:特徵資料規格化(歸一化),線性縮放特徵的各個屬性(維度)到【-1,+1】  
Input: VecFeaMat ---需要scaling的m*n維資料矩陣(可能為訓練集,可能為測試集),每行一個樣本特徵向量,列數為維數;
       bTest--- =1,說明是對樣本進行scaling,此時必須提供lRealBVec,upRealBVec這兩個引數的值(應從對訓練樣本的scaling中得到)
       lRealBVec --- n*1矩陣(即列向量),對訓練樣本scaling時得到的 各維的實際下限資訊lowVec(lRealBVec(i)代表第i列中最小的那個值)
       upRealBVec --- n*1矩陣(即列向量),對訓練樣本scaling時得到的 各維的實際上限資訊lowVec
Output:SVFM---VecFeaMat經scaling之後的樣本(m*n)
       lowVec----(1*n矩陣 ,即行向量)各維特徵的下限(只在對訓練樣本scaling時有意義,即bTest=0時)
       upVec ----(1*n矩陣 ,即行向量)各維特徵的上限(只在對訓練樣本scaling時有意義,即bTest=0時)
  %}

if nargin<2   %預設引數的預設設定
    bTest =0; %預設為bTest=0(即對訓練樣本進行scaling)
end

%縮放目標範圍[-1,+1]
lTargB=-1;
uTargB=+1;

[m n]=size(VecFeaMat);
SVFM=zeros(m,n);

if bTest %對test資料進行scaling
    if nargin <4           %(輸入引數預設)
        error('To do scaling on testset,param lRealB and uRealB are needed.');
    end
    if nargout >1
        error('When do scaling on testset,only one output is supported.');
    end
    
    for iCol =1:n
        if uRealBVec(iCol)== lRealBVec(iCol)   %即當列最大值 最小值相等
            SVFM(:,iCol)=uRealBVec(iCol);  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
            SVFM(:,iCol)=0;
        else                             %test資料 規格化(歸一化)
            SVFM(:,iCol)= lTargB +(VecFeaMat(:,iCol) - lRealBVec(iCol))/( uRealBVec(iCol)- lRealBVec(iCol))*(uTargB-lTargB);
        end
    end

else    %對訓練集資料 進行scaling.需要返回lowVec,upVec
    upVec=zeros(1,n);  %行向量
    lowVec=zeros(1,n);
    
    for iCol=1:n
        lowVec(iCol)=min( VecFeaMat(:,iCol) );
        upVec(iCol)=max( VecFeaMat(:,iCol) );
        if upVec(iCol)==lowVec(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

        
    

14.測試對於整個測試集的正確率 test.m

function test()
% 測試對於整個測試集的識別率
%
% 輸出:accuracy --- 對於測試集合的識別率


display(' ');
display(' ');
display('測試開始...');

nFacesPerPerson = 5;
nPerson = 40;
bTest = 1;
% 讀入測試集合
display('讀入測試集合...');
[imgRow,imgCol,TestFace,testLabel] = ReadFaces(nFacesPerPerson, nPerson, bTest);
display('..............................');

% 讀入相關訓練結果
display('載入訓練引數...');
load('Mat/PCA.mat');
load('Mat/scaling.mat');
load('Mat/trainData.mat');
load('Mat/multiSVMTrain.mat');
display('..............................');

% PCA降維
display('PCA降維處理...');
[m n] = size(TestFace);
TestFace = (TestFace-repmat(meanVec, m, 1))*V; % 經過pca變換降維
TestFace = scaling(TestFace,1,A0,B0);
display('..............................');

% 多類 SVM 分類
display('測試集識別中...');
classes = multiSVMClassify(TestFace);
display('..............................');

% 計算識別率
nError = sum(classes ~= testLabel);
accuracy = 1 - nError/length(testLabel);
display(['對於測試集200個人臉樣本的識別率為', num2str(accuracy*100), '%']);

15. 整個訓練過程train.m
function:整個訓練過程
step: 1)讀取人臉資料(readFaces),儲存至Mat/FaceMat.mat
      2)將fastPCA變換矩陣W(主分量陣)儲存至Mat/PCA.mat
      2)將scaling的各維上、下界資訊儲存至Mat/scaling.mat
      3)將PCA降維並且scaling後的資料儲存至Mat/trainData.mat
         4)將多類SVM訓練資訊儲存至Mat/multiSVMTrain.mat
function train(C,gamma)
%{
name: train()函式
function:整個訓練過程

step: 1)讀取人臉資料(readFaces),儲存至Mat/FaceMat.mat
      2)將fastPCA變換矩陣W(主分量陣)儲存至Mat/PCA.mat
      2)將scaling的各維上、下界資訊儲存至Mat/scaling.mat
      3)將PCA降維並且scaling後的資料儲存至Mat/trainData.mat
      4)將多類SVM訓練資訊儲存至Mat/multiSVMTrain.mat
%}

global imgRow;
global imgCol;

disp(' ');
disp(' ');
disp('訓練開始...');

nPerson=40;
nFacesPerPerson=5;
disp('讀入人臉資料...');
[imgRow,imgCol,FaceContainer,faceLabel]=ReadFaces(nFacesPerPerson,nPerson);
save('Mat/FaceMat.mat','FaceContainer');
disp('讀入完畢...');

nFaces=size(FaceContainer,1);  %樣本(人臉)數目

disp('PCA降維...');
[pcaFaces,W]=fastPCA(FaceContainer,20);   %快速主成分分析(內含儲存操作)
                                          % pcaFaces:200*20矩陣,每行一個樣本(每人5張,共40人)
                                          % W:分離變換矩陣,10304*20  (用於顯示主成分臉)
%visualize_pc(W);   %顯示主成分臉
%save('Mat/PCA.mat','W');
disp('降維完畢...');

X=pcaFaces;
disp('scaling...');
[X,A0,B0]=scaling(X);  %訓練樣本
save('Mat/scaling.mat','A0','B0');
disp('scaling完畢...');

%儲存scaling後的訓練資料至trainData.mat
TrainData=X;
trainLabel=faceLabel;
save('Mat/trainData.mat','TrainData','trainLabel');
export();           %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
disp('儲存trainData.mat完畢...');
 
for iPerson=1:nPerson
    nSplPerClass(iPerson) =sum( (trainLabel == iPerson) );
end

multiSVMStruct =multiSVMTrain(TrainData,nSplPerClass,nPerson,C,gamma);
disp('正在儲存訓練結果...');
save('Mat/multiSVMTrain.mat','multiSVMStruct');
disp('訓練結束');