Matlab PCA技术在图像识别中的应用

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

https://www.bilibili.com/video/BV1C7411A7bj/?spm_id_from=333.337.search-card.all.click&vd_source=a2cd032de35146d13f650f340ea9c552

https://www.bilibili.com/video/BV1X54y1R7g7/?spm_id_from=333.337.search-card.all.click&vd_source=a2cd032de35146d13f650f340ea9c552

%%-------------------------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);

猜你喜欢

转载自blog.csdn.net/starryskyzl/article/details/129120343
今日推荐