EigenFace的使用 python

Posted 编号1993

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EigenFace的使用 python相关的知识,希望对你有一定的参考价值。

参考:

人脸识别之特征脸方法(Eigenface):http://blog.csdn.net/zouxy09/article/details/45276053

Eigenface算法,PCA数学理论,协方差:http://blog.csdn.net/gdut2015go/article/details/46271523

人脸识别算法-特征脸方法(Eigenface)及python实现:http://blog.csdn.net/u010006643/article/details/46417127

人脸识别经典算法之一:特征脸方法(Eigenface):http://blog.csdn.net/feirose/article/details/39552887

PCA数学原理:http://blog.csdn.net/u012005313/article/details/50877366


#######################################################################


关于PCA数学方面,可以看上面的PCA数学原理


EigenFace实现步骤:

1.获取M张图片集合。本实验中,使用4个人共20张图片,每张图片大小为256x256。将获取的20张图片按列排序,其中每张图片数据均拉平至一列(即256x256图像拉平为65536x1矩阵)。此时得到图像矩阵

2.计算平均图像。图像矩阵T中每行表示一个字段,累加各个字段并平均得到平均图像M,此时M的大小为65536x1,可以通过操作转换为256x256格式,就是平均图像:


3.计算差值矩阵。将图像矩阵T每一列减去平均图像M就是差值矩阵

4.求解协方差矩阵。计算差值矩阵D的协方差矩阵C,求得特征向量和特征值。标准的PCA计算公式是:,不过此时我们的差值矩阵维数是65536x20,所以求得协方差矩阵维数达到65536x65536,计算量过于大,不易于处理,并且存储也很困难。根据上面的博文,有一种简化的方法:

   其中V表示特征向量,在等式两边乘以差值矩阵D,求得

 此时,U是的特征向量,而根据上式可得

经过上述变换,我们只需求得的特征值和特征向量U,然后用差值矩阵D乘以特征向量U就是协方差矩阵C的特征向量V。

而计算结果矩阵维数为20x20,大大减少了计算量。最后得到的特征向量矩阵V维数为65536x20,其中每一列代表一个特征脸,同样可以转换为图像


5.计算得到的特征向量矩阵并不是每一个有用(这里没有太弄懂)。因为一般训练图像的数量小于图像的维数,比如M<N^2,那么起作用的特征向量只有M-1个而不是N^2个(因为其他的特征向量对应的特征值为0)。所以我仅使用最大几个特征值对应的特征向量


6.计算权重。假定我使用3个特征向量构成基向量,即可求得训练图像在新的子空间的表示,称之为权重。计算公式为。W为权重向量矩阵,表示训练图像在新子空间的表示,结果维数是20x3;D表示差值图像矩阵,维数为65536x20;B表示基向量矩阵,维数为65536x3


7.此时可以进行图像识别。读取一张新的人脸图像,将图像拉平成一列得到矩阵,使用上面得到的基向量矩阵B计算得到权重


8.利用欧式距离方法用权重计算。当距离小于阈值时说明要判别的脸和训练集内的第k个脸是同一个人。当遍历所有训练集都大于阈值时,根据距离值的大小又可分为是新的人脸或者不是人脸的两种情况。


######################################################


我写的一个算法(python):

#!/usr/bin/env python
#-*- coding: utf-8 -*-

'''
eigenface的python实现
'''

__author__='zj'

import cv2
import os
import sys
import numpy as np
from numpy import *

suffix_list=['.jpg', '.png', '.jpeg', '.JPG', '.PNG', '.JPEG']

'''
提取所有图片以及对应的标签 src_dir格式如下:
.
├── PEOPLE_GRAY_1
│   ├── 10.jpg
│   ├── 12.jpg
│   ├── 3.jpg
│   ├── 5.jpg
│   └── 6.jpg
├── PEOPLE_GRAY_2
│   ├── 12.jpg
│   ├── 14.jpg
│   ├── 21.jpg
│   ├── 6.jpg
│   └── 9.jpg
├── PEOPLE_GRAY_3
│   ├── 38.jpg
│   ├── 46.jpg
│   ├── 49.jpg
│   ├── 54.jpg
│   └── 57.jpg
└── PEOPLE_GRAY_4
    ├── 14.jpg
    ├── 18.jpg
    ├── 20.jpg
    ├── 7.jpg
    └── 9.jpg
'''
def getImgAndLabel(src_dir):
	#初始化返回结果
	imgs=[] #存放图像
	labels=[] #存放类别
	#获取子文件夹名
	catelist=os.listdir(src_dir)
	#遍历子文件夹
	for catename in catelist:
		#设置子文件夹路径
		cate_dir=os.path.join(src_dir, catename)
		#获取子文件名
		filelist=os.listdir(cate_dir)
		#遍历所有文件名
		for filename in filelist:
			#设置文件路径
			file_dir=os.path.join(cate_dir, filename)
			#判断文件名是否为图片格式
			if not os.path.splitext(filename)[1] in suffix_list:
				print file_dir,"is not an image"
				continue
			#endif
			#读取灰度图
			imgs.append(cv2.imread(file_dir, cv2.IMREAD_GRAYSCALE))
			#读取相应类别
			labels.append(catename)
		#endfor
	#endfor
	return imgs,labels	
#end of getImgAndLabel

#将图像数据变为一列
def convertImageToArray(img):
	img_arr=[]	
	height,width=img.shape[:2]
	#遍历图像
	for i in range(height):
		img_arr.extend(img[i,:])
	#endfor
	return img_arr
#end of convertImageToArray

#将每个图像变为一列
def convertImageToArrays(imgs):
	#初始化数组	
	arr=[]
	#遍历每个图像
	for img in imgs:
		arr.append(convertImageToArray(img))
	#endfor
	return array(arr).T
#end of convertImageToArrays

#计算均值数组
def compute_mean_array(arr):
	#获取维数(行数),图像数(列数)
	dimens,nums=arr.shape[:2]
	#新建列表
	mean_arr=[]
	#遍历维数
	for i in range(dimens):
		aver=0
		#求和每个图像在该字段的值并平均
		aver=int(sum(arr[i,:])/float(nums))
		mean_arr.append(aver)
	#endfor
	return array(mean_arr)
#end of compute_mean_array

#将数组转换为对应图像
def convert_array_to_image(arr, height=256, width=256):
	img=[]
	for i in range(height):
		img.append(arr[i*width:i*width+width])
	#endfor
	return array(img)
#end of convert_array_to_image

#计算图像和平均图像之间的差值
def compute_diff(arr, mean_arr):
	return arr-mean_arr
#end of compute_diff

#计算每张图像和平均图像之间的差值
def compute_diffs(arr, mean_arr):
	diffs=[]	
	dimens,nums=arr.shape[:2]
	for i in range(nums):
		diffs.append(compute_diff(arr[:,i], mean_arr))
	#endfor
	return array(diffs).T
#end of compute_diffs

#计算协方差矩阵的特征值和特征向量,按从大到小顺序排列
#arr是预处理图像的矩阵,每一列对应一个减去均值图像之后的图像
def compute_eigenValues_eigenVectors(arr):
	arr=array(arr)
	#计算arr'T * arr
	temp=dot(arr.T, arr)
	eigenValues,eigenVectors=linalg.eig(temp)
	#将数值从大到小排序
	idx=np.argsort(-eigenValues)
	eigenValues=eigenValues[idx]
	#特征向量按列排
	eigenVectors=eigenVectors[:,idx]
	return eigenValues,dot(arr,eigenVectors)
#end of compute_eigenValues_eigenVectors

#计算图像在基变换后的坐标(权重)
def compute_weight(img, vec):
	return dot(img, vec)
#end of compute_weight

#计算图像权重
def compute_weights(imgs, vec):
	dimens,nums=imgs.shape[:2]
	weights=[]	
	for i in range(nums):
		weights.append(compute_weight(imgs[:, i], vec))
	return array(weights)
#end of compute_weights

#计算两个权重之间的欧式距离
def compute_euclidean_distance(wei1, wei2):
	#判断两个向量的长度是否相等
	if not len(wei1) == len(wei2):
		print '长度不相等'
		os._exit(1)
	#endif
	sqDiffVector=wei1-wei2
	sqDiffVector=sqDiffVector**2
	sqDistances=sqDiffVector.sum()
	distance=sqDistances**0.5	
	return distance
#end of compute_euclidean_distance

#计算待测图像与图像库中各图像权重的欧式距离
def compute_euclidean_distances(wei, wei_test):
	weightValues=[]
	nums=wei.shape
	print nums
	for i in range(nums[0]):
		weightValues.append(compute_euclidean_distance(wei[i], wei_test))
	#endfor
	return array(weightValues)
#end of compute_euclidean_distances

if __name__ == '__main__':
	#获取图片库路径
	src_dir=os.path.join(os.getcwd(), "gray")
	#获取图片以及对应类别
	imgs,labels=getImgAndLabel(src_dir)
	'''
	for i in range(len(labels)):
		print labels[i]
	'''
	#将图片转换为数组
	arr=convertImageToArrays(imgs)
	print "arr's shape : ".format(arr.shape)
	#计算均值图像
	mean_arr=compute_mean_array(arr)
	print mean_arr
	print "mean_arr's shape: ".format(mean_arr.shape)
	#mean_img=convert_array_to_image(mean_arr)
	#print mean_img
	#print "type(mean_img) : ".format(type(mean_img))
	#cv2.imwrite("mean.png", mean_img)
	#获取差值图像
	arr_diff=compute_diffs(arr, mean_arr)
	#计算特征值以及特征向量
	eigenValues,eigenVectors=compute_eigenValues_eigenVectors(arr)
	print "eigenValues'shape : ".format(eigenValues.shape)
	print "eigenVectors'shape : ".format(eigenVectors.shape)

	#print eigenValues
	#计算权重向量,此处假定使用特征值最大的前3个对应的特征向量作为基
	weights=compute_weights(arr_diff, eigenVectors[:,:3])
	print "weights.shape : ".format(weights.shape)
	#print weights
	#读取测试图像,此处使用训练库中的一张
	img_test=cv2.imread("gray/PEOPLE_GRAY_1/10.jpg", cv2.IMREAD_GRAYSCALE)
	arr_test=convertImageToArray(img_test)
	diff_test=compute_diff(arr_test, mean_arr)
	wei=compute_weight(diff_test, eigenVectors[:,:3])
	print "test's weight : ".format(wei)		
	#计算欧式距离
	weightValues=compute_euclidean_distances(weights, wei)		
	print "weightValues.shape : ".format(weightValues.shape)
	#打印结果
	for i in range(len(weightValues)):
		print weightValues[i], labels[i]

	'''
	for i in range(len(eigenValues)):
		img=convert_array_to_image(eigenVectors[:,i])
		cv2.imwrite(str(i)+".jpg" ,img)
		#break
		#print eigenValues[i]
	#endfor
	cv2.waitKey()
	print "endl..."'''
#endif



结果由图可知,由于使用的是训练集中的一张图片,所以得到的结果存在欧式距离刚好为0的情况


######################################################################################################


其中的一篇博文里记载的python代码,修改了一些,没办法运行,不过其中一些python函数值得学习以下:

#coding:utf-8  
from numpy import *  
from numpy import linalg as la  
import cv2  
import os  
  
def loadImageSet(add):
	FaceMat=mat(zeros(20, 256*256))
	j=0	
	#获取子文件夹名
	catelist=os.listdir(src_dir)
	#遍历子文件夹
	for catename in catelist:
		#设置子文件夹路径
		cate_dir=os.path.join(src_dir, catename)
		#获取子文件名
		filelist=os.listdir(cate_dir)
		#遍历所有文件名
		for filename in filelist:
			#设置文件路径
			file_dir=os.path.join(cate_dir, filename)
			#判断文件名是否为图片格式
			if not os.path.splitext(filename)[1] in suffix_list:
				print file_dir,"is not an image"
				continue
			#endif
			#读取灰度图
			img=cv2.imread(file_dir, cv2.IMREAD_GRAYSCALE)
			FaceMat[j,:]=mat(img).flatten()
			j+=1	
			#读取相应类别
			labels.append(catename)
		#endfor
	#endfor
	return FaceMat
'''
    FaceMat = mat(zeros((15,98*116)))  
    j =0  
    for i in os.listdir(add):  
        if i.split('.')[1] == 'normal':  
            try:  
                img = cv2.imread(add+i,0)  
            except:  
                print 'load %s failed'%i  
            FaceMat[j,:] = mat(img).flatten()  
            j += 1  
    return FaceMat  
'''
  
def ReconginitionVector(selecthr = 0.8):  
    # step1: load the face image data ,get the matrix consists of all image  
    FaceMat = loadImageSet(os.path.join(os.getcwd(), "gray")).T  
    # step2: average the FaceMat  
    avgImg = mean(FaceMat,1)  
    # step3: calculate the difference of avgimg and all image data(FaceMat)  
    diffTrain = FaceMat-avgImg  
    #step4: calculate eigenvector of covariance matrix (because covariance matrix will cause memory error)  
    eigvals,eigVects = linalg.eig(mat(diffTrain.T*diffTrain))
	#从大到小排序  
    eigSortIndex = argsort(-eigvals)
	#此处利用阈值设置新空间的基向量个数,特征值按从大到小排列,从头相加,其值刚刚大于阈值则其对应特征向量为基向量   
	for i in xrange(shape(FaceMat)[1]):  
        if (eigvals[eigSortIndex[:i]]/eigvals.sum()).sum() >= selecthr:  
            eigSortIndex = eigSortIndex[:i]  
            break  
    covVects = diffTrain * eigVects[:,eigSortIndex] # covVects is the eigenvector of covariance matrix  
    # avgImg 是均值图像,covVects是协方差矩阵的特征向量,diffTrain是偏差矩阵  
    return avgImg,covVects,diffTrain  

#judgeImg - 待处理图像
#FaceVector - 特征向量矩阵
#avgImg - 平均图像矩阵
#diffTrain - 差值图像矩阵
def judgeFace(judgeImg,FaceVector,avgImg,diffTrain):
	#待处理图像减去平均图像
    diff = judgeImg.T - avgImg
	#计算权重向量  
    weiVec = FaceVector.T* diff  
    res = 0  
    resVal = inf
    for i in range(15):  
        TrainVec = FaceVector.T*diffTrain[:,i]  
        if  (array(weiVec-TrainVec)**2).sum() < resVal:  
            res =  i  
            resVal = (array(weiVec-TrainVec)**2).sum()  
    return res+1  
  
if __name__ == '__main__':  
    avgImg,FaceVector,diffTrain = ReconginitionVector(selecthr = 0.9)  
    nameList = ['01','02','03','04','05','06','07','08','09','10','11','12','13','14','15']  
    characteristic = ['centerlight','glasses','happy','leftlight','noglasses','rightlight','sad','sleepy','surprised','wink']  
  
    for c in characteristic:  
  
        count = 0  
        for i in range(len(nameList)):  
  
            # 这里的loadname就是我们要识别的未知人脸图,我们通过15张未知人脸找出的对应训练人脸进行对比来求出正确率  
            loadname = 'gray/PEOPLE_GRAY_1/10.jpg'#+nameList[i]+'.'+c+'.pgm'  
            judgeImg = cv2.imread(loadname,0)  
            if judgeFace(mat(judgeImg).flatten(),FaceVector,avgImg,diffTrain) == int(nameList[i]):  
                count += 1  
        print 'accuracy of %s is %f'%(c, float(count)/len(nameList))  # 求出正确率  


mean函数:



功能:沿着指定的轴计算算术平均值。默认求和数组中每个元素并按元素数求和。可以指定某个轴进行计算,axis=0表示沿着x轴计算,axis=1表示沿着y轴计算。在计算过程中使用数据类型"float64"进行计算,不过返回时会自动转换为integer格式

参数 a:数组或矩阵,不为空

参数axis:int类型,默认为空,表示整个数组进行计算,axis=0时求和每列并求平均值;axis=1时求和每行并求平均值



flatten



功能:将数组折叠成一个维度

参数order:可选,默认为'c',表示按行排列;如果设为'F',表示按列排列



numpy.linalg.eig:



功能:计算正方形数组或矩阵的特征值以及对应的特征向量


返回的特征值和特征向量都是数组类型,并且特征向量是按行排列的

以上是关于EigenFace的使用 python的主要内容,如果未能解决你的问题,请参考以下文章

EigenFace 实现:在 Java 中使用 OpenCV3

eigenface 怎样进行人脸识别

eigenface资料整合

Eigenface与PCA人脸识别算法实验

人工智能机器学习之运用特征脸(eigenface)和sklearn.svm.SVC进行人脸识别

以大于 Python 列表中的值的最小差值对大多数数字进行采样的最快方法