云计算百科
云计算领域专业知识百科平台

OpenCV学习:性别与年龄识别

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学习:性别与年龄识别(本文)
  • 下篇:
赞(0)
未经允许不得转载:网硕互联帮助中心 » OpenCV学习:性别与年龄识别
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!