上一篇主要讲了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系统的安装,请百度)
因为本来算法就是拿别人的,也不会优化,但又不想让运行时间太长;我就想不能让已经训练过的图像再去训练了。
在比较两个列表时,可以将两个list转换为两个set,因为集合很适合计算交集。
集合A和集合B,它们的交集b=A&B;所以a=A-b;c=B-b
# -*-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保存数据,使用网上说的方法,但没有成功。
因为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
#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
祝我和大家在这条路上能够坚持下去。