PCA最显著的特征是用来进行数据处理-对数据降维,不过它也是早期人脸识别的手段.$ $

PCA理论

从下面公式说起 \[ A_{r\times m}X_{m\times n}=Y_{r\times n} \] 假设我们如下看待矩阵
\(X\)表示m维空间的n个点,每一列为一个点.同样的眼光看待\(Y\)
如果r小于m,那么就把这n个点将到低维度的空间了.
那么问题的关键就是求\(A\),其实上面公式就是一个线性变换,A的每一行是r维空间的一个基向量.
降维必然会有信息损失,如何才能使损失最低了?在低纬空间,n个点越分散,保留的信息就越多
对于如何评估分散,其实可以回到高维空间,那就是X的协方差矩阵 \[ C=XX^{T} \] 在m维空间中,每个维度之间方差越小,每个维度自己的方差越大,则点越分散.根据协方差的定义,我们知道对角线上的元素是自己的方差,其他位置是与其它维度的方差.那么除了对角线元素外,其他元素都为零,不就满足越分散的条件了吗?
那么不就是对称矩阵\(C\)的对角化的问题吗?**更奇妙的是C的特征向量矩阵的大小是k*m(k小于m),它不就可以作为我们苦苦寻觅的A的最佳选择吗?.这是有严格的数学证明的,我的理解是:都是使得低维度数据最分散,一种方法得到另一种方法相同结构的解,那么不就可以作为另一种方法的解吗** 但是m有可能很大(图像中,m就上万了),则C矩阵就很大,不利于计算,下面有一点小技巧:
C的特征方程 \[ CV_{i}=\lambda_{i}V_{i} \] 假设: \[ V_{i}=XU_{i} \]\[ CXU_{i}=\lambda_{i}XU_{i}\\ \Rightarrow XX^{T}XU_{i}=\lambda_{i}XU_{i}\\ \Rightarrow X^{T}XU_{i}=\lambda_{i}U_{i} \] 显然,我们可以先求\(U_{i}\),再求\(V_{i}\),而且在本节人脸识别例子中n远小于m,这有利用计算和存储

PCA人脸识别步骤

1)将原始数据按列组成m行n列矩阵X
2)将X的每一列(代表一个属性字段)进行零均值化,即减去这一列的均值
3)求\(D=X^{T}X\)
4)求D的特征值及对应的特征向量,特征向量构成矩阵E
5)求特征脸矩阵(eigenfaces)\(F=X^{T}E\)
6)求训练数据在F空间的映射\(P_{train}=X^{T}F\)
7)求测试数据在F空间的映射\(P_{test}=X_{t}^{T}F\)
8)计算\(P_{train}\)中各个特征脸(eigenface)和测试人脸\(P_{test}\)的欧拉距离.
9)从上面的结果中选择欧拉距离最小的为人脸识别的结果.

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#encoding=utf8
import numpy as np
from PIL import Image
class FaceRecognition(object):
"""利用PCA算法,实现简单的人脸识别"""
def __init__(self, image_size,image_num,image_path):
"""类的初始化
Args:
image_size: 整数; 表示一张人脸总像素点个数
image_num: 整数; 表示训练用到的人脸的个数
image_path: 字符串; 训练的人脸存放的文件夹位置
"""
self.image_size=image_size
self.image_num=image_num
self.image_path=image_path
def load_data(self):
"""加载训练数据
Returns:
image_data: ndarray, (image_size*image_num); 所有人脸的灰度图数据,每一列表示一张人脸
"""
image_data=np.zeros((self.image_size,self.image_num))
for i in range(0,self.image_num):
im=Image.open(self.image_path+str(i+1)+".jpg")
im=im.convert("L")
data = im.getdata()
data = np.array(data)
image_data[:,i]=data
return image_data
def compute_mean_diff(self,image_array):
"""计算每个像素点的均值,并将每一列(一张人脸)的值减去相应像素点的均值
Args:
image_array: ndarray, (image_size*image_num); 所有人脸的灰度图数据,每一列表示一张人脸
Returns:
mean_diff: ndarray, (image_size*image_num); 减去各个像素点均值的人脸矩阵
mean: ndarray, (image_size,); 各个像素点均值
"""
mean=np.mean(image_array,axis=1)
temp=image_array.T-mean
mean_diff=temp.T
return mean_diff,mean
def eigenfaces(self,mean_diff):
"""计算每个像素点的均值,并将每一列(一张人脸)的值减去相应像素点的均值
Args:
mean_diff: ndarray, (image_size*image_num); 减去各个像素点均值的人脸矩阵
Returns:
eigenfaces: ndarray, (image_num*k); 特征向量构成的特征脸矩阵,k是最终选择的特征向量个数
"""
covariance=np.dot(mean_diff.T,mean_diff)
eigenvalues,eigenvectors=np.linalg.eig(covariance)
# 下面注释的代码课实现特征值和特征矩阵按大到小排序
# idx=np.argsort(-eigenvalues)
# eigenvalues=eigenvalues[idx]
# eigenvectors=eigenvectors[:,idx]
idx=np.where(eigenvalues>1)
eigenvectors=eigenvectors[:,idx[0]]
eigenfaces=np.dot(mean_diff,eigenvectors)
return eigenfaces
def recognition(self,train,test,eigenfaces):
"""计算每个像素点的均值,并将每一列(一张人脸)的值减去相应像素点的均值
Args:
train: ndarray, (image_size*image_num) 训练人脸矩阵
test: ndarray, (image_size,) 测试人脸矩阵
eigenfaces: ndarray, (image_num*k); 特征向量构成的特征脸矩阵,k是最终选择的特征向量个数
Returns:
index: 整数, 检测人脸是训练人脸库中的第几张人脸
"""
# 将训练和测试数据映射到eigenfaces空间
projected=np.dot(train.T,eigenfaces)
projected_test=np.dot(test.T,eigenfaces)
# 计算测试人脸和训练库中的各张人脸的欧拉距离
euler_distance=np.sum((projected-projected_test)**2,axis=1)**0.5
# 选择欧拉距离最小的作为人脸识别的结果
recognized_index=np.where(euler_distance==np.min(euler_distance))
index=recognized_index[0][0]+1
return index
if __name__ == '__main__':
# 训练人脸路径
train_image_path="./data/pca_train_data/"
# 构建识别类,并计算出特征脸和像素点均值
recognition=FaceRecognition(180*200,20,train_image_path)
train_grey_image=recognition.load_data()
train_diff_image,mean=recognition.compute_mean_diff(train_grey_image)
eigenfaces=recognition.eigenfaces(train_diff_image)
# 测试数据构建
test_image="./data/pca_test_data/10.jpg"  #修改数值10测试其他人脸是否识别准确.
im=Image.open(test_image)
im=im.convert("L")
temp_data = im.getdata()
temp_data = np.array(temp_data)
test_diff_data=temp_data-mean
# 识别人脸
index=recognition.recognition(train_diff_image,test_diff_data,eigenfaces)
# 显示结果
im=Image.open("./data/pca_train_data/"+str(index)+".jpg")
im.show()

推荐

PCA的数学原理
EigenFace的使用 python