OpenCV学习:性别与年龄识别
前言:前面几篇我们学习了人脸检测、关键点检测、轮廓绘制、表情识别和疲劳检测。这些技术都是基于人脸关键点或几何比例实现的。本篇我们将引入一个更强大的工具——OpenCV 的 DNN 模块,通过加载预训练的深度学习模型,实现性别和年龄识别。只需要一张人脸图片,模型就能输出性别(男/女)和年龄段。
目录
- 一、项目概述
- 二、模型准备
- 三、DNN 模块基础
- 四、完整代码
- 五、结果说明
- 六、总结
一、项目概述
1.1 实现目标
通过摄像头实时检测人脸,判断画面中人物的性别和年龄段。
| 性别 | 男性 / 女性 |
| 年龄段 | 0-2岁 / 4-6岁 / 8-12岁 / 15-20岁 / 25-32岁 / 38-43岁 / 48-53岁 / 60-100岁 |
二、模型准备
2.1 需要的模型文件
性别年龄识别需要三组模型文件:
| 人脸检测 | opencv_face_detector.pbtxt | opencv_face_detector_uint8.pb |
| 年龄识别 | deploy_age.prototxt | age_net.caffemodel |
| 性别识别 | deploy_gender.prototxt | gender_net.caffemodel |
2.2 模型文件说明
| .prototxt / .pbtxt | 模型的配置文件,描述网络结构 |
| .caffemodel / .pb | 模型的权重文件,存储训练好的参数 |
三、DNN 模块基础
3.1 什么是 DNN 模块
OpenCV 的 dnn 模块支持加载多种深度学习框架训练好的模型(Caffe、TensorFlow、ONNX、Darknet 等),并进行前向推理,无需安装 TensorFlow 或 PyTorch。
3.2 核心函数
| cv2.dnn.readNet(model, config) | 加载模型(权重 + 配置) |
| cv2.dnn.blobFromImage(image, …) | 将图像转为模型可接受的输入格式 |
| net.setInput(blob) | 设置模型输入 |
| net.forward() | 执行前向推理 |
3.3 blobFromImage 参数
| image | 输入图像 |
| scalefactor | 缩放因子 |
| size | 模型要求的输入尺寸 |
| mean | 均值,用于减去图像均值 |
| swapRB | 是否交换 R 和 B 通道 |
| crop | 是否裁剪 |
四、完整代码
import cv2
from PIL import Image, ImageDraw, ImageFont # pip install pillow
import numpy as np
# ==================== 模型初始化 ====================
# 模型路径:人脸检测、年龄识别、性别识别
# .pbtxt/.prototxt 是网络结构配置文件,.pb/.caffemodel 是训练好的权重文件
faceProto = "models/opencv_face_detector.pbtxt"
faceModel = "models/opencv_face_detector_uint8.pb"
ageProto = "models/deploy_age.prototxt"
ageModel = "models/age_net.caffemodel"
genderProto = "models/deploy_gender.prototxt"
genderModel = "models/gender_net.caffemodel"
# 加载网络:readNet 接收两个参数,分别是权重文件和配置文件
ageNet = cv2.dnn.readNet(ageModel, ageProto) # 年龄模型
genderNet = cv2.dnn.readNet(genderModel, genderProto) # 性别模型
faceNet = cv2.dnn.readNet(faceModel, faceProto) # 人脸模型
# ==================== 变量初始化 ====================
# 年龄段标签:模型输出 8 个概率值,对应下面 8 个年龄段,概率最大的即为预测结果
ageList = ['0-2岁', '4-6岁', '8-12岁', '15-20岁', '25-32岁', '38-43岁', '48-53岁', '60-100岁']
# 性别标签:模型输出 2 个概率值,分别对应男性、女性
genderList = ['男性', '女性']
# 模型均值:训练时设定的固定值,推理时需要减去这个均值(不要改动)
mean = (78.4263377603, 87.7689143744, 114.895847746)
def getBoxes(net, frame):
"""获取人脸包围框:输入待检测图像帧,返回绘制了人脸框的图像和人脸框列表"""
frameHeight, frameWidth = frame.shape[:2] # 获取图像的高度和宽度
# 图像预处理:将图像转为 DNN 模型要求的 blob 格式
# scalefactor=1.0:不缩放像素值
# size=(300, 300):人脸检测模型要求输入 300×300
# mean=[104, 117, 123]:减去均值,这是人脸检测模型训练时设定的
# swapRB=True:交换 R 和 B 通道(OpenCV 默认 BGR,模型要求 RGB)
# crop=False:不裁剪
blob = cv2.dnn.blobFromImage(frame, scalefactor=1.0, size=(300, 300),
mean=[104, 117, 123], swapRB=True, crop=False)
net.setInput(blob) # 将 blob 设置为网络的输入
detections = net.forward() # 前向传播,得到检测结果(四维数组)
faceBoxes = [] # 存储检测到的人脸框坐标
for i in range(detections.shape[2]):
# detections 形状为 [1, 1, N, 7],每一行 7 个数据
# 第 3 个数据(索引 2)是置信度,表示这里是脸的概率
# 第 4~7 个数据(索引 3~6)是人脸框的归一化坐标(0~1 之间)
confidence = detections[0, 0, i, 2]
if confidence > 0.7: # 置信度大于 0.7 才认为是人脸(过滤掉误检)
# 归一化坐标 × 图像尺寸 = 实际像素坐标
x1 = int(detections[0, 0, i, 3] * frameWidth) # 左上角 x
y1 = int(detections[0, 0, i, 4] * frameHeight) # 左上角 y
x2 = int(detections[0, 0, i, 5] * frameWidth) # 右下角 x
y2 = int(detections[0, 0, i, 6] * frameHeight) # 右下角 y
faceBoxes.append([x1, y1, x2, y2]) # 保存人脸框
# 绘制人脸框(绿色矩形)
# thickness 根据图像高度动态计算,保证不同分辨率下线宽合适
cv2.rectangle(frame, pt1=(x1, y1), pt2=(x2, y2),
color=(0, 255, 0),
thickness=int(round(frameHeight / 150)),
lineType=6)
return frame, faceBoxes # 返回绘制了人脸框的图像和人脸框列表
def cv2AddChineseText(img, text, position, textColor=(0, 255, 0), textSize=30):
"""在图像上绘制中文(OpenCV 的 putText 不支持中文,需借助 PIL)"""
if isinstance(img, np.ndarray):
# OpenCV 是 BGR,PIL 是 RGB,先转换
img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img)
# 加载中文字体:simsun.ttc 是宋体,Windows 系统自带
fontStyle = ImageFont.truetype("simsun.ttc", textSize, encoding="utf-8")
# 在指定位置绘制文本
draw.text(position, text, textColor, font=fontStyle)
# 转回 OpenCV 的 BGR 格式
return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
# ==================== 打开摄像头 ====================
# CAP_DSHOW 是 Windows 下的摄像头驱动,加上可以加快摄像头打开速度
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
while True:
_, frame = cap.read() # 读取一帧
frame = cv2.flip(frame, flipCode=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] # 根据人脸框从原图中裁剪出人脸区域
# 将人脸区域转为模型输入格式(性别年龄模型输入 227×227)
# 注意:这里用的是性别年龄模型自己的均值 mean,与人脸检测的均值不同
blob = cv2.dnn.blobFromImage(face, scalefactor=1.0, size=(227, 227), mean=mean)
# ———- 预测性别 ———-
genderNet.setInput(blob) # 设置输入
genderOuts = genderNet.forward() # 前向推理,输出 2 个概率值
gender = genderList[genderOuts[0].argmax()] # argmax 取概率大的索引,再查标签
# ———- 预测年龄 ———-
ageNet.setInput(blob) # 设置输入
ageOuts = ageNet.forward() # 前向推理,输出 8 个概率值
age = ageList[ageOuts[0].argmax()] # 取概率最大的年龄段
# 格式化输出结果:性别 + 年龄段
result = "{},{}".format(gender, age)
# 在图像上绘制中文结果(显示在人脸框上方)
frame = cv2AddChineseText(frame, result, position=(x1, y1 – 30))
cv2.imshow("result", frame)
if cv2.waitKey(1) == 27: # 按 ESC 键退出
break
cv2.destroyAllWindows()
cap.release()
五、结果说明
5.1 关键步骤
| 1. 加载模型 | 加载人脸检测、年龄、性别三个网络 |
| 2. 检测人脸 | 用 DNN 人脸检测器获取人脸框 |
| 3. 裁剪人脸 | 根据人脸框从原图中截取人脸区域 |
| 4. 预测性别 | 将人脸送入性别网络,输出男/女 |
| 5. 预测年龄 | 将人脸送入年龄网络,输出年龄段 |
| 6. 绘制结果 | 用 PIL 绘制中文标签 |
5.2 三个模型的作用
| 人脸检测 | 300×300 | 人脸框坐标 |
| 性别识别 | 227×227 | 2 个概率值(男、女) |
| 年龄识别 | 227×227 | 8 个概率值(8 个年龄段) |
5.3 应用场景
| 智能广告推送 | 根据性别年龄推荐商品 |
| 客流分析 | 商场统计不同人群比例 |
| 智能门禁 | 辅助身份验证 |
| 互动娱乐 | 拍照测年龄、性别游戏 |
六、总结
核心函数速查
| cv2.dnn.readNet(model, config) | 加载 DNN 模型 |
| cv2.dnn.blobFromImage(image, …) | 图像预处理为 blob 格式 |
| net.setInput(blob) | 设置网络输入 |
| net.forward() | 前向推理,获取输出 |
| argmax() | 取概率最大的类别索引 |
| cv2AddChineseText() | 用 PIL 绘制中文文字 |
模型输出速查
| 性别模型 | 2 个概率 | 0→男性, 1→女性 |
| 年龄模型 | 8 个概率 | 0~7 对应 8 个年龄段 |
注意事项
| 中文显示 | cv2.putText() 不支持中文,需借助 PIL |
| 置信度阈值 | 0.7 是经验值,值越高检测越严格 |
| 模型输入尺寸 | 人脸检测 300×300,性别年龄 227×227 |
| 图像通道顺序 | OpenCV 读入是 BGR,需用 swapRB=True 转为 RGB |
| 两组均值 | 人脸检测用 [104, 117, 123],性别年龄用 mean 变量 |
| 年龄是区间 | 模型输出的是年龄段,不是精确年龄 |
| 摄像头参数 | 使用 cv2.CAP_DSHOW 加快 Windows 下摄像头打开速度 |
系列直达
- 上篇:OpenCV学习:疲劳检测
- 本篇:OpenCV学习:性别与年龄识别(本文)
- 下篇:
网硕互联帮助中心




评论前必须登录!
注册