PCA(principal components analysis)即主成分分析技术,又称主分量分析,旨在利用降维的思想,把多指标转化为少数几个综合指标。
在统计学中,主成分分析PCA是一种简化数据集的技术。它是一个线性变换。这个变换把数据变换到一个新的坐标系统中,使得任何数据投影的第一大方差在第一个坐标(称为第一主成分)上,第二大方差在第二个坐标(第二主成分)上,依次类推。主成分分析经常用于减少数据集的维数,同时保持数据集的对方差贡献最大的特征。这是通过保留低阶主成分,忽略高阶主成分做到的。这样低阶成分往往能够保留住数据的最重要方面。但是,这也不是一定的,要视具体应用而定。
% 假设现在用PCA将二维数据(即只有x轴和y轴)降为1维(只有1根坐标轴),
% 去中心化后,我们需要找到一条过原点的新的坐标轴,使数据与这条新轴的拟合度最高
% 即这些数据在这条轴上的投影的方差需要最大
% 从而使得在只保留这一根轴而去掉其它的坐标轴的情况下,数据的信息保留得最多
% 而这根新的轴就叫做 主成分
% 当然,有些数据并不止2维,比如图片就有多维,也就意味着要保留多个主成分
%
% 那么如何找到这些新的轴(主成分)呢?
% 先将数据去中心化
% 再求出数据的协方差矩阵的特征值和特征向量
% 协方差矩阵的特征值代表的是数据在 新的坐标轴(主成分) 上的投影的方差
% 协方差矩阵的特征向量代表的是 新的坐标轴(主成分)相对于原坐标轴的的方向
% 协方差矩阵有几个特征值,数据就有多少个主成分
% 我们将这些特征值降序排列,从最大的特征值开始逐项相加,同时计算 累加值 占 所有特征值的和 的比例
% 这个比例称之为累积贡献度,当累积贡献度达到我们设定的阈值时停止累加,保留这些累加的特征值而舍去剩下的
% 从而实现PCA的降维效果
%
% 详细案例可参考视频:
https://www.bilibili.com/video/BV1E5411E71z/?spm_id_from=333.337.search-card.all.click
%%-------------------------PCA技术在图像识别中的应用---------------------------------
%本次实验图片来自ORL标准人脸数据库,
%数据库中包含40个人脸,每个人脸都有10张不同角度或光线的图片,共40*100=400张图,图片尺寸为112*92=10304个像素点
%选择每个人脸的前7张图片共280张作为训练集,每个人脸的后3张图片共120张作为测试集
%注意:训练图片和测试图片尺寸必须一样,如果不一致,必须先进行处理再测试。
clc ; clear ; close all ;
%---------------------------矩阵向量化-----------------------------
%matlab中,图像是以矩阵形式存储的
%将每个图像112*92 重新排列成 10304*1 的一个列向量,按列组成 10304 *280的矩阵 X ,280 为训练的图像数
X = zeros(10304, 280); %创建一个10304*280的数据全为0的空矩阵
for i = 1 : 40
for j = 1 : 7
image = imread( [ 'ORL_Faces\s' , num2str(i) , '\', num2str(j), '.pgm'] ) ; %读取图像
%注意的是,里面添加了[ ],来保证这是一个整句。num2str(i) 是将i由数字转换成字符形式
%为了节省存储空间,Matlab为图像提供了特殊的数据类型uint8(8位无符号整数),以此方式存储的图像称作8位图像。
%当使用函数imread()时,其会把灰度图像存入一个8位矩阵,当为RGB图像时,就存入8位RGB矩阵中。
%因此,Matlab读入图像的数据是uint8,而Matlab中数值一般采用double型(64位)存储和运算。
%所以要先将图像转为double格式的才能运算,即将uint8(0-255范围类型)准换为double(0-1范围类型)
image = double(image); %double用于将数据转换为双精度浮点数
for n = 1 : 92 %每张图像矩阵都有92列,注意matlab矩阵的下标从1开始
for m = 1 : 112 %每列有112个元素
X( m+(n-1)*112, j+(i-1)*7 ) = image( m, n ); %矩阵重排
end
end
end
end
%----------------------------去中心化------------------------------
%去中化指的是将坐标原点移到所有图像数据的中心位置
%将X的每一列对应元素相加再除以列数 求得均值u,然后用X的每一列减去u,得到中心化后的矩阵Y
u = zeros( 10304, 1); %创建一个全为0的列向量u
for i = 1 : 280 %X一共280列
u = u+X( : , i ) ; %把每列对应元素相加
end
u = u/280; %求均值
Y = zeros(10304, 280);
for j = 1 : 280
Y( : , j ) = X( : , j ) - u; %用X的每一列减去u
end
%--------------------求协方差矩阵的特征值和特征向量------------------
%协方差矩阵C = (Y * Y') / (n-1); 其中Y'表示Y的转置, n为图像数量
%可以先算C再来求其特征值和特征向量,但C的大小为(10304×280)×(280×10304)=10304*10304,维数和计算量太大
%所以引入奇异值分解(SVD)方法,通过求解 Y'*Y(280*280)的特征值和特征向量来求解C的特征值和特征向量
%什么是SVD? 简单来说,
%矩阵M可以分解为U ∑ V'这三个矩阵相乘 (V'表示V的转置),即M=U * ∑ * V'
%其中M*M'的特征向量是U, M'*M的特征向量是V
%而M'*M的特征值=M*M'的特征值=∑ 的对角线上的元素的平方
%协方差矩阵C = (Y * Y') / (n-1); 特征值为Y * Y'的特征值的1/ (n-1),特征向量与Y * Y'的特征向量相同
%故可以先求Y'*Y的特征值和特征向量,再求Y*Y'的特征向量
C1 = Y'*Y;
[ V1, D1 ] = eig( C1 );
%返回C1的特征值的对角矩阵 D1 和特征向量的矩阵 V1
%D1的主对角线元素即为C1特征值,V的列向量即为C1的特征向量
D2 = diag( D1 ); %返回由 D1 的所有主对角线元素组成的列向量
[D1_sort, D1_index] = sort( D2, 'descend');
%将D2中的元素降序排列返回给D1_sort,D1_index为排序后的元素在原矩阵D2中的行号
V1_sort = V1( : , D1_index ); %对特征向量进行排序,使特征向量与排序后的特征值对应
D_sum = 0; %特征值的初始累积值为0
k = 0 ; %累加的特征值的个数
All_sum = sum(D1_sort); %求出所有特征值的和,sum函数为求和
while( D_sum / All_sum < 0.9 ) %设置累积贡献度的阈值为90%
k = k+1;
D_sum = D_sum + D1_sort( k, 1);
end
k = k-1;
%求出协方差矩阵C的前k个特征值对应的特征向量U
U = zeros( 10304, k );
for i = 1 : k
U( : , i ) = (Y * V1_sort( : , i) ) / sqrt( D1_sort(i) ); %sqrt()求平方根
end
%为了方便,我们也可以直接使用以下函数求协方差矩阵C的特征值和特征向量,但精确度可能会不足
%[ V , D ] = eigs( C, k );
%返回矩阵V和对角矩阵D,
%其中D的主对角线元素即为C的特征值,V的列向量即为对应的特征向量
%k表示从大到小返回矩阵 C 的k个模最大的特征值到D中
%----------------------构建特征空间------------------
%使用留下的k个特征值以及其对应的特征向量构建特征空间
W = U' * Y ; %构建特征空间W,W大小为k*280
%----------------------------测试---------------------------
correct_num = 0;
for i = 1 : 40
for j = 8 : 10
image = imread( [ 'ORL_Faces\s' , num2str(i) , '\', num2str(j), '.pgm'] ) ; %读取图像
image1 = double(image);
test_image = zeros(10304,1) ;
for n = 1 : 92
for m = 1 : 112
test_image( m+(n-1)*112, 1 ) = image1( m, n ); %矩阵向量化
end
end
test_W = U' * (test_image - u ); %将待检测图片投影到特征空间
%寻找最接近的人脸
distance = ones( 1, 280 );
%创建一个数组用来存储待检测图片与所有已知图片(280张)的欧式距离
for x = 1 : 280
distance( 1, x ) = norm( test_W - W( : , x ) ); %norm(a-b);计算两个向量之间的欧式距离
end
%[ M, I ] = min(A) 表示返回A中的最小值M以及对应的索引I, ~表示忽略返回的参数
[ ~, min_index ] = min( distance ); %返回与待检测图片距离最小的已知图片的序号
%判断待检测图片和与其距离最小的图片是否在同一文件夹(是否属于同一人脸)
local1 = ceil( min_index / 7 ); %ceil()向上取整
if local1 == i
correct_num = correct_num + 1;
end
end
end
fprintf('\n\n一共测试了120张人脸图片\n');
fprintf('正确识别了%d张人脸图片\n', correct_num );
fprintf('识别精确率为:%0.2f%%\n\n', (correct_num/120)*100);