一、引言
在计算机视觉领域,人脸检测与分析是非常基础且重要的应用方向。dlib 库凭借其出色的人脸检测和关键点定位能力,成为了很多开发者的首选工具。本文将围绕基于 dlib 的人脸检测、人脸关键点定位以及疲劳检测展开,详细介绍相关代码与原理。
二、人脸检测
(一)原理
dlib 的get_frontal_face_detector
方法采用了 HOG(Histogram of Oriented Gradients,方向梯度直方图)特征结合线性分类器、图像金字塔以及滑动窗口检测的技术。HOG 特征能够很好地描述图像局部的梯度信息,通过对图像不同尺度的金字塔层进行滑动窗口检测,从而可以检测出不同大小的人脸。这种方法相比传统的 Haar 级联分类器,在检测精度和对不同尺度人脸的适应性上都有更好的表现。
(二)代码实现与解析
python
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()
# 绘制人脸框,颜色为绿色,线宽为2
cv2.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
模型为例)关键点位的坐标,这些关键点可以精准地定位到人脸的眼睛、鼻子、眉毛、嘴唇以及轮廓等部位。
(二)代码实现与解析
python
import numpy as np
import cv2
import dlib
img = 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]] # 当前关键点的坐标
# 绘制实心圆标记关键点,颜色为绿色,半径为2
cv2.circle(img, pos, radius=2, color=(0, 255, 0), thickness=-1)
# 在关键点旁标注索引,字体为简单的无衬线字体,大小0.4,颜色白色,线宽1
cv2.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
函数计算关键点的凸包,来绘制眼睛、嘴巴等部位的轮廓。凸包是指包含所有给定点的最小凸多边形,适合用于描述眼睛、嘴巴等具有大致凸形轮廓的区域。
(二)代码实现与解析
python
import numpy as np
import dlib
import cv2
def drawLine(start, end): # 连接指定范围的关键点成线段
pts = shape[start:end] # 获取起始到结束索引的关键点
for l in range(1, len(pts)):
ptA = tuple(pts[l - 1])
ptB = tuple(pts[l])
# 绘制线段,颜色绿色,线宽2
cv2.line(image, ptA, ptB, color=(0, 255, 0), thickness=2)
def drawConvexHull(start, end): # 绘制指定范围关键点的凸包
Facial = shape[start:end + 1]
mouthHull = cv2.convexHull(Facial) # 计算凸包
# 绘制凸包轮廓,颜色绿色,线宽2
cv2.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 帧),就认为处于疲劳状态,从而发出警报。
(二)代码实现与解析
python
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 ear
def 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("无法获取摄像头画面,请检查摄像头!")
break
faces = 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 # 闭眼次数加1
if 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 在人脸分析领域的强大能力。这些技术可以应用在驾驶员监控、学员上课状态检测等多个实际场景中,具有广泛的应用前景。