main.cpp
Go to the documentation of this file.
00001 /*
00002 // Authors: Gabriele Fanelli, Thibaut Weise, Juergen Gall, BIWI, ETH Zurich
00003 // Email: fanelli@vision.ee.ethz.ch
00004 
00005 // You may use, copy, reproduce, and distribute this Software for any
00006 // non-commercial purpose, subject to the restrictions of the
00007 // Microsoft Research Shared Source license agreement ("MSR-SSLA").
00008 // Some purposes which can be non-commercial are teaching, academic
00009 // research, public demonstrations and personal experimentation. You
00010 // may also distribute this Software with books or other teaching
00011 // materials, or publish the Software on websites, that are intended
00012 // to teach the use of the Software for academic or other
00013 // non-commercial purposes.
00014 // You may not use or distribute this Software or any derivative works
00015 // in any form for commercial purposes. Examples of commercial
00016 // purposes would be running business operations, licensing, leasing,
00017 // or selling the Software, distributing the Software for use with
00018 // commercial products, using the Software in the creation or use of
00019 // commercial products or any other activity which purpose is to
00020 // procure a commercial gain to you or others.
00021 // If the Software includes source code or data, you may create
00022 // derivative works of such portions of the Software and distribute
00023 // the modified Software for non-commercial purposes, as provided
00024 // herein.
00025 
00026 // THE SOFTWARE COMES "AS IS", WITH NO WARRANTIES. THIS MEANS NO
00027 // EXPRESS, IMPLIED OR STATUTORY WARRANTY, INCLUDING WITHOUT
00028 // LIMITATION, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
00029 // PARTICULAR PURPOSE, ANY WARRANTY AGAINST INTERFERENCE WITH YOUR
00030 // ENJOYMENT OF THE SOFTWARE OR ANY WARRANTY OF TITLE OR
00031 // NON-INFRINGEMENT. THERE IS NO WARRANTY THAT THIS SOFTWARE WILL
00032 // FULFILL ANY OF YOUR PARTICULAR PURPOSES OR NEEDS. ALSO, YOU MUST
00033 // PASS THIS DISCLAIMER ON WHENEVER YOU DISTRIBUTE THE SOFTWARE OR
00034 // DERIVATIVE WORKS.
00035 
00036 // NEITHER MICROSOFT NOR ANY CONTRIBUTOR TO THE SOFTWARE WILL BE
00037 // LIABLE FOR ANY DAMAGES RELATED TO THE SOFTWARE OR THIS MSR-SSLA,
00038 // INCLUDING DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL OR INCIDENTAL
00039 // DAMAGES, TO THE MAXIMUM EXTENT THE LAW PERMITS, NO MATTER WHAT
00040 // LEGAL THEORY IT IS BASED ON. ALSO, YOU MUST PASS THIS LIMITATION OF
00041 // LIABILITY ON WHENEVER YOU DISTRIBUTE THE SOFTWARE OR DERIVATIVE
00042 // WORKS.
00043 
00044 // When using this software, please acknowledge the effort that
00045 // went into development by referencing the paper:
00046 //
00047 // Fanelli G., Weise T., Gall J., Van Gool L., Real Time Head Pose Estimation from Consumer Depth Cameras
00048 // 33rd Annual Symposium of the German Association for Pattern Recognition (DAGM'11), 2011
00049 
00050 */
00051 
00052 #include <stdlib.h>
00053 #include <string>
00054 #include <math.h>
00055 #include <algorithm>
00056 #include <iostream>
00057 #include <vector>
00058 #include "head_pose_estimation/CRForestEstimator.h"
00059 
00060 #include <ros/ros.h>
00061 #include "sensor_msgs/Image.h"
00062 #include "sensor_msgs/PointCloud2.h"
00063 #include "image_transport/image_transport.h"
00064 #include <cv_bridge/cv_bridge.h>
00065 #include <sensor_msgs/image_encodings.h>
00066 
00067 #include <pcl_ros/point_cloud.h>
00068 #include <pcl/point_types.h>
00069 
00070 #include <kdl/frames.hpp>
00071 #include <angles/angles.h>
00072 #include <geometry_msgs/PoseStamped.h>
00073 #include <geometry_msgs/PointStamped.h>
00074 
00075 #include <tf/transform_listener.h>
00076 #include <tf/transform_broadcaster.h>
00077 
00078 #include "people_msgs/PositionMeasurement.h"
00079 #include "people_msgs/PositionMeasurementArray.h"
00080 
00081 typedef pcl::PointCloud<pcl::PointXYZ> PointCloud;
00082 
00083 using namespace std;
00084 using namespace cv;
00085 using namespace angles;
00086 
00087 // Path to trees
00088 string g_treepath;
00089 // Number of trees
00090 int g_ntrees;
00091 // Patch width
00092 int g_p_width;
00093 // Patch height
00094 int g_p_height;
00095 //maximum distance form the sensor - used to segment the person
00096 int g_max_z = 0;
00097 //head threshold - to classify a cluster of votes as a head
00098 int g_th = 400;
00099 //threshold for the probability of a patch to belong to a head
00100 float g_prob_th = 1.0f;
00101 //threshold on the variance of the leaves
00102 double g_maxv = 800.f;
00103 //stride (how densely to sample test patches - increase for higher speed)
00104 int g_stride = 5;
00105 //radius used for clustering votes into possible heads
00106 double g_larger_radius_ratio = 1.f;
00107 //radius used for mean shift
00108 double g_smaller_radius_ratio = 6.f;
00109 
00110 //pointer to the actual estimator
00111 CRForestEstimator* g_Estimate;
00112 //input 3D image
00113 Mat g_im3D;
00114 
00115 // Frame to transform the head pose to
00116 string g_head_target_frame;
00117 
00118 bool g_head_depth_ready = false;
00119 bool g_cloud_ready = false;
00120 bool g_transform_ready = false;
00121 
00122 double g_head_depth = 0.5;
00123 
00124 string g_cloud_frame;
00125 
00126 double g_roll_bias = 0;
00127 double g_pitch_bias = 0;
00128 double g_yaw_bias = 0;
00129 
00130 CRForestEstimator estimator;
00131 ros::Publisher pose_pub;
00132 
00133 tf::TransformListener* listener;
00134 tf::TransformBroadcaster* broadcaster;
00135 tf::StampedTransform g_transform;
00136 
00137 std::vector< cv::Vec<float,POSE_SIZE> > g_means; //outputs
00138 std::vector< std::vector< Vote > > g_clusters; //full clusters of votes
00139 std::vector< Vote > g_votes; //all votes returned by the forest
00140 
00141 void loadConfig() {
00142         ros::NodeHandle nh("~");
00143         nh.param("tree_path",           g_treepath,             string("trees/tree"));
00144         nh.param("ntrees",              g_ntrees,               10);
00145         nh.param("max_variance",        g_maxv,                 800.0);
00146         nh.param("larger_radius_ratio", g_larger_radius_ratio,  1.0);
00147         nh.param("smaller_radius_ratio",g_smaller_radius_ratio, 6.0);
00148         nh.param("stride",              g_stride,               5);
00149         nh.param("head_threshold",      g_th,                   400);
00150         nh.param("head_target_frame",   g_head_target_frame,    string("/camera_depth_frame"));
00151         nh.param("angular_bias_roll",   g_roll_bias,            0.0);
00152         nh.param("angular_bias_pitch",  g_pitch_bias,           0.0);
00153         nh.param("angular_bias_yaw",    g_yaw_bias,             0.0);
00154 
00155         
00156 }
00157 
00158 void peopleCallback(const people_msgs::PositionMeasurementArray::ConstPtr& msg) {
00159         if(g_cloud_ready && (msg->people.size() > 0)) {
00160                 g_head_depth_ready = true;
00161                 people_msgs::PositionMeasurement person = msg->people[0];
00162                 geometry_msgs::PointStamped head_point, head_point_transformed;
00163                 head_point.header = person.header;
00164                 head_point.point = person.pos;
00165                 listener->transformPoint(g_cloud_frame, head_point, head_point_transformed);
00166                 g_head_depth = head_point_transformed.point.z;
00167         }
00168 }
00169 
00170 void cloudCallback(const sensor_msgs::PointCloud2ConstPtr& msg)
00171 {
00172         PointCloud cloud;
00173         pcl::fromROSMsg(*msg, cloud);
00174 
00175         g_cloud_frame = cloud.header.frame_id;
00176         g_cloud_ready = true;
00177 
00178         if(!g_head_depth_ready) return;
00179 
00180         Mat img3D;
00181         img3D = Mat::zeros(cloud.height, cloud.width, CV_32FC3);
00182         //img3D.create(cloud.height, cloud.width, CV_32FC3);
00183         
00184         int yMin, xMin, yMax, xMax;
00185         yMin = 0; xMin = 0;
00186         yMax = img3D.rows; xMax = img3D.cols;
00187 
00188         //get 3D from depth
00189         for(int y = yMin ; y < img3D.rows; y++) {
00190                 Vec3f* img3Di = img3D.ptr<Vec3f>(y);
00191         
00192                 for(int x = xMin; x < img3D.cols; x++) {
00193                         pcl::PointXYZ p = cloud.at(x,y);
00194                         if((p.z>g_head_depth-0.2) && (p.z<g_head_depth+0.2)) {
00195                                 img3Di[x][0] = p.x*1000;
00196                                 img3Di[x][1] = p.y*1000;
00197                                 img3Di[x][2] = hypot(img3Di[x][0], p.z*1000); //they seem to have trained with incorrectly projected 3D points
00198                                 //img3Di[x][2] = p.z*1000;
00199                         } else {
00200                                 img3Di[x] = 0;
00201                         }
00202                 }
00203         }
00204         
00205         g_means.clear();
00206         g_votes.clear();
00207         g_clusters.clear();
00208         
00209         //do the actual estimate
00210         estimator.estimate(     img3D,
00211                                                         g_means,
00212                                                         g_clusters,
00213                                                         g_votes,
00214                                                         g_stride,
00215                                                         g_maxv,
00216                                                         g_prob_th,
00217                                                         g_larger_radius_ratio,
00218                                                         g_smaller_radius_ratio,
00219                                                         false,
00220                                                         g_th
00221                                                 );
00222         
00223         //assuming there's only one head in the image!
00224         if(g_means.size() > 0) {        
00225                 geometry_msgs::PoseStamped pose_msg;
00226                 pose_msg.header.frame_id = msg->header.frame_id;
00227         
00228                 cv::Vec<float,POSE_SIZE> pose(g_means[0]);
00229 
00230                 tf::Quaternion q;
00231                 q.setRPY(
00232                         from_degrees( pose[5]+180+g_roll_bias ), 
00233                         from_degrees(-pose[3]+180+g_pitch_bias),
00234                         from_degrees(-pose[4]+    g_yaw_bias  )
00235                 );
00236         
00237                 geometry_msgs::PointStamped head_point;
00238                 geometry_msgs::PointStamped head_point_transformed;
00239         
00240                 head_point.header = pose_msg.header;
00241 
00242                 head_point.point.x = pose[0]/1000;
00243                 head_point.point.y = pose[1]/1000;
00244                 head_point.point.z = pose[2]/1000;
00245         
00246                 try {
00247                         listener->waitForTransform(head_point.header.frame_id, g_head_target_frame, ros::Time::now(), ros::Duration(2.0));
00248                         listener->transformPoint(g_head_target_frame, head_point, head_point_transformed);
00249                 } catch(tf::TransformException ex) {
00250                         ROS_WARN(
00251                                 "Transform exception, when transforming point from %s to %s\ndropping pose (don't worry, this is probably ok)",
00252                                 head_point.header.frame_id.c_str(), g_head_target_frame.c_str());
00253                                 ROS_WARN("Exception was %s", ex.what());
00254                         return;
00255                 }
00256                 
00257                 tf::Transform trans;
00258                 
00259                 pose_msg.pose.position = head_point_transformed.point;
00260                 pose_msg.header.frame_id = head_point_transformed.header.frame_id;
00261         
00262                 pose_msg.pose.orientation.x = q.x();
00263                 pose_msg.pose.orientation.y = q.y();
00264                 pose_msg.pose.orientation.z = q.z();
00265                 pose_msg.pose.orientation.w = q.w();
00266 
00267                 trans.setOrigin(tf::Vector3(
00268                         pose_msg.pose.position.x, 
00269                         pose_msg.pose.position.y, 
00270                         pose_msg.pose.position.z
00271                 ));
00272                 trans.setRotation(q);
00273                 g_transform = tf::StampedTransform(trans, pose_msg.header.stamp, pose_msg.header.frame_id, "head_origin");
00274                 // broadcaster->sendTransform(tf::StampedTransform(trans, pose_msg.header.stamp, pose_msg.header.frame_id, "head_origin"));
00275                 g_transform_ready = true;
00276                 pose_msg.header.stamp = ros::Time::now();
00277                 geometry_msgs::PoseStamped zero_pose;
00278                 zero_pose.header.frame_id = "head_origin";
00279                 zero_pose.header.stamp = ros::Time::now();
00280                 zero_pose.pose.orientation.w = 1;
00281                 //pose_pub.publish(pose_msg);
00282                 pose_pub.publish(zero_pose);
00283         }
00284 }
00285 
00286 int main(int argc, char* argv[])
00287 {
00288         ros::init(argc, argv, "head_pose_estimator");
00289         ros::NodeHandle nh;
00290         
00291         listener = new tf::TransformListener();
00292         broadcaster = new tf::TransformBroadcaster();
00293         
00294         image_transport::ImageTransport it(nh);
00295         ros::Subscriber cloud_sub = nh.subscribe<sensor_msgs::PointCloud2>("cloud", 1, cloudCallback);
00296         ros::Subscriber face_pos_sub = nh.subscribe<people_msgs::PositionMeasurementArray>("/face_detector/people_tracker_measurements_array", 1, peopleCallback);
00297         
00298         pose_pub = nh.advertise<geometry_msgs::PoseStamped>("head_pose", 1);
00299         
00300         loadConfig();
00301         ROS_INFO("tree path: %s", g_treepath.c_str());
00302         if( !estimator.loadForest(g_treepath.c_str(), g_ntrees) ){
00303                 ROS_ERROR("could not read forest!");
00304                 exit(-1);
00305         }
00306         
00307         ros::Rate rate(20);
00308         while(ros::ok()) {
00309                 if(g_transform_ready) {
00310                         g_transform.stamp_ = ros::Time::now() + ros::Duration(1.0);
00311                         broadcaster->sendTransform(g_transform);
00312                 }
00313                 ros::spinOnce();
00314                 rate.sleep();
00315         }       
00316         return 0;
00317 }


head_pose_estimation
Author(s): Dan Lazewatsky
autogenerated on Thu Feb 11 2016 23:06:59