MATLAB中PCA⼈脸识别实现
因为需要,花了⼀点时间写了下经典的基于特征脸(EigenFace)的⼈脸识别⽅法的Matlab代码。这⾥仅把该代码分享出来。其实,在较新版本的OpenCV中已经提供了FaceRecognizer这⼀个类,⾥⾯不仅包含了特征脸EigenFace,还有FisherFace和LBPHFace这三种⼈脸识别⽅法,有兴趣的可以参考OpenCV的API⼿册,⾥⾯都有很详细的使⽤例程了。 ⼀、特征脸
特征脸EigenFace从思想上其实挺简单。就相当于把⼈脸从像素空间变换到另⼀个空间,在另⼀个空间中做相似性的计算。这么说,其实图像识别的基本思想都是⼀样的,⾸先选择⼀个合适的⼦空间,将所有的图像变换到这个⼦空间上,然后再在这个⼦空间上衡量相似性或者进⾏分类学习。那为什么要变换到另⼀个空间呢?当然是为了更好的做识别或者分类了。那为什么变换到⼀个空间就好识别或者分类了呢?因为变换到另⼀个空间,同⼀个类别的图像会聚到⼀起,不同类别的图像会距离⽐较远,或者在原像素空间中不同类别的图像在分布上很难⽤个简单的线或者⾯把他们切分开,然后如果变换到另⼀个空间,就可以很好的把他们分开了。有时候,线性(分类器)就可以很容易的把他们分开了。那既然⼈类看起来同类的图像本来就是相似的,不同类的图像就不太相似,那为什么在原始的像素空间他们同类不会很近,不同类不会很远,或者他们为什么不好分开呢?因为图像各种因素的影响,包括光照、视⾓、背景和形状等等不同,会造成同⼀个⽬标的图像都存在很⼤的视觉信息上的不同。如下图所⽰。 世界上没有存在任何两⽚完全相同的叶⼦,虽然他们都是叶⼦。万千世界,同⼀类事物都存在共性,也存在个性,这就是这个世界多彩的原因。那怎么办呢?很⾃然,只要在我们想要的粒度上把同⼀类⽬标的共性出来就好了,⽽且这个共性最好和我们要区分的类是不⼀样的。什么叫我们想要的粒度?我理解和我们的任务相关的。例如我们要区分⼈和车,那⼈的共性就是有脸、有⼿、有脚等等。但如果我们要区分亚洲⼈和⾮洲⼈,那么亚洲⼈的共性就是黄⾊⽪肤等等。可以试着想象,上帝把世界万物组织成⼀个树状结构,树的根就是万物之源,下⼀层可以分成⽣物和⾮⽣物,再下⼀层将⽣物分为……(囧,想象不到),直到最底层,万物,你我,为树的⼀⽚普通得再普通的叶⼦。树越往下,
粒度越⼩,分类越细(哈哈,⾃⼰乱扯的)。停!废话多了点,跑题了,回到刚才的问题,重头戏来了,要变换到什么空间,才具备上述这种良好类内相似、类间区分的效果?到这,我就只能say sorry了。计算机视觉领域发展了⼏⼗年,就为了这⼀个问题倾注了⽆数研究者的智慧与⼼⾎。当然了,也诞⽣和孕育了很多经典和有效的解答。(个⼈理解,上述说的实际上就是特征提取)。从⼀开始的颜⾊特征(颜⾊直⽅图)、纹理特征(Harr、LBP、HOG、SIFT等)、形状特征等到视觉表达Bag of Words,再到特征学习Deep Learning,技术的发展总能带给⼈希望,曙光也越来越清晰,但路还很远,是不?
扯太多了,严重离题了。上⾯说到,特征脸EigenFace的思想是把⼈脸从像素空间变换到另⼀个空间,在另⼀个空间中做相似性的计算。EigenFace选择的空间变换⽅法是PCA,也就是⼤名⿍⿍的主成分分析。它⼴泛的被⽤于预处理中以消去样本特征维度之间的相关性。当然了,这⾥不是说这个。EigenFace⽅法利⽤PCA得到⼈脸分布的主要成分,具体实现是对训练集中所有⼈脸图像的协⽅差矩阵进⾏本征值分解,得对对应的本征向量,这些本征向量(特征向量)就是“特征脸”。每个特征向量或者特征脸相当于捕捉或者描述⼈脸之间的⼀种变化或者特性。这就意味着每个⼈脸都可以表⽰为这些特征脸的线性组合。实际上,空间变换就等同于“搞基”,原始像素空间的基就是单位“基”,经过PCA后空间就是以每⼀个特征脸或者特征向量为基,在这个空间(或者坐标轴)下,每个⼈脸就是⼀个点,这个点的坐标就是这个⼈脸在每个特征基下的投影坐标。哦噢,说得有点绕。
下⾯就直接给出基于特征脸的⼈脸识别实现过程:
1)将训练集的每⼀个⼈脸图像都拉长⼀列,将他们组合在⼀起形成⼀个⼤矩阵A。假设每个⼈脸图像是MxM⼤⼩,那么拉成⼀列后每个⼈脸样本的维度就是d=MxM⼤⼩了。假设有N个⼈脸图像,那么样本矩阵A的维度就是dxN了。
2)将所有的N个⼈脸在对应维度上加起来,然后求个平均,就得到了⼀个“平均脸”。你把这个脸显⽰出来的话,还挺帅的哦。
3)将N个图像都减去那个平均脸图像,得到差值图像的数据矩阵Φ。
4)计算协⽅差矩阵C=ΦΦT。再对其进⾏特征值分解。就可以得到想要的特征向量(特征脸)了。
5)将训练集图像和测试集的图像都投影到这些特征向量上了,再对测试集的每个图像到训练集中的最近邻或者k近邻啥的,进⾏分类即可。
算法说明⽩了都是不明⽩的,所以还是得去看具体实现。因此,可以对照下⾯的代码来弄清楚这些步骤。
另外,对于步骤4),涉及到求特征值分解。如果⼈脸的特征维度d很⼤,例如256x256的⼈脸图像,d
就是65536了。那么协⽅差矩阵C的维度就是dxd=65536x65536。对这个⼤矩阵求解特征值分解是很费⼒的。那怎么办呢?如果⼈脸的样本不
多,也就是N不⼤的话,我们可以通过求解C’=ΦTΦ矩阵来获得同样的特征向量。可以看到这个C’=ΦTΦ只有NxN的⼤⼩哦。如果N远远⼩于d的话,那么这个⼒⽓就省得很值了。那为什么求解C’=ΦTΦ矩阵的特征向量可以获得C=ΦΦT的特征向量?万众瞩⽬时刻,数学以完美舞姿登上舞台。证明如下:
其中,e i是C’=ΦTΦ的第i个特征向量,v i是C=ΦΦT的第i个特征向量,由证明可以看到,v i=Φe i。所以通过求解C’=ΦTΦ的特征值分解得到e i,再左乘Φ就得到C=ΦΦT的特征向量v i了。也就是我们想要的特征脸。
有源带通滤波器节能灯生产设备⼆、Matlab实现
下⾯的代码主要是在著名的⼈脸识别数据库YaleB中进⾏实现。⽤的是裁切后的⼈脸数据库,可以点
击下载。共有38个⼈的⼈脸,⼈脸是在不同的光照下采集的,每个⼈脸图像是32x32个像素。实验在每⼀个的⼈脸图像中随机取5个作为训练图像,剩下的作为测试图像。当然了,实际过程中这个过程需要重复多次,然后得到多次准确率的均值和⽅差才有参考意义,但下⾯的demo就不做这个处理了。计算相似性⽤的是欧⽒距离,但编程实现的时候为了加速,⽤的是简化版,⾄于如何简化的,考验你的时候到了。
trainIdx = [];
管道内衬
testIdx = [];
for i=1:num_class
label = find(labels == i);
indice = randperm(numel(label));
trainIdx = [trainIdx label(indice(1:num_trainImg))];
testIdx = [testIdx label(indice(num_trainImg+1:end))];
end
%% get train and test data
train_x = double(data(:, trainIdx));
train_y = labels(trainIdx);
test_x = double(data(:, testIdx));
test_y = labels(testIdx);
%% computing eigenfaces using PCA
disp('');
tic;
[num_dim, num_imgs] = size(train_x); %% A: #dim x #images
avg_face = mean(train_x, 2); %% computing the average face
X = bsxfun(@minus, train_x, avg_face); %% computing the difference images
%% PCA
if num_dim <= num_imgs
C = X * X';
[V, D] = eig(C);
else
C = X' * X;
[U, D] = eig(C);
V = X * U;
end
eigenfaces = V;
eigenfaces = eigenfaces ./ (ones(size(eigenfaces,1),1) * sqrt(sum(eigenfaces.*eigenfaces))); toc;
%% visualize the average face
P = sqrt(numel(avg_face));
Q = numel(avg_face) / P;
2硅酸铝纤维毡imagesc(reshape(avg_face, P, Q)); title('Mean face');
colormap('gray');
%% visualize some eigenfaces
figure;
num_eigenfaces_show = 9;
for i = 1:num_eigenfaces_show
subplot(3, 3, i)
imagesc(reshape(eigenfaces(:, end-i+1), P, Q));联系人管理
title(['Eigenfaces ' num2str(i)]);
end
colormap('gray');
0204电话录音
%% transform all training images to eigen space (each column for each image)
disp('transform data to ');
X = bsxfun(@minus, train_x, avg_face);
T = eigenfaces' * X;
%% transform the test image to eigen space
X_t = bsxfun(@minus, test_x, avg_face);
T_t = eigenfaces' * X_t;
%% find the best match using Euclidean distance
disp('find the ');
AB = -2 * T_t' * T; % N x M
BB = sum(T .* T); % 1 x M
distance = bsxfun(@plus, AB, BB); % N x M
[score, index] = min(distance, [], 2); % N x 1
%% compute accuracy
matchCount = 0;
for i=1:numel(index)
predict = train_y(index(i));
if predict == test_y(i)
matchCount = matchCount + 1;
end
end
fprintf('**************************************\n');
fprintf('accuracy: %0.3f%% \n', 100 * matchCount / numel(index));
fprintf('**************************************\n');
下⾯是将的图像读⼊matlab的代码。
三、实验结果
⾸先来个帅帅的平均脸:
然后来9个帅帅的特征脸:
在本实验中,实验结果是30.126%左右。如果加上了某些预处理,这个结果就可以跑到62%左右。只是这个预处理我有点解析不通,所以就没放在demo上了。
本⽂如果有什么不对的地⽅,还望⼤家指正。