note
PCA特征脸
参考
- csdn:PCA 降维算法详解 以及代码示例
- csdn:PCA的应用示例
- stackoverflow:eigenfaces are not showing correctly and are very dark
- 博客:Eigenfaces face recognition (MATLAB)
- Wikipedia:Eigenface
- csdn:人脸识别经典算法一:特征脸方法(Eigenface)
- csdn:特征脸(Eigenfaces)
文章目录
- PCA特征脸
- 1. 预处理
- 2. 平均脸
- 3. 规范化
- 4. 特征脸
- (1)svd
- (2)eig
- 5. 重构
- (1)10维
- (2)100维
ATTENTION:
1. 由于作业中并不需要区分训练图片和测试图片,所以以下处理全部都是基于同一组图片的。但这样其实是不严谨的。所以,如果是真正要做PCA的人脸识别,需要分开训练集和测试集,如果测试集掺杂在训练集里面,测试结果将会不公平。 2. 这里只是介绍获取特征脸以及图像恢复,并不涉及后续利用最短欧氏距离的人脸识别。
1. 预处理
从Yale人脸识别数据集上下载了165张图片,每一张是64x64像素。
- 首先,将每一张图片转换成4096x1的列向量,也就是说把每一列的像素加到末尾,使原来的矩阵形成一个大的列向量(行向量也可以,但是下面都是用的列向量当例子)。所以整个图片矩阵是4096x165的。
- 显示图片的时候,可以借助
imshow(x,[])
和reshape
函数,将列向量转换成64x64的矩阵。reshape
函数可以将矩阵的维数进行转换,只要确保转换前后矩阵的包含的数值数目相同即可。
% 显示前九张
for j=1:9subplot(3,3,j);imshow(reshape(X(:,11*(j-1)+1),[64 64]),[]);
end
- 如果直接用
imshow(x)
来操作的话,x矩阵必须是uint8类型,如果是double类型的话,图片将无法显示。如果用imshow(x,[]),就不需要考虑uint8和double类型。由于后续求特征向量的时候,向量的数值都会偏小,所以要到函数imshow(x,[])
,并且[]
里面直接为空,不需要填任何参数,因为该函数具有自动校正功能。如果加了参数,特征脸会变得很暗,几乎看不到。如果将x矩阵强行转换成uint8类型,效果也是特别不好,看到的基本上是一片漆黑。
2. 平均脸
- 求平均脸,是将所有列加起来再除以165,得到一个4096x1的列向量,这个列向量转换成64x64矩阵打印出来就是一张平均脸。
- 由于不能直接对原图片矩阵进行操作,所以需要另外开一个矩阵存储图片。
- 求平均值的时候可以用到
mean
函数,直接使用mean(x)
表示将所有行加起来求平均值。如果使用mean(x,2)
,表示将所有列加起来求平均值。
Y = X;%平均脸
meanface = mean(Y,2);
subplot(2,3,1);
imshow(reshape(meanface(:,1),[64 64]),[]);
3. 规范化
- 为了使数据分布更为均匀,使数据便于后续的计算,所以要处理数据。首先要减去平均脸,然后要进行归一化,也就是除以标准差。
bsxfun
函数是一个功能强大的函数,尤其是对于这种列操作或行操作的运算十分方便。其中@plus
、@minus
、@times
、@rdivide
是表示加减乘除运算。
%减去平均脸
Y = bsxfun(@minus,Y,meanface);%归一化
sig = std(Y);
Y = bsxfun(@rdivide,Y,sig);
4. 特征脸
特征脸就是特征向量转化成图片矩阵显示出来的图像。
- 求特征脸有两种方法,一种是直接使用
svd
函数分解图片矩阵Y,另一种方法是用eig
函数求Y的协方差矩阵的特征向量。 - 因为图片的张数比图片的像素小很多(165 << 4096),所以采用第一种方法,运行速度会快很多。
(1)svd
- 直接取前五个最大的特征向量。
svd
分解矩阵以后,矩阵U就是特征向量矩阵,矩阵S就是特征值矩阵。特征值矩阵S是一个对角矩阵。一般情况下,特征向量矩阵和特征值矩阵都是按列从大到小的降序排列,所以不需要利用fliplr(x)
对矩阵进行处理。
%特征向量
[U,S,V] = svd(Y);%取前5个特征向量
Ureduce = U(:,1:5);% 显示特征脸
for j=1:5subplot(2,3,j+1);imshow(reshape(Ureduce(:,j),64,64),[]);
end
- 其中第一张是平均脸,后面的五张就是特征向量打印出来的特征脸。
- 观察Ureduce矩阵,其实可以发现特征向量里面是有很多负数的。但是这些负数是正常的,而且绝对不可以粗暴地用uint8来转化,不然就会出现一片黑。就像前面说的,放心用
imshow(x,[])
,它会帮忙矫正的。
(2)eig
- 利用
eig
的话,首先得求整个图片矩阵Y的协方差矩阵。 - 利用公式: S = 1 n ∑ i = 1 n x i x i T S=\frac{1}{n}\sum_{i=1}^nx_ix_i^T S=n1i=1∑nxixiT 进行计算。其中n表示一张图片的像素数,即4096。
%协方差矩阵
covmat = 1/4096 * Y * Y';
- 对协方差矩阵求解特征向量,并且提取前五个最大的特征向量。因为
eig
函数求出来的特征向量矩阵V和特征值矩阵D也是按列的从大到小降序排列,所以可以直接选取前五列。
%特征向量
[V,D] = eig(covmat);%取前5个特征向量
Vreduce = V(:,1:5);%显示特征脸
for j=1:5subplot(2,3,j+1);imshow(reshape(Vreduce(:,j),64,64),[]);
end
- 其中第一张是平均脸,后面的五张就是特征向量打印出来的特征脸。
- 观察Vreduce矩阵,其实可以发现特征向量和Ureduce矩阵的刚好是相反数。
5. 重构
将上一步得到的特征向量矩阵用于对图片矩阵Y进行降维操作,然后再对图片矩阵Y升维,并且乘回标准差,加回平均脸。
(1)10维
- 10维是指获取前十个最大特征向量进行降维操作然后再重构。
- 降维操作是利用4096*10的Uk矩阵的转置乘上矩阵Y,得到Yp的维数是5x165。然后再用4096x10的Uk乘上Yp,恢复Yp的维数:4096x165。
%降到10维
dim = 10;
Uk = U(:,1:dim);%列向量 所以Uk要转置
Yp = Uk' * Y;
Yp = Uk * Yp;%还原特征
Yp = bsxfun(@times,Yp,sig);
Yp = bsxfun(@plus,Yp,meanface);for j=1:3subplot(3,2,2*(j-1)+1);imshow(reshape(X(:,j),64,64),[]);subplot(3,2,2*(j-1)+2);imshow(reshape(Yp(:,j),64,64),[]);
end
- 左边是原图,右边就是恢复出来的图片。
- 10维恢复出来的图片,其实大部分轮廓线还是明显地保留了。但是很多细节是丢失的,第二张图张嘴的位置就很明显可以看出来,恢复的图片在张嘴的位置是黑的。
(2)100维
- 道理和10维的一样,只不过将提取的特征向量由前十大变成前一百大。
%降到100维
dim = 100;
Uk = U(:,1:dim);%列向量 所以Uk要转置
Yp = Uk' * Y;
Yp = Uk * Yp;%还原特征
Yp = bsxfun(@times,Yp,sig);
Yp = bsxfun(@plus,Yp,meanface);for j=1:3subplot(3,2,2*(j-1)+1);imshow(reshape(X(:,j),64,64),[]);subplot(3,2,2*(j-1)+2);imshow(reshape(Yp(:,j),64,64),[]);
end
- 左边是原图,右边就是恢复出来的图片。
- 100维的恢复效果已经很好了,不光是轮廓线,连大部分的细节都已经出来了。肉眼上看,和原图并没有相差很大。