(八)OpenCV | 基于深度学习的年龄和性别预测


1. 简介

本文介绍的内容是判断给定图像中人物的年龄和性别等信息。大致思路是首先使用人脸检测器定位人脸位置,然后分别使用年龄检测模型和性别检测模型得到最终的检测结果。


2. Face Detector

在这里插入图片描述

该项目的关键是首先准确定位人脸的位置,上图是所使用的人脸检测模型。


3. Age Detector

在这里插入图片描述


4. Gender Detector

在这里插入图片描述


5. 代码实现

#include<tuple>
#include<vector>
#include<iostream>

#include<opencv2/dnn.hpp>
#include<opencv2/imgproc.hpp>
#include<opencv2/highgui.hpp>

using namespace cv;
using namespace std;
using namespace cv::dnn;

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);
	// 归一化所使用的均值
	Scalar meanVal = Scalar{ 104, 117, 123 };
	// 获取输入图像并转换格式
	cv::Mat inputBlob;
	// 各参数含义(输入图像, 缩放系数, 输出图形尺寸, 均值, 是否交换RB两通道, 是否裁剪图像)
	inputBlob = cv::dnn::blobFromImage(frameOpenCVDNN, inScaleFactor, size, meanVal, true, false);
	// 模型输入,根据关键字"data"
	net.setInput(inputBlob, "data");
	// 模型输出,根据关键字"detection_out"
	cv::Mat detection = net.forward("detection_out");
	// 存放输出结果,并使用detection初始化
	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(int argc, char** argv) {
	// 各模型地址
	string faceProto = "assets/opencv_face_detector.pbtxt";
	string faceModel = "assets/opencv_face_detector_uint8.pb";

	string ageProto = "assets/age_deploy.prototxt";
	string ageModel = "assets/age_net.caffemodel";

	string genderProto = "assets/gender_deploy.prototxt";
	string genderModel = "assets/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" };
	// 模型加载,readNet支持Caffe(.caffemodel), TensorFlow(.pb), Torch(.t7), DarkNet(.weights), DLDT(.bin), ONNX(.onnx)等模型
	// 不支持常用的PyTorch模型,需要首先将其转换为ONNX等格式
	Net faceNet = readNet(faceModel, faceProto);
	Net ageNet = readNet(ageModel, ageProto);
	Net genderNet = readNet(genderModel, genderProto);
	// 读取图像
	Mat image = imread("assets/face.jpg");
	// 人脸检测,并获取结果
	Mat img;
	vector<vector<int>> bboxes;
	tie(img, bboxes) = getFaceBox(faceNet, image, 0.7);
	// 如果没有检测到目标
	if (bboxes.size() == 0) {
		cout << "No face detected!" << endl;
		return -1;
	}
	// 处理结果
	int padding = 20;
	for (auto it = begin(bboxes); it != end(bboxes); ++it) {
		// 由(x1,y1,x2,y2)转换成(x,y,w,h),并增加填充
		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);
		// 设置RoI
		Mat face = img(rec);
		// 处理输入图像
		Mat blob;
		blob = blobFromImage(face, 1, Size(227, 227), MODEL_MEAN_VALUES, false);
		// 设置模型输入
		genderNet.setInput(blob);
		// 预测年龄
		vector<float> genderPreds = genderNet.forward();
		// 得到结果对应的索引,使用std::distance返回结果要么为0要么为1
		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();
		// 得到结果对应的索引,同上
		int max_indice_age = std::distance(agePreds.begin(), max_element(agePreds.begin(), agePreds.end()));
		// 年龄
		string age = ageList[max_indice_age];
		cout << "Age: " << age << endl;
		// 显示内容
		string label = gender + ", " + age;
		// 显示结果
		cv::putText(img, label, Point(it->at(0), it->at(1) - 15), cv::FONT_HERSHEY_SIMPLEX, 0.9, Scalar(0, 255, 255), 2, cv::LINE_AA);
		imshow("Image", img);
		imwrite("assets/out.jpg", img);
	}
	return 0;
}

结果展示:
在这里插入图片描述


参考

  1. https://github.com/spmallick/learnopencv/tree/master/AgeGender.


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值