在计算机视觉领域,实时人脸相关检测技术广泛应用于安防、车载、智能交互等场景。本文将分享两个实用的视觉检测项目------实时年龄性别检测和驾驶员疲劳检测,从技术原理到代码实现,带你快速掌握基于OpenCV的视觉检测开发。
一、项目背景与技术栈
1. 核心技术栈
• OpenCV:用于图像/视频读取、预处理、人脸检测、绘图等核心操作
• DNN模块:加载预训练模型实现年龄和性别预测
• dlib:人脸关键点检测(疲劳检测核心)
• NumPy:数值计算,处理坐标和矩阵运算
• PIL:解决OpenCV中文显示乱码问题
2. 应用场景
• 年龄性别检测:智能门禁、客流分析、个性化推荐
• 疲劳检测:车载安全系统、工业操作岗监控、防疲劳驾驶
二、实战1:实时年龄与性别检测
技术原理
-
人脸检测:通过OpenCV的DNN模块加载预训练的人脸检测模型,定位画面中的人脸区域(人脸包围框);
-
特征提取与预测:将检测到的人脸区域预处理后,输入预训练的年龄/性别分类模型,输出预测结果;
-
结果可视化:在人脸区域上方绘制中文标注,实时展示性别和年龄区间。
核心代码解析
- 模型加载与参数配置
python
import cv2
from PIL import Image, ImageDraw, ImageFont
import numpy as np
# 预训练模型路径
faceProto = "model1/opencv_face_detector.pbtxt"
faceModel= "model1/opencv_face_detector_uint8.pb"
ageProto = "model1/deploy_age.prototxt"
ageModel = "model1/age_net.caffemodel"
genderProto= "model1/deploy_gender.prototxt"
genderModel = "model1/gender_net.caffemodel"
# 加载DNN网络
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) # 模型输入均值(标准化用)
- 人脸检测函数
通过DNN模型检测人脸,返回绘制了人脸框的画面和人脸坐标列表:
python
def getBoxes(net,frame):
frameHeight,frameWidth = frame.shape[:2]
# 将图像转换为DNN输入的blob格式
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),int(round(frameHeight/150)),6)
return frame, faceBoxes
- 中文显示解决(OpenCV原生不支持中文)
python
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)
# 加载宋体字体(需确保系统有simsun.ttc文件)
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)
- 主循环(实时检测)
python
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)
genderOuts = genderNet.forward()
gender = genderList[genderOuts[0].argmax()]
# 年龄预测
ageNet.setInput(blob)
ageOuts = ageNet.forward()
age = ageList[ageOuts[0].argmax()]
# 绘制结果
result = f"{gender},{age}"
frame = cv2AddChineseText(frame,result,(x1,y1-30))
cv2.imshow("年龄性别检测",frame)
if cv2.waitKey(1) == 27: # ESC键退出
break
cv2.destroyAllWindows()
cap.release()
运行注意事项
-
需下载预训练模型文件(opencv_face_detector系列、age_net、gender_net),放在model1目录下;
-
确保系统有simsun.ttc字体文件(Windows默认有,Linux需手动安装);
-
摄像头权限需开启,且无其他程序占用。
三、实战2:驾驶员疲劳检测
技术原理
疲劳检测的核心是眼睛纵横比(EAR):
• 眼睛睁开时,EAR值较高;闭眼时,EAR值会骤降;
• 当EAR值持续低于阈值(如0.3),判定为疲劳状态,触发警报。
- EAR计算函数
python
import numpy as np
import dlib
import cv2
from sklearn.metrics.pairwise import euclidean_distances
def eye_aspect_ratio(eye):
# 计算垂直方向两点距离
A = euclidean_distances(np.array(eye[1]), np.array(eye[5]))
B = euclidean_distances(np.array(eye[2]), np.array(eye[4]))
# 计算水平方向两点距离
C = euclidean_distances(np.array(eye[0]), np.array(eye[3]))
# EAR公式:垂直距离均值 / 水平距离
ear = ((A + B) / 2.0) / C
return ear
- 人脸关键点检测与疲劳判断
python
# 初始化dlib检测器和关键点预测器
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
COUNTER = 0 # 闭眼帧数计数器
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
if not ret:
break
faces = detector(frame, 0)
for rect in faces:
# 获取68个人脸关键点
shape = predictor(frame, rect)
shape = np.matrix([[p.x, p.y] for p in shape.parts()])
# 提取左右眼关键点(68点模型固定索引)
rightEye = shape[36:42]
leftEye = shape[42:48]
# 计算EAR值
rightEAR = eye_aspect_ratio(rightEye)
leftEAR = eye_aspect_ratio(leftEye)
ear = (leftEAR + rightEAR) / 2.0
# 疲劳判断:EAR<0.3视为闭眼,累计50帧触发警报
if ear < 0.30:
COUNTER += 1
if COUNTER >= 50:
frame = cv2AddChineseText(frame, "!!!!危险!!!!", (250, 250), (0, 0, 255), 40)
else:
COUNTER = 0 # 睁眼则重置计数器
# 绘制眼睛轮廓
eyeHull = cv2.convexHull(leftEye)
cv2.drawContours(frame, [eyeHull], -1, (0, 255, 0), 1)
eyeHull = cv2.convexHull(rightEye)
cv2.drawContours(frame, [eyeHull], -1, (0, 255, 0), 1)
# 显示EAR值
info = f"EAR: {ear[0][0]:.2f}"
frame = cv2AddChineseText(frame, info, (0, 30))
cv2.imshow("疲劳检测", frame)
if cv2.waitKey(1) == 27:
break
cv2.destroyAllWindows()
cap.release()
运行注意事项
-
需下载shape_predictor_68_face_landmarks.dat文件(dlib人脸关键点模型);
-
EAR阈值可根据实际场景调整(如光线差的环境可适当降低);
-
帧数阈值(50帧)可根据摄像头帧率调整(默认25帧/秒,50帧约2秒)。
四、项目优化方向
- 性能优化:
◦ 降低图像分辨率(如640x480)减少计算量;
◦ 使用多线程/多进程分离检测和绘制逻辑;
- 精度提升:
◦ 年龄检测:增加更多年龄区间,或使用回归模型替代分类;
◦ 疲劳检测:结合嘴巴开合度(MAR)、头部姿态等多特征判断;
- 功能扩展:
◦ 年龄性别检测:添加人数统计、人脸识别绑定;
◦ 疲劳检测:增加声音警报、截图保存、远程推送等功能;
- 兼容性优化:
◦ 适配不同摄像头分辨率;
◦ 增加模型文件缺失的异常处理。
五、总结
本文通过两个实战项目,展示了OpenCV在实时视觉检测中的应用:年龄性别检测基于DNN预训练模型,核心是人脸检测+分类预测;疲劳检测基于人脸关键点的EAR特征,核心是行为特征提取+阈值判断。
这些技术不仅能快速落地到实际项目中,也能作为计算机视觉入门的经典案例。掌握这些基础后,可进一步探索更复杂的场景,如表情识别、行为分析、多目标追踪等。