目录

一、了解代码
1、目的
基于OpenCV和深度学习模型的人脸识别、年龄估计和性别识别系统。它主要通过摄像头实时捕获视频帧,对每一帧进行人脸检测,并对检测到的人脸进行年龄和性别的预测。
2、流程
1)模型初始化
加载人脸检测、年龄估计和性别识别的模型。这些模型分别通过其配置文件(prototxt或pbtxt文件)和权重文件(caffemodel或pb文件)进行加载。
2)变量初始化
分别定义年龄范围和性别选项。
3)自定义函数
getBoxes
用于从视频帧中检测人脸,并返回带有检测到的人脸框的图像和人脸框的坐标列表。使用faceNet
模型进行人脸检测,将检测结果中置信度大于0.7的人脸框绘制在图像上。
4)自定义函数:
定义函数cv2AddChineseText
用于在图像上添加中文文本。由于OpenCV原生不支持中文文本绘制,该函数首先将OpenCV图像转换为PIL图像,然后使用PIL的ImageDraw
和ImageFont
绘制中文文本,最后再将图像转换回OpenCV格式。
5)视频捕获和处理循环:
使用cv2.VideoCapture
打开视频文件(或摄像头,如果路径是设备ID)。在循环中,逐帧读取视频,使用getBoxes
函数检测人脸。对于每个检测到的人脸,裁剪出人脸区域,然后分别使用ageNet
和genderNet
模型进行年龄和性别预测。使用cv2AddChineseText
函数将预测结果(年龄和性别)以中文文本的形式绘制在图像上的人脸框上方。显示处理后的图像。如果按下Esc
键(ASCII码27),则退出循环。
二、案例实现
1、初始化模型
提前下载好模型,模型的网址如下:
https://github.com/GilLevi/AgeGenderDeepLearning
https://github.com/spmallick/learnopencv/blob/master/AgeGender/opencv_face_detector_uint8.pb
代码内容:
import cv2
from PIL import Image, ImageDraw, ImageFont
import numpy as np
"""======模型初始化========"""
# 模型(网络模型/预训练模型):face/aqe/qender(脸、年龄、性别)
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) # 人脸检测模型
2、变量初始化
初始化设置识别出来的人脸的性别和年龄段,
"""变量初始化"""
# 初始化设置年龄段和性别
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) # 为模型输入图像进行预处理时使用的均值
3、定义函数获取人脸框
def getBoxes(net, frame):
frameHeight,frameWidth = frame.shape[:2] # 获取视频帧的高度、宽度
# 使用OpenCV中的深度神经网络模块将图像转变成深度神经网络可以识别处理的格式
# 1.0表示图像的缩放因子
# 输出blob图像大小为300*300,表示无论输入图像多大都会调整为300*300像素
# [104,117,123]为图像通道的均值列表,BGR,用于从每个像素减去对应通道均值
# 第一个True表示是否交换蓝色和红色通道
# 第二个False表示是否对图像进行归一化处理,即 将每个像素点除以255,以转变为0-1之间的值
blob = cv2.dnn.blobFromImage(frame, 1.0,(300,300),[104,117,123],True,False)
net.setInput(blob) # 调用网络模型,输入上述的blob图片进行人脸检测
detections = net.forward() # 对模型进行前向传播
faceBoxes = [] # faceBoxes存储检测到的人脸边界框坐标
xx = detections.shape[2] # 表示前向传播的第三个维度的结果
for i in range(detections.shape[2]):
# confidence中每一行保存了7个数据,第3个数据表示置信度,第4,5,6,7分别表示人脸归一化后的坐标位置
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]) # 人脸框的坐标
# 绘制人脸框
cv2.rectangle(frame,(x1,y1),(x2,y2),(0,255,0),int(frameHeight/150),6)
return frame,faceBoxes # 返回绘制完人脸框的图像和人脸框的坐标
4、定义绘制中文文本的函数
def cv2AddChineseText(img,text,position,textColor=(0,255,0),textSize=30):
"""向图片中添加中文"""
if (isinstance(img,np.ndarray)): # 判断是否0penCV图片类型
img = Image.fromarray(cv2.cvtColor(img,cv2.COLOR_BGR2RGB)) # 实现array到image的转换
draw = ImageDraw.Draw(img) # 在img图片上创建一个绘图的对象
# 字体的格式
fontStyle = ImageFont.truetype("STXINGKA.TTF",textSize,encoding = "utf-8")
draw.text(position,text,textColor,font=fontStyle)
return cv2.cvtColor(np.asarray(img),cv2.COLOR_BGR2RGB) # 转换回0penCV格式
5、主循环
"""打开摄像头,将每一帧画面传入神经网络中"""
cap = cv2.VideoCapture("人.mp4") # 装载摄像头
while True:
_,frame = cap.read()
#frame = cv2.flip(frame,1) # 镜像处理
# 获取人脸包围框、绘制人脸包围框(可能多个)
frame, faceBoxes = getBoxes(faceNet, frame) # 将人脸检测模型与读取到的视频帧传入函数,返回绘制人脸框后的图像和人脸框的坐标信息
if not faceBoxes: # 没有人脸时检测下一帧图像,后续循坏操作不再继续。
print("当前镜头中没有人")
continue
# 遍历每一个人脸包围框。
for faceBox in faceBoxes:
# 处理frame,将其处理为符合DNN输入的格式
x,y,x1,y1 = faceBox # 读取边界坐标
face = frame[y:y1, x:x1] # 裁剪出来人脸
blob = cv2.dnn.blobFromImage(face, 1.0, (227,227),mean) # 将取出的人脸转化为DNN可识别的格式,模型输入格式转化为为227*277
# 调用模型,预测性别
genderNet.setInput(blob) # 将DNN可识别的图像传入性别检测模型
genderOuts = genderNet.forward() # 前向传播,返回每个类别的预测得分
gender = genderList[genderOuts[0].argmax()] # argmax()返回最大值的索引
# 调用模型,预测年龄
ageNet.setInput(blob)
age0uts = ageNet.forward()
age = ageList[age0uts[0].argmax()]
result = "{},{}".format(gender,age) # 格式化文本(年龄、性别)
frame = cv2AddChineseText(frame, result, (x,y-30)) # 输出中文性别和年龄
cv2.imshow("result", frame)
if cv2.waitKey(1) == 27:
break
cv2.destroyAllWindows()
cap.release()