欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

人脸识别-PCA特征脸

程序员文章站 2022-07-27 21:21:41
人脸识别-PCA特征脸(大BOSS)写在前面你好,我是禅墨!好久,不见!在忙了好久的各科考试之后,我终于闲下来了。自从小凯上次输给我K210之后,一心想搞我,想要报仇。小凯:阿墨,我给你说啊,我XX不服,给你三个小时,你给我做出来一个人脸识别,识别咱两个班一共57人,正确率不能低于96%,我赌100块钱!!!你得在我的监视下完成!禅墨:确定?我怕你后悔!小凯:确定!后悔,不存在的!你搞吧!!!小样!思考了片刻决定用PCA实现!PCA特征脸PCA原理PCA全名为主成分分析,其主要目的就是...

人脸识别-PCA特征脸(大BOSS)

写在前面

你好,我是禅墨!好久,不见!

在忙了好久的各科考试之后,我终于闲下来了。

自从小凯上次输给我K210之后,一心想搞我,想要报仇。

小凯:阿墨,我给你说啊,我XX不服,给你三个小时,你给我做出来一个人脸识别,识别咱两个班一共57人,正确率不能低于96%,我赌100块钱!!!你得在我的监视下完成!

禅墨:确定?我怕你后悔!

小凯:确定!后悔,不存在的!你搞吧!!!小样!

思考了片刻决定用PCA实现!

PCA特征脸

PCA原理

PCA全名为主成分分析,其主要目的就是寻找一个矩阵,然后把原来的一组带有相关性的矩阵映射到寻找到的那个矩阵中,达到降维的目的。一般的,如果我们有M个N维向量,想将其变换为由R个N维向量表示的新空间中,那么首先将R个基按行组成矩阵A,然后将向量按列组成矩阵B,那么两矩阵的乘积AB就是变换结果,其中AB的第m列为A中第m列变换后的结果。 这句话就相当于找到了一个R行N列矩阵,然后乘一个N行M列矩阵,这样就得到了一个R行M列矩阵(其中R<=N),达到降维的目的。其中M和N的含义为,M可以代表样本个数,而N代表每个样本的特征个数,所以最终结果就是把原来N个特征变为了R个特征,达到降维目的。

算法解析

1、构建一个样本集合S={T1,T2,...,TM}S =\{T_1,T_2,...,T_M\},SS 可以看做是一个N行M列的矩阵,也就是有M个样本,每个样本有N个特征。其中TiT_i是一个向量。
2、0均值化,为了便于计算方差的时候需要减去均值,所以如果本身样本是零均值的,就方便计算。

m=1Mi=1MTim = \frac{1}{M}\sum_{i=1}^{M}T_i ,这个是计算均值在python中可以使用

m = T.mean(axis = 1)  

进行计算,其中axis = 1代表按行求均值。
然后A=TmA = T -m 这个相当于把每个样本都减去均值,这样之后就相当于做了0均值化。

3、计算投影矩阵(就是相当于上面的那个R行M列矩阵)
这个投影矩阵其实就是由AATA*A^T矩阵的特征向量构成,但是由于大多数情况AATA*A^T的维度太大(AATA*A^T是N行N列矩阵,如果是一张图片的话N就代表像素点个数,所以是相当大的),所以这个时候就利用数学的小技巧转化为先求ATAA^T*A的特征向量矩阵V,其中V的每一列是一个特征向量,那么V是一个M行M列的矩阵,然后我们再从V中取出前R个最大特征值对应的特征向量,所以V就变成了M行R列矩阵,然后C=AVC = AV,那么这个C矩阵就是计算出的投影矩阵,C为一个N行R列的矩阵。

人脸识别-PCA特征脸

4、把原来样本进行投影

第三步我们得到了一个N行R列的矩阵C,其中每一列是一个特征向量,但是我们在讲PCA原理的时候我们需要一个R行N列的矩阵,每一行是一个特征向量,所以我们可以使用CTC^T,所以我们投影后的样本变为P=CTAP = C^T A 其中P就是一个R行M列的矩阵,可以看出已经达到了降维的目的。

特征脸的实现

特征脸就是我们上面求得的C矩阵,所谓的基于特征脸进行的人脸识别,就是先把人脸映射到一个低纬空间,然后再计算映射后的脸之间的距离,把距离最近的两个特征脸归为同一个人的脸。

所以特征脸的步骤为:

  1. 加载训练集中的脸,转为一个M行N列矩阵T
  2. 对T进行均值化
  3. 找到T的投影矩阵C
  4. 计算投影后的矩阵P
  5. 设置简单的UI界面,进行图片选择与识别
  6. 加载一个测试图片,并利用C矩阵也把其投影为test_P
  7. 计算test_P和P中每个样本的距离,最近的就是结果
  8. 通过结果检索对应的列表,找到并输出人名

Python程序解析

createDatabase函数

创建一个存放所有图片的数据库,具体的步骤看程序注释

def createDatabase(path):
    # 查看路径下所有文件
    TrainFiles = os.listdir(path)
    # 计算有几个文件(图片命名都是以 序号.jpg方式)
    Train_Number = len(TrainFiles) 
    T = []
    # 把所有图片转为1-D并存入T中
    for i in range(0,Train_Number):
        ip = path+'/'+str(i)+'.jpg'
        image = cv.imread(ip,cv.IMREAD_GRAYSCALE)
        image=cv.resize(image,img_size)
        # 转为1-D
        image = image.reshape(image.size,1)
        T.append(image)        
    T = np.array(T)
    # 不能直接T.reshape(T.shape[1],T.shape[0]) 这样会打乱顺序,
    T = T.reshape(T.shape[0],T.shape[1])
    return np.mat(T).T

eigenfaceCore函数

特征脸核心处理函数,对T进行数据处理操作,包括进行均值化,计算特征向量和特征值。

def eigenfaceCore(T):
    # 把均值变为0 axis = 1代表对各行求均值
    m = T.mean(axis = 1)
    A = T-m
    L = (A.T)*(A)

    # 计算AT *A的 特征向量和特征值V是特征值,D是特征向量
    # L = np.cov(A,rowvar = 0)
    V, D = np.linalg.eig(L)
    L_eig = []
    for i in range(A.shape[1]):
            L_eig.append(D[:,i])
    L_eig = np.mat(np.reshape(np.array(L_eig),(-1,len(L_eig))))
    # 计算 A *AT的特征向量
    eigenface = A * L_eig
    return eigenface,m,A  

recognize函数

识别器函数:找到投影矩阵C,计算投影后的矩阵样本P,加载一个测试图片投影为test-p,然后在总样本P中进行比对,找到与test-p距离最近的样本,即为比对结果

def recognize(testImage, eigenface,m,A):
    _,trainNumber = np.shape(eigenface)
    # 投影到特征脸后的
    projectedImage = eigenface.T*(A)
    # 可解决中文路径不能打开问题(相当于英文路径下imread)
    testImageArray = cv.imdecode(np.fromfile(testImage,dtype=np.uint8),cv.IMREAD_GRAYSCALE)
    # 转为1-D
    testImageArray=cv.resize(testImageArray,img_size)
    testImageArray = testImageArray.reshape(testImageArray.size,1)
    testImageArray = np.mat(np.array(testImageArray))
    differenceTestImage = testImageArray - m
    projectedTestImage = eigenface.T*(differenceTestImage)
    distance = []
    for i in range(0, trainNumber):
        q = projectedImage[:,i]
        temp = np.linalg.norm(projectedTestImage - q)
        distance.append(temp)
  
    minDistance = min(distance)
    index = distance.index(minDistance)
    cv.imshow("recognize result",cv.imread('./TrainDatabase'+'/'+str(index+1 )+'.jpg',cv.IMREAD_GRAYSCALE))
    cv.waitKey()
    return index+1

构建可视化界面

制作一个简单的UI界面,通过简单的按钮选择需要识别人脸的图片,点击开始识别,输出结果,检索列表输出人名

def gui():
    root = tk.Tk()
    root.title("pca face")
    #点击选择图片时调用
    def select():
        filename = tkinter.filedialog.askopenfilename()
        if filename != '':
            s=filename
            # jpg图片文件名 和 路径。
            im=Image.open(s)
            tkimg=ImageTk.PhotoImage(im)
            # 执行此函数之前, Tk() 必须已经实例化。
            l.config(image=tkimg)
            btn1.config(command=lambda : example(filename))
            btn1.config(text = "开始识别")
            btn1.pack()
            # 重新绘制
            root.mainloop()
    # 显示图片的位置
    l = tk.Label(root)
    l.pack()
    
    btn = tk.Button(root,text="选择识别的图片",command=select)
    btn.pack()
    
    btn1 = tk.Button(root) # 开始识别按钮,刚开始不显示
    root.mainloop()
if __name__ == "__main__":
    gui()

图片问题

我觉得有必要吐槽一下给的570张图片:jpg , png, jpeg, pmg各种格式各种分辨率应有尽有,真的是搞死我了!(一寸人脸照一般为92*112) 不过还好,只有七八十张,其他的就是正常的jpg格式,怎么办呢,我不可能说一张张进行裁剪,所以 就写个程序,批量裁剪吧。我直接给出核心代码,比较简单,就不废话说明了

for (path, dirnames, filenames) in os.walk(input_dir):
    for filename in filenames:
        if filename.endswith('.jpg'):
            print('正在处理第 %s 张图片' % index)
            img_path = path + '/' + filename
            print(img_path)
            img = cv.imdecode(np.fromfile(img_path,dtype=np.uint8),-1)
            new_img = cv.resize(img, (width, height))
            imwritedir = output_dir + '/' + str(499+index) + '.jpg'
            print(imwritedir)
            cv.imwrite(imwritedir, new_img)
            index += 1
            key = cv.waitKey(30) & 0xff
            if key == 27:
                sys.exit(0)

用过opencv的应该知道,读取中文路径会报错,怎么解决呢,也很简单

cv.imdecode(np.fromfile(img_path,dtype=np.uint8),-1)

用这个代替imread 完美!

做些优化

由于部分照片的问题,背景太花,所以做些调整,就是先把人脸裁剪出来。
这里就是用到上篇博客基于OpenCV的人脸及笑脸检测
就是检测出来,保存人脸尺寸照片

写在后面

计时结束:2:58:56!

哈哈哈,小凯又输了,收钱去!

凭良心说,这一篇博文干货满满。

简单的总结:

1.OpenCV中文路径解决方法

2.批量进行图片的简单裁剪

3.解析PCA算法原理

4.Python程序实现

欢迎关注:禅墨云

公众号:人脸识别-PCA特征脸

本文地址:https://blog.csdn.net/qq_44090770/article/details/106927915