托托又来了——PCA实现人脸识别

托马斯·哈代又来送诗啦,搬好小凳几~~~
Sky seems to end its track;
But no.
The road trails down the hill at the back.
Ever the road!

Purpose

  • Learn how to use PCA
  • Preliminarily understand face recognition features
  • Get more proficient in the use of MATLAB

Principle

  • The introduction of PCA
    PCA 的基本原理是利用离散 K-L 变换提取人脸的主要成分,从而构成特征脸空间,识别时把测试样本投影到该空间,构成一组投影系数,通过与特征脸的距离比较,距离最小的特征脸对应的即是识别结果。

  • Face recognition based on PCA
    基于 PCA 的人脸识别分为三个阶段:第一个阶段利用训练样本集构建特征脸空间;第二个阶段是训练阶段,主要是将训练图像投影到特征脸子空间上;第三个阶段是识别阶段,将测试样本集投影到特征脸子空间,然后与投影后的训练图像相比较,距离最小的为识别结果。
    基于 PCA 的人脸识别其实一种统计性的模板比配方法,原理简单,易于实现,但也有不足,它的识别率会随着关照,人脸角度,训练样本集的数量而变换,但仍不失为一种比较好的方法。

Procedure

  • Basic steps of PCA algorithm
    1:Structure feature space
    在这里插入图片描述
    2:Recognize
    在这里插入图片描述
  • Train
    1:Training methods
    库里一共有400张照片,有40个子文件夹,每个子文件夹对应一个人的10张不同照片,将照片重新划分并按照顺序命名。
    ① 每个人的任意5-8张照片作为训练并作为测试样本库,其余的5-2张图片作为测试的待识别的照片;
    ② 前30-38个人的照片作为训练,后10-2个人作为测试,其中测试中每个人的任意5-8张照片作为测试样本库,即与特征脸矩阵构成特征脸空间,其余的5-2张照片作为待识别的图片。
n = 1;
p = 1;
%分成train和test两个文件夹,且图片重命名
for i = 1:40                                % 一共40个人
     a = 1:10;                               % 每个人都是10张
    Ind = a(:,randperm(size(a,2)));       % size:取矩阵的列数  randperm:打乱顺序
    for h = 1:train_num  
        j= Ind(1,h);
        File = ['C:\Users\Lenovo\Desktop\人脸识别images\s',sprintf('%d',i),'\',sprintf('%d',j),'.pgm'];
        Filesave = ['C:\Users\Lenovo\Desktop\train','\',sprintf('%03d',p),'.pgm'];
        copyfile(File,Filesave)
        p = p + 1;                        % 训练样本总数
    end
    for h = train_num+1:10  
        j= Ind(1,h);
        File = ['C:\Users\Lenovo\Desktop\人脸识别images\s',sprintf('%d',i),'\',sprintf('%d',j),'.pgm'];
        Filesave = ['C:\Users\Lenovo\Desktop\test','\',sprintf('%03d',n),'.pgm'];
        copyfile(File,Filesave)
        n = n + 1;                         % 测试样本总数
    end
end

对于训练2的方式,这里需要分成三个部分:训练集、验证集和测试集。

n = 1;
m = 1;
p = 1;
% 分成train、test、validate三个文件夹,且图片重命名
for i = 1:train_num                         % 前n个人
    a = 1:10;                               % 每个人10张
    Ind = a(:,randperm(size(a,2)));       % size:取矩阵的列数  randperm:打乱顺序
    for h = 1:10 
        j = Ind(1,h);
        File = ['C:\Users\Lenovo\Desktop\人脸识别images\s',sprintf('%d',i),'\',sprintf('%d',j),'.pgm'];
        Filesave = ['C:\Users\Lenovo\Desktop\train','\',sprintf('%03d',p),'.pgm'];
        copyfile(File,Filesave)
        p = p + 1;                        % 训练样本总数
    end
end
for i = train_num + 1 :40                     %40-n个人的前5张
    a = 1:10;                                
    Ind = a(:,randperm(size(a,2)));  
    for h = 1:5  
        j= Ind(1,h);
        File = ['C:\Users\Lenovo\Desktop\人脸识别images\s',sprintf('%d',i),'\',sprintf('%d',j),'.pgm'];
        Filesave = ['C:\Users\Lenovo\Desktop\validate','\',sprintf('%03d',m),'.pgm'];
        copyfile(File,Filesave)
        m = m + 1;                         % 测试样本总数
    end
    for h = 6:10  
        j= Ind(1,h);
        File = ['C:\Users\Lenovo\Desktop\人脸识别images\s',sprintf('%d',i),'\',sprintf('%d',j),'.pgm'];
        Filesave = ['C:\Users\Lenovo\Desktop\test','\',sprintf('%03d',n),'.pgm'];
        copyfile(File,Filesave)
        n = n + 1;                         % 待识别总数
    end
end

2:The process of the photos in library
① 将每一张库的照片转化成N维的向量,然后把这些向量存入一个矩阵里,可将这些向量以列的形式存在矩阵X里;
② 将矩阵里向量的每个元素加起来求出平均值,再用矩阵X中的每个向量减去这个平均值从而得到每个向量的偏差,最后得到了偏差矩阵X’;
③ 中心化并计算协方差矩阵;
④ 利用协方差矩阵进行主成分分析,提取特征脸,获取训练样本特征数据。
以下实验源代码是针对的训练方式1,训练方式2在形成特征脸与特征空间时稍有所不同。

%批量读取指定文件夹下的图片
path = uigetdir;                         % 打开一个模态对话框
img_path = dir(strcat(path,'\*.pgm'));   % 列出.pgm文件
img_num = length(img_path);              % img_num:文件夹中的总数
imagedata = [];                          % 一张图片为一列
if img_num >0
    for j = 1:img_num
        img_name = img_path(j).name;
        temp = imread(strcat(path, '/', img_name));
        temp = double(temp(:));
        imagedata = [imagedata, temp];
    end
end
 
%中心化并计算协方差矩阵
wts = size(imagedata,2);                        % imagedata的列数(等于训练的样本数)
img_pj= mean(imagedata,2);                      % 均值
for i = 1:wts
    imagedata(:,i) = imagedata(:,i) - img_pj;   % 中心化
end
covMat = imagedata'*imagedata;                  % 转置
 
% 利用协方差矩阵进行主成分分析,提取特征脸,获取训练样本特征数据
% COEFF:系数矩阵
% latent:特征值的大小(从大到小)构成的向量
% explained:贡献率
[COEFF, latent, explained] = pcacov(covMat);
% 留下使累计贡献量达95%的k个特征向量
i = 1;
proportion = 0;
while(proportion < 95)
    proportion = proportion + explained(i);
    i = i+1;
end
k = i - 1;
% 求出原协方差矩阵的特征向量,即特征脸
V = imagedata*COEFF;          % N*M阶
V = V(:,1:k);    
%显示前3的特征脸
A = reshape(V(:,1),[112,92]);  
A = mat2gray(A);
axes( handles.axes1 )
imshow([A]);
B = reshape(V(:,2),[112,92]);
B = mat2gray(B);
axes( handles.axes2 )
imshow([B]);
C = reshape(V(:,3),[112,92]);
C = mat2gray(C);
axes( handles.axes3 )
imshow([C]);
% 训练样本在 PCA 特征空间下的表达矩阵 k*M
W = V'*imagedata;
  • Test
    对于测试样本,先读取图像,将其变换到PCA空间再利用近邻法识别。
    1:Read a photo
[filename, path] = uigetfile({'*.pgm'},'choose photo');
str = [path, filename];
im = imread(str);
axes( handles.axes4);
imshow(im);

2:Nearest neighbor recognition

im1 = double(im(:));
objectone = V'*(im1 - img_pj);              %计算待识别图片的投影
size(objectone)
%最小距离法,寻找和待识别图片最为接近的训练图片
for k = 1:wts
    temp1(k) = norm(objectone - W(:,k));    %欧氏距离
end
  • Calculate the accuracy and display the recognition results
    对需要识别的每一张照片进行测试,与训练样本库中的每一张照片进行对比,若得到的最小距离的照片与待识别的照片为同一组(根据输入的每组照片训练数目判断),就标记正确,准确率=正确数/总待识别数。
    为了便于分析,对每一幅测试图像显示距离排名在前三位的识别结果。

1:Display the top 3 recognition results

[s_temp,id]=sort(temp1,'ascend');           %筛选出距离前三小的
axes( handles.axes5 )
imshow(['C:\Users\Lenovo\Desktop\train','\',sprintf('%03d',id(1)),'.pgm'])
axes( handles.axes6 );
imshow(['C:\Users\Lenovo\Desktop\train','\',sprintf('%03d',id(2)),'.pgm'])
axes( handles.axes7 )
imshow(['C:\Users\Lenovo\Desktop\train','\',sprintf('%03d',id(3)),'.pgm'])

2:Acquire accuracy

col_of_data = 40*train_num;              
pathname = uigetdir;
img_path_list = dir(strcat(pathname,'\*.pgm'));
img_num = length(img_path_list);
testdata = [];
if img_num >0
    for j = 1:img_num
        img_name = img_path_list(j).name;
        temp = imread(strcat(pathname, '/', img_name));
        temp = double(temp(:));
        testdata = [testdata, temp];
    end
end
col_of_test = size(testdata,2);            %col_of_test:待测样本数
img_pj= mean(testdata,2);   
 
for i = 1:size(testdata,2)
    testdata(:,i) = testdata(:,i) - img_pj;  
end
object =V'* testdata;
 
num = 0;
for j = 1:col_of_test
    distance = 1e8;
    for k = 1:col_of_data                 %col_of_data:训练样本总数
        temp = norm(object(:,j) - W(:,k));
        if(distance>temp)
            aimone = k;                   %取出距离最小的
            distance = temp;
        end
    end
        if ceil(j/(10-train_num))==ceil(aimone/(train_num))
            num = num + 1;                %TP:预测和真实值都为1
        end
end
accuracy = num/col_of_test;

3:Line chart to show accuracy
ps:图中显示数据是自己多次人工验证的数据。

axes( handles.axes8 )
x=5:1:8;
a=[0.920,0.956,0.967,0.975]; %数据
plot(x,a,'-*b');     %线性,颜色,标记
set(gca,'XTick',[5:1:8]) 
set(gca,'YTick',[0.75:0.05:1]) 
xlabel('训练数目(n/10)')  
ylabel('准确率') 

Result and Analysis

  • Result
    1:The first method
    在这里插入图片描述
    2:The second method
    在这里插入图片描述
  • Analysis
    从上面的运行结果可以看出,PCA方法通过特征脸空间根据近邻法可以找到所属的人,并且从最后的准确率折线图可以看出:随着训练样本库照片的增加,所对应的准确率逐步增大,对于训练1方式,当选取的每个人的8张照片作为训练样本库时,识别的准确率已经达到了97.5%;对于训练2方式,当选取40个人的前35个人作为训练样本库,剩余5个人中的5张照片作为验证集,5张照片作为测试集时,识别的准确率已经达到了1,总体来说PCA的方法可以较好地识别人脸。
    PCA主成分分析法通过将人脸作为一个整体来编码,不关心人脸五官的局部特征,从而可以大大地降低识别的复杂度,且图像的原始灰度数据可以直接学习识别,不需进一步的处理,同时可消除评价指标之间的相关影响;但可以看出实现过程中需要计算协方差矩阵,计算量很大,速度较慢,由于忽略了贡献率小的成分,其可能包含特征的重要信息,从而会引起较大的误差。

Conclusion

emm这是一个图像处理方面的简单实验,其中部分代码是老师提供的,就是用PCA去实现一个简单的人脸识别,并用MATLAB做出一个GUI界面来显示结果。This is just a 实验总结,所以没有写出详细原理,想要详细了解的话可以找阿度~~~
其实我本来立下了“宏伟”big志,写一篇全英文Blog,但是嘎嘎嘎flag倒了,想要将所有内容用英语准确连贯,实在是太费brain了,哭唧唧-- --,莫名瞧不起自己哈哈。
当然,对于其中处理不当的地方,还请大家指出,希望可以一起学交流习,想要代码的可以在下面留言,要是我不busy的话可以邮箱给你嘻嘻嘻!

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: 哦,抱歉,我误解了。addEventListener是JavaScript中常用的一个方法,用于给DOM元素添加事件处理程序。它可以接收三个参数:事件类型、事件处理函数和一个布尔值(可选),表示事件是否在捕获阶段执行。例如: ```javascript document.getElementById("myButton").addEventListener("click", function() { alert("Button clicked!"); }); ``` 这段代码给id为myButton的按钮添加了一个click事件处理程序,当用户点击按钮时,就会弹出一个提示框显示"Button clicked!"。 ### 回答2: addEventListener 是JavaScript中的一个方法,它用于向特定的HTML元素添加事件监听器。 通过使用addEventListener,我们可以为指定的HTML元素绑定不同类型的事件,例如点击、鼠标移动、按键操作等等。 该方法有三个参数: 1. 事件类型:需要绑定的事件类型,例如:click、mousemove、keydown等。 2. 事件处理函数:当事件被触发时执行的函数。 3. 是否在事件捕获阶段触发执行:默认情况下是在事件冒泡阶段执行。 使用addEventListener的好处是,可以为同一个元素添加多个事件监听器,而不会覆盖原有的监听器。这样能够更灵活地处理事件,并且不会与其他代码产生冲突。 下面是一个例子: ```html <button id="myButton">点击我</button> <script> // 获取按钮元素 var button = document.getElementById("myButton"); // 添加click事件监听器 button.addEventListener("click", function(){ console.log("按钮被点击了"); }); // 添加mouseover事件监听器 button.addEventListener("mouseover", function(){ console.log("鼠标移到按钮上"); }); </script> ``` 在上面的例子中,我们给按钮元素绑定了两个事件监听器:点击事件和鼠标移动事件。当按钮被点击或鼠标移到按钮上时,相应的事件处理函数就会执行。 总结来说,addEventListener是一个很有用的方法,它可以让我们更方便地处理和管理事件,从而增强网页的交互性和功能性。 ### 回答3: addEventListener 是JavaScript中的一个方法,用来为指定的元素添加事件监听器。通过使用它,可以实现将特定的功能与特定的事件进行绑定。 在使用 addEventListener 时,需要指定三个参数:事件类型、事件处理函数和一个可选的布尔值,用来控制事件的捕获或冒泡阶段。事件类型可以是HTML DOM中定义的标准事件,也可以是自定义事件。 例如,如果我们要为一个按钮添加点击事件监听器,我们可以使用以下代码: ``` const button = document.querySelector('button'); function handleClick() { console.log('按钮被点击了'); } button.addEventListener('click', handleClick); ``` 在这个例子中,我们首先使用 `document.querySelector` 方法选择了文档中的一个按钮元素,并将其存储在一个变量 `button` 中。然后,我们定义了一个名为 `handleClick` 的函数,该函数在按钮被点击时会被调用,并将一条信息打印到控制台中。最后,我们使用 `addEventListener` 将 `handleClick` 函数添加为按钮的点击事件监听器。 这是一个简单的例子,实际上我们可以使用 `addEventListener` 来监听多种类型的事件,如鼠标移动、键盘按下等等。使用 addEventListener 可以使我们的代码更具可读性和可维护性,同时也能够将逻辑与HTML代码分离开来,使得我们的代码更加模块化和易于管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值