在计算机视觉领域,使用深度学习技术进行实时性别和年龄识别是一项具有挑战性和实用性的任务。本文将深入解析一个使用OpenCV和预训练模型实现的实时性别和年龄识别代码,并逐行进行详细的注释解析,帮助读者理解代码的工作原理和实现细节。
import cv2
import numpy as np
from PIL import ImageDraw
from PIL import ImageFont
from PIL import Image
首先,我们导入所需的库,包括OpenCV(cv2)、NumPy(numpy)和Python Imaging Library(PIL)。这些库用于图像处理和显示。
#-------模型初始化-----
# 模型(网络模型/预训练模型):face/age/gender(脸、年龄、性别)
faceProto="model/opencv_face_detector.pbtxt" # TensorFlow,模型的结构文件
faceModel="model/opencv_face_detector_uint8.pb" # TensorFlow模型权重参数
ageProto="model/deploy_age.prototxt" # TensorFlow,模型的结构文件
ageModel="model/age_net.caffemodel" # TensorFlow模型权重参数
genderProto="model/deploy_gender.prototxt" # TensorFlow,模型的结构文件
genderModel="model/gender_net.caffemodel" # TensorFlow模型权重参数
在这一部分,我们定义了用于人脸检测、年龄识别和性别识别的预训练模型的文件路径。
#加载网络
faceNet=cv2.dnn.readNet(faceModel,faceProto) #人脸 facenet是已经搭建好并训练好的神经网络模型
ageNet=cv2.dnn.readNet(ageModel,ageProto) #年龄
genderNet=cv2.dnn.readNet(genderModel,genderProto) #性别
接下来,我们使用OpenCV的dnn
模块中的readNet
函数加载预训练模型。这些模型包括用于人脸检测的模型、用于年龄识别的模型和用于性别识别的模型。
#--------变量初始化------
mean=(78.4263377603,87.7689143744,114.895847746) #模型均值
ageList=['0-2岁', '4-6岁', '8-12岁', '15-20岁', '25-32岁', '38-43岁', '48-53岁', '60-100岁']
genderList=['男性','女性']
在这里,我们定义了模型的均值和年龄、性别的类别列表。
#-----------自定义函数,获取人脸包围框----------
def getBoxes(net,frame):
frameHeight,frameWidth=frame.shape[:2] #获取高度、宽度
blob=cv2.dnn.blobFromImage(frame,1.0,(300,300),
[104,117,123],True,False)#图片做预处理的
net.setInput(blob)#调用网络模型,输入图片进行人脸检测
detections=net.forward()
faceBoxes=[] #faceBoxes存储检测到的人脸
for i in range(detections.shape[2]):
#confindence中每一行保存了7个数据,第3个数据表示置信度,第4,5,6,7分别表示人脸归一化后的坐标位置
confindence=detections[0,0,i,2]
if confindence > 0.7: #筛选一下,将置信度大于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)
#返回绘制了人脸框的帧frame,人脸包围框faceBoxes
return frame,faceBoxes
这是一个自定义函数,用于获取图像中的人脸包围框。我们首先将图像处理为模型的输入格式,然后调用网络模型进行人脸检测,最后返回绘制了人脸框的图像和检测到的人脸框的坐标。
def cv2AddchineseText(img, text, position, textColor=(0,255,0),textSize = 30):
# 检查图像是否为numpy数组,如果是则将其转换为PIL图像
if (isinstance(img, np.ndarray)):
img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
# 创建一个绘图对象
draw = ImageDraw.Draw(img)
# 加载中文字体
fontStyle = ImageFont.truetype("C:\WINDOWS\FONTS\DENG.TTF", textSize, encoding="utf-8")
# 在图像上绘制文本
draw.text(position, text, textColor, font = fontStyle)
# 将PIL图像转换回OpenCV的BGR格式
return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
这是另一个自定义函数,用于在图像上添加中文文本。我们首先将图像转换为PIL格式,然后使用PIL库中的绘图功能添加文本,最后将图像转换回OpenCV格式。
# 打开摄像头,创建VideoCapture对象,参数为0表示使用默认摄像头
cap=cv2.VideoCapture(0) # 装载摄像头
# 进入视频捕获循环
while True:
# 读取摄像头捕获的每一帧画面
_,frame=cap.read()
# 对捕获的画面进行水平镜像处理
frame=cv2.flip(frame,1) #镜像处理
# 获取人脸包围框、绘制人脸包围框(可能多个)
frame,faceBoxes=getBoxes(faceNet,frame)
# 如果没有检测到人脸,则跳过后续处理并继续下一帧
if not faceBoxes: #没有人脸时检测下一帧,后续循环操作不再继续
frame =cv2AddchineseText(frame,result,(50,100))
continue
# 遍历每一个人脸包围框
for faceBox in faceBoxes:
# 处理frame,将其处理为符合DNN输入的格式
blob = cv2.dnn.blobFromImage(frame,1.0,(227,227),mean)
# 调用性别预测模型,进行性别预测
genderNet.setInput(blob) #人脸检测已经完成,性别的检测,227*227
genderOuts = genderNet.forward()
gender = genderList[genderOuts[0].argmax()]
# 调用年龄预测模型,进行年龄预测
ageNet.setInput(blob)
ageOuts = ageNet.forward()
age = ageList[ageOuts[0].argmax()]
# 将性别和年龄信息格式化为字符串
result="{},{}".format(gender,age) # 格式化文本(年龄、性别)
print(result)
# 在画面上添加中文文本
frame_1 = cv2AddchineseText(frame,result,(0,50))
# 显示处理后的画面
cv2.imshow("result",frame_1)
# 检测是否按下了Esc键,如果按下则退出程序
if cv2.waitKey(1)==27: #按下Esc键,退出程序
break
# 关闭所有窗口
cv2.destroyAllWindows()
# 释放摄像头资源
cap.release()
在这部分代码中,我们打开了摄像头,并开始了一个无限循环,以便持续获取摄像头捕获的每一帧图像。
cap.read()
函数用于读取摄像头的每一帧图像。cv2.flip(frame,1)
将图像进行水平翻转,因为很多摄像头捕获的图像是镜像的。
然后,我们调用getBoxes
函数获取图像中的人脸包围框,并在图像上绘制这些包围框。如果没有检测到人脸,则程序会继续到下一帧。检测每个人脸包围框,调用模型来预测性别和年龄。我们首先将图像处理为适合模型输入的格式(使用blobFromImage
函数),然后分别将其输入到性别识别模型和年龄识别模型中。根据模型输出的概率最大值索引,从年龄和性别列表中获取相应的预测结果,并将它们格式化为文本字符串。调用cv2AddchineseText
函数将性别和年龄文本添加到图像中,并通过cv2.imshow
显示结果。同时,使用cv2.waitKey(1)
等待用户按下Esc键退出程序。