Skip to content

Yanshee 人工智能 7 让机器人看脸辨人

UBTEDU edited this page Sep 3, 2018 · 3 revisions

课程目标

本节课将使用经典的PCA 和 LDA 算法识别不同人的脸。通过本节课的学习你会了解PCA和LDA算法的相关性与区别性,了解数学聚类思想,能通过程序进行简单的矩阵变换,能通过通用算法进行人脸识别。

课程引入原因

人脸识别技术,在近几年的发展有目共睹,由于深度神经网络等技术的发展,人们将人脸的识别率提高到了可以使用的高度。而学习人脸识别技术是我们急需解决的重大事件,本节课将通过讲解PCA与LDA模式判别的两个经典算法来讲解基本的人脸识别原理。该算法简单,便于理解,具有很强的通用性。是你学习模式识别的基础课。

基础概念及知识点介绍

att_face

Att_face, 全称为 ORL 人脸识别数据集,内含 40 个人的脸。 其中每一个人脸文件夹包含 10 张不同角度,不同时间,不同光照情况下的人脸照片。每一张照片为 112 * 92 像素。

PCA

主成分分析(Principal Component Analysis,PCA), 是一种统计方法。通过正交变换将一组可能存在相关性的变量转换为一组线性不相关的变量,转换后的这组变量叫主成分。

对于一幅图像来说,一个像素就是一个特征维度,即每一个特征维度都是正交的。例如一个 320 * 240 的图片总共有特征维度 76800 维,那么这个图像就有76800 个正交维度。在这些特征维度中, 有些维度在不同图像中区别不大, 比如说有一个维度所有图像的值,差别不大。 那么这个维度对区分图像用处不大。主成分提取的核心思想是, 提取那些维度, 不同图像在那些维度协方差最大。这样, 不同图像之间在那个维度区分最大, 而且减少考察维度的计算量。 以下步骤显示了主成分提取的步骤。

PCA实现方法

设输入 矩阵为 X={x_1,x_2,…,x_i,…,x_n}其中每一个x_i为N个样本的第i维特征向量

第一步计算矩阵每行均值

第二步求协方差矩阵

第三步 求协方差矩阵的特征值与特征向量

第四步 将特征向量,按照特征大小, 从上到下按行排列成矩阵。要降成多少维,取前 K 行, 成为矩阵 y

i. 将训练集降维获得降维后的矩阵,均值矩阵,特征向量

ii. 将所有测试集降维获得降维后的矩阵,均值矩阵,特征向量

iii. 寻找降维后矩阵最短距离

LDA

线性判别式分析(Linear Discriminant Analysis, LDA),也叫做Fisher线性判别(Fisher Linear Discriminant ,FLD),是模式识别的经典算法,它是在1996年由Belhumeur引入模式识别和人工智能领域的。其核心思想为将高维的模式识别样本投影到最佳的识别空间,以抽取分类特征的效果。投影后,保证模式样本在新的子空间有最大的类间距离和最小类内距离。即理解为相同分类的样本数据之间无区分性,不同分类样本之间区分性尽可能大。

上面的图像显示了LDA核心思想。其中,原始的红点与蓝点(第一象限的散点)为存在于二维空间的两类不同数据。我们如果不标识出颜色, 这些点就很难加以区分。但是,如果我们将这些点投影到一维直线上(y = -x),所有的红点集中在一起,所有的蓝点集中在一起,并且红点集与蓝点集分界明显。这样,我们很容易就区分出红点与蓝点。下面三节将带领你推导LDA。

LDA实现方法

假设我们的样本数据集

其中x_i为每一个样本的n维向量, y_i为对应类标签. 定义 N_j,(j=0,1,…,k)为第j类样本的个数,X_j,(j=0,1,…,k)为第j类样本的集合。由于是多类问题我们投影到低维空间就不是直线而是超平面,假设我们要投影到k维则对基向量集合

定义第j类样本的均值向量

定义第j 类样本的协方差矩阵

i. 计算类内散度矩阵Sw

ii. 计算类间散度矩阵Sb

iii. 〖S^(-1)〗_w S_b

iv. 计算〖S^(-1)〗_w S_b的最大的d个特征值和对应的d个特征向量,得到投影矩阵

v. 对样本集中的每一个样本特征xi,转化为新的样本(例,根据实际行列情况变换W)

得到新输出样本集

PCA vs LDA

相同点

i. 数据降维

ii. 数据特征分解

iii. 两者数据都符合高斯分布

不同点

i. PCA 无监督学习, LDA 监督学习

ii. LDA最多降到 K-1维, 而PCA无限制

iii. LDA除了用于降维,还可以用于分类

iv. LDA选择最大投影降维, 而 PCA 选择图像最大协方差降维

环境准备

解压att_face

下载以下压缩文件,并将解压后的文件放入树莓派系统中

下载地址: https://github.com/UBTEDU/YanShee-Curriculum/blob/master/img/project17/att_faces.zip

numpy 配置

见课程机器人看图识数如何配置numpy 第三方模块。

程序流程

PCA 流程

LDA 流程

程序指引

在存储本脚本的路径下输入

python project3.py eigenface 30 9 /home/pi/project/new_att_faces/att_faces/ 

.py 后的第一个参数为使用的算法名

.py 后的第二个参数为下降道德维数

.py 后的第三个参数为每个人的训练样本数

.py 后的第四个参数为att_faces的绝对文件路径

#encoding=utf-8
############################################################
#################Author: Xuyang Jie#########################

#################Date: 26/03/2018###########################
############################################################

import cv2
import os
import time
import sys
import numpy as np


class picture(object):
	def __init__(self,path,name):
		self.name = name
		self.path = path
		self.train_vec,self.test_vec, self.train_name, self.test_name, self.train_gray, self.test_gray = self.load_image() 

	# image to one raw vector
	def mat2vector(self,mat):
		rows,cols = mat.shape
		img_vec = np.zeros((1, rows* cols))
		img_vec = np.reshape(mat,(1, rows * cols))
		return img_vec

	# pre-process
	def pre_process(self,path):
		image = cv2.imread(path)
		grayimage = cv2.cvtColor(image,cv2.COLOR_RGB2GRAY)
		return grayimage

	# load images
	def load_image(self):
		integer = int(sys.argv[3])
		file_index = 0
		train_gray_image_list = []
		test_gray_image_list = []
		whole = np.random.permutation(range(10))

		if self.name == 'fisherface':
			height = 20
			width = 20
		else:
			height = 112
			width = 92

		train_vec = np.zeros((40 * integer, height * width))
		test_vec = np.zeros((40 * (10 - integer), height * width))
		train_face_name = []
		test_face_name = []
		for parent, dirnames, filenames in os.walk(self.path):
			for dirname in dirnames:
				train_list = np.random.randint(10, size = 1)
				counter = 0
				for index in range(10):
					gray_img = self.pre_process(self.path + dirname + '/' + str(whole[index]) + '.pgm')
					if self.name == 'fisherface':
						gray_img = cv2.resize(gray_img,(height,width))
					vec = self.mat2vector(gray_img)
					#time.sleep(1)
					if index < integer:
						train_vec[file_index * integer + index,:] = vec
						train_face_name.append(dirname)
						train_gray_image_list.append(gray_img)
					else:
						test_vec[file_index * (10 - integer) + (index - integer)] = vec
						test_face_name.append(dirname)
						test_gray_image_list.append(gray_img)
				file_index += 1

		return train_vec,test_vec, train_face_name, test_face_name, train_gray_image_list,test_gray_image_list 
	# PCA
	def eigenface(self,k):
		data = np.float32(np.mat(self.train_vec))
		height,width = self.train_vec.shape
		data_mean = np.mean(self.train_vec,axis = 0)
		data_mean_all = np.tile(data_mean,(height,1))
		data_diff = self.train_vec - data_mean_all
		S = data_diff.dot(data_diff.T)
		eig_val, eig_vec = np.linalg.eig(S)
		sortindics = np.argsort(eig_val)
		i = len(eig_vec) - 1
		new_eig_vec = np.zeros((eig_vec.shape))
		for index in sortindics:
			new_eig_vec[i] = eig_vec[index]
			i -= 1
		new_eig_vec = new_eig_vec[:,0:k]
		new_eig_vec = data_diff.T.dot(new_eig_vec)
		#for i in range(k):
			#L = np.linalg.norm(new_eig_vec[:,i])
			#new_eig_vec[:,i] /= L
		return new_eig_vec, data_mean

	#	LDA
	def fisherface(self,k):
		rows,cols = self.train_vec.shape
		Sw = np.zeros((cols,cols))
		Sb = np.zeros((cols,cols))
		total_train = rows
		each_p = int(sys.argv[3])
		index = 0

		if each_p == 1:
			return self.train_vec

		# Calculate Sw
		while(index < total_train):
			mean_class = np.mean(self.train_vec[index:index + each_p], axis = 0).reshape((cols ,1))
			sw = np.zeros((cols,cols))
			for each_sample in range(index,index + each_p):
				diff = self.train_vec[each_sample].reshape((cols,1))
				sw += diff.dot(diff.T)
			Sw += sw
			index += each_p
		index = 0

		# All_mean
		mean_all = np.mean(self.train_vec,axis = 0).reshape((cols,1))

		# Caluculate Sb
		while(index < total_train):
			mean_class = np.mean(self.train_vec[index:index + each_p],axis = 0)
			diff = mean_class - mean_all
			Sb += ((each_p) * diff.dot(diff.T))
			index += each_p

		# Caluculate eig_Val and eig_vec
		eig_val,eig_vec = np.linalg.eig(np.linalg.inv(Sw).dot(Sb))
		sortIndices = np.argsort(eig_val)
		i = len(eig_vec) - 1
		new_eig_vec = np.zeros((eig_vec.shape))
		for index in sortIndices:
			new_eig_vec[i] = eig_vec[index]
			i -= 1
		new_eig_vec = new_eig_vec[0:k,:]
		W = new_eig_vec.T
		return W

	def show(self,test_index,train_index,predict):
		res = cv2.resize(self.test_gray[test_index],(320,240),interpolation = cv2.INTER_CUBIC)
		cv2.putText(res,'Predict:' + self.train_name[predict] + ' Actual:' + self.test_name[test_index],(30,40),cv2.FONT_HERSHEY_SIMPLEX,0.7,(255,255,255),1)
		cv2.imshow('Test Face',res)
		#res2 = cv2.resize(data_test_new[test_index],(320,240),interpolation = cv2.INTER_CUBIC)
		#cv2.imshow('PCA down to ' + str(down_to_dim) + '(Test)',res2)
		res3 = cv2.resize(self.train_gray[predict],(320,240),interpolation = cv2.INTER_CUBIC)
		cv2.putText(res3,self.train_name[predict],(50,80),cv2.FONT_HERSHEY_SIMPLEX,3,(255,255,255),3)
		cv2.imshow('Train Face',res3)
		#res4 = cv2.resize(train_d[distindexlist[0]].T,(320,240),interpolation = cv2.INTER_CUBIC)
		#cv2.imshow('PCA down to '+ str(down_to_dim) + '(Train)',res4)
		cv2.waitKey(0)

	def predict(self):
		true_num = 0
		num_train = self.train_vec.shape[0]
		num_test = self.test_vec.shape[0]

		if self.name == 'eigenface':
			new_eig_vec, data_mean = self.eigenface(int(sys.argv[2]))
			diff = self.train_vec - np.tile(data_mean,(num_train,1))
			train_data = diff.dot(new_eig_vec)
			diff = self.test_vec - np.tile(data_mean,(num_test,1))
			test_data = diff.dot(new_eig_vec)
			for i in range(num_test):
				testface = test_data[i,:]
				diffMat = train_data - np.tile(testface,(num_train,1))
				sqDiffMat = diffMat ** 2
				sqDistance = sqDiffMat.sum(axis = 1)
				sortDistIndicies = sqDistance.argsort()
				indexMin = sortDistIndicies[0]
				if self.train_name[indexMin] == self.test_name[i]:
					true_num += 1
				self.show(test_index = i,train_index = indexMin,predict = indexMin)
		else:
			if int(sys.argv[3]) == 1:
				train_data = self.train_vec
				test_data = self.test_vec
			else:
				W = self.fisherface(int(sys.argv[2]))
				train_data = self.train_vec.dot(W)
				test_data = self.test_vec.dot(W)
			for test_index in range(test_data.shape[0]):
				distlist = []
				for train_index in range(train_data.shape[0]):
					dist = np.linalg.norm((test_data[test_index] - train_data[train_index]))
					distlist.append(dist)
				distindexlist = np.argsort(distlist)
				#print distindexlist
				if(self.train_name[distindexlist[0]] == self.test_name[test_index]):
					true_num += 1
				self.show(test_index = test_index,train_index = train_index,predict = distindexlist[0])
		accuracy = float(true_num)/num_test
		print 'The classify accuracy is %.2f%%' % (accuracy * 100) 




if __name__=='__main__':
	face = picture(str(sys.argv[4]),str(sys.argv[1]))
	print 'Predicting'
	start = time.time()
	face.predict()
	print 'Finish predicting\tUse time:%.2f' % (time.time() - start)

扩展实验

(1) 学习LBP算法,并了解LBP算法相对于纯PCA或是LDA算法人脸识别的优越性。

可以参考opencv 官网

(2) 学习了解PCA 与 LDA 推导过程

可以参考 模式识别第二版 第四章 线性判别函数