限時乾貨下載:關注我們「機器視覺」,回復「資料」獲取獲取機器視覺教程,行業報告等資源,百度盤群組分享連結更新時間:2016-10-12,失效請在文末留言,不要在後臺留言
轉自:http://blog.csdn.net/zouxy09,zouxy09@qq.com
因為需要,花了一點時間寫了下經典的基於特徵臉(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的特徵向量?萬眾矚目時刻,數學以完美舞姿登上舞臺。證明如下:
其中,ei是C』=ΦTΦ的第i個特徵向量,vi是C=ΦΦT的第i個特徵向量,由證明可以看到,vi=Φei。所以通過求解C』=ΦTΦ的特徵值分解得到ei,再左乘Φ就得到C=ΦΦT的特徵向量vi了。也就是我們想要的特徵臉。
二、Matlab實現
下面的代碼主要是在著名的人臉識別資料庫YaleB(http://lib.csdn.net/base/mysql)中進行實現。用的是裁切後的人臉資料庫,可以點擊CroppedYale(http://vision.ucsd.edu/extyaleb/CroppedYaleBZip/CroppedYale.zip)下載。共有38個人的人臉,人臉是在不同的光照下採集的,每個人臉圖像是32x32個像素。實驗在每一個的人臉圖像中隨機取5個作為訓練圖像,剩下的作為測試圖像。當然了,實際過程中這個過程需要重複多次,然後得到多次準確率的均值和方差才有參考意義,但下面的demo就不做這個處理了。計算相似性用的是歐氏距離,但編程實現的時候為了加速,用的是簡化版,至於如何簡化的,考驗你的時候到了。
[cpp] view plain
% 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的代碼。
[cpp] view plain
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
三、實驗結果
首先來個帥帥的平均臉:
然後來9個帥帥的特徵臉:
在本實驗中,實驗結果是30.126%左右。如果加上了某些預處理,這個結果就可以跑到62%左右。只是這個預處理我有點解析不通,所以就沒放在demo上了。
本文如果有什麼不對的地方,還望大家指正。
限時乾貨下載:關注我們「機器視覺」,回復「資料」獲取機器視覺教程,行業報告等資源。持續更新中。。。
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >
如何分享到朋友圈點擊右上角「...」 三個點,在彈出菜單選擇「分享到朋友圈」機器視覺--訂閱號』機器視覺『公眾號:機器視覺,工業自動化,機器人,圖像處理技術等領域新媒體信息平臺。回復下面數字,獲取相關文章:
001:計算機視覺領域研究資源及期刊、會議介紹
002:德國kuka機器人與世界冠軍桌球對決
003:人臉檢測的C/C++原始碼
004:人工智慧眼中的世界,竟然如此詭(魔)異(性)?!
005:機器人視覺中的物體表達問題-攝像機標定-視覺計算理論
006:三維計算機視覺技術
007:無奈與迷茫-30歲以上電子工程師請進
008:逆天科技小製作 自製迷你「磁懸浮列車」玩具方法圖解,快動手給孩子做一個
009:Google 以圖搜圖 - 相似圖片搜索原理 - Java實現
010:關於機器學習中的一些數學方法
011:雙遠心工業鏡頭的原理簡述
012:哥們花20萬從德國帶回來一工具箱,亮瞎了!!全鋁頭盔一次成型!!
013:工業相機在汽車零部件檢測中的應用