OpenCV实时检测人脸、年龄和性别(附Python代码+模型详解)
本文面向 OpenCV 初学者,手把手教你写一个实时摄像头程序,自动检测人脸,并预测每个人的年龄和性别。文中会详细解释模型文件、代码逻辑和常见问题。
一、效果展示
运行程序后,摄像头会实时显示画面,每个人脸会被绿色框标出,并标注出性别(boy/girl)和年龄段(如 25-32years)。就像下面这样(示意图):
[摄像头画面] ┌──────────────────────┐ │ ┌─────┐ │ │ │ boy,│ │ │ │25-32│ │ │ └─────┘ │ │ │ └──────────────────────┘
二、准备工作
2.1 安装 Python 库
pip install opencv-python pillow numpy
-
opencv-python:图像处理、摄像头、深度学习推理 -
pillow:在图片上绘制中文文字 -
numpy:OpenCV 底层依赖
2.2 下载预训练模型文件
你需要以下 6 个文件(放在项目的 model/ 目录下):
| 用途 | 结构文件 | 权重文件 |
|---|---|---|
| 人脸检测 | opencv_face_detector.pbtxt |
opencv_face_detector_uint8.pb |
| 性别预测 | deploy_gender.prototxt |
gender_net.caffemodel |
| 年龄预测 | deploy_age.prototxt |
age_net.caffemodel |
这些文件在 OpenCV 官方仓库或 CSDN 上都能搜到,注意文件名必须完全一致。
2.3 中文字体(可选)
如果你希望显示中文,请准备一个中文字体文件(如 simsun.ttc),放在 font/ 目录下。
如果你不想折腾,可以直接显示英文(我会在代码中给出两种方案)。
三、完整代码(可直接运行)
将以下代码保存为 age_gender_live.py:
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
# ---------- 1. 加载模型 ----------
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"
faceNet = cv2.dnn.readNet(faceModel, faceProto)
ageNet = cv2.dnn.readNet(ageModel, ageProto)
genderNet = cv2.dnn.readNet(genderModel, genderProto)
# ---------- 2. 定义标签和均值 ----------
ageList = ['0-2years','3-6years','8-12years','15-20years',
'25-32years','38-43years','48-53years','60-100']
genderList = ['boy','girl']
mean = (78.426, 87.76891, 114.8958477) # 年龄/性别模型需要的均值
# ---------- 3. 人脸检测函数(带画框)----------
def getFaceBoxes(net, frame):
h, w = frame.shape[:2]
blob = cv2.dnn.blobFromImage(frame, 1.0, (300,300), [104,117,123], swapRB=True)
net.setInput(blob)
detections = net.forward() # 形状 (1,1,N,7)
faceBoxes = []
for i in range(detections.shape[2]):
conf = detections[0,0,i,2]
if conf > 0.7:
x1 = int(detections[0,0,i,3] * w)
y1 = int(detections[0,0,i,4] * h)
x2 = int(detections[0,0,i,5] * w)
y2 = int(detections[0,0,i,6] * h)
faceBoxes.append([x1,y1,x2,y2])
cv2.rectangle(frame, (x1,y1), (x2,y2), (0,255,0), 2)
return frame, faceBoxes
# ---------- 4. 中文绘制函数(可选)----------
def drawChinese(img, text, pos, color=(0,255,0), size=25):
img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img_pil)
font = ImageFont.truetype("font/simsun.ttc", size, encoding="utf-8")
draw.text(pos, text, color, font=font)
return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
# ---------- 5. 主程序:摄像头实时推理 ----------
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("无法打开摄像头")
exit()
while True:
ret, frame = cap.read()
if not ret:
break
frame = cv2.flip(frame, 1) # 水平镜像,更像照镜子
# 步骤1:人脸检测
frame, faceBoxes = getFaceBoxes(faceNet, frame)
if len(faceBoxes) == 0:
cv2.imshow("Age Gender Predict", frame)
if cv2.waitKey(1) == 27:
break
continue
# 步骤2:对每个人脸预测年龄和性别
for (x1,y1,x2,y2) in faceBoxes:
face = frame[y1:y2, x1:x2] # 切片裁剪人脸区域
if face.size == 0:
continue
# 准备输入(年龄/性别模型要求 227x227)
blob = cv2.dnn.blobFromImage(face, 1.0, (227,227), mean, swapRB=False)
# 性别预测
genderNet.setInput(blob)
genderPred = genderNet.forward()
gender = genderList[genderPred[0].argmax()]
# 年龄预测
ageNet.setInput(blob)
agePred = ageNet.forward()
age = ageList[agePred[0].argmax()]
# 显示结果
label = f"{gender},{age}"
# 如果你没有中文字体,用下面这行英文显示:
# cv2.putText(frame, f"{gender} {age}", (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2)
frame = drawChinese(frame, label, (x1, y1-25))
cv2.imshow("Age Gender Predict", frame)
if cv2.waitKey(1) == 27: # ESC 退出
break
cap.release()
cv2.destroyAllWindows()
四、核心知识点详解(小白必看)
4.1 预训练模型:什么是 .pbtxt 和 .pb?
-
.pbtxt:网络的结构文件(相当于建筑图纸) -
.pb或.caffemodel:网络的权重文件(相当于钢筋混凝土和装修)
两者缺一不可。cv2.dnn.readNet(权重, 结构) 的作用就是加载别人已经训练好的模型,我们直接使用,不需要自己训练。
4.2 detections 到底长什么样?
人脸检测网络的输出 detections 是一个四维数组,形状为 (1, 1, N, 7):
-
前两维固定为 1(batch 和 class 维度)
-
第三维 N 表示检测到的人脸候选框数量
-
第四维 7 表示每个框的 7 个信息:
[图片ID, 类别ID, 置信度, x1_norm, y1_norm, x2_norm, y2_norm]
所以提取第 i 个框的置信度要写成 detections[0, 0, i, 2],而不是 detections[i][2]。
4.3 frame 是什么?为什么能 frame[y1:y2, x1:x2]?
在 OpenCV 中,frame 是一个 NumPy 三维数组,形状为 (高, 宽, 3),存储着 BGR 三个通道的像素值。
对数组进行切片 [y1:y2, x1:x2] 就能截取出矩形区域——也就是人脸部分的图像,赋值给 face 变量,供后续模型使用。
4.4 blobFromImage 做了什么?
将原始图像转换成神经网络可接受的格式:
-
缩放到模型要求的尺寸(人脸检测 300×300,年龄性别 227×227)
-
减去均值(使数据分布接近训练时的状态)
-
可选交换通道顺序(BGR ↔ RGB)
-
输出四维数组
[1, 3, H, W]
4.5 如何从模型输出得到性别和年龄?
性别模型输出一个 (1,2) 的数组,例如 [[0.2, 0.8]],分别表示 [boy, girl] 的概率。argmax() 返回最大值的索引(这里是 1),再从 genderList 中取出对应标签 'girl'。
年龄模型同理,输出 (1,8),对应 ageList 中的 8 个年龄段。
四、常见问题与解决方案
Q1:运行时提示“无法找到模型文件”
A:检查路径。可以把模型文件放在脚本同级的 model/ 文件夹内,或者使用绝对路径。
Q2:中文显示为方块或报错
A:要么将正确的字体文件放到 font/ 目录下,要么改用英文显示(代码中已给出备选)。
Q3:检测不到人脸或者误检太多
A:调整置信度阈值。代码中使用 >0.7,可以尝试降低到 0.5(提高召回率)或升高到 0.9(提高精确率)。
Q4:程序很卡,CPU 占用高
A:年龄性别模型比较消耗性能。可以:
-
降低摄像头分辨率:
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320) -
每隔 3 帧才做一次年龄性别预测,其余帧只做人脸检测
五、总结与拓展
通过这个项目,你已经掌握了:
-
如何使用 OpenCV DNN 模块加载预训练模型
-
如何实时进行人脸检测、年龄估计、性别识别
-
如何在图像上绘制中文文字
-
理解
detections四维数组的索引方式
你可以继续尝试:
-
换成更轻量的人脸检测器(如 MediaPipe),提高速度
-
增加表情识别、口罩检测等任务
-
将结果保存到文件或上传到服务器
学习的本质就是不断拆解、实验、总结。希望这篇文章能帮你迈出 OpenCV 深度学习应用的第一步。有任何问题,欢迎在评论区交流!
本文代码已测试环境:Python 3.8,OpenCV 4.5.4,Windows 10。模型文件请自行准备。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)