文章内容如下:
1)项目简介
2)开发环境和使用的技术知识
3)功能实现
4)项目源码
一。项目简介
此项目是基于HOG和Dlib开发的一套实时无感考勤系统。首先是待考勤人员的个人信息录入。然后在过道或者入口处装置人脸考勤机,当有人经过时,摄像头抓取人脸并上传服务器,对抓取到的人脸进行人脸识别,判断此人身份,若此人在信息比对库中且第一次考勤,则考勤成功,并显示考勤人员姓名与比对距离,如非第一次考勤,并在一定时间隔外则更新考勤 。
二。开发环境和使用的技术知识
开发环境:Python,Opencv
技术知识:HOG+Dlib技术、Opencv视频读取-缩放-翻转-写入技术
三。功能实现
1. 人脸注册:将人脸特征存进feaure.csv
2. 人脸识别:将检测的人脸特征与feaure.csv人脸特征作比较,如果比中把考勤记录写入attendance.csv
1. 人脸注册:将人脸特征存进feaure.csv
①调取视频流,能显示人脸画面
cap = cv2.VideoCapture(0)"""调取摄像头"""
while True:
ret,frame = cap.read()
frame = cv2.flip(frame,1)"""y轴镜像"""
cv2.imshow('Face attendance',frame)"""展示每一帧图片"""
if cv2.waitKey(10) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
② 步骤如下:
第1步:启动HOG人脸检测器,读取人脸上下左右坐标,绘制人脸方框;
hog_face_detector = dlib.get_frontal_face_detector()"""启动hog人脸检测器"""
detections = hog_face_detector(frame,1)"""获取人脸参数,缩放因子为1"""
for face in detections:
l,t,r,b = face.left(),face.top(),face.right(),face.bottom()"""获取人脸左,上,右,下的坐标"""
cv2.rectangle(frame,(l,t),(r,b),(0,255,0),2)"""绘制人脸框"""
第2步: 启动人脸关键点检测器获取人脸68个关键点,再转为特征描述符;
shape_detector = dlib.shape_predictor('./1/shape_predictor_68_face_landmarks.dat')"""启动人脸关键点检测器"""
detections = hog_face_detector(frame,1)"""获取人脸参数,缩放因子为1"""
for face in detections:
points = shape_detector(frame,face)"""获取人脸68关键点"""
for point in points.parts():
cv2.circle(frame,(point.x,point.y),2,(0,255,0),-10)"""绘制68关键点"""
face_descriptor = face_descriptor_extractor.compute_face_descriptor(frame,points)"""获取(转为)特征描述符"""
face_descriptor = [f for f in face_descriptor]"""为方便转换成列表存进feaure.csv"""
①②组合在一起复合代码如下:
def faceRegister(label_id=1,name='Jie Wang',count=3,interval=3):
#1)-检测人脸
#准备工作
hog_face_detector = dlib.get_frontal_face_detector()#①启动hog人脸检测器
shape_detector = dlib.shape_predictor('./1/shape_predictor_68_face_landmarks.dat')#①启动人脸关键点检测器
face_descriptor_extractor = dlib.face_recognition_model_v1('./1/dlib_face_recognition_resnet_model_v1.dat')#①启动特征描述符检测器
cap = cv2.VideoCapture(0)#②调取摄像头
start_time = time.time()#开始时间,是为了interval设置的
collect_count = 0#初始采集次数
f = open('./2/feature.csv','a',newline="")#采集到的特征描述符放在这个文件里
csv_writer = csv.writer(f)
#采集3次特征
while True:
ret,frame = cap.read()
frame = cv2.flip(frame,1)#y轴镜像
detections = hog_face_detector(frame,1)#获取人脸参数,缩放因子为1
for face in detections:
l,t,r,b = face.left(),face.top(),face.right(),face.bottom()#获取人脸左,上,右,下的坐标
points = shape_detector(frame,face)#获取人脸68关键点
for point in points.parts():
cv2.circle(frame,(point.x,point.y),2,(0,255,0),-10)#绘制68关键点
cv2.rectangle(frame,(l,t),(r,b),(0,255,0),2)#绘制人脸框
if collect_count < count:
now = time.time()
if now - start_time > interval:
face_descriptor = face_descriptor_extractor.compute_face_descriptor(frame,points)#获取特征描述符
face_descriptor = [f for f in face_descriptor]#转换成列表方便存进feaure.csv
line = [label_id,name,face_descriptor]
csv_writer.writerow(line)#写入csv_writer
collect_count +=1
start_time = now
print("采集次数:{collect_count}".format(collect_count = collect_count))
copy_collect_count = collect_count
# print(face_descriptor)
else:
if 3 == copy_collect_count:
print("采集完毕")
copy_collect_count = 0
break
#展示
cv2.imshow('Face attendance',frame)#展示每一帧图片
if cv2.waitKey(10) & 0xFF == ord('q'):
break
f.close()
cap.release()
cv2.destroyAllWindows()
faceRegister()
2. 人脸识别:将检测的人脸特征与feaure.csv人脸特征作比较,如果比中把考勤记录写入attendance.csv
①从磁盘中调取注册时的人脸特征描述符,解析它
def getFeatureList():
label_list = []
name_list = []
featuree_list = None"""后面用的np.concatenate()方法决定了此处不能是空列表,否则无法拼接"""
with open('./2/feature.csv','r') as f:
csv_reader = csv.reader(f)
for line in csv_reader:
label_id = line[0]
name = line[1]
face_descriptor = eval(line[2])"""从csv文件拿出来的列表是字符串,需要eval处理一下转为list"""
face_descriptor = np.asarray(face_descriptor,dtype=np.float64)
face_descriptor = np.reshape(face_descriptor,(1,-1))"""转成1xn格式,期望就是这样"""
label_list.append(label_id)"""label_list拼接过程"""
name_list.append(name)"""name_list拼接过程"""
if featuree_list is None:"""featuree_list拼接过程"""
featuree_list = face_descriptor
else:
featuree_list = np.concatenate((featuree_list,face_descriptor),axis=0)
# print(name)
return label_list,name_list,featuree_list
label_list,name_list,featuree_list = getFeatureList()"""读取人脸注册时的特征"""
②特征描述符比对,计算与库的距离
distance = np.linalg.norm((face_descriptor-featuree_list),axis=1)"""范数求差"""
min_index = np.argmin(distance)"""获取最短距离索引"""
min_distance = np.argmin(distance)"""获取最短距离"""
if min_distance < threshold:"""识别到打印信息"""
predict_id = label_list[min_distance]
predict_name = name_list[min_distance]
③依据条件确认是否记录考勤
need_insert = False"""用于后面判断是否变化了考勤,是则写入到attendance.csv文件"""
if predict_name in recog_record:"""第一次检测到考勤则记录人脸信息,记录的是名字与考勤时间组成的键值对,如这次检测与上次记录差3s,则再考勤一次。上述都不是则跳过"""
if time.time()-recog_record[predict_name]>3:
need_insert = True
recog_record[predict_name] = time.time()
print(predict_name)
else:
print("无需重复签到!")
need_insert = False
pass
else:
need_insert = True
recog_record[predict_name] = time.time()
print(predict_name)
④将考勤记录写入文件
if need_insert:"""考勤较上次变化了则记录"""
time_local = time.localtime(recog_record[predict_name])"""时间戳"""
time_str = time.strftime("%Y-%m-%d %H:%M:%S",time_local)"""化时间戳为年月日"""
line = [predict_id,predict_name,min_distance,time_str]
csv_writer.writerow(line)
print("{name} {time}考勤记录已成功更新入文件!".format(name=predict_name,time=time_str))
①-④组合在一起复合代码如下:
def faceRecognizer(threshold = 0.5):
#1)实时获取视频流中人脸特征描述符;
#1准备工作
label_list,name_list,featuree_list = getFeatureList()#读取人脸注册时的特征
hog_face_detector = dlib.get_frontal_face_detector()#①启动hog人脸检测器
shape_detector = dlib.shape_predictor('./1/shape_predictor_68_face_landmarks.dat')#①启动人脸关键点检测器
face_descriptor_extractor = dlib.face_recognition_model_v1('./1/dlib_face_recognition_resnet_model_v1.dat')#①启动特征描述符检测器
cap = cv2.VideoCapture(0)#②调取摄像头
recog_record = {}#为存考勤信息做准备
f = open('./2/attendance.csv','a',newline="")#为文件性质记录考勤数据做准备
csv_writer = csv.writer(f)
fps_time = time.time()#画面显示fps
#2获取人脸识别中采集到的特征
while True:
ret,frame = cap.read()
frame = cv2.flip(frame,1)#y轴镜像
detections = hog_face_detector(frame,1)#获取人脸参数,缩放因子为1
for face in detections:
l,t,r,b = face.left(),face.top(),face.right(),face.bottom()#获取人脸左,上,右,下的坐标
points = shape_detector(frame,face)#获取人脸68关键点
cv2.rectangle(frame,(l,t),(r,b),(0,255,0),2)#绘制人脸框
face_descriptor = face_descriptor_extractor.compute_face_descriptor(frame,points)#获取特征描述符
face_descriptor = [f for f in face_descriptor]#转换成列表方便存进feaure.csv
#3计算与库的距离
distance = np.linalg.norm((face_descriptor-featuree_list),axis=1)#范数求差
min_index = np.argmin(distance)#获取最短距离索引
min_distance = np.argmin(distance)#获取最短距离
if min_distance < threshold:#识别到打印信息
predict_id = label_list[min_distance]
predict_name = name_list[min_distance]
#6给人脸框添加内容
cv2.putText(frame,predict_name+" "+str(round(min_distance,2)),(l,b+30),cv2.FONT_HERSHEY_COMPLEX_SMALL,1,(0,255,0),1)
#4是否记录考勤
need_insert = False#用于后面判断是否变化了考勤,是则写入到attendance.csv文件
if predict_name in recog_record:#第一次检测到考勤则记录人脸信息,记录的是名字与考勤时间组成的键值对,如这次检测与上次记录差3s,则再考勤一次。上述都不是则跳过
if time.time()-recog_record[predict_name]>3:
need_insert = True
recog_record[predict_name] = time.time()
print(predict_name)
else:
print("无需重复签到!")
need_insert = False
pass
else:
need_insert = True
recog_record[predict_name] = time.time()
print(predict_name)
if need_insert:#考勤较上次变化了则记录
time_local = time.localtime(recog_record[predict_name])#时间戳
time_str = time.strftime("%Y-%m-%d %H:%M:%S",time_local)#化时间戳为年月日
line = [predict_id,predict_name,min_distance,time_str]
csv_writer.writerow(line)
print("{name} {time}考勤记录已成功更新入文件!".format(name=predict_name,time=time_str))
else:
print("未识别到")
#5增加FPS
now = time.time()
fps = 1/(now - fps_time)
fps_time = now
cv2.putText(frame,"FPS:"+str(round(fps,2)),(20,40),cv2.FONT_HERSHEY_COMPLEX_SMALL,1,(0,255,0),1)
cv2.imshow('Face attendance',frame)#展示每一帧图片
if cv2.waitKey(10) & 0xFF == ord('q'):
break
f.close()
cap.release()
cv2.destroyAllWindows()
#读取人脸注册时的特征
def getFeatureList():
label_list = []
name_list = []
featuree_list = None#后面用的np.concatenate()方法决定了此处不能是空列表,否则无法拼接
with open('./2/feature.csv','r') as f:
csv_reader = csv.reader(f)
for line in csv_reader:
label_id = line[0]
name = line[1]
face_descriptor = eval(line[2])#从csv文件拿出来的列表是字符串,需要eval处理一下转为list
face_descriptor = np.asarray(face_descriptor,dtype=np.float64)
face_descriptor = np.reshape(face_descriptor,(1,-1))#转成1xn格式,期望就是这样
label_list.append(label_id)#label_list拼接过程
name_list.append(name)#name_list拼接过程
if featuree_list is None:#featuree_list拼接过程
featuree_list = face_descriptor
else:
featuree_list = np.concatenate((featuree_list,face_descriptor),axis=0)
# print(name)
return label_list,name_list,featuree_list
label_list,name_list,featuree_list = getFeatureList()
faceRecognizer(threshold = 10)
四。项目源码
源码链接:点击这里