上一篇主要講了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
祝我和大家在這條路上能夠堅持下去。