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

祝我和大家在這條路上能夠堅持下去。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章