主成分分析算法的实现

实验内容与完成情况:

  • 相关知识点
  1. PCA:用于数据压缩和特征提取,通过计算协方差矩阵及其特征值与特征向量,提取主成分。
  2. 特征脸:使用 PCA 提取面部图像的主要特征,将人脸表示为特征向量的线性组合。
  3. 矩阵操作与线性代数:数据矩阵构造:将多张图片的数据展开为列向量。均值中心化:将每个向量减去训练集的均值向量。协方差矩阵计算与特征分解。
  4. 欧几里得距离:用于比较测试图像与训练图像特征向量的相似性。
  • 实验分析
  1. 训练集数据预处理:通过 os 和 PIL.Image 遍历文件夹,读取训练图片。将图片统一调整为灰度模式和固定尺寸 (256×384),然后展开为 1D 向量。
  2. 构造训练数据矩阵:将训练图片的 1D 向量集合构造成训练数据矩阵(列为图片向量)。
  3. 特征提取 :计算训练数据矩阵的均值并进行中心化。使用协方差矩阵求解特征值和特征向量,筛选重要特征。计算训练数据的特征脸。
  4. 测试图片处理与投影:读取测试图片并统一尺寸,进行中心化。将测试图片投影到特征脸空间,得到测试图片特征向量。
  5. 特征匹配:通过计算测试图片与训练图片的欧几里得距离,找到最相似的训练图片。返回匹配图片的名称并展示。
  • 实验代码
import os
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import image as img
from PIL import Image


# 创建训练数据矩阵
def create_database(train_database_path='TrainDatabase/', target_size=(256, 384)):
    # 获取所有图片文件
    ls = os.listdir(train_database_path)
    # 筛选出所有的图片文件
    image_files = [f for f in ls if os.path.isfile(os.path.join(train_database_path, f)) and f.endswith('.jpg')]

    train_number = len(image_files)  # 图片数量
    print(f"Number of training images: {train_number}")

    temp_array = []
    for i in range(train_number):
        train_image_path = os.path.join(train_database_path, image_files[i])

        # 打印调试信息
        print(f"Trying to load: {train_image_path}")

        # 读取并调整图片尺寸
        train_image = Image.open(train_image_path).convert('L')  # 转为灰度图像
        train_image = train_image.resize(target_size)  # 调整尺寸为256x384

        # 将图片转为1D数组并添加到训练集
        one_d_train_image = np.reshape(np.array(train_image), (-1, 1), order="F")
        temp_array.append(one_d_train_image)

    one_d_train_image_all_set = np.transpose(np.asmatrix(np.array(temp_array)))  # 使用 np.asmatrix
    return one_d_train_image_all_set


# 输入训练数据矩阵求解PCA
def eigen_face_core(one_d_train_image_all_set):
    mean_of_train_database = np.asmatrix(np.mean(one_d_train_image_all_set, 1))  # 使用 np.asmatrix
    train_number = one_d_train_image_all_set.shape[1]

    centered_image_vectors_temp = []
    for i in range(train_number):
        temp = one_d_train_image_all_set[:, i] - mean_of_train_database
        centered_image_vectors_temp.append(temp)
    centered_image_vectors = np.transpose(np.asmatrix(np.array(centered_image_vectors_temp)))  # 使用 np.asmatrix

    # 计算协方差矩阵
    covariance_matrix_temp = np.dot(np.transpose(centered_image_vectors), centered_image_vectors)

    # 求特征值和特征向量
    eigenvalues, feature_vector = np.linalg.eig(covariance_matrix_temp)

    # 筛选特征向量
    eigen_vector = []
    for i in range(feature_vector.shape[1]):
        if eigenvalues[i] > 1:  # 只保留特征值较大的特征向量
            eigen_vector.append(feature_vector[:, i])
    eigen_vector = np.transpose(np.asmatrix(np.array(eigen_vector)))  # 使用 np.asmatrix

    # 计算特征脸
    eigen_faces = np.dot(centered_image_vectors, eigen_vector)

    return mean_of_train_database, eigen_faces, centered_image_vectors


# 输入测试数据,PCA识别
def recognition(test_image, mean_of_train_database, eigen_faces, centered_image_vectors):
    projected_image = []
    train_number = eigen_faces.shape[1]

    for i in range(train_number):
        temp = np.dot(np.transpose(eigen_faces), centered_image_vectors[:, i])
        projected_image.append(temp)
    projected_image = np.transpose(np.asmatrix(np.array(projected_image)))  # 使用 np.asmatrix

    # 处理测试图片
    in_image = np.reshape(np.array(test_image), (-1, 1), order="F")
    difference = in_image - mean_of_train_database
    projected_test_image = np.dot(np.transpose(eigen_faces), difference)

    # 计算欧几里得距离
    euclidean_distance = []
    for i in range(train_number):
        q = projected_image[:, i]
        temp = np.linalg.norm(projected_test_image - q) ** 2
        euclidean_distance.append(temp)
    euclidean_distance = np.transpose(np.asmatrix(np.array(euclidean_distance)))  # 使用 np.asmatrix

    # 找到距离最小的训练图片
    euclidean_distance = np.array(euclidean_distance)
    euclidean_distance_min_index = np.argmin(euclidean_distance) + 1

    output_name = str(euclidean_distance_min_index) + '.jpg'
    return output_name


# 主函数
def main():
    image_order = input('Enter test image name (a number between 1 to 11, q to quit): ')
    if image_order == 'q':
        quit()

    num = int(image_order)
    while num < 1 or num > 11:
        print('Illegal input, please input again')
        image_order = input('Enter test image name (a number between 1 to 11, q to quit): ')
        if image_order == 'q':
            quit()
        num = int(image_order)

    test_image_path = 'TestDatabase/' + str(image_order) + '.jpg'
    test_image = img.imread(test_image_path)

    plt.imshow(test_image)
    plt.axis('off')
    plt.title('Input')
    plt.show()

    one_d_train_image_all_set = create_database()  # 动态加载训练集
    mean_of_train_database, eigen_faces, centered_image_vectors = eigen_face_core(one_d_train_image_all_set)
    output_name = recognition(test_image, mean_of_train_database, eigen_faces, centered_image_vectors)

    selected_image = 'TrainDatabase/' + output_name
    selected_image = img.imread(selected_image)

    plt.imshow(selected_image)
    plt.axis('off')
    plt.title('Equivalent Image')
    plt.show()


if __name__ == '__main__':
    while 1:
        main()
  • 运行截图

  • 实验总结

通过特征脸提取训练集的主要特征,并使用欧几里得距离匹配测试图片。结果验证了PCA的有效性,但因数据量少及对光照等变化敏感,识别效果有限。未来可通过数据扩展和算法优化提升性能。

出现的问题:照片大小不合适报错。

解决方案(列出遇到的问题和解决办法,列出没有解决的问题):调整了图片大小。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值