一、前言
在计算机视觉入门项目中,人脸相关应用最容易上手、效果也最直观。本文基于 OpenCV + Dlib,分三部分实现:
- 实时表情识别(正常 / 微笑 / 大笑)
- 高精度人脸检测(Dlib CNN 检测器)
- 年龄与性别实时预测(OpenCV DNN 预训练模型)
二、环境配置
python
pip install opencv-python numpy dlib pillow scikit-learn
模块一:基于 Dlib 关键点的实时表情识别
1. 实现思路
- 使用 Dlib 68 点人脸关键点定位
- 提取嘴部区域关键点
- 计算两个比值:
- MAR:嘴巴宽高比 → 判断是否大笑
- MJR:嘴宽 / 脸颊宽 → 判断是否微笑
- 绘制嘴部轮廓 + 实时显示表情结果
- 解决 OpenCV 中文乱码问题
2. 完整代码
python
import numpy as np
import dlib
import cv2
from sklearn.metrics.pairwise import euclidean_distances
from PIL import Image, ImageDraw, ImageFont
# 计算嘴的宽高比 MAR
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
# 计算嘴宽度 / 脸颊宽度比值 MJR
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
# OpenCV 显示中文
def cv2AddChineseText(img, text, position, textColor=(0, 255, 0), textSize=30):
if (isinstance(img, np.ndarray)):
img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img)
fontStyle = ImageFont.truetype("simsun.ttc", textSize, encoding="utf-8")
draw.text(position, text, textColor, font=fontStyle)
return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
# 初始化检测器
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()
faces = detector(frame, 0)
for face in faces:
shape = predictor(frame, face)
shape = np.array([[p.x, p.y] for p in shape.parts()])
mar = MAR(shape)
mjr = MJR(shape)
result = "正常"
if mar > 0.5:
result = "大笑"
elif mjr > 0.45:
result = "微笑"
# 绘制嘴部轮廓
mouthHull = cv2.convexHull(shape[48:61])
frame = cv2AddChineseText(frame, result, mouthHull[0,0])
cv2.drawContours(frame, [mouthHull], -1, (0, 255, 0), 1)
cv2.imshow("Frame", frame)
if cv2.waitKey(1) == 27:
break
cv2.destroyAllWindows()
cap.release()
3. 效果说明
- 正常:无明显表情
- 微笑:嘴角轻微上扬
- 大笑:嘴巴明显张开
- 实时绘制嘴部轮廓,支持多人脸
模块二:Dlib CNN 高精度人脸检测
1. 实现思路
- 使用 Dlib 官方 CNN 人脸检测器:
mmod_human_face_detector.dat - 对图片进行人脸检测
- 绘制人脸矩形框
- 适合复杂场景、侧脸、遮挡场景检测
2. 完整代码
python
import dlib
import cv2
# 加载 CNN 人脸检测器
cnn_face_detector = dlib.cnn_face_detection_model_v1("mmod_human_face_detector.dat")
# 读取图片
img = cv2.imread("people1.png")
# 检测人脸
faces = cnn_face_detector(img, 0)
# 遍历绘制框
for d in faces:
rect = d.rect
left = rect.left()
top = rect.top()
right = rect.right()
bottom = rect.bottom()
cv2.rectangle(img, (left, top), (right, bottom), (0, 255, 0), 3)
cv2.imshow("result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
3. 特点
- 比传统 HOG 检测器更准
- 对遮挡、小角度人脸更鲁棒
- 适合静态图片高精度检测
模块三:基于 OpenCV DNN 的年龄与性别实时预测
1. 实现思路
- 使用 OpenCV DNN 加载 Caffe 预训练模型
- 先检测人脸区域
- 对人脸区域分别预测性别与年龄
- 实时在画面上标注中文结果
- 支持摄像头实时检测
2. 完整代码
python
import cv2
from PIL import Image, ImageDraw, ImageFont
import numpy as np
# ================== 模型路径 ==================
faceProto = "model/opencv_face_detector.pbtxt"
faceModel = "model/opencv_face_detector_uint8.pb"
ageProto = "model/deploy_age.prototxt"
ageModel = "model/age_net.caffemodel"
genderProto = "model/deploy_gender.prototxt"
genderModel = "model/gender_net.caffemodel"
# 加载网络
ageNet = cv2.dnn.readNet(ageModel, ageProto)
genderNet = cv2.dnn.readNet(genderModel, genderProto)
faceNet = cv2.dnn.readNet(faceModel, faceProto)
# 年龄与性别列表
ageList = ['0-2岁', '4-6岁', '8-12岁', '15-20岁', '25-32岁', '38-43岁', '48-53岁', '60-100岁']
genderList = ['男性', '女性']
mean = (78.4263377603, 87.7689143744, 114.895847746)
# 获取人脸框
def getBoxes(net, frame):
frameHeight, frameWidth = frame.shape[:2]
blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300), [104, 117, 123], True, False)
net.setInput(blob)
detections = net.forward()
faceBoxes = []
for i in range(detections.shape[2]):
confidence = detections[0, 0, i, 2]
if confidence > 0.7:
x1 = int(detections[0, 0, i, 3] * frameWidth)
y1 = int(detections[0, 0, i, 4] * frameHeight)
x2 = int(detections[0, 0, i, 5] * frameWidth)
y2 = int(detections[0, 0, i, 6] * frameHeight)
faceBoxes.append([x1, y1, x2, y2])
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
return frame, faceBoxes
# 中文显示
def cv2AddChineseText(img, text, position, textColor=(0, 255, 0), textSize=30):
if (isinstance(img, np.ndarray)):
img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img)
fontStyle = ImageFont.truetype("simsun.ttc", textSize, encoding="utf-8")
draw.text(position, text, textColor, font=fontStyle)
return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
# 打开摄像头
cap = cv2.VideoCapture(0)
while True:
_, frame = cap.read()
frame = cv2.flip(frame, 1)
frame, faceBoxes = getBoxes(faceNet, frame)
if not faceBoxes:
print("当前镜头中没有人")
continue
for faceBox in faceBoxes:
x1, y1, x2, y2 = faceBox
face = frame[y1:y2, x1:x2]
blob = cv2.dnn.blobFromImage(face, 1.0, (227, 227), mean)
# 预测性别
genderNet.setInput(blob)
gender = genderList[genderNet.forward()[0].argmax()]
# 预测年龄
ageNet.setInput(blob)
age = ageList[ageNet.forward()[0].argmax()]
# 显示结果
result = "{},{}".format(gender, age)
frame = cv2AddChineseText(frame, result, (x1, y1 - 30))
cv2.imshow("result", frame)
if cv2.waitKey(1) == 27:
break
cv2.destroyAllWindows()
cap.release()
3. 效果说明
- 实时框选人脸
- 显示:男性 / 女性 + 年龄段
- 支持多人脸、实时摄像头
- 中文清晰不乱码
三、项目总结
本文分三大模块完整实现:
- 表情识别:基于 68 关键点,判断正常、微笑、大笑
- 人脸检测:Dlib CNN 高精度检测,适合复杂场景
- 年龄性别预测:OpenCV DNN 预训练模型,实时预测