dlib 实战:人脸检测、关键点定位与疲劳检测的全流程实现
一、引言
在计算机视觉领域,人脸检测与分析是非常基础且重要的应用方向。dlib 库凭借其出色的人脸检测和关键点定位能力,成为了很多开发者的首选工具。本文将围绕基于 dlib 的人脸检测、人脸关键点定位以及疲劳检测展开,详细介绍相关代码与原理。
二、人脸检测
(一)原理
dlib 的get_frontal_face_detector
方法采用了 HOG(Histogram of Oriented Gradients,方向梯度直方图)特征结合线性分类器、图像金字塔以及滑动窗口检测的技术。HOG 特征能够很好地描述图像局部的梯度信息,通过对图像不同尺度的金字塔层进行滑动窗口检测,从而可以检测出不同大小的人脸。这种方法相比传统的 Haar 级联分类器,在检测精度和对不同尺度人脸的适应性上都有更好的表现。
(二)代码实现与解析
import cv2
import dlib# 生成人脸检测器,采用HOG等技术,检测效果优于OpenCV的Haar级联分类器
detector = dlib.get_frontal_face_detector()
img = cv2.imread("peopl.jpg")
# 检测人脸,参数1表示上采样次数,增大该值可提高小人脸检测效果,但降低速度
faces = detector(img, 1)
for face in faces: # 获取人脸框的坐标x1 = face.left()y1 = face.top()x2 = face.right()y2 = face.bottom()# 绘制人脸框,颜色为绿色,线宽为2cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
# 显示绘制人脸框后的图像
cv2.imshow("result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
首先,我们导入cv2
和dlib
库,使用dlib.get_frontal_face_detector()
创建人脸检测器。然后读取图像,调用检测器的detector
方法检测图像中的人脸,得到人脸框的集合。接着遍历每个人脸框,获取其左上角和右下角的坐标,并用cv2.rectangle
函数绘制出人脸框。最后显示处理后的图像并等待用户操作后关闭窗口。
三、人脸关键点定位
(一)原理
dlib 的shape_predictor
模型是基于训练好的回归树集合来工作的。它首先通过人脸检测器确定人脸的大致区域,然后在该区域内利用回归树逐步预测出 68 个(以shape_predictor_68_face_landmarks.dat
模型为例)关键点位的坐标,这些关键点可以精准地定位到人脸的眼睛、鼻子、眉毛、嘴唇以及轮廓等部位。
(二)代码实现与解析
import numpy as np
import cv2
import dlibimg = cv2.imread("dlrb2.jpg") # 读取图像
detector = dlib.get_frontal_face_detector() # 构造人脸检测器
faces = detector(img, 0) # 检测人脸,上采样次数为0
# 加载人脸关键点预测器模型
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
for face in faces: # 对每个人脸进行关键点检测shape = predictor(img, face) # 获取关键点# 将关键点转换为numpy数组形式的坐标landmarks = np.array([[p.x, p.y] for p in shape.parts()])# 绘制每个关键点for idx, point in enumerate(landmarks):pos = [point[0], point[1]] # 当前关键点的坐标# 绘制实心圆标记关键点,颜色为绿色,半径为2cv2.circle(img, pos, radius=2, color=(0, 255, 0), thickness=-1)# 在关键点旁标注索引,字体为简单的无衬线字体,大小0.4,颜色白色,线宽1cv2.putText(img, str(idx), pos, cv2.FONT_HERSHEY_SIMPLEX, fontScale=0.4,color=(255, 255, 255), thickness=1, lineType=cv2.LINE_AA)
cv2.imshow(winname="img", mat=img)
cv2.waitKey()
cv2.destroyAllWindows()
这段代码先读取图像并创建人脸检测器,检测出图像中的人脸。然后加载关键点预测器模型,对每个人脸调用predictor
方法获取关键点。将关键点转换为 numpy 数组后,遍历每个关键点,用cv2.circle
绘制实心圆标记,并用cv2.putText
标注关键点的索引,最后显示图像。
四、人脸特征绘制
(一)原理
利用人脸关键点的坐标信息,我们可以通过绘制线段连接特定的关键点来勾勒出人脸的轮廓、眉毛、鼻子等结构,也可以通过cv2.convexHull
函数计算关键点的凸包,来绘制眼睛、嘴巴等部位的轮廓。凸包是指包含所有给定点的最小凸多边形,适合用于描述眼睛、嘴巴等具有大致凸形轮廓的区域。
(二)代码实现与解析
import numpy as np
import dlib
import cv2def drawLine(start, end): # 连接指定范围的关键点成线段pts = shape[start:end] # 获取起始到结束索引的关键点for l in range(1, len(pts)):ptA = tuple(pts[l - 1])ptB = tuple(pts[l])# 绘制线段,颜色绿色,线宽2cv2.line(image, ptA, ptB, color=(0, 255, 0), thickness=2)def drawConvexHull(start, end): # 绘制指定范围关键点的凸包Facial = shape[start:end + 1]mouthHull = cv2.convexHull(Facial) # 计算凸包# 绘制凸包轮廓,颜色绿色,线宽2cv2.drawContours(image, contours=[mouthHull], contourIdx=-1, color=(0, 255, 0), thickness=2)image = cv2.imread("dlrb2.jpg")
image = cv2.resize(image,(420,474))
detector = dlib.get_frontal_face_detector() # 构造脸部位置检测器
faces = detector(image, 0) # 检测人脸方框位置
# 读取人脸关键点定位模型
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
for face in faces: # 对检测到的每个人脸进行处理shape = predictor(image, face) # 获取关键点# 将关键点转换为坐标(x,y)的形式shape = np.array([[p.x, p.y] for p in shape.parts()])drawConvexHull(start=36, end=41) # 绘制右眼凸包drawConvexHull(start=42, end=47) # 绘制左眼凸包drawConvexHull(start=48, end=59) # 绘制嘴外部凸包drawConvexHull(start=60, end=67) # 绘制嘴内部凸包drawLine(start=0, end=17) # 绘制脸颊点线drawLine(start=17, end=22) # 绘制左眉毛点线drawLine(start=22, end=27) # 绘制右眉毛点线drawLine(start=27, end=36) # 绘制鼻子点线
cv2.imshow(winname="Frame", mat=image)
cv2.waitKey()
cv2.destroyAllWindows()
这里定义了drawLine
和drawConvexHull
两个函数,分别用于连接关键点成线段和绘制关键点的凸包。在主程序中,读取并调整图像大小后,检测人脸并获取关键点。然后调用这两个函数,分别绘制出眼睛、嘴巴的凸包以及脸颊、眉毛、鼻子的线段,最后显示处理后的图像。
五、疲劳检测
(一)原理
疲劳检测主要基于眼睛纵横比(Eye Aspect Ratio,EAR)的概念。眼睛的纵横比是通过计算眼睛垂直方向关键点之间的距离与水平方向关键点之间距离的比值得到的。当人疲劳时,眼睛会不自觉地闭合,此时眼睛的纵横比会降低。我们设定一个阈值(这里设为 0.3),当纵横比持续低于该阈值一段时间(这里是 50 帧),就认为处于疲劳状态,从而发出警报。
(二)代码实现与解析
import numpy as np
import dlib
import cv2
from sklearn.metrics.pairwise import euclidean_distances # 计算欧式距离
from PIL import Image, ImageDraw, ImageFont # 用于在图像上添加中文文本def eye_aspect_ratio(eye): # 计算眼睛纵横比A = euclidean_distances(eye[1].reshape(1, 2), eye[5].reshape(1, 2))B = euclidean_distances(eye[2].reshape(1, 2), eye[4].reshape(1, 2))C = euclidean_distances(eye[0].reshape(1, 2), eye[3].reshape(1, 2))ear = ((A + B) / 2.0) / C # 纵横比计算return eardef cv2AddChineseText(img, text, position, textColor=(255, 0, 0), textSize=50): # 在图像上添加中文文本if isinstance(img, np.ndarray): # 判断是否为OpenCV图像(numpy数组)img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))draw = ImageDraw.Draw(img)# 替换为本地中文字体路径,如宋体、微软雅黑等font = ImageFont.truetype("simhei.ttf", textSize, encoding="utf-8")draw.text(position, text, textColor, font=font)return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)def drawEye(eye): # 绘制眼眶凸包eyeHull = cv2.convexHull(eye)cv2.drawContours(frame, contours=[eyeHull], contourIdx=-1, color=(0, 255, 0), thickness=-1)COUNTER = 0 # 闭眼持续次数统计
detector = dlib.get_frontal_face_detector() # 构造脸部位置检测器
# 读取人脸关键点定位模型
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
cap = cv2.VideoCapture(0) # 打开摄像头while True:ret, frame = cap.read()if not ret:print("无法获取摄像头画面,请检查摄像头!")breakfaces = detector(frame, 0) # 获取人脸for face in faces: # 循环遍历每一个人脸shape = predictor(frame, face) # 获取关键点# 将关键点转换为坐标(x,y)的形式shape = np.array([[p.x, p.y] for p in shape.parts()])rightEye = shape[36:42] # 右眼关键点索引范围leftEye = shape[42:48] # 左眼关键点索引范围rightEAR = eye_aspect_ratio(rightEye) # 计算右眼纵横比leftEAR = eye_aspect_ratio(leftEye) # 计算左眼纵横比ear = (leftEAR + rightEAR) / 2.0 # 左右眼纵横比均值if ear < 0.3: # 小于0.3认为闭眼或眨眼COUNTER += 1 # 闭眼次数加1if COUNTER >= 50: # 若持续50帧闭眼,发出危险警报frame = cv2AddChineseText(frame, text="!!!危险!!!", position=(250, 250))else:COUNTER = 0 # 闭眼次数清零drawEye(leftEye) # 绘制左眼凸包drawEye(rightEye) # 绘制右眼凸包info = "EAR: {:.2f}".format(ear[0][0])# 在图像上显示眼睛闭合程度值frame = cv2AddChineseText(frame, info, position=(0, 30)) if frame is not None:cv2.imshow(winname="Frame", mat=frame)else:print("图像无效,跳过显示")if cv2.waitKey(1) == 27: # 按Esc键退出break
cv2.destroyAllWindows()
cap.release() # 释放摄像头
首先定义了计算眼睛纵横比、添加中文文本和绘制眼眶凸包的函数。然后初始化人脸检测器、关键点预测器和摄像头。在循环中,不断从摄像头读取画面,检测人脸并获取关键点。计算左右眼的纵横比,根据纵横比判断是否闭眼,若持续闭眼达到一定帧数则发出警报,否则绘制眼睛凸包并显示眼睛闭合程度值。最后,按 Esc 键退出程序并释放摄像头资源。
六、总结
本文详细介绍了基于 dlib 库的人脸检测、人脸关键点定位以及疲劳检测的原理与代码实现。从基础的人脸检测,到精准的关键点定位,再到利用关键点信息进行人脸特征绘制和疲劳检测,展示了 dlib 在人脸分析领域的强大能力。这些技术可以应用在驾驶员监控、学员上课状态检测等多个实际场景中,具有广泛的应用前景。