天天看點

基于MATLAB的PCA人臉識别實作基于MATLAB的PCA人臉識别實作

基于MATLAB的PCA人臉識别實作

前言

其實這個程式早就完成了,而且還要比另一篇基于opencv的博文還要早,這裡主要是回顧一下。關于PCA人臉識别的步驟已經在另一篇博文中有講解,這裡就不多說了,直接上程式。代碼下載下傳位址 或移步到 github

目錄

文章目錄

  • 基于MATLAB的PCA人臉識别實作
    • 前言
    • 目錄
    • 1. 函數設計
      • 1.1 函數調用過程
      • 1.2 函數介紹
    • 2. 資料準備
    • 3. 主函數
    • 4. 源碼

1. 函數設計

1.1 函數調用過程

基于MATLAB的PCA人臉識别實作基于MATLAB的PCA人臉識别實作

1.2 函數介紹

從整體上分為訓練和測試兩大函數,訓練函數pca_train将訓練的模型資料以pca_data.mat形式儲存到目前目錄。測試函數pca_test執行時從pca_data.mat從加載資料。

訓練函數:

pca_train(path,trainImageNameList, newSize, trainClassType, energy)
%pca_train(path,trainImageNameList, newSize, trainClassType, energy)
%功能:根據訓練樣本,計算并儲存classType,newSize,originSize,平均臉,特征臉,投影矩陣,到pca_data.mat
%輸入:
% path:訓練樣本路徑
% trainImageNameList:訓練圖像名稱清單(元胞數組)
% newSize:縮減後的圖像尺度
% trainClassType:訓練樣本類别标号(列向量)
% energy:能量比
%輸出:
%儲存pca_data.mat到目前目錄
           

測試函數:

testClassType = pca_test(path, testImageNameList, trueClassType)
%testClassType = pca_test(path, testImageNameList, trueClassType)
%功能:訓練樣本,得到特征空間的投影矩陣,并求測試樣本的類别
%輸入:
% path:測試樣本路徑
% testImageNameList:測試圖像名稱清單(元胞數組)
% trueClassType:測試真實類别
%輸出:
%testClassType:分類結果
           

資料陣準備子函數:

[samples, samplesMean, rawNum, rolNum, originSize]=arrDataMat(path, imageNameList, newSize)     
%[samples, samplesMean, rawNum, rolNum, originSize]=arrDataMat(path, imageNameList, newSize) 
%子函數,根據圖像名稱清單,讀取圖像資料,并灰階化,轉化成 樣本數*[newSize(1)*newSize(2)]資料陣
%輸入:
%path:圖像路徑
%imageNameList:圖像名稱清單,類型為元胞數組
%newSize:縮減後圖像尺度
%輸出:
%samples:資料矩陣(一行為一個樣本)
%samplesMean:資料陣平均值(行向量)
%rawNum:樣本數
%rolNum:原始的變量維數,即像素的行*像素的列
%originSize:縮減前圖檔尺寸
           

特征向量施密特正交化機關化:

vv = simitzj(v, d)
%vv = simitzj(v, d)
%功能:對輸入的實對稱的特征值,特征向量施密特正交化,機關化
%輸入:
%v:特征向量
%d:特征值
%輸出:
%vv:正交化機關化後的特征向量
           

2. 資料準備

訓練樣本圖檔與測試樣本圖檔分别位于兩個檔案夾,在訓練或測試時,需要擷取訓練或測試樣本圖檔的名稱清單(以cell數組形式),是以編寫了一個函數用于擷取指定目錄下的所有檔案名稱,用于在主函數中調用。

fileList=getFileList(path)
% fileList=getFileList(path)
%輸入:
%path:所擷取的檔案清單的路徑
%輸出:
%fileList:path路徑下檔案清單,cell數組
           

3. 主函數

path='D:\使用者目錄\Documents\MATLAB\Face_Image\face_1\';
trainpath=[path,'train\'];
testpath=[path,'test\'];
trainImageNameList=getFileList(trainpath);
testImageNameList=getFileList(testpath);
for i=1:size(trainImageNameList,1)
    trainClassType(i,:)=ceil(i/5);
end
trueClassType = (1:40)';
newSize=[50,50];
energy=0.9;
pca_train(trainpath,trainImageNameList, newSize, trainClassType, energy);
testClassType = pca_test(testpath, testImageNameList, trueClassType);
%将人臉原圖像與 特征臉反投影後的人臉對比
l=15; %要比對第幾張
%顯示一張人臉原圖像
i1=imread([trainpath,trainImageNameList{l}]);
i1=rgb2gray(i1);
figure;
imshow(i1);
%将特征臉反投影回去
load pca_data;
zeroMeanTrainSamples2=trainNew*T'; %特征臉反投影得到零均值的人臉
trainSamples2=zeroMeanTrainSamples2+repmat(trainSamplesMean,size(zeroMeanTrainSamples2,1),1); %加上均值
i2=reshape(trainSamples2(l,:)',newSize(1),newSize(2)); %還原為矩陣
i2=imresize(i2,originSize); %還原為原始圖像尺寸
figure;
imshow(i2);
           

執行結果:

平均臉:

基于MATLAB的PCA人臉識别實作基于MATLAB的PCA人臉識别實作

一個人的人臉原圖檔:

基于MATLAB的PCA人臉識别實作基于MATLAB的PCA人臉識别實作

投影後的圖像:

基于MATLAB的PCA人臉識别實作基于MATLAB的PCA人臉識别實作

識别準确度:

基于MATLAB的PCA人臉識别實作基于MATLAB的PCA人臉識别實作

注:因為人臉樣本圖像未經過标準化處理,背景區域較大,可能導緻識别率僅為0.95,而且在訓練過程中由于圖檔尺寸太大會導緻計算亮過大,不得不對圖檔縮減,投影後在還原為原始尺寸,導緻pca投影後與原圖像相比失真較大。

4. 源碼

function pca_train(path,trainImageNameList, newSize, trainClassType, energy)
%pca_train(path,trainImageNameList, newSize, trainClassType, energy)
%功能:根據訓練樣本,計算并儲存classType,newSize,originSize,平均臉,特征臉,投影矩陣,到pca_data.mat
%輸入:
% path:訓練樣本路徑
% trainImageNameList:訓練圖像名稱清單(元胞數組)
% newSize:縮減後的圖像尺度
% trainClassType:訓練樣本類别标号(列向量)
% energy:能量比
%輸出:
%儲存pca_data.mat到目前目錄

save('pca_data.mat','trainClassType');
fprintf('儲存trainClassType到pca_data.mat成功!\n');
save('pca_data.mat','newSize','-append');
fprintf('儲存newSize到pca_data.mat成功!\n');

%step1:調用子函數,計算訓練樣本的資料陣,和平均臉
[trainSamples, trainSamplesMean, trainNum, ~, originSize]=arrDataMat(path, trainImageNameList, newSize);
trainMeanFace = reshape(trainSamplesMean',newSize(1),newSize(2));
save('pca_data.mat','trainSamplesMean','-append');
fprintf('儲存trainSamplesMean到pca_data.mat成功!\n');
save('pca_data.mat','originSize','-append');
fprintf('儲存originSize到pca_data.mat成功!\n');
figure;
trainMeanFaceOriginSize = imresize(trainMeanFace, originSize);
imshow(trainMeanFaceOriginSize); %顯示平均臉
title('Mean face of the training samples');
%step2:求協方差陣的特征值和向量并排序,正交化機關化,求投影矩陣
%求樣本的協方差矩陣,并求特征值和特征向量,确定出降的維數,求投影矩陣
%不直接求a'a的特征值特征向量,而是采用SVD的方法,利用aa'的特征值特征向量來求a'a的特征值和向量
trainZeroMeanSamples=trainSamples-repmat(trainSamplesMean,trainNum,1);%計算零均值的人臉樣本
cov = trainZeroMeanSamples*trainZeroMeanSamples';%求協方差矩陣
[v, d] = eig(cov);
lamna = diag(d);
[D, indx] = sort(lamna,1,'descend');%對特征值進行排序
rankV = v(:,indx);%對特征向量排序
t = 0;
tt = sum(D);
for i=1:trainNum %選出累積能量占%99特征值
    t = t + D(i);
    ratio = t/tt;
    if(ratio>=energy)
        break;
    end
end
T_len=i;%選出特征值的個數
T2 = rankV(:,1:i);%選出特征向量
D2 = D(1:i);%選出特征值
T3 = simitzj(T2,D2); %特征向量的歸一化,正交化
%求a'a的特征值特征向量,還原為原始協方差的特征向量
L = repmat((1./sqrt(D2))',trainNum,1);
T=trainZeroMeanSamples'*(T3.*L);%投影矩陣
% Data{4} = T;
save('pca_data.mat','T','-append');
fprintf('儲存T到pca_data.mat成功!\n');

%step3:求訓練樣本的特征臉
trainNew = trainZeroMeanSamples*T; %求訓練樣本特征臉
% Data{5} = trainNew;
% save('Data.mat','Data');
% disp('資料儲存成功!');
save('pca_data.mat','trainNew','-append');
fprintf('儲存trainNew到pca_data.mat成功!\n');
end
           
function testClassType = pca_test(path, testImageNameList, trueClassType)
%testClassType = pca_test(path, testImageNameList, trueClassType)
%功能:訓練樣本,得到特征空間的投影矩陣,并求測試樣本的類别
%輸入:
% path:測試樣本路徑
% testImageNameList:測試圖像名稱清單(元胞數組)
% trueClassType:測試真實類别
%輸出:
%testClassType:分類結果

load ('pca_data.mat','trainClassType','newSize','trainSamplesMean','T','trainNew');

%調用子函數,将測試樣本轉化為資料陣
[testSamples, ~, testNum]=arrDataMat(path, testImageNameList, newSize);
testZeroMeanSamples = testSamples-repmat(trainSamplesMean,testNum,1);
testNew = testZeroMeanSamples*T;%求測試樣本的特征臉
n = size(trainNew,1);
m = size(testNew,1);
dis = zeros(m,n);
for i=1:m %求距離矩陣
    for j=1:n
        dis(i,j) = sqrt(sum((testNew(i,:)-trainNew(j,:)).^2));
    end
end
K=1; %KNN最近鄰的k值
[~, sortDisIndex] = sort(dis, 2, 'ascend');
KnnClassType = zeros(m, n);
for i=1:m
    KnnClassType(i,:)=trainClassType(sortDisIndex(i,:))';
end
testClassType = mode(KnnClassType(:,1:K), 2);
if nargin == 3
    total = length(trueClassType);
    count = 0;
    for i=1:total
        if testClassType(i) == trueClassType(i)
            count = count+1;
        end
    end
    rate = count/total;
    fprintf('分類的準确度是%f\n',rate);
    figure;
    h=bar([rate,1-rate]);
    set(h,'barwidth',.2);
    set(gca,'xticklabel',{'true rate','false rate'});
end
end
           
%子函數:準備原始資料陣
function [samples, samplesMean, rawNum, rolNum, originSize]=arrDataMat(path, imageNameList, newSize)        
%[samples, samplesMean, rawNum, rolNum, originSize]=arrDataMat(path, imageNameList, newSize) 
%子函數,根據圖像名稱清單,讀取圖像資料,并灰階化,轉化成 樣本數*[newSize(1)*newSize(2)]資料陣
%輸入:
%path:圖像路徑
%imageNameList:圖像名稱清單,類型為元胞數組
%newSize:縮減後圖像尺度
%輸出:
%samples:資料矩陣(一行為一個樣本)
%samplesMean:資料陣平均值(行向量)
%rawNum:樣本數
%rolNum:原始的變量維數,即像素的行*像素的列
%originSize:縮減前圖檔尺寸

rawNum = size(imageNameList,1); %rawNum:樣本數
rolNum=newSize(1)*newSize(2); %原始次元
samples = zeros(rawNum, rolNum);
img = imread([path,imageNameList{1}]);
originSize = size(img);
originSize = originSize(1:2);
clear img;
%準備樣本矩陣
 for k=1:rawNum
     imageTemp_ = imread([path,imageNameList{k}]);
     imageTemp = im2double(imageTemp_);
     if length(size(imageTemp))==3
        imageTemp = rgb2gray(imageTemp); %灰階化
        imageTemp = histeq(imageTemp); %直方圖均衡化
     end
    imageTemp2 = imresize(imageTemp, newSize);
    imageTemp3  = imageTemp2(:)';
    samples(k,:) = imageTemp3;
end
samplesMean = mean(samples); %樣本均值
end
           
%子函數,進行施密特正交化,對實對稱矩陣的特征向量求正交矩陣
function vv = simitzj(v, d)
%vv = simitzj(v, d)
%功能:對輸入的實對稱的特征值,特征向量施密特正交化,機關化
%輸入:
%v:特征向量
%d:特征值
%輸出:
%vv:正交化機關化後的特征向量
ii=1;
k=0;
nn=length(d);
vv=zeros(size(v));
while ii<=nn
    jj=ii-k;
    b=0;
    while jj<ii
        b=b+dot(vv(:,jj),v(:,ii))/dot(vv(:,jj),vv(:,jj))*vv(:,jj);
        jj=jj+1;
    end
    vv(:,ii)=v(:,ii)-b;
    ii=ii+1;
    if ii<=nn && d(ii)==d(ii-1)
        k=k+1;
    else
        k=0;
    end
end
for ii=1:nn
    vv(:,ii)=vv(:,ii)/sqrt(dot(vv(:,ii),vv(:,ii)));
end
end
           
function fileList=getFileList(path)
% fileList=getFileList(path)
%輸入:
%path:所擷取的檔案清單的路徑
%輸出:
%fileList:path路徑下檔案清單,cell數組
list=dir(path);
n=size(list,1);
fileList=cell(n-2,1);
k=1;
for i=1:n
    if strcmp(list(i).name,'.') || strcmp(list(i).name, '..')
        continue;
    end
    fileList{k}=list(i).name;
    k=k+1;
end
end
           

繼續閱讀