本文本次将使用CNN训练的模型预测性别与年龄,关于CNN的原理,可以参考以下文章:
1、https://www.cnblogs.com/fydeblog/p/7450413.html
2、https://blog.csdn.net/liangchunjiang/article/details/79030681
3、https://blog.csdn.net/ice_actor/article/details/78648780
模型文件下载:https://download.csdn.net/download/daker_huang/10972270
学会看描述文件是一项必备的技能,根据.prototxt描述文件,我们需要设置相关参数,如性别的描述文件中:
上图中输入层是"data",图像大小是227*227。
上图中最后一层是"prob",也是net.forward()中需要填写的。
这里还需要用到人脸检测的部分,因为计算机是通过脸部判断性别与年龄的。
实例:
#include<opencv2/opencv.hpp>
#include<iostream>
#include<opencv2/dnn.hpp>
using namespace cv;
using namespace std;
using namespace cv::dnn;
//年龄预测的3大文件
string age_model = "D:/test/dnn/gender and age_dnn/age_net.caffemodel";
string age_prototxt = "D:/test/dnn/gender and age_dnn/deploy_age.prototxt";
string age_label = "D:/test/dnn/gender and age_dnn/age_labels.txt";
//性别预测的3大文件
string gender_model = "D:/test/dnn/gender and age_dnn/gender_net.caffemodel";
string gender_prototxt = "D:/test/dnn/gender and age_dnn/deploy_gender.prototxt";
string gender_label = "D:/test/dnn/gender and age_dnn/gender_labels.txt";
//人脸检测文件
string face_file = "D:/OpenCv/opencv 4.0.0/sources/data/haarcascades/haarcascade_frontalface_alt.xml";
void age_predict(Net &net, Mat &image);//预测年龄
void gender_predict(Net &net, Mat &image);//预测性别
vector<string> age_labels(); //年龄标签
vector<string> gender_labels(); //性别标签
int main(int argc, char** argv)
{
Mat src = imread("D:/test/cnn_gender_age_people.png");
if (!src.data)
{
cout << "图片未找到..." << endl;
return -1;
}
CascadeClassifier faces_clf;
faces_clf.load(face_file);
//namedWindow("input image", WINDOW_AUTOSIZE);
//imshow("input image", src);
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
vector<Rect>faces;//存储人脸矩形数据
Mat dst;
faces_clf.detectMultiScale(gray, faces, 1.02, 2, 0, Size(24, 24));
Net age_net = readNetFromCaffe(age_prototxt, age_model);
Net gender_net = readNetFromCaffe(gender_prototxt, gender_model);
for (size_t t = 0; t < faces.size(); t++)
{
dst = src(faces[t]); //划取roi区域
rectangle(src, faces[t], Scalar(30, 255, 30), 2, 8, 0);
age_predict(age_net, dst);
gender_predict(gender_net, dst);
}
namedWindow("output result image", WINDOW_AUTOSIZE);
imshow("output result image", src);
waitKey(0);
return 0;
}
vector<string> age_labels()
{
vector<string>agenames;
ifstream fs(age_label);
if (!fs.is_open())
{
cout << "标签文件不存在,请检查路径..." << endl;
exit(-1);
}
string names;
while (!fs.eof())
{
getline(fs, names);
if (names.length())
{
string temp = names.substr(names.find(" ") + 1);
agenames.push_back(temp);
}
}
return agenames;
}
vector<string> gender_labels()
{
vector<string>gendernames;
ifstream fs(gender_label);
if (!fs.is_open())
{
cout << "标签文件不存在,请检查路径..." << endl;
exit(-1);
}
string names;
while (!fs.eof())
{
getline(fs, names);
if (names.length())
{
string temp = names.substr();
gendernames.push_back(temp);
}
}
return gendernames;
}
void age_predict(Net & net, Mat & image)
{
Mat inputblob = blobFromImage(image, 1.0, Size(227, 227));
net.setInput(inputblob, "data");
Mat prob = net.forward("prob");
Mat reprob = prob.reshape(1, 1);//把probreshape成1行n列的矩阵
double classprob; //相似度
Point index_name; //标签所在的位置
minMaxLoc(reprob, NULL, &classprob, NULL, &index_name);
int classid = index_name.x;
vector<string>ages = age_labels();
putText(image, format("age:%s", ages.at(classid).c_str()), Point(2, 20),
FONT_HERSHEY_SIMPLEX, 0.6, Scalar(0, 0, 255), 2, 8, 0);
}
void gender_predict(Net & net, Mat & image)
{
Mat inputblob = blobFromImage(image, 1.0, Size(227, 227));
net.setInput(inputblob, "data");
Mat prob = net.forward("prob");
Mat reprob = prob.reshape(1, 1);//把probreshape成1行n列的矩阵
double classprob; //相似度
Point index_name; //标签所在的位置
minMaxLoc(reprob, NULL, &classprob, NULL, &index_name);
int classid = index_name.x;
vector<string>genders = gender_labels();
putText(image, format("sex:%s", genders.at(classid).c_str()), Point(2, 50),
FONT_HERSHEY_SIMPLEX, 0.6, Scalar(66, 118, 175), 2, 8, 0);
}
运行结果: