姿態(tài)評(píng)估技術(shù)與框架
姿態(tài)評(píng)估(Pose estimation)是計(jì)算機(jī)視覺的研究熱點(diǎn)之一,姿態(tài)評(píng)估的算法應(yīng)用主要可以分為兩部完成,第一步是對(duì)象檢測(cè),對(duì)象區(qū)域的定位與查找,截取圖像ROI,第二步是根據(jù)對(duì)象檢測(cè)定位截取ROI區(qū)域作為輸入,完成姿態(tài)評(píng)估與預(yù)測(cè)。當(dāng)前主要用于姿態(tài)評(píng)估的深度學(xué)習(xí)網(wǎng)絡(luò)框架有以下幾種:
OpenPose框架:
是一個(gè)開源的姿態(tài)評(píng)估算法框架,支持多任姿態(tài)評(píng)估,可以實(shí)現(xiàn)實(shí)時(shí)的人體對(duì)象檢測(cè)、身體、頭部、手部姿態(tài)評(píng)估與人臉關(guān)機(jī)點(diǎn)檢測(cè),支持2D與3D,簡(jiǎn)易的C++接口調(diào)用與自定義訓(xùn)練。
DeepCut框架:
跟OpenPose類似的姿態(tài)評(píng)估框,支持多人檢測(cè)與姿態(tài)評(píng)估,特別是在圖像與視頻中運(yùn)動(dòng)場(chǎng)景下,包括足球與籃球運(yùn)動(dòng)場(chǎng)景。
其它的類似框架還包括有AlphaPose、DeepPose、PoseNet等。姿態(tài)評(píng)估算法主要應(yīng)用場(chǎng)景在移動(dòng)機(jī)器人、虛擬現(xiàn)實(shí)、人體跌倒檢測(cè)、危險(xiǎn)動(dòng)作識(shí)別、機(jī)器人動(dòng)作訓(xùn)練、活體驗(yàn)證等。
OpenVINO™ 2021.02版本中涉及到姿態(tài)評(píng)估方面的支持主要有下列幾個(gè)預(yù)訓(xùn)練模型:
模型名稱 |
模型說明 |
head-pose-estimation-adas-0001 |
頭部姿態(tài)評(píng)估,簡(jiǎn)單的全卷積網(wǎng)絡(luò) |
human-pose-estimation-0001 |
基于OpenPose, MobileNetv1做特征提取,18對(duì)關(guān)鍵點(diǎn) |
human-pose-estimation-0002 |
基于EfficientHRNet,17對(duì)關(guān)鍵點(diǎn)檢測(cè) |
human-pose-estimation-0003 |
基于EfficientHRNet,17對(duì)關(guān)鍵點(diǎn)檢測(cè) |
human-pose-estimation-0004 |
基于EfficientHRNet,17對(duì)關(guān)鍵點(diǎn)檢測(cè) |
表-1
從human-pose-estimation-0002到human-pose-estimation-0004輸入圖像的分辨率增大,計(jì)算復(fù)雜度增加,檢測(cè)精度提升。從human-pose-estimation-0001到human-pose-estimation-0004都支持多人的姿態(tài)評(píng)估。OpenVINO™ 通過上述五個(gè)模型實(shí)現(xiàn)基本的人體姿態(tài)評(píng)估需求。這里我們以頭部姿態(tài)評(píng)估為例來完成姿態(tài)評(píng)估模型的OpenVINO™ 部署與代碼演示。
頭部姿態(tài)評(píng)估模型
OpenVINO™ 支持的頭部姿態(tài)評(píng)估模型head-pose-estimation-adas-0001的輸入與輸出格式分別如下:
輸入格式為NxCxHxW=1x3x60x60,期望的彩色圖像通道順序BGR、大小為60x60
輸出層名稱與格式如下:
name: "angle_y_fc", shape: [1, 1] - Estimated
name: "angle_p_fc", shape: [1, 1] - Estimated pitch
name: "angle_r_fc", shape: [1, 1] - Estimated roll
在三個(gè)維度方向?qū)崿F(xiàn)頭部動(dòng)作識(shí)別,它們分別是:
pitch是俯仰角,是“點(diǎn)頭“
yaw是偏航角,是‘搖頭’
roll是旋轉(zhuǎn)角,是“翻滾
它們的角度范圍分別為:YAW [-90,90], PITCH [-70,70], ROLL [-70,70]
這三個(gè)專業(yè)詞匯其實(shí)是來自無人機(jī)與航空領(lǐng)域,計(jì)算機(jī)視覺科學(xué)家一大愛好就是搞新詞,就把它們借用到頭部姿態(tài)評(píng)估中,它們的意思圖示如下:
圖-1(來自網(wǎng)絡(luò))
對(duì)應(yīng)到頭部姿態(tài)評(píng)估中:
圖-2 (來自網(wǎng)絡(luò))
該網(wǎng)絡(luò)模型的格式通過Netron查看之后你會(huì)發(fā)現(xiàn)就是一些簡(jiǎn)單的Conv-ReLU-BN的疊加,最后是一個(gè)多任務(wù)的分支輸出,模型結(jié)構(gòu)跟2017年一篇論文里面的模型有點(diǎn)相似,我大膽猜測(cè)一波,OpenVINO™ 頭部姿態(tài)模型的開發(fā)者肯定是了解過此篇論文的,論文地址如下:
https://arxiv.org/pdf/1710.00925.pdf
論文中給出的模型結(jié)構(gòu)如下:
圖-3 (來自論文Fine-Grained Head Pose Estimation Without Keypoints)
可以看到它的backbone部分是ResNet50,在OpenVINO™ 中考慮到圖像的輸入分辨率與速度需求ResNet50的backbone被替換為一些簡(jiǎn)單的Conv-ReLU-BN疊加。圖示如下:
圖-4
代碼實(shí)現(xiàn)
首先需要完成人臉檢測(cè),然后把對(duì)人臉部分ROI區(qū)域適當(dāng)?shù)南蛏显黾哟笮≈笸瓿山貓D,重新resize之后再作為輸入頭部姿態(tài)評(píng)估模型推理,得到三個(gè)角度,根據(jù)角度數(shù)值的范圍獲得最終的頭部姿態(tài)評(píng)估結(jié)果。代碼實(shí)現(xiàn)部分的人臉檢測(cè)作為上一篇的對(duì)象檢測(cè)部分關(guān)聯(lián)知識(shí),這里就不再展開詳細(xì)說明。主要說明如何通過人臉檢測(cè)結(jié)果截取ROI區(qū)域,調(diào)用頭部姿態(tài)評(píng)估模型實(shí)現(xiàn)姿態(tài)評(píng)估、預(yù)測(cè)解析與結(jié)果顯示,所以頭部姿態(tài)評(píng)估的代碼部分可以分為以下幾步,它們分別是:
第一步:加載頭部姿態(tài)識(shí)別模型與設(shè)置輸入輸出格式
// load emotion model
InferenceEngine::CNNNetworkhead_pose_net = ie.ReadNetwork(head_model_xml, head_model_bin);
InferenceEngine::InputsDataMaphead_pose_inputs = head_pose_net.getInputsInfo();
InferenceEngine::OutputsDataMaphead_pose_outputs = head_pose_net.getOutputsInfo();
std::string hp_input_name = "";
for (auto item :head_pose_inputs) {
hp_input_name = item.first;
auto input_data = item.second;
input_data->setPrecision(Precision::U8);
input_data->setLayout(Layout::NCHW);
}
for (auto item :head_pose_outputs) {
auto output_data = item.second;
output_data->setPrecision(Precision::FP32);
}
第二步:獲取可執(zhí)行網(wǎng)絡(luò)與推理請(qǐng)求
auto executable_hp_network = ie.LoadNetwork(head_pose_net, "CPU");
auto hp_request = executable_hp_network.CreateInferRequest();
第三步:截取輸入、推理與解析輸出
// check out of boundary
if (box.x< 0) {
box.x = 0;
}
if (box.y< 0) {
box.y = 0;
}
if ((box.width + box.x) >= curr_frame.cols) {
box.width = curr_frame.cols - box.x;
}
if ((box.height+ box.y) >= curr_frame.rows) {
box.height = curr_frame.rows - box.y;
}
fetch_head_pose(curr_frame, hp_request, box, hp_input_name);
其中fetch_head_pose函數(shù)方法的代碼實(shí)現(xiàn)如下:
void fetch_head_pose(cv::Mat &image, InferenceEngine::InferRequest&request, cv::Rect&face_roi,
std::string &e_input) {
cv::Mat faceROI = image(face_roi);
auto blob = request.GetBlob(e_input);
matU8ToBlob
request.Infer();
// output prase
auto output1 = request.GetBlob("angle_y_fc");
auto output2 = request.GetBlob("angle_p_fc");
auto output3 = request.GetBlob("angle_r_fc");
const float* y_pred = static_cast
const float* p_pred = static_cast
const float* r_pred = static_cast
std::string head_pose = "";
if (p_pred[0] > 20 || p_pred[0] < -20) {
head_pose += "pitch, ";
}
if (r_pred[0] > 20 || r_pred[0] < -20) {
head_pose += "roll, ";
}
if (y_pred[0] > 20 || y_pred[0] < -20) {
head_pose += "yaw, ";
}
putText(image, head_pose, face_roi.tl(), cv::FONT_HERSHEY_SIMPLEX, 1.0, Scalar(255, 0, 255), 2, 8);
}
最終程序運(yùn)行結(jié)果如下:
圖-5