0%

基于 2DPCA 的人脸识别方法——特征脸法[3]

基于 2DPCA 的人脸识别方法——特征脸法[3]

2DPCA

论文:Two-dimensional PCA: a new approach to appearance-based face representation and recognition | IEEE Journals & Magazine | IEEE Xplore

提出了 2DPCA 的方法,是对基于PCA的特征脸法的改进。该方案直接对图片本身(矩阵)进行操作,而不是像 PCA 一样,把一张图片拉成一条列向量再进行计算。这种方法显著减少了协方差矩阵的维度,减少了计算量。2DPCA 使用较少的特征向量就能够较好地对人脸进行重构。

Matlab 复现

基本思路和 PCA 差不多,只不过一些地方有些区别。

1. 原始数据处理与划分

这里用了ORL数据集,与 PCA 相比,2DPCA 不再需要把一张脸 reshape 成一个列向量了。

1
2
3
4
5
6
7
8
9
10
11
12
13
% 400张图,一共40人,一人10张
load('ORL4646.mat');

% 取出前40%作为测试数据,剩下60%作为训练数据
test_data_index = [];
train_data_index = [];
for i=0:39
test_data_index = [test_data_index 10*i+1:10*i+4];
train_data_index = [train_data_index 10*i+5:10*(i+1)];
end

test_data = ORL4646(:, :, test_data_index);
train_data = ORL4646(:, :,train_data_index);

2. 求平均脸

由于 2DPCA 保留图像原矩阵,因此这里的平均脸是通过对第三维度求平均得到的。

1
mean_face = mean(train_data, 3);

3. 计算散布矩阵

计算散布本质上和计算协方差矩阵是一样的,只是多除以一个 N-1,下图公式直接省略了 N-1,因为其对目标不会有影响。
散布矩阵
Xi 表示第 i 张图片(矩阵),因此这个公式实际上表示的就是:遍历每一张图片(矩阵)Xi,求出相应的值后累加。

1
2
3
4
5
6
7
8
9
% 协方差矩阵
cov_matrix = zeros(46, 46);
for i=1:size(train_data, 3)
centered_face = train_data(:,:,i) - mean_face;
cov_matrix = cov_matrix + centered_face' * centered_face;
end

% 散布矩阵
scatter_matrix = cov_matrix / (size(train_data, 3) - 1);

4. 求散布矩阵的特征值和特征向量

1
2
3
4
5
6
7
8
9
10
[eigen_vectors, dianogol_matrix] = eig(scatter_matrix);

% 从对角矩阵获取特征值
eigen_values = diag(dianogol_matrix);

% 对特征值进行排序,获得排列后的特征值和索引
[sorted_eigen_values, index] = sort(eigen_values, 'descend');

% 获取排序后的征值对应的特征向量
sorted_eigen_vectors = eigen_vectors(:, index);

这里得到的特征向量直接就构成了 46x46 (即图片原始尺寸)的矩阵,取出其中的一些列,可以构成投影矩阵。与 PCA 不同的是,这里的矩阵并不是一张脸的模样。

5. 取一个人的脸,进行重建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
index = 1;
for i=10:5:46

% 取得投影矩阵
project_matrix = sorted_eigen_vectors(:,1:i);

% 重建人脸并显示
rebuild_faces = project_matrix * (project_matrix' * single_face) + mean_face;
subplot(2, 4, index);
index = index + 1;
fig = show_face(rebuild_faces);
title(sprintf("i=%d", i));

if (i == 45)
waitfor(fig);
end
end

效果如下图所示,与PCA相比,2DPCA只要较少的特征向量就可以很好地重塑人脸。

2DPCA 人脸重构

下图是使用 PCA 进行人脸重构,可以与上图对比,注意 i 值的大小比对,i 值是使用的特征向量数量。

PCA 人脸重塑

6. 进行人脸识别

将待测人脸通过前面求得的投影矩阵投影到子空间后,计算与已知人脸的距离,并使用最近邻分类器KNN进行分类,这部分和 PCA 一模一样。最终得到的识别率正确率在 93% 左右。

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
index = 1;
X = [];
Y = [];
for i=10:5:46

% 取得投影矩阵
project_matrix = sorted_eigen_vectors(:,1:i);

% 测试、训练数据降维
projected_train_data = zeros(i,46,size(train_data, 3));
for j=1:size(train_data, 3)
projected_train_data(:,:,j) = project_matrix' * (train_data(:,:,j) - mean_face);
end

projected_test_data = zeros(i,46,size(test_data, 3));
for j=1:size(test_data, 3)
projected_test_data(:,:,j) = project_matrix' * (test_data(:,:,j) - mean_face);
end

% 开始测试识别率
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% KNN的k值
k = 1;

% 用于保存最小的k个值的矩阵
% 用于保存最小k个值对应的人标签的矩阵
minimun_k_values = zeros(k,1);
label_of_minimun_k_values = zeros(k,1);

% 测试脸的数量
test_face_number = size(projected_test_data, 3);

% 识别正确数量
correct_predict_number = 0;

% 遍历每一个待测试人脸
for each_test_face_index = 1:test_face_number

each_test_face = projected_test_data(:, :, each_test_face_index);

% 先把k个值填满,避免在迭代中反复判断
for each_train_face_index = 1:k
minimun_k_values(each_train_face_index,1) = norm(each_test_face - projected_train_data(:, :,each_train_face_index));
label_of_minimun_k_values(each_train_face_index,1) = floor((train_data_index(1,each_train_face_index) - 1) / 10) + 1;
end

% 找出k个值中最大值及其下标
[max_value, index_of_max_value] = max(minimun_k_values);

% 计算与剩余每一个已知人脸的距离
for each_train_face_index = k+1:size(projected_train_data, 3)

% 计算距离
distance = norm(each_test_face - projected_train_data(:, :,each_train_face_index));

% 遇到更小的距离就更新距离和标签
if (distance < max_value)
minimun_k_values(index_of_max_value,1) = distance;
label_of_minimun_k_values(index_of_max_value,1) = floor((train_data_index(1,each_train_face_index) - 1) / 10) + 1;
[max_value, index_of_max_value] = max(minimun_k_values);
end
end

% 最终得到距离最小的k个值以及对应的标签
% 取出出现次数最多的值,为预测的人脸标签
predict_label = mode(label_of_minimun_k_values);
real_label = floor((test_data_index(1,each_test_face_index) - 1) / 10)+1;

if (predict_label == real_label)
%fprintf("预测值:%d,实际值:%d,正确\n",predict_label,real_label);
correct_predict_number = correct_predict_number + 1;
else
%fprintf("预测值:%d,实际值:%d,错误\n",predict_label,real_label);
end
end

correct_rate = correct_predict_number/test_face_number;

X = [X i];
Y = [Y correct_rate];

fprintf("i=%d,总测试样本:%d,正确数:%d,正确率:%1f\n", i,test_face_number,correct_predict_number,correct_rate);
end

plot(X,Y);
hold on;

全部代码

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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
clear all;

% 400张图,一共40人,一人10张
load('ORL4646.mat');

% 训练数据与测试数据的生成
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% 取出前40%作为测试数据,剩下60%作为训练数据
test_data_index = [];
train_data_index = [];
for i=0:39
test_data_index = [test_data_index 10*i+1:10*i+4];
train_data_index = [train_data_index 10*i+5:10*(i+1)];
end

test_data = ORL4646(:, :, test_data_index);
train_data = ORL4646(:, :,train_data_index);

% 取得中心化数据
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% 求平均脸,即每一层分别求均值
mean_face = mean(train_data, 3);
waitfor(show_face(mean_face));

% 求得协方差矩阵的特征值与特征向量
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% 协方差矩阵
cov_matrix = zeros(46, 46);
for i=1:size(train_data, 3)
centered_face = train_data(:,:,i) - mean_face;
cov_matrix = cov_matrix + centered_face' * centered_face;
end

% 散布矩阵
scatter_matrix = cov_matrix / (size(train_data, 3) - 1);

[eigen_vectors, dianogol_matrix] = eig(scatter_matrix);

% 从对角矩阵获取特征值
eigen_values = diag(dianogol_matrix);

% 对特征值进行排序,获得排列后的特征值和索引
[sorted_eigen_values, index] = sort(eigen_values, 'descend');

% 获取排序后的征值对应的特征向量
sorted_eigen_vectors = eigen_vectors(:, index);

% 筛选出特征脸
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% 取出第一个人的人脸,用于重建
single_face = train_data(:, :, 1) - mean_face;

index = 1;
X = [];
Y = [];
for i=10:5:46

% 取得投影矩阵
project_matrix = sorted_eigen_vectors(:,1:i);

% 重建人脸并显示
rebuild_faces = project_matrix * (project_matrix' * single_face) + mean_face;
subplot(2, 4, index);
index = index + 1;
fig = show_face(rebuild_faces);
title(sprintf("i=%d", i));

if (i == 45)
waitfor(fig);
end

% 测试、训练数据降维
projected_train_data = zeros(i,46,size(train_data, 3));
for j=1:size(train_data, 3)
projected_train_data(:,:,j) = project_matrix' * (train_data(:,:,j) - mean_face);
end

projected_test_data = zeros(i,46,size(test_data, 3));
for j=1:size(test_data, 3)
projected_test_data(:,:,j) = project_matrix' * (test_data(:,:,j) - mean_face);
end

% 开始测试识别率
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% KNN的k值
k = 1;

% 用于保存最小的k个值的矩阵
% 用于保存最小k个值对应的人标签的矩阵
minimun_k_values = zeros(k,1);
label_of_minimun_k_values = zeros(k,1);

% 测试脸的数量
test_face_number = size(projected_test_data, 3);

% 识别正确数量
correct_predict_number = 0;

% 遍历每一个待测试人脸
for each_test_face_index = 1:test_face_number

each_test_face = projected_test_data(:, :, each_test_face_index);

% 先把k个值填满,避免在迭代中反复判断
for each_train_face_index = 1:k
minimun_k_values(each_train_face_index,1) = norm(each_test_face - projected_train_data(:, :,each_train_face_index));
label_of_minimun_k_values(each_train_face_index,1) = floor((train_data_index(1,each_train_face_index) - 1) / 10) + 1;
end

% 找出k个值中最大值及其下标
[max_value, index_of_max_value] = max(minimun_k_values);

% 计算与剩余每一个已知人脸的距离
for each_train_face_index = k+1:size(projected_train_data, 3)

% 计算距离
distance = norm(each_test_face - projected_train_data(:, :,each_train_face_index));

% 遇到更小的距离就更新距离和标签
if (distance < max_value)
minimun_k_values(index_of_max_value,1) = distance;
label_of_minimun_k_values(index_of_max_value,1) = floor((train_data_index(1,each_train_face_index) - 1) / 10) + 1;
[max_value, index_of_max_value] = max(minimun_k_values);
end
end

% 最终得到距离最小的k个值以及对应的标签
% 取出出现次数最多的值,为预测的人脸标签
predict_label = mode(label_of_minimun_k_values);
real_label = floor((test_data_index(1,each_test_face_index) - 1) / 10)+1;

if (predict_label == real_label)
%fprintf("预测值:%d,实际值:%d,正确\n",predict_label,real_label);
correct_predict_number = correct_predict_number + 1;
else
%fprintf("预测值:%d,实际值:%d,错误\n",predict_label,real_label);
end
end

correct_rate = correct_predict_number/test_face_number;

X = [X i];
Y = [Y correct_rate];

fprintf("i=%d,总测试样本:%d,正确数:%d,正确率:%1f\n", i,test_face_number,correct_predict_number,correct_rate);
end

plot(X,Y);
hold on;

% 输入向量,显示脸
function fig = show_face(vector)
fig = imshow(mat2gray(reshape(vector, [46, 46])));
end

% 显示矩阵中某些脸
function fig = show_faces(faces)
count = 1;
index_of_image_to_show = [1,5,10,15,20,25,30,35,40,100];
for i=index_of_image_to_show
subplot(2,5,count);
fig = show_face(faces(:, :, i));
title(sprintf("i=%d", i));
count = count + 1;
end
end

参考资料

Two-dimensional PCA: a new approach to appearance-based face representation and recognition | IEEE Journals & Magazine | IEEE Xplore

对于2D PCA的理解与复现 - 知乎 (zhihu.com)

本文地址: https://www.chimaoshu.top/基于-2DPCA-的人脸识别方法——特征脸法-3/
版权声明:本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议,转载请注明出处。

Welcome to my other publishing channels