人脸任务在计算机视觉领域中十分重要,本项目主要使用了两类技术:人脸检测+人脸识别。

代码分为两部分内容:人脸注册人脸识别

  • 人脸注册:将人脸特征存储进数据库,这里用feature.csv代替
  • 人脸识别:将人脸特征与CSV文件中人脸特征进行比较,如果成功匹配则写入考勤文件attendance.csv

文章前半部分为一步步实现流程介绍,最后会有整理过后的完整项目代码。

一、项目实现

A. 注册:

导入相关包

import cv2
import numpy as np
import dlib
import time
import csv
# from argparse import ArgumentParser
from PIL import Image, ImageDraw, ImageFont

设计注册功能

注册过程我们需要完成的事:

  • 打开摄像头获取画面图片
  • 在图片中检测并获取人脸位置
  • 根据人脸位置获取68个关键点
  • 根据68个关键点生成特征描述符
  • 保存
  • (优化)展示界面,加入注册时成功提示等

1、基本步骤

我们首先进行前三步

# 检测人脸,获取68个关键点,获取特征描述符
def faceRegister(faceId=1, userName='default', interval=3, faceCount=3, resize_w=700, resize_h=400):'''faceId:人脸IDuserName: 人脸姓名faceCount: 采集该人脸图片的数量interval: 采集间隔'''cap = cv2.VideoCapture(0)# 人脸检测模型hog_face_detector = dlib.get_frontal_face_detector()# 关键点 检测模型shape_detector = dlib.shape_predictor('./weights/shape_predictor_68_face_landmarks.dat')# resnet模型face_descriptor_extractor = dlib.face_recognition_model_v1('./weights/dlib_face_recognition_resnet_model_v1.dat')while True:ret, frame = cap.read()# 镜像frame = cv2.flip(frame,1)# 转为灰度图frame_gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)# 检测人脸detections = hog_face_detector(frame,1)for face in detections:# 人脸框坐标 左上和右下l, t, r, b = face.left(), face.top(), face.right(), face.bottom()# 获取68个关键点points = shape_detector(frame,face)# 绘制关键点for point in points.parts():cv2.circle(frame,(point.x,point.y),2,(0,255,0),1)# 绘制矩形框cv2.rectangle(frame,(l,t),(r,b),(0,255,0),2)cv2.imshow("face",frame)if cv2.waitKey(10) & 0xFF == ord('q'):breakcap.release()cv2.destroyAllWindowsfaceRegister()      

此时一张帅脸如下:

2、描述符的采集

之后,我们根据参数,即faceCount 和 Interval 进行描述符的生成和采集

(这里我默认是faceCount=3,Interval=3,即每3秒采集一次,共3次)

def faceRegister(faceId=1, userName='default', interval=3, faceCount=3, resize_w=700, resize_h=400):'''faceId:人脸IDuserName: 人脸姓名faceCount: 采集该人脸图片的数量interval: 采集间隔'''cap = cv2.VideoCapture(0)# 人脸检测模型hog_face_detector = dlib.get_frontal_face_detector()# 关键点 检测模型shape_detector = dlib.shape_predictor('./weights/shape_predictor_68_face_landmarks.dat')# resnet模型face_descriptor_extractor = dlib.face_recognition_model_v1('./weights/dlib_face_recognition_resnet_model_v1.dat')# 开始时间start_time = time.time()# 执行次数collect_times = 0while True:ret, frame = cap.read()# 镜像frame = cv2.flip(frame,1)# 转为灰度图frame_gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)# 检测人脸detections = hog_face_detector(frame,1)for face in detections:# 人脸框坐标 左上和右下l, t, r, b = face.left(), face.top(), face.right(), face.bottom()# 获取68个关键点points = shape_detector(frame,face)# 绘制人脸关键点for point in points.parts():cv2.circle(frame, (point.x, point.y), 2, (0, 255, 0), 1)# 绘制矩形框cv2.rectangle(frame, (l, t), (r, b), (0, 255, 0), 2)# 采集:if collect_times < faceCount:# 获取当前时间now = time.time()# 时间限制if now - start_time > interval:# 获取特征描述符face_descriptor = face_descriptor_extractor.compute_face_descriptor(frame,points)# dlib格式转为数组face_descriptor = [f for f in face_descriptor]collect_times += 1start_time = nowprint("成功采集{}次".format(collect_times))else:# 时间间隔不到intervalprint("等待进行下一次采集")passelse:# 已经成功采集完3次了print("采集完毕")cap.release()cv2.destroyAllWindows()returncv2.imshow("face",frame)if cv2.waitKey(10) & 0xFF == ord('q'):breakcap.release()cv2.destroyAllWindows()faceRegister()  
等待进行下一次采集
...
成功采集1次
等待进行下一次采集
...
成功采集2次
等待进行下一次采集
...
成功采集3次
采集完毕

3、完整的注册

最后就是写入csv文件

这里加入了注册成功等的提示,且把一些变量放到了全局,因为后面人脸识别打卡时也会用到。

# 加载人脸检测器
hog_face_detector = dlib.get_frontal_face_detector()
cnn_detector = dlib.cnn_face_detection_model_v1('./weights/mmod_human_face_detector.dat')
haar_face_detector = cv2.CascadeClassifier('./weights/haarcascade_frontalface_default.xml')# 加载关键点检测器
points_detector = dlib.shape_predictor('./weights/shape_predictor_68_face_landmarks.dat')
# 加载resnet模型
face_descriptor_extractor = dlib.face_recognition_model_v1('./weights/dlib_face_recognition_resnet_model_v1.dat')
# 绘制中文
def cv2AddChineseText(img, text, position, textColor=(0, 255, 0), textSize=30):if (isinstance(img, np.ndarray)):  # 判断是否OpenCV图片类型img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))# 创建一个可以在给定图像上绘图的对象draw = ImageDraw.Draw(img)# 字体的格式fontStyle = ImageFont.truetype("./fonts/songti.ttc", textSize, encoding="utf-8")# 绘制文本draw.text(position, text, textColor, font=fontStyle)# 转换回OpenCV格式return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
# 绘制左侧信息
def drawLeftInfo(frame, fpsText, mode="Reg", detector='haar', person=1, count=1):# 帧率cv2.putText(frame, "FPS:  " + str(round(fpsText, 2)), (30, 50), cv2.FONT_ITALIC, 0.8, (0, 255, 0), 2)# 模式:注册、识别cv2.putText(frame, "Mode:  " + str(mode), (30, 80), cv2.FONT_ITALIC, 0.8, (0, 255, 0), 2)if mode == 'Recog':# 检测器cv2.putText(frame, "Detector:  " + detector, (30, 110), cv2.FONT_ITALIC, 0.8, (0, 255, 0), 2)# 人数cv2.putText(frame, "Person:  " + str(person), (30, 140), cv2.FONT_ITALIC, 0.8, (0, 255, 0), 2)# 总人数cv2.putText(frame, "Count:  " + str(count), (30, 170), cv2.FONT_ITALIC, 0.8, (0, 255, 0), 2)
# 注册人脸
def faceRegiser(faceId=1, userName='default', interval=3, faceCount=3, resize_w=700, resize_h=400):# 计数count = 0# 开始注册时间startTime = time.time()# 视频时间frameTime = startTime# 控制显示打卡成功的时长show_time = (startTime - 10)# 打开文件f = open('./data/feature.csv', 'a', newline='')csv_writer = csv.writer(f)cap = cv2.VideoCapture(0)while True:ret, frame = cap.read()frame = cv2.resize(frame, (resize_w, resize_h))frame = cv2.flip(frame, 1)# 检测face_detetion = hog_face_detector(frame, 1)for face in face_detetion:# 识别68个关键点points = points_detector(frame, face)# 绘制人脸关键点for point in points.parts():cv2.circle(frame, (point.x, point.y), 2, (0, 255, 0), 1)# 绘制框框l, t, r, b = face.left(), face.top(), face.right(), face.bottom()cv2.rectangle(frame, (l, t), (r, b), (0, 255, 0), 2)now = time.time()if (now - show_time) < 0.5:frame = cv2AddChineseText(frame,"注册成功 {count}/{faceCount}".format(count=(count + 1), faceCount=faceCount),(l, b + 30), textColor=(255, 0, 255), textSize=30)# 检查次数if count < faceCount:# 检查时间if now - startTime > interval:# 特征描述符face_descriptor = face_descriptor_extractor.compute_face_descriptor(frame, points)face_descriptor = [f for f in face_descriptor]# 描述符增加进data文件line = [faceId, userName, face_descriptor]# 写入csv_writer.writerow(line)# 保存照片样本print('人脸注册成功 {count}/{faceCount},faceId:{faceId},userName:{userName}'.format(count=(count + 1),faceCount=faceCount,faceId=faceId,userName=userName))frame = cv2AddChineseText(frame,"注册成功 {count}/{faceCount}".format(count=(count + 1), faceCount=faceCount),(l, b + 30), textColor=(255, 0, 255), textSize=30)show_time = time.time()# 时间重置startTime = now# 次数加一count += 1else:print('人脸注册完毕')f.close()cap.release()cv2.destroyAllWindows()returnnow = time.time()fpsText = 1 / (now - frameTime)frameTime = now# 绘制drawLeftInfo(frame, fpsText, 'Register')cv2.imshow('Face Attendance Demo: Register', frame)if cv2.waitKey(10) & 0xFF == ord('q'):breakf.close()cap.release()cv2.destroyAllWindows()

此时执行:

faceRegiser(3,"用户B")

人脸注册成功 1/3,faceId:3,userName:用户B
人脸注册成功 2/3,faceId:3,userName:用户B
人脸注册成功 3/3,faceId:3,userName:用户B
人脸注册完毕

其features文件:

B. 识别、打卡

识别步骤如下:

  • 打开摄像头获取画面
  • 根据画面中的图片获取里面的人脸特征描述符
  • 根据特征描述符将其与feature.csv文件里特征做距离判断
  • 获取ID、NAME
  • 考勤记录写入attendance.csv里

这里与上面流程相似,不过是加了一个对比功能,距离小于阈值,则表示匹配成功。就加快速度不一步步来了,代码如下:

# 刷新右侧考勤信息
def updateRightInfo(frame, face_info_list, face_img_list):# 重新绘制逻辑:从列表中每隔3个取一批显示,新增人脸放在最前面# 如果有更新,重新绘制# 如果没有,定时往后移动left_x = 30left_y = 20resize_w = 80offset_y = 120index = 0frame_h = frame.shape[0]frame_w = frame.shape[1]for face in face_info_list[:3]:name = face[0]time = face[1]face_img = face_img_list[index]# print(face_img.shape)face_img = cv2.resize(face_img, (resize_w, resize_w))offset_y_value = offset_y * indexframe[(left_y + offset_y_value):(left_y + resize_w + offset_y_value), -(left_x + resize_w):-left_x] = face_imgcv2.putText(frame, name, ((frame_w - (left_x + resize_w)), (left_y + resize_w) + 15 + offset_y_value),cv2.FONT_ITALIC, 0.5, (0, 255, 0), 1)cv2.putText(frame, time, ((frame_w - (left_x + resize_w)), (left_y + resize_w) + 30 + offset_y_value),cv2.FONT_ITALIC, 0.5, (0, 255, 0), 1)index += 1return frame
# 返回DLIB格式的face
def getDlibRect(detector='hog', face=None):l, t, r, b = None, None, None, Noneif detector == 'hog':l, t, r, b = face.left(), face.top(), face.right(), face.bottom()if detector == 'cnn':l = face.rect.left()t = face.rect.top()r = face.rect.right()b = face.rect.bottom()if detector == 'haar':l = face[0]t = face[1]r = face[0] + face[2]b = face[1] + face[3]nonnegative = lambda x: x if x >= 0 else 0return map(nonnegative, (l, t, r, b))
# 获取CSV中信息
def getFeatList():print('加载注册的人脸特征')feature_list = Nonelabel_list = []name_list = []# 加载保存的特征样本with open('./data/feature.csv', 'r') as f:csv_reader = csv.reader(f)for line in csv_reader:# 重新加载数据faceId = line[0]userName = line[1]face_descriptor = eval(line[2])label_list.append(faceId)name_list.append(userName)# 转为numpy格式face_descriptor = np.asarray(face_descriptor, dtype=np.float64)# 转为二维矩阵,拼接face_descriptor = np.reshape(face_descriptor, (1, -1))# 初始化if feature_list is None:feature_list = face_descriptorelse:# 拼接feature_list = np.concatenate((feature_list, face_descriptor), axis=0)print("特征加载完毕")return feature_list, label_list, name_list
# 人脸识别
def faceRecognize(detector='haar', threshold=0.5, write_video=False, resize_w=700, resize_h=400):# 视频时间frameTime = time.time()# 加载特征feature_list, label_list, name_list = getFeatList()face_time_dict = {}# 保存name,time人脸信息face_info_list = []# numpy格式人脸图像数据face_img_list = []# 侦测人数person_detect = 0# 统计人脸数face_count = 0# 控制显示打卡成功的时长show_time = (frameTime - 10)# 考勤记录f = open('./data/attendance.csv', 'a')csv_writer = csv.writer(f)cap = cv2.VideoCapture(0)# resize_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))//2# resize_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) //2videoWriter = cv2.VideoWriter('./record_video/out' + str(time.time()) + '.mp4', cv2.VideoWriter_fourcc(*'MP4V'), 15,(resize_w, resize_h))while True:ret, frame = cap.read()frame = cv2.resize(frame, (resize_w, resize_h))frame = cv2.flip(frame, 1)# 切换人脸检测器if detector == 'hog':face_detetion = hog_face_detector(frame, 1)if detector == 'cnn':face_detetion = cnn_detector(frame, 1)if detector == 'haar':frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)face_detetion = haar_face_detector.detectMultiScale(frame_gray, minNeighbors=7, minSize=(100, 100))person_detect = len(face_detetion)for face in face_detetion:l, t, r, b = getDlibRect(detector, face)face = dlib.rectangle(l, t, r, b)# 识别68个关键点points = points_detector(frame, face)cv2.rectangle(frame, (l, t), (r, b), (0, 255, 0), 2)# 人脸区域face_crop = frame[t:b, l:r]# 特征face_descriptor = face_descriptor_extractor.compute_face_descriptor(frame, points)face_descriptor = [f for f in face_descriptor]face_descriptor = np.asarray(face_descriptor, dtype=np.float64)# 计算距离distance = np.linalg.norm((face_descriptor - feature_list), axis=1)# 最小距离索引min_index = np.argmin(distance)# 最小距离min_distance = distance[min_index]predict_name = "Not recog"if min_distance < threshold:# 距离小于阈值,表示匹配predict_id = label_list[min_index]predict_name = name_list[min_index]# 判断是否新增记录:如果一个人距上次检测时间>3秒,或者换了一个人,将这条记录插入need_insert = Falsenow = time.time()if predict_name in face_time_dict:if (now - face_time_dict[predict_name]) > 3:# 刷新时间face_time_dict[predict_name] = nowneed_insert = Trueelse:# 还是上次人脸need_insert = Falseelse:# 新增数据记录face_time_dict[predict_name] = nowneed_insert = Trueif (now - show_time) < 1:frame = cv2AddChineseText(frame, "打卡成功", (l, b + 30), textColor=(0, 255, 0), textSize=40)if need_insert:# 连续显示打卡成功1sframe = cv2AddChineseText(frame, "打卡成功", (l, b + 30), textColor=(0, 255, 0), textSize=40)show_time = time.time()time_local = time.localtime(face_time_dict[predict_name])# 转换成新的时间格式(2016-05-05 20:28:54)face_time = time.strftime("%H:%M:%S", time_local)face_time_full = time.strftime("%Y-%m-%d %H:%M:%S", time_local)# 开始位置增加face_info_list.insert(0, [predict_name, face_time])face_img_list.insert(0, face_crop)# 写入考勤表line = [predict_id, predict_name, min_distance, face_time_full]csv_writer.writerow(line)face_count += 1# 绘制人脸点cv2.putText(frame, predict_name + " " + str(round(min_distance, 2)), (l, b + 30), cv2.FONT_ITALIC, 0.8,(0, 255, 0), 2)# 处理下一张脸now = time.time()fpsText = 1 / (now - frameTime)frameTime = now# 绘制drawLeftInfo(frame, fpsText, 'Recog', detector=detector, person=person_detect, count=face_count)# 舍弃face_img_list、face_info_list后部分,节约内存if len(face_info_list) > 10:face_info_list = face_info_list[:9]face_img_list = face_img_list[:9]frame = updateRightInfo(frame, face_info_list, face_img_list)if write_video:videoWriter.write(frame)cv2.imshow('Face Attendance Demo: Recognition', frame)if cv2.waitKey(10) & 0xFF == ord('q'):breakf.close()videoWriter.release()cap.release()cv2.destroyAllWindows()

然后效果就和我们宿舍楼下差不多了~

我年轻的时候,我大概比现在帅个几百倍吧,哎。

二、总代码

上文其实把登录和注册最后一部分代码放在一起就是了,这里就不再复制粘贴了,相关权重文件下载链接:opencv/data at master · opencv/opencv · GitHub

当然本项目还有很多需要优化的地方,比如设置用户不能重复、考勤打卡每天只能一次、把csv改为链接成数据库等等,后续代码优化完成后就可以部署然后和室友**了。

openCV实战项目--人脸考勤相关推荐

  1. CV实战项目--人脸考勤

    向AI转型的程序员都关注了这个号

  2. 再次升级,985博士整理的71个OpenCV实战项目教程开放下载!

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 近期小白学视觉公众号推出了多篇Python+OpenCV实战项目的 ...

  3. 重磅升级,52个Python+OpenCV实战项目教你掌握图像处理

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 近期小白学视觉公众号推出了多篇Python+OpenCV实战项目的 ...

  4. OpenCV实战之人脸美颜美型算法

    人脸美颜美型是十分常见的图像处理功能,应用于手机拍照.视频直播.视频会议等图像视频流处理领域.如下图所示是一款拍照软件中针对人脸美颜美型功能的具体介绍. 人脸美颜美型是一个综合性较高的算法,包括基于机 ...

  5. 实战项目:考勤系统的需求分析

    同学们,你马上就要接受一个实际项目的考验了,你需要运用你所学,全力以赴! 如果你已经忘记前面所学,赶紧去复习! 这个超级演练的教学目标是: 1.学习如何从零开始组织需求开发的工作. 2.学习如何在需求 ...

  6. opencv实战项目:基于opencv的车牌号码识别

    首先,呈上我自己根据代码写的步骤流程,方便记忆,字有点丑,哈哈哈!!! 好吧,图片看不清,那就上代码 import cv2 import imutils import numpy as np impo ...

  7. opencv实践项目-人脸检测

    目录 1. opencv CascadeClassifier人脸检测步骤 2. CascadeClassifier分类器简介 2.1 从文件中加载级联分类器 2.2 目标检测方法 3. 代码实现 1. ...

  8. OpenCV实战之人脸美颜美型(三)——人脸关键点检测

    目录 前言 标注方式 4点/5点/6点标注 68点标注 人脸关键点评价指标 检测方法概述 使用OpenCV实现人脸关键点检测 Facemark API 基于LBF的人脸关键点检测 API调用 前言 人 ...

  9. OpenCV实战之人脸美颜美型(四)——肤色检测

    前言 肤色检测技术在人脸检测与识别.视频监控系统.裸图像检测.基于内容的图像检索.手势识别.肤色病学诊断.驾驶员疲劳检测.人机交互等领域有着广泛的应用,近年来得到了广泛的关注和研究. 针对美颜美型应用 ...

最新文章

  1. docker制作镜像篇(基于容器)
  2. 详解Paint的setPathEffect(PathEffect effect)
  3. 解决一个坑爹的mininet的pingall失败的问题(自己编译安装openvswitch后)
  4. 前沿视频教室——《C#图解教程》是本好书,强烈推荐!
  5. 你的大脑在自动驾驶,而你一无所知
  6. python爬虫跨域_AJAX跨域简单讲解【Python版】
  7. [NOTE] XMLHttpRequest
  8. PC-CSS-多浏览器支持HTML5
  9. js 获取最后一个字符
  10. 计算机装打印机,电脑怎么安装打印机
  11. Intel SGX背景、其他可信计算技术和TEE技术(翻译自Intel SGX Explained)
  12. 360随身wifi作为无线网卡使用(在电脑有线网卡和无线网卡都没有的情况下)
  13. Fiddler抓包流程
  14. Windows 免密码登录
  15. html禁止双击选中文本,CSS阻止页面双击选中文本
  16. 我的世界服务器vip账号和密码是多少,我的世界hypixel服务器会员有什么区别 hypixel服务器会员介绍...
  17. 固定码率(CBR)与可变码率(VBR)
  18. 4.flask第三方组件
  19. 驾培行业应对新形势“自学直考”新格局冲击剖析
  20. 在线客服系统如何应对移动化市场需求

热门文章

  1. 通过两张趣图,看工程师的自我反省
  2. 显示器系列1--常见参数详解
  3. 为什么在室内4G信号会变弱
  4. 英语常考知识点汇总笔记
  5. vmware 14 安装window 7时,出现Network boot from Intel E1000
  6. source insight 4.0 配色方案及配置方法
  7. PHP初级【10天小积累-第二天】
  8. Summernote文字编辑器添加代码插入功能
  9. 【NVM】NVM 常用笔记
  10. 用java 发送邮件 版本2(根据配置文件 选择发送情话或者励志文)