dlib库人脸检测

发布时间:2026/9/16 7:25:09
dlib库人脸检测 一.dlib人脸检测一.基于dlib人脸检测的两种方法dlib 的人脸检测功能在计算机视觉领域应用广泛它提供了基于传统机器学习HOG和深度学习CNN的两种成熟方案让开发者可以根据项目需求在速度和精度之间灵活选择。二.核心原理HOG 线性SVM模型传统方法这是 dlib 默认且最常用的检测器其核心流程如下HOG 特征提取先将图像划分为小的细胞单元如8x8像素计算每个单元内像素的梯度方向直方图以此来捕捉人脸的边缘和轮廓特征如眼睛、鼻子、嘴巴的形状。滑动窗口检测使用一个固定大小的窗口如64x64像素在图像上滑动并对每个窗口内的 HOG 特征进行分类。SVM 分类器dlib 使用预训练的线性支持向量机SVM来判断窗口内的特征是否构成人脸。SVM 通过寻找一个最优超平面来区分人脸和非人脸区域。多尺度与后处理为了检测不同大小的人脸dlib 会构建图像金字塔在多个缩放尺度上运行检测器。最后使用非极大值抑制NMS来合并重叠的检测框保留置信度最高的结果。CNN模型深度学习方法这是 dlib 提供的基于深度学习的高精度检测器其底层架构为 MMODMax-Margin Object Detection。它使用卷积神经网络自动学习人脸的高层语义特征能够直接预测人脸边界框的坐标和置信度因此在复杂场景下表现更好。三.使用方法1.导入dlib库创建人脸检测器使用H0G算法、线性分类器、金字塔图像结构和滑动窗口检测等技术读入图片img。import dlib detector dlib.get_frontal_face_detector() img cv2.imread(reba2.webp)2.使用人脸检测器检测图像中的人脸返回检测到的人脸框。detectorimage待检测的可能含有人脸的图像。n表示采用上采样的次数。上采样会让图像变大能够检测到更多人脸对象提高小人脸的检测效果。通常建议将此参数设置为 0 或 1。较大的值会增加检测的准确性但会降低处理速度。faces返回检测图像中的所有人脸框。faces detector(img, 0)3.遍历人脸框获取每个人脸框的坐标分别绘制出每张人脸。for face in faces: # 获取人脸框的坐标 x1 face.left() y1 face.top() x2 face.right() y2 face.bottom() # 绘制人脸 cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)4.显示捕获到的人脸框cv2.imshow(img, img) cv2.waitKey(0) cv2.destroyAllWindows()5.运行结果效果不错二.dlib与opencv人脸检测的对比下表从算法原理、性能表现和适用场景等维度对 OpenCV 与 dlib 的常见人脸检测方案进行对比方案算法原理特点OpenCV Haar 级联基于图像灰度值的矩形特征差异使用 AdaBoost 级联分类器经典快速但对光照、姿态和遮挡非常敏感误检率相对较高OpenCV DNN 模块一个推理引擎可加载预训练的深度学习模型如 SSD、Caffe精度高、鲁棒性强通常作为现代应用的首选dlib HOG SVM提取图像的梯度方向直方图特征使用线性 SVM 分类CPU 上的速度标杆在正面和轻微非正面人脸上表现稳健dlib CNN (MMOD)基于最大边距目标检测的深度学习模型精度极高对遮挡、姿态变化鲁棒但在无 GPU 加速时速度很慢检测人脸时的区别opencv检测到的人脸范围较大dlib检测到的人脸范围较小可以看到左边把额头大部分都框进去了右边却没有。三.dlib实现关键点定位这里需要加载 dlib 官方预训练的68 点人脸关键点检测器。它的核心作用不是判断“图里有没有人脸”而是在已经知道人脸位置矩形框的前提下进一步定位人脸上的68 个特征点坐标。 包括眼睛、鼻子、眉毛、轮廓等shape_predictor_68_face_landmarks.dat使用方法1.导入opencvdlibnumpy库import cv2 import dlib import numpy as np2.加载图片生成人脸检测器对图片进行人脸检测返回检测图像中的所有人脸载入模型(加载预测器人脸68关键点检测器)img cv2.imread(./reba2.webp) detector dlib.get_frontal_face_detector() faces detector(img, 0) predictor dlib.shape_predictor(./shape_predictor_68_face_landmarks.dat)3.遍历每一张人脸获取他们的关键点将关键点转换为坐标(xy)的形式for face in faces: # 获取每一张脸的关键点(实现检测) shape predictor(img, face) # 获取关键点 # 将关键点转换为坐标(xy)的形式 # landmarks np.matrix([[p.x, p.y] for p in shape.parts()]) landmarks np.array([[p.x, p.y] for p in shape.parts()])4.绘制每一张脸的关键点(绘制shape中的每个点)for idx, point in enumerate(landmarks): # 当前关键点的坐标 pos [point[0], point[1]] # 针对当前关键点绘制一个实心圆 cv2.circle(img, pos, 2, (0, 255, 0), thickness-1) # 普通大小的等宽字体 cv2.putText(img, str(idx), pos, cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1, cv2.LINE_AA) # 线条类型:抗齿线条cv2.putText 用于在图像上绘制文本各参数含义如下img要绘制文本的目标图像。str(idx)要绘制的文本内容这里将当前关键点的索引 idx 转为字符串用于标注每个关键点的编号。pos文本左下角的起始坐标即当前关键点的位置 (x, y)。cv2.FONT_HERSHEY_SIMPLEX字体类型表示使用普通大小的衬线字体。0.4字体缩放比例数值越小文字越小这里用于在关键点附近绘制较小的编号。(255, 255, 255)文本颜色这里为白色BGR 顺序。1线条粗细即文字的笔画宽度数值越大文字越粗。cv2.LINE_AA线条类型表示使用抗锯齿线条让文字边缘更平滑。5.展示图片cv2.imshow(img, img) cv2.waitKey(0) cv2.destroyAllWindows()6.运行结果四.轮廓绘制使用方法1.导入库import numpy as np import dlib import cv22.读取图片创建人脸位置检测器检测人脸读取人脸关键点定位模型image cv2.imread(reba2.webp) detector dlib.get_frontal_face_detector() # 构造脸部位置检测器 faces detector(image, 0) # 检测人脸方位置 # 读取人脸关键点定位模型 predictor dlib.shape_predictor(./shape_predictor_68_face_landmarks.dat)3.遍历人脸获取关键点位置将关键点转换为坐标xy的形式分别绘制凸包和点线。凸包Convex Hull在平面几何中凸包是指包含一组点集的最小凸多边形。直观理解可以想象用一根橡皮筋把所有点紧紧箍住橡皮筋最终形成的形状就是这组点的凸包。凸包上的任意两点连线都完全落在凸包内部或边界上因此它能够简洁地勾勒出点集的外围轮廓。在人脸关键点定位中凸包常用于将离散的关键点连接成平滑的轮廓区域。例如将眼睛周围的关键点如索引 36 到 41求凸包后可以绘制出覆盖整个眼部的闭合区域从而更直观地展示眼睛、嘴巴等部位的形状和范围。for face in faces: # 对检测到的rects逐个遍历 shape predictor(image, face) # 获取关键点 # 将关键点转换为坐标(x,y)的形式 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) /code/pre函数1drawConvexHull(start, end)这是一个自定义函数用于将指定范围内的关键点构成一个凸包并绘制成轮廓。一般眼睛、嘴巴等部位使用凸包来绘制各参数含义如下start起始关键点的索引表示从第几个关键点开始取点。end结束关键点的索引表示取到第几个关键点为止。Facial shape[start : end1]从全部 68 个关键点中截取索引从 start 到 end 的这一段点集。因为切片是左闭右开所以结束位置要写成 end1 才能把第 end 个点包含进来。mouthHull cv2.convexHull(Facial)调用 OpenCV 的凸包函数对截取出的点集求凸包得到包围这些点的最小凸多边形顶点集合。cv2.drawContours(image, [mouthHull], -1, (0, 255, 0), 2)把求得的凸包绘制到图像上。其中 image 是目标图像[mouthHull] 表示要绘制的轮廓列表-1 表示绘制所有轮廓颜色为绿色 (0, 255, 0)线宽为 2 像素。def drawConvexHull(start,end): # 将指定的点构成一个凸包绘制成轮廓一般眼睛、嘴使用凸包用来绘制 Facial shape[start: end1] mouthHull cv2.convexHull(Facial) # 凸包函数 cv2.drawContours(image, [mouthHull], -1, (0, 255, 0), 2)函数2drawLine(start, end)这是一个自定义函数用于将指定范围内的关键点按顺序用线段连接起来绘制成点线轮廓。一般眉毛、鼻子、脸颊等部位使用点线来绘制各参数含义如下start起始关键点的索引表示从第几个关键点开始取点。end结束关键点的索引表示取到第几个关键点为止。pts shape[start : end]从全部 68 个关键点中截取索引从 start 到 end-1 的这一段点集。因为切片是左闭右开所以第 end 个点不会被包含进来。for l in range(1, len(pts))从第 1 个点开始遍历到最后一个点每次取相邻的两个点来画线。ptA tuple(pts[l - 1])取当前线段的前一个点并转换为元组形式作为线段的起点。ptB tuple(pts[l])取当前线段的后一个点并转换为元组形式作为线段的终点。cv2.line(image, ptA, ptB, (0, 255, 0), 2)在图像上从 ptA 到 ptB 绘制一条直线。其中 image 是目标图像颜色为绿色 (0, 255, 0)线宽为 2 像素。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)4.展示图片cv2.imshow(Frame, image) cv2.waitKey(0) cv2.destroyAllWindows()5.运行结果五.笑容检测使用方法1.导包import numpy as np import dlib import cv2 from sklearn.metrics.pairwise import euclidean_distances # 计算欧氏距离 from PIL import Image, ImageDraw, ImageFont2.创建人脸检测器 检测人脸读取人脸关键点定位模型读入两张图片frame1frame2检测人脸返回faces12.detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(./shape_predictor_68_face_landmarks.dat) frame1 cv2.imread(2.jpg) frame2 cv2.imread(3.jpg) faces1 detector(frame1, 0) faces2 detector(frame2, 0)3.遍历人脸获取关键点位置将关键点转换为坐标xy的形式调用MAR函数计算嘴部的高宽比调用MJR函数计算嘴宽/脸颊宽默认表情为正常当我们的嘴部的高宽比大于0.5表情变为大笑当我们的嘴宽/脸颊宽大于0.45表情为微笑将嘴部形状绘制为凸包把我们的中文文字添加到图片上最后画出轮廓。for face in faces1: shape predictor(frame1, face) # 获取关键点 # 将关键点转换为坐标(x, y)的形式 shape np.array([[p.x, p.y] for p in shape.parts()]) mar MAR(shape) # 计算嘴部的高宽比 mjr MJR(shape) # 计算“嘴宽/脸颊宽” result 正常 # 默认是正常表情 print(mar, mar, \tmjr, mjr) # 测试一下实际值可以根据该值确定 if mar 0.5: # 可更具项目要求调整阈值。 result大笑 elif mjr 0.45: # 超过闽值为微笑 result 微笑 mouthHull cv2.convexHull(shape[48:61]) # 构造嘴型凸包 frame1 cv2AddChineseText(frame1, result, mouthHull[0,0]) # 多人脸 cv2.drawContours(frame1, [mouthHull], -1, (0, 255, 0), 1)函数1MAR(shape)这是一个自定义函数用于计算嘴部的高宽比Mouth Aspect Ratio通过比较嘴部关键点的纵向距离与横向距离来判断嘴巴的张开程度从而辅助识别笑容。各参数含义如下shape包含 68 个人脸关键点坐标的数组每个元素为 (x, y) 形式的坐标。A euclidean_distances(shape[50].reshape(1, 2), shape[58].reshape(1, 2))计算第 50 号关键点与第 58 号关键点之间的欧氏距离即嘴部左侧上下唇之间的垂直距离。B euclidean_distances(shape[51].reshape(1, 2), shape[57].reshape(1, 2))计算第 51 号关键点与第 57 号关键点之间的欧氏距离即嘴部中间上下唇之间的垂直距离。C euclidean_distances(shape[52].reshape(1, 2), shape[56].reshape(1, 2))计算第 52 号关键点与第 56 号关键点之间的欧氏距离即嘴部右侧上下唇之间的垂直距离。D euclidean_distances(shape[48].reshape(1, 2), shape[54].reshape(1, 2))计算第 48 号关键点与第 54 号关键点之间的欧氏距离即嘴部左右两侧嘴角之间的水平距离嘴宽。return ((A B C) / 3) / D先对 A、B、C 三个垂直距离取平均值得到嘴部的平均高度再除以嘴宽 D得到嘴部的高宽比。嘴巴张得越大该比值越大因此可通过设定阈值如 0.5来判断是否在大笑。def MAR(shape): A euclidean_distances(shape[50].reshape(1, 2), shape[58].reshape(1, 2)) B euclidean_distances(shape[51].reshape(1, 2), shape[57].reshape(1, 2)) C euclidean_distances(shape[52].reshape(1, 2), shape[56].reshape(1, 2)) D euclidean_distances(shape[48].reshape(1, 2), shape[54].reshape(1, 2)) return ((A B C) / 3) / D函数2MJR(shape)这是一个自定义函数用于计算嘴宽与脸颊宽的比值Mouth-to-Jaw Ratio通过比较嘴部宽度与下颌宽度来判断嘴巴的横向张开程度从而辅助识别微笑。各参数含义如下shape包含 68 个人脸关键点坐标的数组每个元素为 (x, y) 形式的坐标。M euclidean_distances(shape[48].reshape(1, 2), shape[54].reshape(1, 2))计算第 48 号关键点与第 54 号关键点之间的欧氏距离即嘴部左右两侧嘴角之间的水平距离嘴宽。J euclidean_distances(shape[3].reshape(1, 2), shape[13].reshape(1, 2))计算第 3 号关键点与第 13 号关键点之间的欧氏距离即下颌左右两侧之间的宽度脸颊宽。return M / J用嘴宽 M 除以脸颊宽 J得到嘴宽与脸颊宽的比值。微笑时嘴角向两侧拉伸嘴宽相对脸颊变宽该比值会增大因此可通过设定阈值如 0.45来判断是否为微笑。def MJR(shape): M euclidean_distances(shape[48].reshape(1, 2), shape[54].reshape(1, 2)) # 嘴宽度 J euclidean_distances(shape[3].reshape(1, 2), shape[13].reshape(1, 2)) # 下颌的宽度 return M / J函数3cv2AddChineseText(img, text, position, textColor, textSize)这是一个自定义函数用于在 OpenCV 图像上绘制中文文本。由于 OpenCV 自带的 cv2.putText 不支持中文因此借助 PILPillow库来实现中文的绘制。各参数含义如下img要绘制中文的目标图像通常为 OpenCV 读取的 BGR 格式图像。text要绘制的文本内容这里传入的是表情识别结果如“正常”“微笑”“大笑”等中文文字。position文本绘制的起始坐标即文本左上角在图像上的位置 (x, y)。textColor文本颜色默认值为 (0, 255, 0)即绿色RGB 顺序。textSize字体大小默认值为 30数值越大文字越大。isinstance(img, np.ndarray)判断传入的图像是否为 OpenCV 的 numpy 数组类型。如果是则说明是 OpenCV 图像需要先转换为 PIL 图像才能绘制中文。cv2.cvtColor(img, cv2.COLOR_BGR2RGB)将 OpenCV 的 BGR 颜色空间转换为 PIL 使用的 RGB 颜色空间避免颜色显示错乱。Image.fromarray(...)将 numpy 数组转换为 PIL 的 Image 对象。ImageDraw.Draw(img)在 PIL 图像上创建一个绘图对象后续的文本绘制操作都通过它来完成。ImageFont.truetype(simsun.ttc, textSize, encodingutf-8)加载中文字体文件宋体 simsun.ttc并指定字体大小和 utf-8 编码确保中文能够正常显示。draw.text(position, text, textColor, fontfontStyle)在指定位置绘制文本其中 position 是坐标text 是内容textColor 是颜色font 是字体样式。cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)绘制完成后将 PIL 图像转换回 numpy 数组并把 RGB 颜色空间转换回 OpenCV 使用的 BGR 格式最后返回给调用方继续处理def cv2AddChineseText(img, text, position, textColor(0, 255, 0), textSize30): 向图片中添加中文 if (isinstance(img, np.ndarray)): # 判断是否0penCV图片类型 img Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) # 实现array到image的转换 draw ImageDraw.Draw(img) # 在img图片上创建一个绘图的对象 # 字体的格式 fontStyle ImageFont.truetype(simsun.ttc, textSize, encodingutf-8) draw.text(position, text, textColor, fontfontStyle) # 绘制文本 return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR) # 转换回0penCV格式4.展示图片cv2.imshow(Frame1, frame1) cv2.imshow(Frame2, frame2) cv2.waitKey(0) cv2.destroyAllWindows()5.运行结果