由MTCNN关键点估计人头姿态

转载 2018年04月15日 17:41:55

一、前言

      本篇主要记录由mtcnn检测得的关键点作人头姿态估计,思路较为简单,mtcnn是一种可以检测输出5个关键点的人脸检测算法,分别是左眼,右眼,鼻尖,嘴的左角和嘴的右角。当获得图像中人脸的5个2D关键点,再由Opencv中POSIT的姿态估计算法将5个世界坐标系的模板3D关键点通过旋转、平移等变换投射至这5个2D关键点,进而估计得变换参数,最后求得2D平面中的人头的姿态参数,分别为Yaw:摇头  左正右负、Pitch:点头 上负下正、Roll:摆头(歪头)左负 右正

二、Mtcnn-light

    对于mtcnn,网上具有较多开源版本,这里使用light版本,优点是速度较快,缺点为模型准确性略有下降,为输出5个关键点,对原来src/mtcnn.cpp中增加重载函数 void findFace(Mat &image , vector<struct Bbox> &resBox ); 

三、人头姿态估计

     人头姿态估计代码参考自https://blog.csdn.net/zzyy0929/article/details/78323363

#include "network.h"
#include "mtcnn.h"
#include <time.h>

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include "opencv2/opencv.hpp"

void rot2Euler(cv::Mat faceImg,const cv::Mat& rotation3_3)
{
	cv::resize(faceImg,faceImg,cv::Size(faceImg.cols*2,faceImg.rows*2));
	double q0 = std::sqrt(1+rotation3_3.at<double>(1,1)+rotation3_3.at<double>(2,2)+rotation3_3.at<double>(3,3))/2;
        double q1 = (rotation3_3.at<double>(3,2)-rotation3_3.at<double>(2,3))/(4*q0);
	double q2 = (rotation3_3.at<double>(1,3)-rotation3_3.at<double>(3,1))/(4*q0);
        double q3 = (rotation3_3.at<double>(2,1)-rotation3_3.at<double>(1,2))/(4*q0);
	double yaw = std::asin( 2*(q0*q2 + q1*q3) );
        double pitch = std::atan2(2*(q0*q1-q2*q3), q0*q0-q1*q1-q2*q2+q3*q3);
	double roll = std::atan2(2*(q0*q3-q1*q2), q0*q0+q1*q1-q2*q2-q3*q3);
        std::cout<<"yaw:"<<yaw<<" pitch:"<< pitch <<" roll:"<< roll<< std::endl;
	char ch[20];
        sprintf(ch, "yaw:%0.4f", yaw);
	cv::putText(faceImg,ch,cv::Point(20,40), FONT_HERSHEY_SIMPLEX,1,Scalar(255,23,0),2,3);
	sprintf(ch, "pitch:%0.4f", pitch);
        cv::putText(faceImg,ch,cv::Point(20,80), FONT_HERSHEY_SIMPLEX,1,Scalar(255,23,0),2,3);
	sprintf(ch, "roll:%0.4f", roll);
        cv::putText(faceImg,ch,cv::Point(20,120), FONT_HERSHEY_SIMPLEX,1,Scalar(255,23,0),2,3);
	
	cv::imshow("faceImg",faceImg);

}

void headPosEstimate(const cv::Mat & faceImg , const std::vector<cv::Point2d>& facial5Pts )
{

	// 3D model points
	std::vector<cv::Point3f> model_points;
        model_points.push_back(cv::Point3d(-165.0f, 170.0f, -115.0f));       // Left eye 
        model_points.push_back(cv::Point3d( 165.0f, 170.0f, -115.0f));        // Right eye
	model_points.push_back(cv::Point3d(0.0f, 0.0f, 0.0f));               // Nose tip
        model_points.push_back(cv::Point3d(-150.0f, -150.0f, -125.0f));      // Left Mouth corner
        model_points.push_back(cv::Point3d(150.0f, -150.0f, -125.0f)); 	     // Right Mouth corner

	// Camera internals
	double focal_length = faceImg.cols;
	cv::Point2d center = cv::Point2d(faceImg.cols/2,faceImg.rows/2);
	cv::Mat camera_matrix =(cv::Mat_<double>(3,3) << focal_length, 0, center.x, 0,focal_length,center.y,0, 0,1);
        cv::Mat dist_coeffs = cv::Mat::zeros(4,1,cv::DataType<double>::type);

	cv::Mat rotation_vector;
	cv::Mat translation_vector;

	cv::solvePnP(model_points,facial5Pts , camera_matrix, dist_coeffs,rotation_vector, translation_vector);
	
	/*投影一条直线而已
	std::vector<Point3d> nose_end_point3D;
	std::vector<Point2d> nose_end_point2D;
	nose_end_point3D.push_back(cv::Point3d(0,0,1000.0));

        projectPoints(nose_end_point3D, rotation_vector, translation_vector,camera_matrix, dist_coeffs, nose_end_point2D);
	//std::cout << "Rotation Vector " << std::endl << rotation_vector << std::endl;
	//std::cout << "Translation Vector" << std::endl << translation_vector << std::endl;
    
	cv::Mat temp(faceImg);
	cv::line(temp ,facial5Pts[2], nose_end_point2D[0], cv::Scalar(255,0,0), 2);
        cv::imshow("vvvvvvvv" ,temp );
	cv::waitKey(1);  */
    
	cv::Mat rotation3_3;
	cv::Rodrigues(rotation_vector,rotation3_3);
  	rot2Euler(faceImg.clone(),rotation3_3);
}

void showheadPost(const cv::Mat& img, std::vector<struct Bbox>& resBox)
{
    for( vector<struct Bbox>::iterator it=resBox.begin(); it!=resBox.end();it++){
        if((*it).exist){
		int it_x1 = (*it).y1; //这里需注意,(*it).y1表示第一个点的x坐标,(*it).x1表示y坐标
		int it_y1 = (*it).x1;
		int it_x2 = (*it).y2;
                int it_y2 = (*it).x2;
			
		const cv::Mat faceImg = img(Rect(it_x1 ,it_y1 ,it_x2-it_x1 ,it_y2-it_y1 ));
        	std::vector<Point2d> face5Pts;  //脸部5个点的坐标,原点坐标为(0,0)
                for(int i=0 ;i<5 ; ++i ){
			face5Pts.push_back(Point2f(*(it->ppoint+i)-it_x1 , *(it->ppoint+i+5)-it_y1 ));
		}
			
		headPosEstimate(faceImg , face5Pts);
			
	}	
    }
}

int main()
{
    cv::Mat img = cv::imread("ldh.jpg");
    mtcnn find(img.rows, img.cols);
    std::vector<struct Bbox> resBox;
    find.findFace(img , resBox);
    showheadPost(img,resBox);

    waitKey(0);

    return 0;
}

四、实验结果

    载入两张图片实验,结果如下所示,可以评估侧脸程度,不过失败时会出现nan的计算





头部姿态估计

  • 2015年11月25日 11:32
  • 1KB
  • 下载

相机位姿估计1:根据四个特征点估计相机姿态 随文Demo

  • 2016年11月20日 21:01
  • 2.5MB
  • 下载

人体姿态估计(human pose estimate)

人体姿态估计后,可以做很多有意思的应用。 网上找到的了一些据2D图片估计姿态的一些算法,很好玩 Demo http://www.vision.ee.ethz.ch/~hpedemo/...
  • kastolo
  • kastolo
  • 2013-10-08 13:36:02
  • 3193

obj人头模型

  • 2014年12月16日 03:44
  • 16.61MB
  • 下载

人头数据标注

CY 说标注出来的是困难样本,不参与训练的样本。 这样的话参与训练的样本本身都是高质量的样本。 其实只要标记出来就没有问题。 第二次标注的数据的效果比第一次标注的大model 精度还低了1个点,...
  • keyanxiaocaicai
  • keyanxiaocaicai
  • 2015-05-15 17:32:13
  • 646

基于特征匹配的摄像头姿态估计

在完成摄像机标定后可以获得摄像机内参数矩阵K,我们就可以与之前的特征检测结合起来,从摄像头不同角度位置拍摄同一本书的图像中获取摄像头的位置(即旋转平移矩阵)。 方法可以见下述博客一直到重构场景上面的...
  • lyq123180
  • lyq123180
  • 2016-08-08 09:44:14
  • 1176

转载:单目相机姿态解算(OpenCV)

转载:http://blog.csdn.net/chenmohousuiyue/article/details/78157509 ...
  • healingwounds
  • healingwounds
  • 2017-11-30 17:16:53
  • 530

MTCNN训练数据整理

分为三层网路PRO 1. 输入:积分图像 输出: prob1 边框概率,可以得出粗略得边框信息 conv4-2 边框偏移 2. 输入:根据第一步提取的边框,提取图片,作为batch进行输入 输出:...
  • AMDS123
  • AMDS123
  • 2017-02-17 14:25:16
  • 9772

基于MTCNN的人脸自动对齐技术原理及其Tensorflow实现测试

人脸识别是计算机视觉研究领域的一个热点。而人脸识别包含了诸多步骤,下图所示(摘自http://www.techshino.com/upfiles/images/%E4%BA%BA%E8%84%B8%E...
  • sparkexpert
  • sparkexpert
  • 2017-06-28 17:16:21
  • 5361

MTCNN训练详细过程

深度学习交流QQ群:116270156 MTCNN主要包括三个部分,PNet,RNet,ONet 其中PNet在训练阶段的输入尺寸为12*12,RNet的输入尺...
  • sinat_24143931
  • sinat_24143931
  • 2017-11-30 10:56:57
  • 1777
收藏助手
不良信息举报
您举报文章:由MTCNN关键点估计人头姿态
举报原因:
原因补充:

(最多只允许输入30个字)