matlab PCA-SVD簡單地實現特徵臉方法(Eigenface)

當我們要用一張圖片跟大量圖片對比尋找最相似(最小歐幾里得距離等)時用本文的方法能減少內存使用量加快運算速度。


優化的歐幾里得距離計算方法:

function d = EuclideanDistance(a,b)
% DISTANCE - computes Euclidean distance matrix
%
% E = EuclideanDistance(A,B)
%
%    A - (MxD) matrix 
%    B - (NxD) matrix
%
% Returns:
%    E - (MxN) Euclidean distances between vectors in A and B
%
%
% Description : 
%    This fully vectorized (VERY FAST!) m-file computes the 
%    Euclidean distance between two vectors by:
%
%                 ||A-B|| = sqrt ( ||A||^2 + ||B||^2 - 2*A.B )
%
% Example : 
%    A = rand(100,400); B = rand(200,400);
%    d = EuclideanDistance(A,B);

% Author   : Roland Bunschoten
%            University of Amsterdam
%            Intelligent Autonomous Systems (IAS) group
%            Kruislaan 403  1098 SJ Amsterdam
%            tel.(+31)20-5257524
%            [email protected]
% Last Rev : Oct 29 16:35:48 MET DST 1999
% Tested   : PC Matlab v5.2 and Solaris Matlab v5.3
% Thanx    : Nikos Vlassis

% Copyright notice: You are free to modify, extend and distribute 
%    this code granted that the author of the original code is 
%    mentioned as the original author of the code.

if (nargin ~= 2)
    b=a;
end

if (size(a,2) ~= size(b,2))
   error('A and B should be of same dimensionality');
end

aa=sum(a.*a,2); bb=sum(b.*b,2); ab=a*b'; 
d = sqrt(abs(repmat(aa,[1 size(bb,1)]) + repmat(bb',[size(aa,1) 1]) - 2*ab));


這兩天在學習PCA在人臉識別裏的作用,並試圖用matlab實現,在做到eigenface的時候由於對線性代數和SVD分解的半知不解,所以一直不能實現圖像的正確匹配。網上的文章沒有直接解決我遇到的困難,所以寫一篇文章,希望給後來者一些提示,以此少走一些彎路。

在其他很多文章裏都詳細介紹了PCA和SVD的原理,我這裏就不在贅述,而是直接解釋怎麼用matlab代碼實現這個步驟。

(1)、假設我們現在有待匹配數據的集合rawFaceMatrix,其中假設size(rawFaceMatrix)= 12000*90,rawFaceMatrix的每一列都是一張unroll的圖片,有90行代表有90張圖片。現在我們要求出這個矩陣的平均值即爲平均臉,並且將數據減去這個平均值(This will prevent PCA from representing patterns that are the same in every image.)


    meanFace = zeros(size(rawFaceMatrix,1),1);
    meanFace = mean(rawFaceMatrix,2);
    A = zeros(size(rawFaceMatrix));
    A = rawFaceMatrix - repmat(meanFace,1,size(rawFaceMatrix,2));

獲得的A矩陣就是減去平均臉後的矩陣,這也是我們後續要比對的數據庫了。

(2)、給定一張相同大小的圖片testImg,size(testImg)= 100*120,我們現在把它由一行變成一列,同樣的減去平均臉。

   

    unroll_img = testImg(:);
    testImg = unroll_img - meanFace;

(3)、對A矩陣即爲數據庫進行SVD分解以壓縮數據,我這裏給出一個matlab的子程序doPCA.m。


function [prinComponents, weightCols] = doPCA(A, numComponentsToKeep)

    weightCols = zeros(numComponentsToKeep, size(A,2));
    prinComponents = zeros(size(A,1), numComponentsToKeep);
    [U,S,V] = svd(A,'econ'); %it is much more efficient when we only need the top few components, as we do here.
    U = U(:,1:numComponentsToKeep);
    S = S(1:numComponentsToKeep,1:numComponentsToKeep);
    V = V(:,1:numComponentsToKeep);
    prinComponents = U;
    weightCols = S*V';

end


輸入A和需要取的特徵值個數numComponentsToKeep,返回prinComponents(基底)和weightCols(在基底上投影的權重矩陣)。相關線性代數內容不再贅述。

(4)、求出我們需要對比的圖片在上述基底上的投影,這樣我們就能將其與數據庫裏的圖像進行對比(也就是跟weightCols進行對比)。


test_weightCols = prinComponents\testImg;

test_weightCols即爲對比圖像在上述基底上的投影,注意我們在這裏用了matlab裏特有的“ \ ”運算。

(5)、現在我們已經將待對比圖片和數據庫都進行了簡化處理,它們之間可以直接求歐氏距離,我們取最小值就能得到最優解。這裏給一個子程序求歐氏距離(可以替換成文章開頭給出的歐幾里得距離優化算法)跟對應的index。


function [minDist, indexOfClosest] = indexOfClosestColumn(A, test_weightCols)

    n = size(A,2);
    minDist = Inf;
    for i = 1 : n
        temp = sum((A(:,i)-test_weightCols).^2);
        dist = sqrt(temp);
        if dist < minDist
            minDist = dist;
            indexOfClosest = i;
        end
    end
    
end

現在我們取得最優解的index。


[minDist, indexOfClosestMatch] = indexOfClosestColumn(weightCols, test_weightCols);

再配合一條顯示語句就能顯示最優解的圖片:

viewFace( rawFaceMatrix(:,indexOfClosestMatch), imgHeight); 


以下爲viewFace.m。


function viewFace( faceColumn, imgHeight)

    imshow(reshape(faceColumn,imgHeight, []),[0 255]);
end


至此簡單的matlab代碼實現主成分分析在人臉識別中的應用就完成了。(將其中doPCA替換成相應的Fisherface代碼就能實現Fisherface的功能)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章