Opencv 深度学习识别性别和检测年龄

目录

1基于CNN的性别分类建模原理

1.1 人脸识别

1.2 性别预测

1.3 年龄预测

1.4 结果

2 代码

参考


本教程中,我们将讨论应用于面部的深层学习的有趣应用。我们将估计年龄,并从单个图像中找出该人的性别。模型由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 

#include 

#include 

#include 

#include 

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>> 元组容器,可返回多个值

*/

tuple>> 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 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());


vector> bboxes;


for (int i = 0; i < detectionMat.rows; i++)

{

//预测概率

float confidence = detectionMat.at(i, 2);


if (confidence > conf_threshold)

{

//左上角点,坐标被归一化

int x1 = static_cast(detectionMat.at(i, 3) * frameWidth);

int y1 = static_cast(detectionMat.at(i, 4) * frameHeight);

//右下角角点,坐标被归一化

int x2 = static_cast(detectionMat.at(i, 5) * frameWidth);

int y2 = static_cast(detectionMat.at(i, 6) * frameHeight);

vector 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 ageList = { "(0-2)", "(4-6)", "(8-12)", "(15-20)", "(25-32)",

"(38-43)", "(48-53)", "(60-100)" };


//性别标签

vector 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> 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 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 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 深度学习识别性别和检测年龄)