1. 一个非常好的人脸识别代码包:
https://www.github.com/bytefish/facerec
https://github.com/bytefish/facerecognition_guide
http://bytefish.de/blog/eigenfaces/
http://www.bytefish.de/blog/fisherfaces/
第一个网址是一个非常好的人脸识别代码包地址,里面实现了eigenface和fisherface两种人脸识别方法,分别用matlab/octave和python实现;
第二个网址好像是较早的一个实现版本,但是里面包括了详细的说明文档;
第三个和第四个网址是作者的写的关于eigenface和fisherface的博客。
值得一提的是,opencvfaceRecognizer的说明文档貌似也是这位作者所写的(http://docs.opencv.org/trunk/modules/contrib/doc/facerec/index.html)。
2. 利用facerec代码包中的eigenface进行人脸识别:
2.1 训练eigenface model:
close all; clear; clc; % read images image_path = 'att_faces_select'; [X y width height names] = read_images(image_path); % compute a model [d,n] = size(X); num_components = 30; eigenface = eigenfaces(X,y,num_components); %% plot the first (atmost) 16 eigenfaces figure; title('Eigenfaces'); hold on; for i=1:min(16, size(eigenface.W,2)) subplot(4,4,i); comp = cvtGray(eigenface.W(:,i), width, height); imshow(comp); %colormap(jet(256)); title(sprintf('Eigenface %i', i)); end %% 3D plot of projection (first three classes, add those you want) if(d >= 3) figure; hold on; for i = findclasses(eigenface.y, [1,2,3]) % LineSpec: red dots 'r.' plot3(eigenface.P(1,i), eigenface.P(2,i), eigenface.P(3,i), 'r.'), view(45,-45); text(eigenface.P(1,i), eigenface.P(2,i), eigenface.P(3,i), num2str(eigenface.y(i))); end end %% save eigenfaces and names save eigenface.mat eigenface names
首先利用read_images函数读入训练图像,read_images函数的参数是训练图像所在路径,注意,不同subject的图像应放在独自的文件夹中,read_images函数会给每个文件夹一个类别值(从1递增),并会记录每个文件夹的名字作为subject的名字。函数的输出参数X是把所有训练图像连接到一起组成的数据,其中每个二维图像都被转换为一维数据,存储成一列;输出参数y是各个图像所属类别值的集合;names是各个图像文件夹名字,即作为subject的名字。
之后调用eigenfaces函数训练eigenface,其中第三个输入参数为component个数,该参数可以省略,省略时eigenfaces函数内部取图像数目减1为component个数(是否应取类别个数减1为component个数呢?待考),输出参数为训练出的eigenface model。
之后会显示前16个eigenfaces.
之后会3d显示所有eigenface的位置,显示的是每个eigenface投影空间中三个数据组成的坐标值。Findclasses作用(???)
最后将eigenface和names保存。
2.2 预测图像
close all;
clear;
clc;
% user defined variable
threshold = 0.65;
%% load eigenface model
load eigenface.mat
%% load image
%filename = 'train_set/lcy_train/frame4_face0.jpg';
%filename = 'train_set/meilin_train/frame4_face0.jpg';
%filename = 'test_set/lcy_test/frame24_face0.jpg';
%filename = 'test_set/meilin_test/frame4_face0.jpg';
filename = 'att_faces_select/s3/2.pgm';
try
T = double(imread(filename));
catch
lerr = lasterror;
fprintf(1,'Cannot read image %s', filename)
end
[height width channels] = size(T);
% greyscale the image if we have 3 channels
if(channels == 3)
T = (T(:,:,1) + T(:,:,2) + T(:,:,3)) / 3;
end
test_image = [];
try
test_image = [test_image, reshape(T,width*height,1)];
catch
lerr = lasterror;
fprintf(1,'Image cannot be added to the Array. Wrong image size?\n')
end
%% predict
k = 9;
% Predicts nearest neighbor for given Eigenfaces model.
model = eigenface;
Q = model.W' * (test_image - model.mu);
[c, v, mean_k_dis, mean_all_dis] = my_knn(model.P, model.y, Q, k);
%% show result
figure;
imshow(T,[]);
display(sprintf('subject: %s, mean_k_dis=%.2f, mean_all_dis=%.2f, dis_ratio=%.2f', names{c}, mean_k_dis, mean_all_dis, mean_k_dis/mean_all_dis));
display(sprintf('\tv=%i, k=%i', v, k));
if mean_k_dis/mean_all_dis < threshold
title(names{c});
else
title('unknown');
end
首先载入eigenface model数据;
其次载入待预测的图像数据,如果是彩色图,将其转换成灰度图;并将二维数据重新排列成一维向量数据;
之后利用训练好的eigenface model对test_image进行投影得到feature数据Q, 再对Q做knn分类,knn分类用my_knn函数实现,该函数改写自代码包中的knn函数。my_knn函数返回值包括:
c: test_image所属类别;
v:与test_image所属同一类别的neighbor个数;
mean_k_dis: Q和k个neighbor的平均距离,这k个neighbors是与Q距离最近的K个;
mean_all_dis: Q和所有vector的平均距离。
最后显示结果,从控制台显示subject名字, mean_k_dis, mean_all_dis和二者的比值,并显示v和k的值;
当mean_k_dis/mean_all_dis小于一定阈值时,认为正确识别,图像的title显示subject名字,否则认为未正确识别,图像的title显示unknown。
图像结果:
控制台输出结果:
subject: s3, mean_k_dis=3059.42, mean_all_dis=4770.58, dis_ratio=0.64
v=9, k=9
my_knn实现:
function [c, v, mean_k_dis, mean_all_dis] = my_knn(P, y, Q, k) %% My k-nearest neighbor classification. %% %% Args: %% P [dim x num_data] reference vectors %% Q [dim x 1] query vector %% y [1 x num_data] classes corresponding to P. (y = {1,2,...,n}) %% k [int] nearest neighbors used in this prediction %% %% Returns: %% c [int] Class identified by the majority of k neighbors. %% v [int] Number of majority of k neighbors. %% mean_k_dis [double] Mean distance of Q to the k neighbors. %% mean_all_dis [double] Mean distance of Q to all vectors of P. n = size(P,2); % clip k if (nargin == 3) k=1; elseif (k>n) k=n; end Q = repmat(Q, 1, n); distances = sqrt(sum(power((P-Q),2),1)); [distances, idx] = sort(distances); y = y(idx); y = y(1:k); h = histc(y,(1:max(y))); [v,c] = max(h); min_k_dist = distances(1:k); mean_all_dis = mean(distances); mean_k_dis = mean(min_k_dist); end