[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 人臉識別OpenCV
- 基於深度學習的人臉性別識別系統(含UI介面,Python程式碼)深度學習UIPython
- 利用OpenCV和深度學習來實現人類活動識別OpenCV深度學習
- 深度學習——性別識別深度學習
- 基於二哈實現多人人臉學習和識別
- 【Linux學習】OpenCV+ROS 實現人臉識別(Ubantu16.04)LinuxOpenCVROS
- 機器學習實戰-SVM模型實現人臉識別機器學習模型
- 【計算視覺】人臉屬性識別演算法 | 性別+種族+年齡+表情視覺演算法
- 基於OpenCV+dlib開發一個人臉識別應用OpenCV
- 基於Android平臺實現人臉識別Android
- 基於深度學習的機器人目標識別和跟蹤深度學習機器人
- 實戰 | 基於深度學習模型VGG的影象識別(附程式碼)深度學習模型
- 人臉識別不到位,兄弟秒變父與子!AI 研習社「人臉年齡識別挑戰賽」火熱進行中AI
- 【實戰】基於OpenCV的水錶字元識別(OCR)OpenCV字元
- OpenCV-Python 人臉眼睛嘴識別OpenCVPython
- [雪峰磁針石部落格]計算機視覺opcencv工具深度學習快速實戰1人臉識別計算機視覺深度學習
- 深度學習-行人重識別實戰(2020)深度學習
- matlab實現人臉識別(數學基礎原理)Matlab
- 基於PCA和SVM的人臉識別PCA
- 人臉識別檢測專案實戰
- 【ROS】OpenCV+ROS 實現人臉識別(Ubantu16.04)ROSOpenCV
- 機器視覺學習筆記:臉性別識別視覺筆記
- 關於舉辦“人臉識別和深度偽造”沙龍的通知
- 【opencv3】 svm實現手寫體與人臉識別OpenCV
- 基於開源模型搭建實時人臉識別系統(四):人臉質量模型
- 基於開源模型搭建實時人臉識別系統(五):人臉跟蹤模型
- faced:基於深度學習的CPU實時人臉檢測深度學習
- 【專案】Python人臉識別(GUI介面)—— 基於pyopencvPythonGUIOpenCV
- 基於mtcnn/facenet/tensorflow實現人臉識別登入系統CNN
- Python-OpenCV人臉識別之資料集生成PythonOpenCV
- js 識別身份證號的性別、生日、年齡JS
- 手把手教你運用深度學習構建影片人臉識別模型(Python實現)深度學習模型Python
- 人臉識別之人臉檢測的重要性
- 人臉識別系列(二):DeepID1
- 基於 OpenCv 和 Python 的手指識別及追蹤OpenCVPython
- 人臉識別學習筆記二:進階篇筆記
- 人臉識別學習筆記一:入門篇筆記
- Java 基於ArcFace人臉識別2.0 服務端DemoJava服務端