• 基于Python实现Eigenface人脸识别、特征脸识别


    资源下载地址:https://download.csdn.net/download/sheziqiong/85836917
    资源下载地址:https://download.csdn.net/download/sheziqiong/85836917

    实验内容和原理

    PCA

    PCA(Principal Components Analysis)是一种统计分析、简化数据集的方法。它利用正交变换来对一系列可能相关的变量的观测值进行线性变换,从而投影为一系列线性不相关变量的值,这些不相关变量称为主成分(Principal Components)。

    (线性代数知识回顾:PCA 的数学原理[http://blog.codinglabs.org/articles/pca-tutorial.html])

    基本思想:

    将坐标轴中心移到数据的中心,然后旋转坐标轴,使得数据在 C1 轴上的方差最大,即全部 n 个数据个体在该方向上的投影最为分散。意味着更多的信息被保留下来。C1 成为第一主成分。

    第二主成分:找一个 C2,使得 C2 与 C1 的协方差(相关系数)为 0,以免与 C1 信息重叠,并且使数据在该方向的方差尽量最大。

    以此类推,找到第三主成分,第四主成分……第 p 个主成分。p 个随机变量可以有 p 个主成分。

    通过 PCA,可以通过只保留对方差贡献最大的特征的方式来降低数据的维数(可以通过特征值的大小来判断贡献度)。PCA 相当于找到了原有高维数据在低维度的投影钟,数据损失量最小的那个,作为提取出的主要特征。

    数学定义:

    PCA 的数学定义是:一个正交化线性变换,把数据变换到一个新的坐标系统中,使得这一数据的任何投影的第一大方差在第一个坐标(称为第一主成分)上,第二大方差在第二个坐标(第二主成分)上,依次类推。

    定义一个 n × m 的矩阵, XT 为去平均值(以平均值为中心移动至原点)的数据,其行为数据样本,列为数据类别。则 X 的奇异值分解为 X = WΣVT,其中 m × m 矩阵 W 是 XXT 的特征向量矩阵, Σ 是 m × n 的非负矩形对角矩阵,V 是 n × n 的 XTX 的特征向量矩阵。据此:

    当 m < n 1 时,V 在通常情况下不是唯一定义的,而 Y 则是唯一定义的。W 是一个正交矩阵,YTWT=XT,且 YT 的第一列由第一主成分组成,第二列由第二主成分组成,依此类推。

    为了得到一种降低数据维度的有效办法,我们可以利用 WL 把 X 映射到一个只应用前面 L 个向量的低维空间中去:

    在欧几里得空间给定一组点数,第一主成分对应于通过多维空间平均点的一条线,同时保证各个点到这条直线距离的平方和最小。去除掉第一主成分后,用同样的方法得到第二主成分。依此类推。在 Σ 中的奇异值均为矩阵 XXT 的特征值的平方根。每一个特征值都与跟它们相关的方差是成正比的,而且所有特征值的总和等于所有点到它们的多维空间平均点距离的平方和。

    特征脸

    可以通过在一大组描述不同人脸的图像上进行主成分分析(PCA)获得。任意一张人脸图像都可以被认为是这些标准脸的组合。例如,一张人脸图像可能是特征脸 1 的 10%,加上特征脸 2 的 55%,在减去特征脸 3 的 3%。值得注意的是,它不需要太多的特征脸来获得大多数脸的近似组合。另外,由于人脸是通过一系列向量(每个特征脸一个比例值)而不是数字图像进行保存,可以节省很多存储空间。

    实现:

    准备一个训练集的人脸图像。构成训练集的图片需要在相同的照明条件下拍摄的,并将所有图像的眼睛和嘴对齐。他们还必须在预处理阶段就重采样到一个共同的像素分辨率(R×C)。现在,简单地将原始图像的每一行的像素串联在一起,产生一个具有 R×C 个元素的行向量,每个图像被视为一个向量。现在,假定所有的训练集的图像被存储在一个单一的矩阵 T 中,矩阵的每一列是一个图像。

    减去均值向量. 均值向量 a 要首先计算,并且 T 中的每一个图像都要减掉均值向量。

    计算协方差矩阵 S 的特征值和特征向量。每一个特征向量的维数与原始图像的一致,因此可以被看作是一个图像。因此这些向量被称作特征脸。他们代表了图像与均值图像差别的不同方向。通常来说,这个过程的计算代价很高(如果可以计算的话)。

    选择主成分。一个 D x D 的协方差矩阵会产生 D 个特征向量,每一个对应 R × c 图像空间中的一个方向。具有较大特征值的特征向量会被保留下来,一般选择最大的 N 个,或者按照特征值的比例进行保存,如保留前 95%。

    这些特征脸现在可以用于标识已有的和新的人脸:我们可以将一个新的人脸图像(先要减去均值图像)投影到特征脸上,以此来记录这个图像与平均图像的偏差。每一个特征向量的特征值代表了训练集合的图像与均值图像在该方向上的偏差有多大。将图像投影到特征向量的子集上可能丢失信息,但是通过保留那些具有较大特征值的特征向量的方法可以减少这个损失。

    实验步骤与分析

    输入图像的预处理

    因为之后也会需要对自己的照片进行处理,所以直接忽略了给出的数据库中的眼睛信息,使用了 python 自带的人脸识别库进行头像裁剪。

    对裁剪出的头像做一遍直方图均衡化,平衡不同光照的情况

        img = cv.imread(path, -1)
        FACE = cv.CascadeClassifier(r'haarcascade_frontalface_default.xml')
        Scan = FACE.detectMultiScale(img,scaleFactor=1.1,minNeighbors=3)
        if len(Scan) == 1 :
            for (x,y,w,h) in Scan:
                cropped = img[y:y+h, x:x+w]
                # cropped is where face is
                cv.rectangle(img, (x,y), (x+w, y+h), (255, 255, 255), 1)
                cropped = cv.equalizeHist(cropped)
                cropped = cv.resize(cropped, (250, 250))
                # change it to 250 * 250 (then will be 62500)
                cv.imshow("ori",img)
                cv.imshow("figure",cropped)
                cv.waitKey(1)
                print(path)
                # change it to 1-D and append it to T
                cropped = cropped.reshape(cropped.size, 1)
                T.append(cropped)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    PCA

    在实验中我自己实现了一遍 PCA,可以通过输入能量来选择前若干个特征脸。但最终还是决定使用 OpenCV 自带的 PCACompute 来运算。(在实验结果章节中会对比计算结果)

    其中使用自己实现的 PCA 算法的代码为 train_my.py

    ENERGY = float(input("input the energy(float in 0~1)"))
    Sum = 0
    for i in range(vs.shape[0]):
        Sum += vs[i]/sum(vs)
        if Sum >= ENERGY:
            break
        NUM_EIGEN_FACES = max([i, NUM_EIGEN_FACES])
    vs = vs[0:NUM_EIGEN_FACES].copy()
    Vs = Vs[:, 0:NUM_EIGEN_FACES].copy()
    print(Vs.shape)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    直接使用 cv2 库中的 PCA 算法(此处是用户自定义输入采用前 NUM_EIGEN_FACES 个脸):

    Mean, Vs = cv.PCACompute(T, mean=None, maxComponents=NUM_EIGEN_FACES)
    
    • 1

    EigenFace

    下面的代码用于保存和显示 EigenFace

    for V in Vs:
        eigenFace = V.reshape((250, 250))
        # here we will show the eigenface
        cv.imshow('figure', eigenFace)
        cv.waitKey(1)
        eigenFaces.append(eigenFace)
    
    eigenFaces = np.array(eigenFaces)
    eigenFace_mean = eigenFaces.mean(axis=0)
    
    Mean = Mean.reshape((250, 250))
    plt.figure()
    plt.title('Mean')
    plt.subplot(1, 2, 1)
    plt.imshow(Mean, cmap = plt.get_cmap('gray'))
    plt.subplot(1, 2, 2)
    plt.imshow(eigenFace_mean, cmap = plt.get_cmap('gray'))
    plt.show()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    储存 recognize 所需的矩阵信息

    np.save('save_database\\save_Mean', Mean)
    np.save('save_database\\save_A', T)
    np.save('save_database\\eigenface', eigenFaces)
    
    • 1
    • 2
    • 3

    识别

    读取图像及预处理的相关操作和 train 时一样,不再赘述(这里应该注意,必须对图像进行一样的预处理)

    识别前需进行的矩阵运算:

    • PRO 矩阵用于存储将图像映射到若干特征脸的空间时,每张图像对应的坐标
    • 将需要识别的图像也映射到这个空间中(都是乘上特征脸矩阵即可)
    PRO = A.dot(Vectors)
    # …
    # to 1-D
    Array = cropped.reshape(cropped.size,1)
    Array = np.mat(np.array(Array)).squeeze()
    # get the difference and project it to the eiganface space
    meanVector = Mean.flatten()
    meanVector = meanVector.squeeze()
    diff = Array - meanVector
    diff = diff.squeeze()
    pro = diff.dot(Vectors)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    然后计算空间中离需要识别的图像距离最近的点

    distance = []
    for i in range(0, A.shape[0]):
        cur = PRO[i,:]
        temp = np.linalg.norm(pro - cur)
        distance.append(temp)
        print('No.' + str(i) + ' = ' + str(distance[i]))
    
    minDistance = min(distance)
    index = distance.index(minDistance) - 1
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    再打开数据库对应的文件夹,找到对应图像并显示即可

    # load the train-data to find the most similar photo
    TRAIN_PATH = 'BioFaceDatabase\BioID-FaceDatabase-V1.2'
    TrainFiles = os.listdir(TRAIN_PATH)
    paths = glob.glob(os.path.join(TRAIN_PATH, '*.pgm'))
    paths.sort()
    # …
    result = cv.imread(paths[index], -1)
    testImg = cv.putText(testImg, "Most Similar: ", (40, 50), cv.FONT_HERSHEY_PLAIN, 1.0, (0, 0, 0), 2)
    testImg = cv.putText(testImg, paths[index].split ( "\\" ) [ -1 ], (40, 100), cv.FONT_HERSHEY_PLAIN, 1.0, (0, 0, 0), 2)
    cv.imshow("ori",testImg)
    cv.imshow("recognize result",result)
    cv.waitKey(0)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    实验结果

    Train

    预处理:

    平均脸:

    使用 cv2 自带的 PCACompute 算出的特征脸叠加(前十张):

    使用自己实现的 PCA 算出的特征脸叠加(能量值 0.8):

    随便选取的一张特征脸:

    识别:

    Cv2 自带的 PCA 的结果:

    • 自己的照片

    可以看出,不同人的照片之间的距离还是相当大的

    • 数据库中给出的照片

    自己实现的 PCA 的识别效果:

    资源下载地址:https://download.csdn.net/download/sheziqiong/85836917
    资源下载地址:https://download.csdn.net/download/sheziqiong/85836917

  • 相关阅读:
    问 ChatGPT 关于GPT的事情:扩展篇
    Ansible ad-hoc 临时命令
    自制摩斯电码连接器【CW-LINK】
    离子液体负载修饰磁性纳米材料四氧化三铁(Fe3O4)(齐岳bio)
    InnoDB底层存储结构探秘
    基于SSM的校园音乐点歌系统平台
    NLP教程(5) - 语言模型、RNN、GRU与LSTM
    三十一、java版 SpringCloud分布式微服务云架构之Java ArrayList
    加权基因共表达网络在肌少症基因筛选中的应用研究
    抖音seo矩阵系统源代码分享
  • 原文地址:https://blog.csdn.net/sheziqiong/article/details/125541899