人脸识别之特征脸方法(Eigenface)算法
http://blog.csdn.net/zouxy09编程
由于须要,花了一点时间写了下经典的基于特征脸(EigenFace)的人脸识别方法的Matlab代码。这里仅把该代码分享出来。其实,在较新版本的OpenCV中已经提供了FaceRecognizer这一个类,里面不只包含了特征脸EigenFace,还有FisherFace和LBPHFace这三种人脸识别方法,有兴趣的能够参考OpenCV的API手册,里面都有很详细的使用例程了。dom
1、特征脸ide
特征脸EigenFace从思想上其实挺简单。就至关于把人脸从像素空间变换到另外一个空间,在另外一个空间中作类似性的计算。这么说,其实图像识别的基本思想都是同样的,首先选择一个合适的子空间,将全部的图像变换到这个子空间上,而后再在这个子空间上衡量类似性或者进行分类学习。那为何要变换到另外一个空间呢?固然是为了更好的作识别或者分类了。那为何变换到一个空间就好识别或者分类了呢?由于变换到另外一个空间,同一个类别的图像会聚到一块儿,不一样类别的图像会距离比较远,或者在原像素空间中不一样类别的图像在分布上很难用个简单的线或者面把他们切分开,而后若是变换到另外一个空间,就能够很好的把他们分开了。有时候,线性(分类器)就能够很容易的把他们分开了。那既然人类看起来同类的图像原本就是类似的,不一样类的图像就不太类似,那为何在原始的像素空间他们同类不会很近,不一样类不会很远,或者他们为何很差分开呢?由于图像各类因素的影响,包括光照、视角、背景和形状等等不一样,会形成同一个目标的图像都存在很大的视觉信息上的不一样。以下图所示。学习
世界上没有存在任何两片彻底相同的叶子,虽然他们都是叶子。万千世界,同一类事物都存在共性,也存在个性,这就是这个世界多彩的缘由。那怎么办呢?很天然,只要在咱们想要的粒度上把同一类目标的共性找出来就行了,并且这个共性最好和咱们要区分的类是不同的。什么叫咱们想要的粒度?我理解和咱们的任务相关的。例如咱们要区分人和车,那人的共性就是有脸、有手、有脚等等。但若是咱们要区分亚洲人和非洲人,那么亚洲人的共性就是黄色皮肤等等。能够试着想象,上帝把世界万物组织成一个树状结构,树的根就是万物之源,下一层能够分红生物和非生物,再下一层将生物分为……(囧,想象不到),直到最底层,万物,你我,为树的一片普通得再普通的叶子。树越往下,粒度越小,分类越细(哈哈,本身乱扯的)。停!废话多了点,跑题了,回到刚才的问题,重头戏来了,要变换到什么空间,才具有上述这种良好类内类似、类间区分的效果?到这,我就只能say sorry了。计算机视觉领域发展了几十年,就为了这一个问题倾注了无数研究者的智慧与心血。固然了,也诞生和孕育了不少经典和有效的解答。(我的理解,上述说的实际上就是特征提取)。从一开始的颜色特征(颜色直方图)、纹理特征(Harr、LBP、HOG、SIFT等)、形状特征等到视觉表达Bag of Words,再到特征学习Deep Learning,技术的发展总能带给人但愿,曙光也愈来愈清晰,但路还很远,是不? 测试
扯太多了,严重离题了。上面说到,特征脸EigenFace的思想是把人脸从像素空间变换到另外一个空间,在另外一个空间中作类似性的计算。EigenFace选择的空间变换方法是PCA,也就是大名鼎鼎的主成分分析。它普遍的被用于预处理中以消去样本特征维度之间的相关性。固然了,这里不是说这个。EigenFace方法利用PCA获得人脸分布的主要成分,具体实现是对训练集中全部人脸图像的协方差矩阵进行本征值分解,得对对应的本征向量,这些本征向量(特征向量)就是“特征脸”。每一个特征向量或者特征脸至关于捕捉或者描述人脸之间的一种变化或者特性。这就意味着每一个人脸均可以表示为这些特征脸的线性组合。实际上,空间变换就等同于“搞基”,原始像素空间的基就是单位“基”,通过PCA后空间就是以每个特征脸或者特征向量为基,在这个空间(或者坐标轴)下,每一个人脸就是一个点,这个点的坐标就是这我的脸在每一个特征基下的投影坐标。哦噢,说得有点绕。idea
下面就直接给出基于特征脸的人脸识别实现过程:spa
1)将训练集的每个人脸图像都拉长一列,将他们组合在一块儿造成一个大矩阵A。假设每一个人脸图像是MxM大小,那么拉成一列后每一个人脸样本的维度就是d=MxM大小了。假设有N我的脸图像,那么样本矩阵A的维度就是dxN了。.net
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的特征向量?万众瞩目时刻,数学以完美舞姿登上舞台。证实以下:
其中,ei是C’=ΦTΦ的第i个特征向量,vi是C=ΦΦT的第i个特征向量,由证实能够看到,vi=Φei。因此经过求解C’=ΦTΦ的特征值分解获得ei,再左乘Φ就获得C=ΦΦT的特征向量vi了。也就是咱们想要的特征脸。
2、Matlab实现
下面的代码主要是在著名的人脸识别数据库YaleB中进行实现。用的是裁切后的人脸数据库,能够点击CroppedYale下载。共有38我的的人脸,人脸是在不一样的光照下采集的,每一个人脸图像是32x32个像素。实验在每个的人脸图像中随机取5个做为训练图像,剩下的做为测试图像。固然了,实际过程当中这个过程须要重复屡次,而后获得屡次准确率的均值和方差才有参考意义,但下面的demo就不作这个处理了。计算类似性用的是欧氏距离,但编程实现的时候为了加速,用的是简化版,至于如何简化的,考验你的时候到了。
% Face recognition using eigenfaces close all, clear, clc; %% 20 random splits num_trainImg = 5; showEigenfaces = true; %% load data disp('loading data...'); dataDir = './CroppedYale'; datafile = 'Yale.mat'; if ~exist(datafile, 'file') readYaleDataset(dataDir, datafile); end load(datafile); %% Five images per class are randomly chosen as the training %% dataset and remaining images are used as the test dataset disp('get training and testing data...'); num_class = size(unique(labels), 2); 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('computing eigenfaces...'); 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; 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'); %% transform all training images to eigen space (each column for each image) disp('transform data to eigen space...'); 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 best match...'); 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');
下面是将CroppedYale的图像读入matlab的代码。
function readYaleDataset(dataDir, saveName) dirs = dir(dataDir); data = []; labels = []; for i = 3:numel(dirs) imgDir = dirs(i).name; imgDir = fullfile(dataDir, imgDir); imgList = dir(fullfile(imgDir, '*.pgm')); for j = 1:numel(imgList) imgName = imgList(j).name; if strcmp('Ambient.pgm', imgName(end-10:end)) continue; end im = imread(fullfile(imgDir, imgName)); if size(im, 3) ==3 im = rgb2gray(im); end im = imresize(im, [32 32]); im = reshape(im, 32*32, 1); data = [data im]; end labels = [labels ones(1, numel(imgList)-1) * (i-2)]; end save(saveName, 'data', 'labels'); end
3、实验结果
首先来个帅帅的平均脸:
而后来9个帅帅的特征脸:
在本实验中,实验结果是30.126%左右。若是加上了某些预处理,这个结果就能够跑到62%左右。只是这个预处理我有点解析不通,因此就没放在demo上了。
本文若是有什么不对的地方,还望你们指正。