Eigenface就是将人脸图像进行编码,映射到低维子空间上,在低维空间计算两幅人脸图像的距离,以此来进行人脸识别。映射到低维子空间的方法采用主成分分析(Principal Component Analysis,PCA)
1.将人脸图像(均为灰度图)拉成一个列向量存储在矩阵 A={A1,A2,⋯,An} 中, A∈Rd×n ,其中, d 是每张人脸图片的像素数目, n 是图像数目。为了保证后面的计算正常进行,我们假定图像的像素值已经转换成了double类型。
f = imread('face.jpg'); % f是uint8类型
f = im2double(f); % f转换成double类型
f = reshape(f, [d, 1]); % f拉成列向量
在我们的例子中包含25张人脸图像,如下图所示
2.计算平均脸 A¯¯¯=∑i=1nAi ,平均脸也可以看做是一幅人脸图像
3.将原始图像每个维度中心化, Φi=Ai−A¯¯¯ , Φ={Φ1,Φ2,⋯,Φn} , Φ∈Rd×n
4.对 Φ 进行主成分分析, ΦΦT=λiωi ,即求解 ΦΦT 的特征值和特征向量,将特征向量进行施密特正交化,对 λi 进行降序排列,选出前 s 个特征值对应的特征向量 Ω(Ω∈Rd×n) 作为新空间的基,将原始数据投影到新空间上
Φnewi=ΩTΦi
在新空间中采用距离度量两幅图像的相似性
注: ΦΦT=λiωi,λi 是原始数据在第 k 个方向上投影的方差值,为最大程度保留原始数据的信息,可以选择一个阈值 θ ,使得 s 满足下面的式子:
∑i=1s−1λi∑i=1nλi<θ and ∑i=1sλi∑i=1nλi≥θ
5.在我们的例子中,人脸图像数目 n 远小于图像的像素数目 d ,如果直接计算 ΦΦT 的特征值和特征向量,计算复杂度较高,对于100 × 100 大小的图片, ΦΦT 的大小为10000 × 10000,带来的存储和计算复杂度都是难以承受的,为此,我们可以先计算 ΦTΦ 的特征值和特征向量,即
ΦTΦμi=λiμi
ΦΦT(Φμi)=λi(Φμi)
令 ωi=Φμi ,则有 ΦΦT=λiωi
注意: ΦTΦ∈Rn×n ,而 ΦΦT∈Rd×d ,通过这样计算出来的特征向量只有 n 个,那么对于剩余的特征向量怎么办呢?事实上, n 个 d 维向量张成的空间维度最多为 n ( n<d ),我们可以直接采用这 n 个 d 维向量作为它的基,这 n 个 d 维向量可以用这组基完全表示,那么,采用另外一组基表示这个空间时,它最多需要 n 个基,换句话说,这 n 个 d 维向量在剩下的特征向量上的投影都是0,即 ΦΦT 最多有n个不为0的特征值,原始数据在剩余的特征向量上的投影都是0,所以并不需要剩余的特征向量
warning: ΦΦT 类似于 Φ 的协方差矩阵,也可以对协方差矩阵求解特征值和特征向量,采用5的方法求解时,千万不要先求解 ΦT 的协方差矩阵,再求解特征值和特征向量,个中差异请自行体会
下面撸代码,运行前需要在同一个文件夹建立一个train文件夹,在里面放入人脸图片,直接运行会提示你选择要识别的图片
clear all
s = dir('train');
s(1:2) = [];
d = 128 * 128; % 每张图片像素总数
n = length(s); % 训练集内照片总数
img = zeros(d, n);
for k = 1:n
path = strcat('train/', s(k).name);
f = imread(path);
if(numel(size(f)) == 3)
f = rgb2gray(f);
end
f = imresize(f, [128, 128]);
f = reshape(f, [d, 1]);
f = im2double(f);
img(:, k) = f;
end
mean_img = mean(img, 2);
for k = 1:n
img(:, k) = img(:, k) - mean_img;
end
[w, a, explained] = pcacov(img' * img);
w = img * w;
face_num = 0; % face_num用于存储需要特征脸的数目,协方差>=95%
per = 0;
while(per<95)
face_num = face_num + 1;
per = per + explained(face_num);
end
w = w(:, 1:face_num);
for k = 1:face_num
w(:, k) = w(:, k)/norm(w(:, k));
end
eigenface = zeros(face_num, n); % eigenface用于储存在人脸图像在特征空间的表达
for k = 1:n
eigenface(:, k) = w' * img(:, k);
end
[file, path] = uigetfile({'*.*', 'All Files'}, '选择您要识别的图片:');
path = strcat(path,file);
f = imread(path);
subplot(1, 2, 1);
imshow(f);
title('要识别的图片');
if(numel(size(f)) == 3)
f = rgb2gray(f);
end
f = imresize(f, [128, 128]);
f = reshape(f, [d, 1]);
f = im2double(f);
f = f - mean_img;
f = w' * f; % 将要识别的图片投影到特征空间上
% 在数据库中找寻与识别图片最相近的图片
distance = Inf;
best_k = 0;
for k = 1:n
d = norm(f - eigenface(:, k));
if(distance > d)
best_k = k;
distance = d;
end
end
path = strcat('train/', s(best_k).name);
subplot(1, 2, 2);
imshow(path);
title('数据库中最接近的图片');