概述
本次实战的所有数据、文献、代码及输出均在以下百度云链接。
链接:https://pan.baidu.com/s/1tR3VCyHxL7EZx_su_3mxNg
提取码:siso
1. 要求
实战的数据及要求如下:
- 用到的数据库为XM2VTS中的前20个人。每个人有8张照片,分4个时段拍摄, 每个时段两张,每个时段间隔1个月。8张照片的编号为 1_1, 1_2 (拍摄于第一时段), 2_1, 2_2(拍摄于第二时段), 3_1, 3_2(拍摄于第三时段), 4_1, 4_2(拍摄于第四时段)。而20个人的照片分别在20个数据文件夹中。
注:图片也可从 http://www.ee.surrey.ac.uk/CVSSP/xm2vtsdb/自行下载- 所有图像都经过剪切,只留下了人脸的部分。
- 程序使用1_1, 1_2, 2_1, 2_2这4幅图像进行训练,用3_1, 3_2, 4_1, 4_2这4幅图像进行测试,统计出准确率。
- 所参照的论文为 Face recognition using eigenfaces
2. 主成分分析(PCA)
主成分分析(Principal Component Analysis,PCA)是一种典型的降维算法,属于非监督学习。其主要思想是通过正交变换将一组可能存在相关性的变量转换为一组线性不相关的变量。
具体推导笔记如下:(打公式太累了,直接贴图)
3. 主成分分析在人脸识别中的应用
具体应用步骤如下:
- 对于一张2-D(比如说N*N维)图像,我们在处理时通常将其向量化为N²*1的列向量。如果有M张图像,则最后处理得到N²*M维的数据矩阵
- 通过对N²*M维度的数据取平均,可以得到N²*1的平均脸图
- 将N²*M维度的数据矩阵减去平均脸图像,得到N²*M维的差值矩阵 Φ Φ Φ
- 求差值矩阵的N²*N²维协方差矩阵 C = Φ Φ ′ C=ΦΦ' C=ΦΦ′
- 对协方差矩阵C进行特征值分解, C = U Σ U ′ C=USigma U' C=UΣU′。首先对特征值进行降序排序,再根据能量百分率准则选择合适的特征值数目及对应数目的特征向量,通常百分率取0.99或者0.95
- 假设选取的特征向量数目为K,那么对应特征脸图的数目也为K,得到N²*K维的变换矩阵U。将差值矩阵投影到K个特征脸图上,得到K*M维的权重矩阵 Y = U ′ M Y=U'M Y=U′M。权重向量可以用来识别不同的人脸,这样看,相当于把一个N²维的数据降到了K维
- 对测试图像进行分类时,先将其列向量化,再减去平均脸图,然后投影到K个平均脸图上得到该测试图像的权重向量。最后计算测试图像权重向量与M张训练图像权重向量的距离,根据欧氏距离最小准则,选择距离最小的权重向量对应的类别作为测试图像的分类结果
注:这些特征向量就是特征脸图,它反映了人脸图中的变化特性,特征脸图对应的特征值越大,说明图像数据投影在这个特征脸图方向上的方差越大。同时也说明,如果保留所有的特征值,那么每一个人脸图都可以看作这些特征脸图的线性组合。那些较小的特征值对应的是噪声的影响,舍弃它们对应的特征脸图几乎不会对分类结果造成影响,因为它们包含的能量是极小的。
Small Tip:
一张图片的维度为N*N,M张图片列向量化后维度为N²*M,使得协方差矩阵C的维度为N²*N²。当N非常大时,直接对C进行特征值分解会导致计算非常慢,常常引起内存爆表,该怎么解决呢?
C = Φ Φ ′ C=ΦΦ' C=ΦΦ′
C ′ = Φ ′ Φ C'=Φ'Φ C′=Φ′Φ
假设 v i v_i vi为 C C C的第 i i i个特征向量, e i e_i ei为 C ′ C' C′的第 i i i个特征向量。
C ′ e i = λ e i C'e_i=lambda e_i C′ei=λei
Φ ′ Φ e i = λ e i Φ'Φe_i=lambda e_i Φ′Φei=λei
Φ Φ ′ Φ e i = λ Φ e i ΦΦ'Φe_i=lambda Φe_i ΦΦ′Φei=λΦei
C Φ e i = λ Φ e i CΦe_i=lambda Φe_i CΦei=λΦei
而 C v i = λ v i Cv_i=lambda v_i Cvi=λvi
v i = Φ e i v_i=Φe_i vi=Φei
因此可以先求得 C ′ C' C′的特征向量,再通过左乘差值矩阵得到C的特征向量。
这种方法的好处是将对N²*N²矩阵进行特征值分解变成了对M*M矩阵进行特征值分解,大大减少了计算量。
4. 实现代码
这次代码基于matlab,具体实现如下:
读取图片数据:
% readFiles.m
% directory表示图片所在文件夹,mode表示读取训练图像数据还是测试图像数据
function [ Images, Labels ] = readFiles( directory, mode)
trainingdata=struct('filename',[]); % 把训练/测试图片的名字存入trainingdata结构体
switch(mode)
case 'train'
trainingdata(1).filename=['1_1Cropped'];
trainingdata(2).filename=['1_2Cropped'];
trainingdata(3).filename=['2_1Cropped'];
trainingdata(4).filename=['2_2Cropped'];
case 'test'
trainingdata(1).filename=['3_1Cropped'];
trainingdata(2).filename=['3_2Cropped'];
trainingdata(3).filename=['4_1Cropped'];
trainingdata(4).filename=['4_2Cropped'];
end
DIR = dir(directory);
DIR(1:2) = []; % 去除. 和 .. 两个文件夹
newM = 128; % 新图片的尺寸
newN = 128;
Images = zeros(newM*newN, 80); % 训练/测试都有80张图片
Labels = zeros(1, 80); % 训练/测试图像对应的标签
count = 0;
for i =1:length(DIR)
if DIR(i).isdir
SUBDIR = dir([directory,'',DIR(i).name]); % 依次遍历各个子目录
SUBDIR(1:2) = [];
for j = 1:length(SUBDIR)
if length(SUBDIR(j).name)>14 % 如果是剪切后的图片,提取如1_1Cropped字符串
string = SUBDIR(j).name(end-13:end-4);
flag = 0;
for k = 1:length(trainingdata)
if strcmp(string, trainingdata(k).filename) % 和训练/测试图片名称作比较, 如果是标记为flag=1
flag = 1;
break
end
end
if flag ==1
count = count+1; % 记录用于训练/测试的数量
A = imread([directory,'',DIR(i).name,'',SUBDIR(j).name]); % 读取该训练/测试图片
if length(size(A)) == 3
A = rgb2gray(A); % rgb转灰度图
end
A = imresize(A,[newM,newN]); % 会自动进行插值运算
Images(:, count) = uint8(reshape(A, newM*newN, 1));
Labels(1, count) = str2num(DIR(i).name);
end
end
end
end
end
disp([num2str(count) ' pictures for ' mode 'ing total.'])
end
主程序:
% faceRecognizePca.m
clc; clear all; close all;
directory = 'database';
M = 128;
N = 128;
% 获取训练/测试人脸图
[trainImages, trainLabels] = readFiles(directory, 'train');
[testImages, testLabels] = readFiles(directory, 'test');
% 平均人脸图
meanImages = mean(trainImages, 2);
meanImg = uint8(reshape(meanImages, [M, N]));
imshow(meanImg);
imwrite(meanImg, 'output/meanFace.jpg', 'jpg');
%% 求协方差矩阵
reduceImages = trainImages - repmat(meanImages, 1, 80);
% 由于图片维度很大,所以我们选择先求M'*M,再通过变换求回来
C = reduceImages'*reduceImages;
[vec, sigma] = eig(C);
V = reduceImages*vec;
sigma = diag(sigma);
% 将特征值由大到小进行排序
[sigma, idx] = sort(sigma, 'descend');
V = V(:, idx);
% 根据能量百分率准则,提取占总能量99%的特征图
k = sum((cumsum(sigma)/sum(sigma))<=0.99);
eigImages = V(:, 1:k);
figure();
% 画出前9张特征脸
for i = 1 : 9
subplot(3,3,i);
imshow(reshape(eigImages(:, i), [M, N]));
imwrite(reshape(eigImages(:, i), [M, N]), ['output/eigenFace' num2str(i) '.jpg'], 'jpg')
end
%% 利用欧氏距离最小准则来进行分类
% 将训练图片和测试图片投影到特征图上获得权重向量
trainW = eigImages'*trainImages;
testW = eigImages'*testImages;
predTest = zeros(size(testLabels));
for i = 1 : size(testImages, 2)
diff = sqrt(sum((repmat(testW(:, i), 1, 80)-trainW).^2, 1));
[~, minIdx] = min(diff); % 找到欧氏距离最小对应的权重向量
predTest(i) = testLabels(minIdx);
end
acc = sum(predTest==testLabels)/size(testImages, 2);
disp(['Accuracy of the test images is ' num2str(acc*100) '%.'])
实验过程中得到的一些中间结果:
图1 :平均脸图
图2:前9个特征值对应的特征脸图
结语:以上内容和代码加入了自己的想法和验证,如有不当之处还请指正。
欢迎转载,标明出处。
最后
以上就是傲娇红酒为你收集整理的基于主成分分析(PCA)的人脸识别算法的全部内容,希望文章能够帮你解决基于主成分分析(PCA)的人脸识别算法所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复