Labview2019+python3.6实现人脸识别(二)

上一篇主要讲了Labview可视化界面的搭建,图像实时采集、图像保存。链接:https://blog.csdn.net/weilixin88/article/details/103377779

本文主要说一下Labview调用的两个python脚本。其中我被Labview的一维图像数据如何转换为三维numpy数据卡了一天,只能说自己太水。

首先说被“特征训练”事件调用的python脚本——Feature.py

Feature.py脚本共导入了四个模块——dlib、cv2、os、numpy

dlib是比opencv自带的人脸检测更精准的人脸检测库,它能检测人脸的68个特征点,甚至更多。(当然,dlib不单单只能用于人脸检测,还有很多功能。)(dlib库在windows系统的安装,请百度)

一、Feature.py设计思路

因为本来算法就是拿别人的,也不会优化,但又不想让运行时间太长;我就想不能让已经训练过的图像再去训练了。

在比较两个列表时,可以将两个list转换为两个set,因为集合很适合计算交集。

例如:

集合A和集合B,它们的交集b=A&B;所以a=A-b;c=B-b

二、Feature.py的完整代码

# -*-coding:utf-8-*-

import dlib
import cv2
import os
import numpy as np


#获得当前项目的根目录——也就是当前脚本的目录的上一级目录
object_path = os.path.dirname(os.getcwd())

#导入正脸探测器(实例化)
detector = dlib.get_frontal_face_detector()
#导入人脸关键点识别器
predictor = dlib.shape_predictor(object_path + '/.py/model/shape_predictor_68_face_landmarks.dat')
#导入人脸识别模型
model = dlib.face_recognition_model_v1(object_path + '/.py/model/dlib_face_recognition_resnet_model_v1.dat')



#读取图像文件夹中的图片,获得图片名字列表——因为图片的名字是唯一的
def read_img(dir_img_path):
    #文件夹中文件名列表
    name_list = []
    #文件夹中文件路径列表
    path_list = []
    for file_name in os.listdir(dir_img_path):
        file_d = dir_img_path + '/' + file_name
        #判断是不是文件夹
        if os.path.isdir(file_d):
            read_img(file_d)
        else:
            if file_d[-4:] == '.png':
                path_list.append(file_d)
                #查找最后一个'/'
                index = file_d.rfind('/')
                name_list.append(file_d[index+1:-4])
    return name_list,path_list
#读取已训练过的图片的名字——也是唯一的
def read_txt(dir_txt_path):
    name = []
    with open(dir_txt_path,'r') as txt:
        txt_data = txt.readlines()

        for row in txt_data:
            if row[-1] == ',':
                row = row[:-1]
            row_list = row.split(',')
            name += row_list
    return name

#获取新照片的特征函数
def Feature(dir_txt_path,dir_img_path,mode_path):
    #定义一个Flag变量
    Flag = False
    # 读取TXT文件中保存的以训练过的图片的名字,并获得名字列表
    txt_name = read_txt(dir_txt_path)
    # 读取img文件夹下的图片文件的名字,并获得名字列表
    img_name,img_path = read_img(dir_img_path)
    #将列表转换为集合
    txt_name_set = set(txt_name)
    img_name_set = set(img_name)
    #两个集合的公共部分
    set_txt_img = txt_name_set & img_name_set
    #img_name_set比txt_name_set多出的数据——也就是新添加的图片
    img_more_txt_set = img_name_set - set_txt_img
    #set转list
    img_more_txt_list = list(img_more_txt_set)

    if len(img_more_txt_list)>0:
        #循环新添加的图片名列表,找到对应路径,并生成其对应的特征向量
        #新添加图片的特征向量列表
        new_face_features = []
        for name in img_more_txt_list:

            #在所有img_name中查找name的索引
            name_index = img_name.index(name)
            #依据索引找到图片名对应的路径
            name_path = img_path[name_index]
            ##############提取人脸特征###################
            img = cv2.imread(name_path)
            gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

            # 使用探测器识别图像中的人脸,形成一个人脸列表
            face_dets = detector(gray_img, 1)
            # 索引每一个人脸区域
            for i, det in enumerate(face_dets):
                # 获取每个人脸的68个特征点
                face_predictor = predictor(img, det)
                # 获取每个人脸特征向量
                face_feature = model.compute_face_descriptor(img, face_predictor)
                # #将数据类型转换为Numpy类型
                # value = np.array(face_feature)
                new_face_features.append(face_feature)

            #############################################
        # 读取已有模型,就是为了保存新的模型
        try:
            npz_datas = np.load(mode_path)
        except:
            np.savez(mode_path, names=img_more_txt_list, face_features=new_face_features)
        else:
            npz_name = npz_datas['names']
            npz_feature = npz_datas['face_features']
            new_npz_name = np.hstack((npz_name,img_more_txt_list))
            new_npz_feature = np.vstack((npz_feature,new_face_features))
            np.savez(mode_path, names=new_npz_name, face_features=new_npz_feature)

        #新添加图片已训练后,将新图片的名字添加到txt文件中
        with open(dir_txt_path, 'a') as f:
            for i in img_more_txt_list:
                    f.write(i + ',')

        Flag = True
    return Flag



if __name__ == '__main__':
    dir_path = object_path + '/img'
    print(dir_path)
    mode_path = object_path + '/.py/model/face_model.npz'
    a = Feature('img_name_data.txt', dir_path,mode_path)
    print(a)

我保存特征向量的方式是使用numpy.savez(),保存为npz的二进制文件,但是没有找到在不覆盖原有数据的情况下,追加数据。所以,我的办法是先把npz中的数据先读到list中,然后将新数据append到list,最后重新保存到npz中。另外我尝试使用pandas保存数据,使用网上说的方法,但没有成功。

三、Face_recognition.py设计思路

因为Labview发给该脚本的是图像的一维数据,数据类型为uint8。但特征提取时需要的是三维的图像数据,所以先的把一维数据转为三维数据,因为我设置图像大小是高240,宽320的RGBA,所以转换代码如下:

#一维转三维
def Transfor(a):
    flatNumpyArray = np.array(a)
    #重排数组为240*320行,4列的二维数组,另外只要前三列
    RGBimage = flatNumpyArray.reshape((240 * 320, 4))[:, :3]
    #c数组的大小必须和图像大小一致
    c = np.zeros(shape=(240,320,3),dtype=np.uint8)
    j = 0
    for i in range(240*320):
        if i%320 == 0:
            c[j] = RGBimage[i:i+320]
            j += 1
    return c

然后就是将新图像提取人脸特征向量,接着将这个特征向量和已有特征向量分别计算欧式距离,然后将欧式距离对应的图像名和欧式距离组合成字典排序,找到最小欧式距离,也就找到了名字。最后设定一个相识度阈值,就能确定人脸是谁了。

检测代码如下:

class Test_face:
    def init(self):
        self.dist = []      #测试图像与已有图像特征的欧式距离列表
        self.face_rect = None
        self.name = None
    def test_face(self,photo,face_data):
        test_gray_img = cv2.cvtColor(photo, cv2.COLOR_BGR2GRAY)
        #使用探测器识别每张图像中的人脸,形成一个人脸列表
        self.face_rect = detector(test_gray_img, 1)
        #索引每一个人脸区域
        if len(self.face_rect) != 0:

            for i, det in enumerate(self.face_rect):
                #获取每个人脸的68个特征点
                test_face_predictor = predictor(photo, det)
                #获取每个人脸特征向量
                test_face_feature = model.compute_face_descriptor(photo, test_face_predictor)
                #将数据类型转换为Numpy类型
                test_value = np.array(test_face_feature)
                #将测试图像的人脸特征向量和已知人脸特征向量求范数(范数还是没明白)
                for i in face_data['face_features']:
                    dist_ = np.linalg.norm(i - test_value)
                    self.dist.append(dist_)
                #将名字和计算的欧式距离组合为字典
                names_dist = dict(zip(face_data['names'], self.dist))
                names_dist_sorted = sorted(names_dist.items(), key=lambda x: x[1])
                # #debug
                # print(names_dist_sorted)
                #规定相识度不得小于0.4
                if names_dist_sorted[0][1] > 0.4:
                    self.name = 'Unkonw'
                else:
                    self.name = names_dist_sorted[0][0]
        return self.name

四、完整的Face_recognition.py

#coding:utf-8
import dlib
import cv2
import os
import numpy as np


#获得当前项目的根目录——也就是当前脚本的目录的上一级目录
object_path = os.path.dirname(os.getcwd())

#导入正脸探测器(实例化)
detector = dlib.get_frontal_face_detector()
#导入人脸关键点识别器
predictor = dlib.shape_predictor(object_path + '/.py/model/shape_predictor_68_face_landmarks.dat')
#导入人脸识别模型
model = dlib.face_recognition_model_v1(object_path + '/.py/model/dlib_face_recognition_resnet_model_v1.dat')

class Test_face:
    def init(self):
        self.dist = []      #测试图像与已有图像特征的欧式距离列表
        self.face_rect = None
        self.name = None
    def test_face(self,photo,face_data):
        test_gray_img = cv2.cvtColor(photo, cv2.COLOR_BGR2GRAY)
        #使用探测器识别每张图像中的人脸,形成一个人脸列表
        self.face_rect = detector(test_gray_img, 1)
        #索引每一个人脸区域
        if len(self.face_rect) != 0:

            for i, det in enumerate(self.face_rect):
                #获取每个人脸的68个特征点
                test_face_predictor = predictor(photo, det)
                #获取每个人脸特征向量
                test_face_feature = model.compute_face_descriptor(photo, test_face_predictor)
                #将数据类型转换为Numpy类型
                test_value = np.array(test_face_feature)
                #将测试图像的人脸特征向量和已知人脸特征向量求范数(范数还是没明白)
                for i in face_data['face_features']:
                    dist_ = np.linalg.norm(i - test_value)
                    self.dist.append(dist_)
                #将名字和计算的欧式距离组合为字典
                names_dist = dict(zip(face_data['names'], self.dist))
                names_dist_sorted = sorted(names_dist.items(), key=lambda x: x[1])
                # #debug
                # print(names_dist_sorted)
                #规定相识度不得小于0.4
                if names_dist_sorted[0][1] > 0.4:
                    self.name = 'Unkonw'
                else:
                    self.name = names_dist_sorted[0][0]
        return self.name
#一维转三维
def Transfor(a):
    flatNumpyArray = np.array(a)
    #重排数组为240*320行,4列的二维数组,另外只要前三列
    RGBimage = flatNumpyArray.reshape((240 * 320, 4))[:, :3]
    #c数组的大小必须和图像大小一致
    c = np.zeros(shape=(240,320,3),dtype=np.uint8)
    j = 0
    for i in range(240*320):
        if i%320 == 0:
            c[j] = RGBimage[i:i+320]
            j += 1
    return c
#人脸识别
def CatchPICFromVideo(array,mode_path):
    #导入已有模型数据
    face_data = np.load(mode_path)
    #定义实例对象
    recognition_face = Test_face()
    recognition_face.init()
    #一维转三维
    frame = Transfor(array)
    name = recognition_face.test_face(frame,face_data)
    name = name.split('_')[0]
    return name

OK,能想到的就怎么多了。如果有什么疑问的,请在评论区留言,我们一起探讨。如果哪位大神能给小弟提点儿建议就更好了。

另外我将完整的Labview代码和python代码已上传上来。链接https://download.csdn.net/download/weilixin88/12013999

祝我和大家在这条路上能够坚持下去。

原创文章 24 获赞 45 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weilixin88/article/details/103392246