NA_08: Face Recognition by Egenfaces

Numerical algorithms, University of Konstanz, ST 2008

by Vladimir Bondarenko, 10-June.

In todays Lab, we learn how to apply the principle component analysis for classification purpose. The objects we are going to classify are our own faces.

Contents

0. Face data

Images are 2D arrays. Classical PCA, however, works on vector data, as we have seen in the Height-Weight example in the PCA and SVD Labs. Therefore before the analysis, the 2D images must be reshaped into looong 1D vectors. This can be done simply concatenating the n columns of the m-by-n image matrix. In MATLAB, this vectorization operation for a matrix A is just a = A(:).

There are three face databases available: att, yale and konstanz. The first two are freely available in the Internet, the last one was created from the images of the participants of this course. You can specify the database, with which you would like to work, in the variable 'db':

db = 'att.mat';
db = 'yale.mat';
db = 'konstanz.mat';

To load the face data into MATLAB workspace just run: load(db). In the workspace, three variables will appear:

db = 'konstanz';
load(db);

Explore the faces:

For each subject (person), several face images corresponding to different face expressions were collected. You can explore those by specifying the exprNum variable in this cell:

NN  = size(F,1);        % Overal number of face images.
fps = NN/N;             % Number of faces per subject.

% Display faces
exprNum = 2;            % The number of the particular face expression.

nrows = round(sqrt(N));
ncols = ceil(N/nrows);
figure(1); clf; set(gcf, 'Name', 'Faces');
for ii=1:N
    subplot(nrows,ncols,ii);
    tmp = (ii-1)*fps + exprNum;
    imagesc( reshape(F(tmp,:),m,n) );
    colormap gray; axis equal tight off;
    title(num2str(ii));
end

1. PCA

As it is often the case in applied math, we are interested in a better representation of our data, i.e., in a better basis (than the standard one). We assume that there is some kind of linear dependency among the face vectors and thus the principle axes obtained by PCA should provide a fairly good basis. In the following, we will check this hypothesis.

Task 1: SVD

Caution: the MATLAB svd() and svd( ,0) commands will compute square matrices V of dimension of several thousands. It is unnecessary since we are going to use only several first columns of V. Use the command svd(,'econ') instead to compute the first N columns of V only.

YOUR CODE HERE:

[U S V] = svd(F, 'econ');
PC = U*S;                                % the principle components
figure(2); clf; semilogy(diag(S), '.');

Task 2: Explore the "eigenfaces"

The new basis, the principle axes, have the same dimension that the original face images and thus can be reshaped and visualized as a matrix. The first 6 eigenface are displayed below. Can you recognise yourself in them?

YOUR CODE HERE:

nrows = 2;
ncols = 3;
figure(3); clf; set(gcf, 'Name', 'EigenFaces');
for ii=1:nrows*ncols
    subplot(nrows,ncols,ii);
    imagesc( reshape(V(:,ii),m,n) );
    colormap gray; axis equal tight off
    title(['\sigma_' num2str(ii) ' = ' num2str(S(ii,ii), '%1.2e') ]);
end

2. Construct Eigenfaces subspace

The eigenfaces span the N-dimensional subspace where our Face data are. Howevere, according to our hypothesis of linear dependency, much less number of basis vectors must provide a reasonable representation of the Face data. Let us try to reduce the number of eigenfaces from N to nDim and see whether the nDim basis vectors indeed yield a satisfactory reconstrustruction.

nDim = 20;                   % dimensionality of the eigenfaces subspace
V1 = V(:,1:nDim);            % reduced basis of eigenfaces

Task 3: Explore reconstructions

A random face f is selected from the Face matrix F.

fn = randint(1,1,[1, NN]);            % Select face at random
f   = F(fn,:)';
% YOUR CODE HERE:
pc1 = PC(fn,1:nDim);                 % project it on the first nDim principle axes
f1 = V1*pc1';                        % reconstructed face
rmse = norm(f-f1)/norm(f);           % rms reprojection error

% Plot the original and reconstructed images
figure(4); clf;
subplot(1,2,1); imagesc(reshape(f,m,n)); title('Original');
axis equal tight off
subplot(1,2,2); imagesc(reshape(f1, m,n));
title(['||\epsilon_{projection}|| = ' num2str(rmse*100,'%2.2f') ' %']);
colormap gray; axis equal tight off

3. Construct the feature space

So far, each subject is associated with several nDim-vectors of principle components. Our goal is to define a single characteristic "feature vector" for each person. In the most straightforward way, such a vector can be constructed simply by averaging the corresponding principle component vectors. Those N nDim-feature vectors define the feature space that will be searched by the face recognition algorithms. Notice the enormous dimensionality reduction that occured when moving from the m-times-n-dimensional space of all images to the nDim-dimensional feature space.

You do not have to compute the feature vectors yourself. The code below does it for you. The resulting feature vectors are stored in the rows of the N-by-nDim dimensional matrix PC_mean.

The next plot shows the face images reconstructed from the feature vectors.

PC_mean = zeros(N, nDim);       % average feature vectors for each subject
for ii=1:N
    ix1 = fps*(ii-1)+1; ix2 = ix1+fps-1;
    PC_mean(ii,:) = mean(PC(ix1:ix2,1:nDim));
end
% Plot faces reconstructed from the feature vectors
nrows = round(sqrt(N));
ncols = ceil(N/nrows);
figure(1); set(gcf, 'Name', 'Face averages');
for ii=1:N
    subplot(nrows,ncols,ii);
    imagesc(reshape(V1*PC_mean(ii,:)',m,n));
    colormap gray; axis equal tight off
    title(num2str(ii));
end

4. Face recognition

After we have constructed the feature space, the algorithm for face recognition is straightforward.

For a (vectorized) face image f:

Task 4: Face recognition

Your plot should look like the one below.

YOUR CODE HERE:

% Select a face at random and compute its feature vector:
fn = randint(1,1,[1, NN]);
f = F(fn,:);                        % test face
pc1 = V1'*f';                       % its principle component

% Compute distances in the feature space
Dist = PC_mean - ones(N,1)*pc1';
dist = sqrt(mean(Dist.^2, 2));
[min_dist, min_ix] = min(dist);

% Plot
figure(5); clf; set(gcf, 'Name', 'Face recognition')
subplot(2,2,1); imagesc( reshape(f,m,n) );
axis equal tight off; colormap gray;
title('Test face');
subplot(2,2,2); imagesc( reshape( F((min_ix-1)*fps+7,:), m,n) );
axis equal tight off; colormap gray;
title('Recognized as');
subplot(2,2,[3 4]); bar(dist, 'FaceColor', 'b');
xlabel('Subject'); ylabel('Distance [a.u.]');

5. Real-time face recognition

In reality, the face recognition with this simple approach is not very reliable. Notice the distances in the plot below.

camF = double(t);
camf = camF(:);

pc1 = V1'*camf;                       % its principle component
% Compute distances in the feature space
Dist = PC_mean - ones(N,1)*pc1';
dist = sqrt(mean(Dist.^2, 2));
[min_dist, min_ix] = min(dist);

% Plot
figure(5); clf; set(gcf, 'Name', 'Face recognition')
subplot(2,2,1); imagesc( camF );
axis equal tight off; colormap gray;
title('Camera image');
subplot(2,2,2); imagesc( reshape( F((min_ix-1)*fps+5,:), m,n) );
axis equal tight off; colormap gray;
title('Recognized as');
subplot(2,2,[3 4]); bar(dist, 'facecolor', 'b');
xlabel('Subject'); ylabel('Distance [a.u.]');