dlib人脸68关键点检测与轮廓绘制实战

dlib 人脸 68 关键点检测与轮廓绘制实战

本文以两段完整的 Python 代码为主线,逐步拆解 dlib shape_predictor_68 模型的调用方式,并重点对比两种轮廓绘制策略:顺序连线(drawLine)凸包轮廓(drawConvexHull)。读者可直接复制代码运行。


一、技术背景

人脸关键点(Landmark)检测是人脸分析的核心基础,其下游任务包括:

应用 依赖的关键点
人脸对齐(Face Alignment) 眼角、鼻尖、嘴角 5 点
表情识别 眉毛、嘴唇区域变化量
活体检测 眼睛/嘴唇开合幅度
3D 人脸重建 全 68 点三维坐标拟合
AR 特效贴图 实时轮廓区域定位

dlib 提供的 shape_predictor_68_face_landmarks 模型基于级联回归树(Cascade Regression Trees),在检测到人脸框后,迭代回归出 68 个关键点的精确坐标,推理速度快、精度高。


二、68 点分布总览

68 个关键点按照解剖区域划分如下:

区域 索引范围 点数
下颌轮廓 0 -- 16 17
左眉 17 -- 21 5
右眉 22 -- 26 5
鼻梁 27 -- 30 4
鼻底 31 -- 35 5
左眼 36 -- 41 6
右眼 42 -- 47 6
外嘴唇 48 -- 59 12
内嘴唇 60 -- 67 8

点 0 在左侧下颌角,点 16 在右侧下颌角,点 27 在鼻根,索引沿顺时针方向递增。


三、完整检测流程

整个流程分为六步,核心依赖两个 dlib 对象:

  • get_frontal_face_detector() --- 基于 HOG + 线性分类器的人脸检测器,返回人脸框列表
  • shape_predictor(.dat) --- 级联回归树关键点预测器,输入人脸框,输出 68 个 (x, y) 坐标

四、代码一:关键点检测与标注(3关键点检测.py

4.1 初始化检测器与预测器

python 复制代码
import numpy as np
import cv2
import dlib

# HOG人脸检测器(内置,无需加载文件)
detector = dlib.get_frontal_face_detector()

# 68点关键点预测器(需要下载 .dat 文件)
# 下载地址:https://github.com/davisking/dlib-models
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")

.dat 文件说明:模型文件约 99MB,使用级联回归树存储。首次使用需从 GitHub dlib-models 仓库下载,放于脚本同级目录即可。


4.2 检测人脸并预测关键点

python 复制代码
img = cv2.imread("handou2.jpg")

# 参数 0:上采样次数,0=不上采样(速度最快),1=上采样一次(检测更多小人脸)
faces = detector(img, 0)

for face in faces:
    # predictor 返回 dlib.full_object_detection 对象
    shape = predictor(img, face)

    # 将 68 个 dlib.point 对象转为 numpy 数组,shape=(68, 2)
    landmarks = np.array([[p.x, p.y] for p in shape.parts()])

关键参数解析:

参数 含义 推荐值
detector(img, n) 中的 n 上采样次数,增大可检测远处小人脸,但速度成倍下降 0 或 1
shape.parts() 返回 68 个 dlib.point 对象的列表 ---

4.3 绘制关键点与编号

python 复制代码
for idx, point in enumerate(landmarks):
    pos = [point[0], point[1]]

    # 绘制实心圆(绿色,半径 2)
    cv2.circle(img, pos, 2, (0, 255, 0), 1)

    # 在圆旁标注索引编号(红色,抗锯齿字体)
    cv2.putText(img, str(idx), pos,
                cv2.FONT_HERSHEY_SIMPLEX, 0.4,
                (0, 0, 255), 1, cv2.LINE_AA)

cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.LINE_AA(抗锯齿线条) 相比默认的 cv2.LINE_8,文字边缘更平滑,适合小字号渲染。


五、代码二:结构化轮廓绘制(4轮廓绘制.py

5.1 两种绘制方法设计思路

代码定义了两个辅助函数,分别应对不同区域的轮廓特点:


5.2 drawLine:顺序连线(适合开放曲线)

python 复制代码
def drawLine(start, end):
    pts = shape[start:end]          # 切片取对应区域的点集
    for l in range(1, len(pts)):
        ptA = tuple(pts[l - 1])     # 前一个点
        ptB = tuple(pts[l])         # 当前点
        cv2.line(image, ptA, ptB, (0, 255, 0), 2)

适用区域(开放轮廓,有明确起止点):

python 复制代码
drawLine(0,  17)   # 下颌轮廓(左→右弧线)
drawLine(17, 22)   # 左眉(由内向外)
drawLine(22, 27)   # 右眉
drawLine(27, 36)   # 鼻梁 + 鼻底(上→下)

注意shape[start:end] 是 Python 切片,不含 end 位置的点 。因此 drawLine(0, 17) 实际绘制 0~16 共 17 个点之间的 16 段线。


5.3 drawConvexHull:凸包轮廓(适合封闭区域)

python 复制代码
def drawConvexHull(start, end):
    Facial = shape[start:end + 1]          # 含 end(+1 补偿切片)
    mouthHull = cv2.convexHull(Facial)     # 计算凸包
    cv2.drawContours(image, [mouthHull], -1, (0, 255, 0), 2)

适用区域(封闭轮廓,需包裹整体):

python 复制代码
drawConvexHull(36, 41)   # 左眼(6点)
drawConvexHull(42, 47)   # 右眼(6点)
drawConvexHull(48, 59)   # 外嘴唇(12点)
drawConvexHull(60, 67)   # 内嘴唇(8点)

cv2.convexHull 原理:给定一组点,计算能包裹所有点的最小凸多边形(Graham Scan 算法,时间复杂度 O(n log n))。眼眶、嘴唇等区域轮廓近似凸形,因此效果优良。

drawLine vs drawConvexHull 关键区别

  • drawLine(start, end) 中切片 shape[start:end]不含 end
  • drawConvexHull(start, end) 中切片 shape[start:end+1]end(+1 补偿)

5.4 索引速查表


六、两段代码输出效果对比

对比项 3关键点检测.py 4轮廓绘制.py
输出内容 68 个绿色圆点 + 红色编号 连线 + 凸包轮廓(无编号)
视觉效果 调试友好,点位清晰 结构干净,贴近人脸轮廓
适合场景 标注验证、模型调试 特效贴图、视觉展示
信息密度 高(每点均有索引) 低(只保留结构线)

七、完整代码整合

7.1 代码一:关键点检测

python 复制代码
import numpy as np
import cv2
import dlib

img = cv2.imread("handou2.jpg")
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")

faces = detector(img, 0)
for face in faces:
    shape = predictor(img, face)
    landmarks = np.array([[p.x, p.y] for p in shape.parts()])
    for idx, point in enumerate(landmarks):
        pos = [point[0], point[1]]
        cv2.circle(img, pos, 2, (0, 255, 0), 1)
        cv2.putText(img, str(idx), pos,
                    cv2.FONT_HERSHEY_SIMPLEX, 0.4,
                    (0, 0, 255), 1, cv2.LINE_AA)

cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

7.2 代码二:结构化轮廓绘制

python 复制代码
import numpy as np
import dlib
import cv2

image = cv2.imread("handou2.jpg")
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")

def drawLine(start, end):
    pts = shape[start:end]
    for l in range(1, len(pts)):
        cv2.line(image, tuple(pts[l-1]), tuple(pts[l]), (0, 255, 0), 2)

def drawConvexHull(start, end):
    Facial = shape[start:end+1]
    hull = cv2.convexHull(Facial)
    cv2.drawContours(image, [hull], -1, (0, 255, 0), 2)

faces = detector(image, 1)
for face in faces:
    shape = predictor(image, face)
    shape = np.array([[p.x, p.y] for p in shape.parts()])

    # 凸包封闭区域
    drawConvexHull(36, 41)   # 左眼
    drawConvexHull(42, 47)   # 右眼
    drawConvexHull(48, 59)   # 外嘴唇
    drawConvexHull(60, 67)   # 内嘴唇

    # 顺序连线开放区域
    drawLine(0,  17)          # 下颌
    drawLine(17, 22)          # 左眉
    drawLine(22, 27)          # 右眉
    drawLine(27, 36)          # 鼻子

cv2.imshow("image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

八、常见问题与注意事项

Q1:运行报错 RuntimeError: Unable to open shape_predictor_68_face_landmarks.dat

模型文件缺失,从以下地址下载(bz2 压缩包,解压后约 99MB):

复制代码
https://github.com/davisking/dlib-models/raw/master/shape_predictor_68_face_landmarks.dat.bz2

.dat 文件放到 Python 脚本同级目录。


Q2:detector(img, 0)detector(img, 1) 有什么区别?

参数 效果 速度影响
n=0 原始分辨率检测,速度最快 基准
n=1 图像放大 2x 后检测,可找到更小/更远的人脸 约慢 4x
n=2 放大 4x,极小人脸也可检测 约慢 16x

代码一用 n=0(静态图,追求速度);代码二用 n=1(精度优先,确保关键点准确)。


Q3:drawConvexHull 中为什么要写 end+1

Python 切片 a[start:end] 不含 end,而 drawLine 利用了这一点(终止点不需要绘制)。但 drawConvexHull 需要完整的闭合区域点集(如左眼的 6 个点全部参与凸包计算),所以要写 end+1


九、扩展方向

方向 说明
眨眼检测(EAR) 计算眼睛纵横比,EAR < 阈值时判断为闭眼
打哈欠检测(MAR) 计算嘴部开合比,MAR > 阈值判断张嘴
驾驶疲劳监测 结合眨眼频率 + 打哈欠触发告警
人脸对齐 用眼角坐标做仿射变换,将人脸旋转至水平
MediaPipe 替代 谷歌 MediaPipe 提供 468 点检测,精度更高,支持实时

小结

本文完整介绍了 dlib shape_predictor_68 模型的调用链路,核心要点如下:

  1. 两步检测 :先用 get_frontal_face_detector() 找人脸框,再用 shape_predictor 精细化到 68 个点
  2. 坐标转换np.array([[p.x, p.y] for p in shape.parts()]) 是固定范式,转为 numpy 数组便于后续处理
  3. 开放曲线用 drawLine (下颌、眉毛、鼻子),封闭区域用 drawConvexHull(眼睛、嘴唇)
  4. 切片陷阱drawLine[start:end]drawConvexHull[start:end+1],务必区分

掌握 68 点检测后,即可在此基础上快速实现疲劳检测、表情分析、AR 特效等更复杂的人脸应用。

ector()找人脸框,再用shape_predictor 精细化到 68 个点 2. **坐标转换**:np.array([[p.x, p.y] for p in shape.parts()])是固定范式,转为 numpy 数组便于后续处理 3. **开放曲线用drawLine**(下颌、眉毛、鼻子),**封闭区域用 drawConvexHull**(眼睛、嘴唇) 4. **切片陷阱**:drawLine[start:end]drawConvexHull[start:end+1]`,务必区分

掌握 68 点检测后,即可在此基础上快速实现疲劳检测、表情分析、AR 特效等更复杂的人脸应用。

相关推荐
程序媛徐师姐1 天前
Python基于OpenCV的马赛克画的设计与实现【附源码、文档说明】
python·opencv·django·马赛克绘画·python马赛克绘画系统·马赛克画·python马赛克画
sali-tec1 天前
C# 基于OpenCv的视觉工作流-章49-人脸检测
图像处理·人工智能·opencv·算法·计算机视觉
木心术12 天前
设备管理网管系统:详细下一步行动指南
前端·人工智能·opencv
秋月的私语2 天前
遥感影像拼接线优化工具:基于Qt+GDAL+OpenCV的从零到一实践
开发语言·qt·opencv
不懒不懒2 天前
【基于OpenCV+Dlib的人脸相关检测实战:疲劳、年龄性别、表情全实现】
人工智能·opencv·计算机视觉
sali-tec2 天前
C# 基于OpenCv的视觉工作流-章48-图像找荐
人工智能·opencv·计算机视觉
minglie13 天前
zynq环境用opencv测摄像头
人工智能·opencv·计算机视觉
Fleshy数模3 天前
基于MediaPipe实现人体姿态与脸部关键点检测
python·opencv·计算机视觉
輕華3 天前
OpenCV答题卡识别:从图像预处理到自动评分
人工智能·opencv·计算机视觉