模式识别PCA方法实现人脸识别-Python


一、学习目标

理解PCA的基本思路,掌握基本的使用PCA进行人脸分类的方法。


二、学习内容

1、了解基本的PCA原理;

2、掌握基本的读取数据的方法,能基本上完成pgm文件的读取;

3、能够利用PCA进行数据压缩,并利用基本的决策方法实现人脸分类。


三、学习时间

2022年6月27日-2022年6月29日


四、学习产出

1、CSDN技术博客一篇;

2、基于PCA的人脸识别Python程序一个。


五、正文部分

1、什么是PCA?

主成分分析(Principal Component Analysis, PCA)方法是K. Pearson在1901年提出的一种数据分析方法,其出发点是:从一组特征中计算出一组按重要性从大到小排列的新特征,它们是原有特征的线性组合,并且相互之间是不相关的。

K-L变换是推广的PCA方法,其思想与PCA较为类似,只是在构造产生矩阵的时候略有不同,因为考虑到了类别信息。

2、使用PCA方法进行人脸识别的步骤

我们使用的样本集一共有20个类别,每个类别有8个,前7个被作为训练样本,已知其类别,每类的最后一个样本作为待识别样本。

首先需要读取文件,训练所用的数据集为.pgm文件,是一种灰度图像的存储格式,可以利用函数Image.open打开这类文件。

1)前期尝试及准备

首先我们要将数据读取为一个数组的形式,每个数组的元素为一个图片对应的一维化处理之后的形式。

为了增加表现力,可以查阅相关资料实现相应的图片展示出来的函数。

(1) 实现列表维度的判断
 img=np.array(img)
 print(img.shape)

将原本的列表转换为一个np数组,然后看它对应的维度即可。问题在于如果将它转换为一个np数组之后就不再能使用append函数来添加数组,所以在它遍历完所有的一次之后再进行相应的转换。利用这种方法可以得到将所有数组添加之后的数组维度如下:

(8, 112, 92)
(8, 112, 92)
(8, 112, 92)
(8, 112, 92)
(8, 112, 92)
(8, 112, 92)
(8, 112, 92)
(8, 112, 92)
(8, 112, 92)
(8, 112, 92)
(20, 8, 112, 92)

读取样本时需要对于每个图片读取的像素进行降维处理,将其变为一个1维的数组,具体的思路是:

 for i in range(N):
    # 读取第i列里面所有元素,将这些元素都放到一个数组里。
    C1=X[:,i]
    for j in range(M):
        # 读取第i列里面的元素,依次把它们添加到数组里面。
        D[i*M+j,0]=C1[j]
(2) 将图片展示出来

利用的matplotlib里面的imshow函数来显示

plt.imshow(img)
ple.show()

不过由于利用的Image.open读取的图片,所以颜色上有点奇怪。
可以通过添加参数cmap='gray’之后变为正常的灰度图片。

plt.imshow(im,cmap='gray')  
plt.show()

2)相关函数及代码

(1)图像处理相关
①对图片进行数据增强

可以利用直方图均衡化来实现图片对比度增强,增强的过程中还利用到了线性插值方法。

def histeq(im,nbr_bins=256):
    imhist,bins=np.histogram(im.flatten(),nbr_bins)
    cdf=imhist.cumsum()
    cdf=255.0*cdf/cdf[-1]
    im2=interp(im.flatten(),bins[:-1],cdf)
    return im2.reshape(im.shape)
②对数据进行降维操作,变成一个列矩阵
def read_X_to_col(X,M,N,n):
    D=np.zeros((M*N,1))
    D1=np.zeros((1,M*N))
    if n==0:
    	# 顺次从原矩阵中取出每一列的元素,放到一维列矩阵里。
        for i in range(N):
            C1=X[:,i]
            for j in range(M):
                D[i*M+j,0]=C1[j]
        return D
    else:
        for i in range(M):
            C1=X[i,:]
            for j in range(N):
                D1[0,i*N+j]=C1[j]
        return D1
(2)对样本进行中心化处理,获得协方差矩阵
① 计算样本均值

利用for循环将所有的类别的人脸作为训练数据集,求解出来其均值作为均值向量。

XM=np.ndarray((M*N,1))
for i in range(num_class):
    for j in range(0,num_inclass-1):
        XM=XM+X0[i,j]
XM = XM/num_all
②求解协方差矩阵

利用训练集的前七个样本作为协方差矩阵的计算组成部分,用前七个样本的特征向量减去均值向利用这个向量与其转置进行点积,即可得到对应的协方差矩阵,在这里利用了一个新的矩阵来存放样本特征,即:

X=np.ndarray((N*M,num_class*(num_inclass-1),1))
for i in range(num_class):
    for j in range(0,(num_inclass-1)):
        X[:,i*(num_inclass-1)+j]=X0[i,j]-XM
X=X.reshape(N*M,num_class*(num_inclass-1))
R=np.dot(X.T,X)
# 求R的特征值和特征向量
# 其中D为特征值,按降序排列,V为对应的特征向量
D,V=np.linalg.eig(R)
U=np.ndarray((N*M,num_class*(num_inclass-1)))
for i in range(V.shape[1]):
    U[:,i]=np.dot(X,V[:,i])/np.sqrt(D[i])
Tips:R矩阵的构造原理

在提取本征脸的时候我们需要先把图像都标准化为 N × N N\times N N×N的人脸图像,为了方便后续的运算,需要将该 N × N N\times N N×N的矩阵转化成 N 2 × 1 N^2\times 1 N2×1的列向量(这里就要用到我们写的将X矩阵转化成列向量的函数 read_X_to_col(X,M,N,n))

def read_X_to_col(X,M,N,n):
    D=np.zeros((M*N,1))
    D1=np.zeros((1,M*N))
    if n==0:
        for i in range(N):
            C1=X[:,i]
            for j in range(M):
                D[i*M+j,0]=C1[j]
        return D
    else:
        for i in range(M):
            C1=X[i,:]
            for j in range(N):
                D1[0,i*N+j]=C1[j]
        return D1

之后再进行样本总协方差矩阵的计算,通过公式:

∑ = 1 m ∑ i = 1 m ( x i − μ ) ( x i − μ ) T = 1 m X X T \quad\quad \sum =\frac{1}{m} \sum^m_{i=1}(x_i-\mu)(x_i-\mu)^T =\frac{1}{m}XX^T =m1i=1m(xiμ)(xiμ)T=m1XXT

X : N 2 × M ∑ : N 2 × N 2 \quad\quad X:N^2\times M\quad\quad\quad\quad\quad \sum:N^2\times N^2 X:N2×M:N2×N2

可以看出如果直接对 ∑ \sum 求取其特征值和特征向量,运算量将十分庞大,由于 M × M M\times M M×M M < < N 2 M<<N^2 M<<N2,我们可以构造矩阵 R = X X T R : M × M R=XX^T \quad R:M\times M R=XXTR:M×M通过求取 R R R 的特征值和特征向量,进而得到原来协方差矩阵 ∑ \sum 的特征值和特征向量。

推导过程如下:

X T X v i = λ i v i \quad X^TXv_i=\lambda_iv_i XTXvi=λivi
X X T X v i = λ i X v i XX^TXv_i=\lambda_iXv_i XXTXvi=λiXvi
∑ X v i = λ i X v i \sum Xv_i=\lambda_iXv_i Xvi=λiXvi 记: u i = X v i u_i=Xv_i ui=Xvi
∑ u i = λ i u i \sum u_i=\lambda_iu_i ui=λiui

从上述推导可以看出: M × M M\times M M×M 维矩阵 X T X X^TX XTX N 2 × N 2 N^2\times N^2 N2×N2维矩阵 X X T XX^T XXT 具有相同的特征值,而特征向量具有以下的关系:

u i = X v i u_i=Xv_i ui=Xvi

∑ \sum 的正交归一的特征向量是:

u i = 1 λ i X v i i = 1 , . . . , m u_i=\frac{1}{\sqrt{\lambda_i}}Xv_i\quad\quad\quad i=1,...,m ui=λi 1Xvii=1,...,m

∑ \sum 最多有 M M M 个非零特征向量值。

通过以上的推导和相关公式,我们可以求得正交归一化的特征向量,进而对原始数据(转化成列向量的人脸数据)进行降维处理得到本征脸并将其显示出来。

kk1=kk2=0
    for i in range(num_class):
        Y=read_col_to_X(U[:,i],M,N)
        if kk1<5:
            kk1=kk1+1
        elif kk1==5:
            kk1=1
            kk2=kk2+1
        plt.figure(20)
        plt.subplot(4,5,kk1+kk2*5)
        plt.imshow(Y)
    plt.show()
(3)利用特征向量实现相应的特征提取,将测试的数据进行降维处理,便于之后利用分类器进行分类操作
# 降维然后分类
y=np.ndarray((num_class,num_inclass,num_class*(num_inclass-1),1))
for i in range(num_class):
    for j in range(num_inclass):
        y[i,j]=np.dot(U.T,X0[i,j])
(4)利用最近邻分类器进行样本分类,最终的预测结果存放在数组pred中
right_num=0
pred=list()
for k in range(num_class):
    Dist=np.ndarray((num_class,num_inclass-1))
    for i in range(num_class):
        for j in range(num_inclass-1):
            Dist[i,j]=np.linalg.norm(y[k,num_inclass-1]-y[i,j])
    #存放每一类的预测出来的类别
    #Dist[i,j]每个东西对应的最小值
    # np.argmin()
    pred_tmp=np.argmin(Dist)//7
    pred.append(pred_tmp)
(5)利用测试样本的原本的种类与预测出来的样本进行对比,计算预测准确率
for i in range(num_class):
    if pred[i]==i:
        right_num=right_num+1
    print('第{0: <2}个样本被分为第{1: <2}类,实际类别为{2: <2}'.format(i+1,pred[i]+1,i+1))
right_rate=right_num/num_class
print(f'利用该种方法获得的人脸识别准确率为{right_rate:.2%}')

六、①、碎碎念

这篇博客是我们学校模式识别课程最后的一个实验作业,老师让我们通过上课所学的PCA算法实现一个小小的人脸识别系统,就是上面这篇博客的内容。她说虽然这个方法很早之前就提出来了,但是至今都应用在很多领域,我们也不能小看很早之前提出的算法,这些都是前人智慧的结晶,至于为什么还在用这么“古老”的方法,那是因为还没提出更好的方法,或许未来就是看博客的你提出了更好的方法呢 ~

或许博客里的代码并不很好Ctrl+C Ctrl+V,但是却是我们改了许久的学习成果,也算是写给自己看的,图一乐啦~

至于这里的“我们”是什么意思?那就是“我们”的意思啊哈哈哈~

PCA带图像% Face recognition by Santiago Serrano %人脸识别代码 clear all close all clc % number of images on your training set. %训练集数目 M=10; %Chosen std and mean. %It can be any number that it is close to the std and mean of most of the images. um=100; ustd=80; %read and show images(bmp); %读入M个训练图像并显示在一个窗口上 S=[]; %img matrix figure(1); for i=1:M str=strcat('C:\Documents and Settings\Administrator\桌面\6311661897288432PCA\97288432PCA\',int2str(i),'.bmp'); %concatenates two strings that form the name of the image eval('img=imread(str);'); subplot(ceil(sqrt(M)),ceil(sqrt(M)),i) imshow(img) if i==3 title('Training set','fontsize',18) end drawnow; [irow icol]=size(img); % get the number of rows (N1) and columns (N2)获得图像的大小,所有图像的大小要一致 temp=reshape(img',irow*icol,1); %creates a (N1*N2)x1 matrix一幅图像构造一个向量 向量的大小和图像大小有关 S=[S temp]; %X is a N1*N2xM matrix after finishing the sequence 生成一个向量矩阵,M个图像有M列 %this is our S end %Here we change the mean and std of all images. We normalize all images. %This is done to reduce the error due to lighting conditions. %下面是对图像规范化,更具所有图像的的平均值和方差 for i=1:size(S,2) temp=double(S(:,i)); m=mean(temp); st=std(temp); S(:,i)=(temp-m)*ustd/st+um; end %show normalized images 显示规范化后的图像 figure(2); for i=1:M str=strcat(int2str(i),'.bmp'); img=reshape(S(:,i),icol,irow); img=img'; eval('imwrite(img,str)'); subplot(ceil(sqrt(M)),ceil(sqrt(M)),i) imshow(img) drawnow; if i==3 title('Normalized Training Set','fontsize',18) end end %mean image;显示平均图像,所有图像叠加在一起 m=mean(S,2); %obtains the mean of each row instead of each column tmimg=uint8(m); %converts to unsigned 8-bit integer. Values range from 0 to 255 img=reshape(tmimg,icol,irow); %takes the N1*N2x1 vector and creates a N2xN1 matrix img=img'; %creates a N1xN2 matrix by transposing the image. figure(3); imshow(img); title('Mean Image','fontsize',18) % Change image for manipulation %对图像变换便于处理
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风独凉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值