[OpenCV实战]1 基于深度学习识别人脸性别和年龄
目录
本教程中,我们将讨论应用于面部的深层学习的有趣应用。我们将估计年龄,并从单个图像中找出该人的性别。模型由GilLevi和TalHassner训练(https://talhassner.github.io/home/publication/2015_CVPR)。本文介绍了如何在OpenCV中使用该模型的步骤说明。Opencv版本3.4.3以上。代码教程代码可以分为四个部分:
1基于CNN的性别分类建模原理
作者使用非常简单的卷积神经网络结构,类似于Caffenet和Alexnet。网络使用3个卷积层、2个全连接层和一个最终的输出层。下面给出了这些层的细节。COV1:第一卷积层具有96个内核大小7的节点。COV2:第二个卷积层Conv层具有256个具有内核大小5的节点。CONV3:第三个CONV层具有384个内核大小为3的节点。两个完全连接的层各自具有512个节点。
训练数据来源:https://talhassner.github.io/home/projects/Adience/Adience-data.html
检测程序主要有四块:检测人脸检测、性别检测、年龄显示和输出。
1.1 人脸识别
我们将使用人脸检测器(tensorflow模型)进行人脸检测。该模型很简单,即使在CPU上也是相当快的。详细见论文:
https://arxiv.org/pdf/1502.00046.pdf
1.2 性别预测
将性别预测设定为一个分类问题。性别预测网络(caffe模型)中的输出层类型为两类,2个节点表示“男性”和“女性”两类。以这两个输出的最大值作为最终的性别。
1.3 年龄预测
理想情况下,年龄预测应该作为一个回归问题来处理。然而通过回归准确估计年龄是很有挑战性的。即使是人类也无法通过观察一个人来准确预测年龄。但是我们能够知道他们是20多岁还是30多岁。由于这个原因,把这个问题描述为一个分类问题是明智的,因为我们试图估计这个人所处的年龄组。例如,0-2范围内的年龄是一个类,4-6是另一个类,依此类推。因此数据集分为以下8个年龄组[(0-2)、(4-6)、(8-12)、(15-20)、(25-32)、(38-43)、(48-53)、(60-100)]。因此,年龄预测网络在最后一层有8个节点,表示所述年龄范围。
应该记住,从一幅图像中预测年龄并不是一个很容易解决的问题,因为感知到的年龄取决于许多因素,而同龄的人在世界各地可能看起来很不一样。而且,人们非常努力地隐藏他们的真实年龄!
我们加载年龄网络(caffe模型)并使用前向通道获得输出。由于网络结构类似于性别网络,所以我们可以从所有输出中提取出最大值来得到预测的年龄组
1.4 结果
尽管性别预测网络表现良好,但年龄预测网络仍未达到我们的预期。所以添加人脸对齐算法或者数据样本很多时候,可以通过回归的模型来检测。但是性别人脸检测还是很准确的。
2 代码
在VS2017下运行了C++代码,其中OpenCV版本至少要3.4.5以上。不然模型读取会有问题。三个模型文件太大,见下载链接:
https://download.csdn.net/download/luohenyj/10993309
https://github.com/luohenyueji/OpenCV-Practical-Exercise
如果没有积分(系统自动设定资源分数)看看参考链接。我搬运过来的,大修改没有。
其中tensorflow和caffe模型都可以用opencv中的readnet函数读取,流程很简单。看看代码就会。
代码提供了C++和Python版本,但是python版本没有运行,原因opencv版本太低,不想升级。代码都有详细的注释。
C++版本:
#include <tuple>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <iterator>
using namespace cv;
using namespace cv::dnn;
using namespace std;
/**
* @brief Get the Face Box object 人脸定位
*
* @param net 人脸检测网络
* @param frame 检测图像
* @param conf_threshold 阈值
* @return tuple<Mat, vector<vector<int>>> 元组容器,可返回多个值
*/
tuple<Mat, vector<vector<int>>> getFaceBox(Net net, Mat &frame, double conf_threshold)
{
//图像复制
Mat frameOpenCVDNN = frame.clone();
int frameHeight = frameOpenCVDNN.rows;
int frameWidth = frameOpenCVDNN.cols;
//缩放尺寸
double inScaleFactor = 1.0;
//检测图大小
Size size = Size(300, 300);
// std::vector<int> meanVal = {104, 117, 123};
Scalar meanVal = Scalar(104, 117, 123);
cv::Mat inputBlob;
inputBlob = cv::dnn::blobFromImage(frameOpenCVDNN, inScaleFactor, size, meanVal, true, false);
net.setInput(inputBlob, "data");
//四维矩阵输出
cv::Mat detection = net.forward("detection_out");
//提取结果信息
cv::Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>());
vector<vector<int>> bboxes;
for (int i = 0; i < detectionMat.rows; i++)
{
//预测概率
float confidence = detectionMat.at<float>(i, 2);
if (confidence > conf_threshold)
{
//左上角点,坐标被归一化
int x1 = static_cast<int>(detectionMat.at<float>(i, 3) * frameWidth);
int y1 = static_cast<int>(detectionMat.at<float>(i, 4) * frameHeight);
//右下角角点,坐标被归一化
int x2 = static_cast<int>(detectionMat.at<float>(i, 5) * frameWidth);
int y2 = static_cast<int>(detectionMat.at<float>(i, 6) * frameHeight);
vector<int> box = { x1, y1, x2, y2 };
//人脸坐标
bboxes.push_back(box);
//图像框选
cv::rectangle(frameOpenCVDNN, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(0, 255, 0), 2, 4);
}
}
return make_tuple(frameOpenCVDNN, bboxes);
}
int main(void)
{
//人脸模型
string faceProto = "model/opencv_face_detector.pbtxt";
string faceModel = "model/opencv_face_detector_uint8.pb";
//年龄模型
string ageProto = "model/age_deploy.prototxt";
string ageModel = "model/age_net.caffemodel";
//性别模型
string genderProto = "model/gender_deploy.prototxt";
string genderModel = "model/gender_net.caffemodel";
//均值
Scalar MODEL_MEAN_VALUES = Scalar(78.4263377603, 87.7689143744, 114.895847746);
//年龄段标签
vector<string> ageList = { "(0-2)", "(4-6)", "(8-12)", "(15-20)", "(25-32)",
"(38-43)", "(48-53)", "(60-100)" };
//性别标签
vector<string> genderList = { "Male", "Female" };
//导入网络
Net ageNet = cv::dnn::readNet(ageProto, ageModel);
Net genderNet = cv::dnn::readNet(genderProto, genderModel);
Net faceNet = cv::dnn::readNetFromTensorflow(faceModel, faceProto);
//打开摄像头
VideoCapture cap;
cap.open(0);
if (cap.isOpened())
{
cout << "camera is opened!" << endl;
}
else
{
return 0;
}
int padding = 20;
while (waitKey(1) < 0)
{
// read frame 读图
Mat frame;
cap.read(frame);
if (frame.empty())
{
waitKey();
break;
}
frame = imread("./images/couple1.jpg");
//人脸坐标
vector<vector<int>> bboxes;
//人脸检测结果图
Mat frameFace;
//人脸定位
//tie()函数解包frameFace和bboxes
tie(frameFace, bboxes) = getFaceBox(faceNet, frame, 0.7);
//人脸判断
if (bboxes.size() == 0)
{
cout << "No face detected, checking next frame." << endl;
continue;
}
//逐个提取人脸检测
for (auto it = begin(bboxes); it != end(bboxes); ++it)
{
//框选人脸
Rect rec(it->at(0) - padding, it->at(1) - padding, it->at(2) - it->at(0) + 2 * padding, it->at(3) - it->at(1) + 2 * padding);
//避免人脸框选超过图像边缘
rec.width = ((rec.x + rec.width) > frame.cols) ? (frame.cols - rec.x - 1) : rec.width;
rec.height = ((rec.y + rec.height) > frame.rows) ? (frame.rows - rec.y - 1) : rec.height;
// take the ROI of box on the frame,原图中提取人脸
Mat face = frame(rec);
//性别检测
Mat blob;
blob = blobFromImage(face, 1, Size(227, 227), MODEL_MEAN_VALUES, false);
genderNet.setInput(blob);
// string gender_preds; 获取前向传播softmax结果
vector<float> genderPreds = genderNet.forward();
// find max element index max_element用于找寻最大值
// distance function does the argmax() work in C++ distance返回最大值和第一个值下标的距离
int max_index_gender = std::distance(genderPreds.begin(), max_element(genderPreds.begin(), genderPreds.end()));
//获得检测结果
string gender = genderList[max_index_gender];
cout << "Gender: " << gender << endl;
//年龄识别
ageNet.setInput(blob);
vector<float> agePreds = ageNet.forward();
// finding maximum indicd in the age_preds vector 找到年龄预测最大下表
int max_indice_age = std::distance(agePreds.begin(), max_element(agePreds.begin(), agePreds.end()));
string age = ageList[max_indice_age];
cout << "Age: " << age << endl;
// label 输出标签
string label = gender + ", " + age;
//在人脸定位图上显示结果
cv::putText(frameFace, label, Point(it->at(0), it->at(1) - 15), cv::FONT_HERSHEY_SIMPLEX, 0.9, Scalar(0, 255, 255), 2, cv::LINE_AA);
}
//保存结果
imshow("Frame", frameFace);
imwrite("out.jpg", frameFace);
}
}
python版本:
# Import required modules
import cv2 as cv
import time
import argparse
def getFaceBox(net, frame, conf_threshold=0.7):
frameOpencvDnn = frame.copy()
frameHeight = frameOpencvDnn.shape[0]
frameWidth = frameOpencvDnn.shape[1]
blob = cv.dnn.blobFromImage(frameOpencvDnn, 1.0, (300, 300), [104, 117, 123], True, False)
net.setInput(blob)
detections = net.forward()
bboxes = []
for i in range(detections.shape[2]):
confidence = detections[0, 0, i, 2]
if confidence > conf_threshold:
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)
bboxes.append([x1, y1, x2, y2])
cv.rectangle(frameOpencvDnn, (x1, y1), (x2, y2), (0, 255, 0), int(round(frameHeight/150)), 8)
return frameOpencvDnn, bboxes
parser = argparse.ArgumentParser(description='Use this script to run age and gender recognition using OpenCV.')
parser.add_argument('--input', help='Path to input image or video file. Skip this argument to capture frames from a camera.')
args = parser.parse_args()
faceProto = "age_gender/model/opencv_face_detector.pbtxt"
faceModel = "age_gender/model/opencv_face_detector_uint8.pb"
ageProto = "age_gender/model/age_deploy.prototxt"
ageModel = "age_gender/model/age_net.caffemodel"
genderProto = "age_gender/model/gender_deploy.prototxt"
genderModel = "age_gender/model/gender_net.caffemodel"
MODEL_MEAN_VALUES = (78.4263377603, 87.7689143744, 114.895847746)
ageList = ['(0-2)', '(4-6)', '(8-12)', '(15-20)', '(25-32)', '(38-43)', '(48-53)', '(60-100)']
genderList = ['Male', 'Female']
# Load network
ageNet = cv.dnn.readNet(ageModel, ageProto)
genderNet = cv.dnn.readNet(genderModel, genderProto)
faceNet = cv.dnn.readNet(faceModel, faceProto)
# Open a video file or an image file or a camera stream
cap = cv.VideoCapture(args.input if args.input else 0)
padding = 20
while cv.waitKey(1) < 0:
# Read frame
t = time.time()
hasFrame, frame = cap.read()
if not hasFrame:
cv.waitKey()
break
frameFace, bboxes = getFaceBox(faceNet, frame)
if not bboxes:
print("No face Detected, Checking next frame")
continue
for bbox in bboxes:
# print(bbox)
face = frame[max(0,bbox[1]-padding):min(bbox[3]+padding,frame.shape[0]-1),max(0,bbox[0]-padding):min(bbox[2]+padding, frame.shape[1]-1)]
blob = cv.dnn.blobFromImage(face, 1.0, (227, 227), MODEL_MEAN_VALUES, swapRB=False)
genderNet.setInput(blob)
genderPreds = genderNet.forward()
gender = genderList[genderPreds[0].argmax()]
# print("Gender Output : {}".format(genderPreds))
print("Gender : {}, conf = {:.3f}".format(gender, genderPreds[0].max()))
ageNet.setInput(blob)
agePreds = ageNet.forward()
age = ageList[agePreds[0].argmax()]
print("Age Output : {}".format(agePreds))
print("Age : {}, conf = {:.3f}".format(age, agePreds[0].max()))
label = "{},{}".format(gender, age)
cv.putText(frameFace, label, (bbox[0], bbox[1]-10), cv.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 2, cv.LINE_AA)
cv.imshow("Age Gender Demo", frameFace)
# cv.imwrite("age-gender-out-{}".format(args.input),frameFace)
print("time : {:.3f}".format(time.time() - t))
参考
https://www.learnopencv.com/age-gender-classification-using-opencv-deep-learning-c-python/
[OpenCV实战]1 基于深度学习识别人脸性别和年龄的更多相关文章
- 基于深度学习的人脸性别识别系统(含UI界面,Python代码)
摘要:人脸性别识别是人脸识别领域的一个热门方向,本文详细介绍基于深度学习的人脸性别识别系统,在介绍算法原理的同时,给出Python的实现代码以及PyQt的UI界面.在界面中可以选择人脸图片.视频进行检 ...
- [OpenCV实战]15 基于深度学习的目标跟踪算法GOTURN
目录 1 什么是对象跟踪和GOTURN 2 在OpenCV中使用GOTURN 3 GOTURN优缺点 4 参考 在这篇文章中,我们将学习一种基于深度学习的目标跟踪算法GOTURN.GOTURN在Caf ...
- [OpenCV实战]5 基于深度学习的文本检测
目录 1 网络加载 2 读取图像 3 前向传播 4 处理输出 3结果和代码 3.1结果 3.2 代码 参考 在这篇文章中,我们将逐字逐句地尝试找到图片中的单词!基于最近的一篇论文进行文字检测. EAS ...
- 基于深度学习的人脸识别系统(Caffe+OpenCV+Dlib)【一】如何配置caffe属性表
前言 基于深度学习的人脸识别系统,一共用到了5个开源库:OpenCV(计算机视觉库).Caffe(深度学习库).Dlib(机器学习库).libfacedetection(人脸检测库).cudnn(gp ...
- 基于深度学习的人脸识别系统(Caffe+OpenCV+Dlib)【三】VGG网络进行特征提取
前言 基于深度学习的人脸识别系统,一共用到了5个开源库:OpenCV(计算机视觉库).Caffe(深度学习库).Dlib(机器学习库).libfacedetection(人脸检测库).cudnn(gp ...
- 基于深度学习的人脸识别系统(Caffe+OpenCV+Dlib)【二】人脸预处理
前言 基于深度学习的人脸识别系统,一共用到了5个开源库:OpenCV(计算机视觉库).Caffe(深度学习库).Dlib(机器学习库).libfacedetection(人脸检测库).cudnn(gp ...
- 基于深度学习的人脸识别系统系列(Caffe+OpenCV+Dlib)——【四】使用CUBLAS加速计算人脸向量的余弦距离
前言 基于深度学习的人脸识别系统,一共用到了5个开源库:OpenCV(计算机视觉库).Caffe(深度学习库).Dlib(机器学习库).libfacedetection(人脸检测库).cudnn(gp ...
- [OpenCV实战]12 使用深度学习和OpenCV进行手部关键点检测
目录 1 背景 2 实现 3 结果和代码 4 参考 手部关键点检测是在手指上找到关节以及在给定图像中找到指尖的过程.它类似于在脸部(面部关键点检测)或身体(人体姿势估计)上找到关键点.但是手部检测不同 ...
- 基于深度学习的人脸识别系统Win10 环境安装与配置(python+opencv+tensorflow)
一.需要下载的软件.环境及文件 (由于之前见识短浅,对Anaconda这个工具不了解,所以需要对安装过程做出改变:就是Python3.7.2的下载安装是可选的,因为Anaconda已经为我们解决Pyt ...
随机推荐
- 从零开始学Graph Database:什么是图
摘要:本文从零开始引导与大家一起学习图知识.希望大家可以通过本教程学习如何使用图数据库与图计算引擎.本篇将以华为云图引擎服务来辅助大家学习如何使用图数据库与图计算引擎. 本文分享自华为云社区<从 ...
- 运行eeui项目不出现 WiFI真机同步 IP地址
从git上 clone项目之后,安装依赖 npm install eeui环境配置 npm install eeui-cli -g 问题:npm run dev 后项目一直不出现 WiFI真 ...
- Go | 基本数据类型的相互转换
基本数据类型的相互转换 Go在不同类型的变量之间赋值时需要显示转换,不能自动转换 基本语法 表达式 T(v): 将值v转换成类型T T就是数据类型: int32, int64, float32... ...
- 【保姆教程】RuoYi-Radius搭建实现portal认证
[保姆教程]RuoYi-Radius搭建实现portal认证 一.简介 以若依后台管理框架V4.6.0做为基础框架,实现了ToughRADIUS大部分功能,支持标准RADIUS协议(RFC 2865, ...
- 支持JDK19虚拟线程的web框架,之二:完整开发一个支持虚拟线程的quarkus应用
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本篇是<支持JDK19虚拟线程的web ...
- Oracle:ORA-00911: invalid character解决办法
问题记录:用jmeter执行sql语句,报错:ORA-00911: invalid character. 解决方法:sql语句末尾";"导致,去掉即可解决. 过程记录: 使用jme ...
- DNS 解析 prefeath
本文将详细介绍DNS预解析prefetch的主要内容 概述 DNS(Domain Name System, 域名系统),是域名和IP地址相互映射的一个分布式数据库.DNS 查询就是将域名转换成 IP ...
- MYSQL一键导库脚本
上周完成了一个性能测试环境搭建,有富余时间的同时研究了一个一键导库的脚本,一周的开始先马住!!! 一.思路 准备:54.158服务器上分别已经装好了MYSQL数据库 目的:把部分库从54导出并导入到1 ...
- 第2-3-1章 文件存储服务系统-nginx/fastDFS/minio/阿里云oss/七牛云oss
目录 文件存储服务 1. 需求背景 2. 核心功能 3. 存储策略 3.1 本地存储 3.2 FastDFS存储 3.3 云存储 3.4 minio 4. 技术设计 文件存储服务 全套代码及资料全部完 ...
- Kubernetes集群管理工具kubectl
Kubernetes集群管理工具kubectl 概述 kubectl是Kubernetes集群的命令行工具,通过kubectl能够对集群本身进行管理,并能够在集群上进行容器化应用的安装和部署 命令格式 ...