实验内容与完成情况:
- 相关知识点
- PCA:用于数据压缩和特征提取,通过计算协方差矩阵及其特征值与特征向量,提取主成分。
- 特征脸:使用 PCA 提取面部图像的主要特征,将人脸表示为特征向量的线性组合。
- 矩阵操作与线性代数:数据矩阵构造:将多张图片的数据展开为列向量。均值中心化:将每个向量减去训练集的均值向量。协方差矩阵计算与特征分解。
- 欧几里得距离:用于比较测试图像与训练图像特征向量的相似性。
- 实验分析
- 训练集数据预处理:通过 os 和 PIL.Image 遍历文件夹,读取训练图片。将图片统一调整为灰度模式和固定尺寸 (256×384),然后展开为 1D 向量。
- 构造训练数据矩阵:将训练图片的 1D 向量集合构造成训练数据矩阵(列为图片向量)。
- 特征提取 :计算训练数据矩阵的均值并进行中心化。使用协方差矩阵求解特征值和特征向量,筛选重要特征。计算训练数据的特征脸。
- 测试图片处理与投影:读取测试图片并统一尺寸,进行中心化。将测试图片投影到特征脸空间,得到测试图片特征向量。
- 特征匹配:通过计算测试图片与训练图片的欧几里得距离,找到最相似的训练图片。返回匹配图片的名称并展示。
- 实验代码
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的有效性,但因数据量少及对光照等变化敏感,识别效果有限。未来可通过数据扩展和算法优化提升性能。
出现的问题:照片大小不合适报错。
解决方案(列出遇到的问题和解决办法,列出没有解决的问题):调整了图片大小。