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的功能)


你可能感兴趣的:(图像处理,MATLAB,Eigenface)