PCA 人脸识别 Python 附源码

一. 前言

本文旨在记录自己在做PCA人脸识别的时候遇到的一些问题和解决的办法,并分享自己的源码。本文更侧重于代码的分享,对于PCA的具体原理就不再过多赘述,具体内容涉及到特征脸的输出、重构后的人脸图片输出以及准确率的预测,欢迎大家一起交流讨论!

二. 简介

本文采用orl4646的数据集,其中包括400张46*46的人脸照片,照片中一共有40个不同的人的人脸照片,每个人有10张人脸照片。本文采用PCA降维的主成分分析的方法,采用7:3的比例把数据分为训练集和验证集。把46*46的人脸图片降维到100维,通过比较验证集和训练集的余弦相似度,采用KNN的方法预测模型。

三. 代码

首先在主函数前,是一些超参数的设定和导包:

import scipy.io as scio
import numpy as np


# 将所有照片按顺序分成40个组
num_groups = 40
# 每组有10张照片
images_per_group = 10
images_per_train_set = 7
images_per_validation_set = 3
# 降维后的训练集和验证集
reduced_face_train = np.random.rand(280, 100)
reduced_face_valid = np.random.rand(120, 100)
# 降维后的验证集人脸与所有测试集的人脸的余弦相似度
similarities = np.random.rand(280)
output_folder = "C:\\Users\\10099\\PycharmProjects\\PCA\\reduced_face"
# 正确预测的数量
count = 0

在主函数中,我们首先要提取数据集中的数据,并将数据集分为训练集和验证集。从orl4646中提取到的是numpy的数据,形状为46*46*400。在提取之后,我们要把这个数据拉长为二维向量,行数为像素的个数46*46,列数为样本的数量,具体代码如下:

    train_set = np.empty([46, 46, num_groups * images_per_train_set])
    valid_set = np.empty([46, 46, num_groups * images_per_validation_set])
    data = scio.loadmat("ORL4646.mat")
    image_data = data['ORL4646']
    num_groups = 40
    images_per_group = 10
    image_groups = []
    # 将数据分为测试集和验证集
    for i in range(num_groups):
        start_index = i * images_per_group
        end_index = (i + 1) * images_per_group
        start_index2 = i * images_per_train_set
        start_index3 = i * images_per_validation_set
        train_set[:, :, start_index2:start_index2 + images_per_train_set] = image_data[:, :, start_index:start_index + images_per_train_set]
        valid_set[:, :, start_index3:start_index3 + images_per_validation_set] = image_data[:, :, start_index + images_per_train_set:end_index]

    train_set = train_set.reshape((46*46, num_groups*images_per_train_set))
    valid_set = valid_set.reshape((46*46, num_groups*images_per_validation_set))

如果想要直观的观察到图片的话,可以采用plt来查看具体某一张图片:

Image1 = Image.fromarray(train_set[:, 0].reshape(46, 46)
plt.imshow(Image1)
plt.show()

可视化操作,把训练集的数据保存到到文件夹中,验证集同理:

    # 保存训练集图片
    for i in range(280):
        Image_train = Image.fromarray(train_set[:, i].reshape(46, 46))
        Image_train = Image_train.convert("L")
        filename = os.path.join(output_folder, f"{i}.jpg")
        Image_train.save(filename)

下面就要进行关键的PCA操作,首先要将图片规格化,减去每个特征的平均,也就是减去平均脸。然后计算协方差矩阵,得到特征值和特征向量。由于这里的特征值和特征向量是复数的形式,虽然虚数部分为0,但是在代码中还是要取实数的部分,否则会报错。由于特征值默认就是从大到小排列的,因此只要取特征向量的前100个:

    # 求平均脸
    mean_face = np.mean(train_set, axis=1)
    # 样本中心化
    train_set = train_set - mean_face[:, np.newaxis]
    # 计算协方差矩阵
    cov_train = np.cov(train_set)
    # 计算特征值和特征向量
    eigenvalues, eigenvectors = np.linalg.eig(cov_train)
    # 转置特征
    eigen_face = np.transpose(np.real(eigenvectors[:, :100]))

特征脸就是协方差矩阵的特征向量,但是在输出特征脸之前,要把特征向量归一化,否则特征脸的亮度会非常低,看起来就是黑色的,因此要定义一个normalization函数实现图片归一化:

# 将图片数据标准化
def normalize(input_data):
    min_value = np.min(input_data)
    max_value = np.max(input_data)
    # 将数据缩放到0-255范围
    normalized_data = 255 * (input_data - min_value) / (max_value - min_value)
    return normalized_data

特征脸输出:

    # 输出前100个特征脸
    for i in range(100):
        Image1 = Image.fromarray(normalize(eigen_face.reshape(46, 46)))
        Image1 = Image1.convert("L")
        filename = os.path.join(output_folder, f"{i}.jpg")
        Image1.save(filename)

如果要输出重构后的人脸图像的话,就是要将前100的特征向量分别和人脸图片做内积,每一次内积会得到一个常数,这个常数代表着这个人脸在这个特征向量的维度的投影的值,再把这个常数乘以该特征向量,得到一个特征,再把这些特征加起来再加上平均脸,就得到了可视化的重构后的人脸图像:

    # 输出重构之后的人脸图像
    ref_pic = mean_face
    for i in range(280):
        for j in range(100):
            ref_pic = ref_pic + np.dot(eigen_face[j, :], train_set[:, i])*eigen_face[j, :]
        ref_pic = normalize(ref_pic)
        image1 = Image.fromarray(ref_pic.reshape(46, 46))
        image1 = image1.convert("L")
        filename = os.path.join(output_folder, f"{i}.jpg")
        image1.save(filename)
        ref_pic = mean_face

求训练集降维后的人脸向量:

    # 求训练集降维后的人脸向量
    for i in range(280):
        for j in range(100):
            reduced_face_train[i, j] = np.dot(eigen_face[j, :], train_set[:, i])

以上,对训练集的处理已经全部完毕,下面要对验证集进行降维:

    # 求验证集降维之后的数据
    for i in range(120):
        for j in range(100):
            reduced_face_valid[i, j] = np.dot(eigen_face[j, :], valid_set[:, i])

对验证集降维后,用余弦相似度判断验证集中每一个人脸向量和训练集的相似度,构造predict函数对相似度进行排序,取前7个相似度最高的人脸,最多的那一个即预测的人脸:

    #predict函数
    def predict(vector_similarities):
        sorted_similarities = np.argsort(vector_similarities)
        max_similarities = sorted_similarities[-7:] // 7
        counts = np.bincount(max_similarities)
        most_common_value = np.argmax(counts)
        return most_common_value


    # 判断模型的准确率
    for i in range(120):
        for j in range(280):
            # 计算余弦相似度
            cos_similarity = cosine_similarity(reduced_face_valid[i, :], reduced_face_train[j, :])
            similarities[j] = cos_similarity
            # 预测结果和真实结果相比较
        if predict((similarities.flatten())) == i//3:
            count = count + 1
            print(f"第{i}个图像预测正确!")
        else:
            print(f"第{i}个图像预测错误!预测结果为{predict((similarities.flatten()))},实际结果为{i//3}")
    print("准确率为:", count/120*100, '%')

四.源码链接

源码链接:https://pan.baidu.com/s/1xTXLKINg1b3UExkqE1UtLw?pwd=5gra 
提取码:5gra

五.结语

有任何疑问欢迎和我交流,如果这篇文章帮助到了你的话,请你点一个赞可以吗?十分感谢!

  • 11
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值