detect_hands.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002 * Software License Agreement (BSD License)
00003 *
00004 *  Copyright (c) 2010, Garratt Gallagher
00005 *  All rights reserved.
00006 *
00007 *  Redistribution and use in source and binary forms, with or without
00008 *  modification, are permitted provided that the following conditions
00009 *  are met:
00010 *
00011 *   * Redistributions of source code must retain the above copyright
00012 *     notice, this list of conditions and the following disclaimer.
00013 *   * Redistributions in binary form must reproduce the above
00014 *     copyright notice, this list of conditions and the following
00015 *     disclaimer in the documentation and/or other materials provided
00016 *     with the distribution.
00017 *   * Neither the name Garratt Gallagher nor the names of other
00018 *     contributors may be used to endorse or promote products derived
00019 *     from this software without specific prior written permission.
00020 *
00021 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032 *  POSSIBILITY OF SUCH DAMAGE.
00033 *********************************************************************/
00034 
00035 
00036 #include <boost/thread/thread.hpp>
00037 #include <boost/thread/mutex.hpp>
00038 #include <boost/thread/condition.hpp>
00039 
00040 #include <ros/ros.h>
00041 #include <tf/transform_listener.h>
00042 #include <sensor_msgs/PointCloud.h>
00043 #include <mapping_msgs/PolygonalMap.h>
00044 #include <body_msgs/Hands.h>
00045 #include <sensor_msgs/point_cloud_conversion.h>
00046 #include <pcl_tools/pcl_utils.h>
00047 #include <nnn/nnn.hpp>
00048 #include <pcl_tools/segfast.hpp>
00049 
00050 
00051 #include "pcl/io/pcd_io.h"
00052 #include "pcl/point_types.h"
00053 #include <pcl/ModelCoefficients.h>
00054 
00055 #include <pcl/io/pcd_io.h>
00056 #include <pcl/point_types.h>
00057 
00058 #include <pcl/features/normal_3d.h>
00059 
00060 #include <pcl/filters/extract_indices.h>
00061 #include <pcl/filters/passthrough.h>
00062 
00063 #include <pcl/sample_consensus/method_types.h>
00064 #include <pcl/sample_consensus/model_types.h>
00065 #include <pcl/segmentation/sac_segmentation.h>
00066 
00067 
00068 class TimeEvaluator{
00069    timeval tmark;
00070    std::vector<double> times;
00071    std::vector<std::string> eventnames;
00072    std::string name;  //name is used to identify the evaluator as a whole
00073 
00074 
00075 public:
00076    TimeEvaluator(std::string _name="Time Evaluator"){
00077       name=_name;
00078       //be default the clock starts running when TimeEvaluator is initialized
00079 
00080       gettimeofday(&tmark, NULL);
00081 
00082    }
00083    //records time diff;
00084    void mark(std::string _name=""){
00085       //Give the event a name:
00086       if(_name.size())
00087          eventnames.push_back(_name);
00088       else{
00089          int count=eventnames.size();
00090          char tname[10];
00091          sprintf(tname,"E%d",count);
00092          eventnames.push_back(std::string(tname));
00093       }
00094       //record the time since last event
00095       struct timeval tv;
00096       gettimeofday(&tv, NULL);
00097       times.push_back((double)(tv.tv_sec-tmark.tv_sec) + (tv.tv_usec-tmark.tv_usec)/1000000.0);
00098    }
00099 
00100    void print(){
00101       std::cout<<name;
00102       for(uint i=0;i<times.size();++i)
00103          std::cout<<"  "<<eventnames[i]<<": "<< std::setprecision (5) << times[i];
00104       std::cout<<std::endl;
00105    }
00106 
00107 
00108 
00109 
00110 };
00111 
00112 
00113 
00114 float gdist(pcl::PointXYZ pt, Eigen::Vector4f v){
00115    return sqrt((pt.x-v(0))*(pt.x-v(0))+(pt.y-v(1))*(pt.y-v(1))+(pt.z-v(2))*(pt.z-v(2))); //
00116 }
00117 
00118 void flipvec(Eigen::Vector4f palm, Eigen::Vector4f fcentroid,Eigen::Vector4f &dir ){
00119    if((fcentroid-palm).dot(dir) <0)
00120       dir=dir*-1.0;
00121 
00122  }
00123 
00124 geometry_msgs::Point32 eigenToMsgPoint32(Eigen::Vector4f v){
00125         geometry_msgs::Point32 p;
00126         p.x=v(0); p.y=v(1); p.z=v(2);
00127         return p;
00128 }
00129 geometry_msgs::Point eigenToMsgPoint(Eigen::Vector4f v){
00130         geometry_msgs::Point p;
00131         p.x=v(0); p.y=v(1); p.z=v(2);
00132         return p;
00133 }
00134 
00135 pcl::PointXYZ eigenToPclPoint(Eigen::Vector4f v){
00136    pcl::PointXYZ p;
00137    p.x=v(0); p.y=v(1); p.z=v(2);
00138    return p;
00139 }
00140 
00141 //find the points that are ajoining a cloud, but not in it:
00142 //cloud: the full cloud
00143 //cloudpts a vector of indices into cloud that represents the cluster for which we want to find near points
00144 //centroid: the centroid of the nearby pts
00145 //return: true if points were found within 5cm
00146 bool findNearbyPts(pcl::PointCloud<pcl::PointXYZ> &cloud, std::vector<int> &cloudpts, Eigen::Vector4f &centroid){
00147    std::vector<int> inds(cloud.size(),1); //a way of marking the points we have looked at
00148    // 1: not in the cluster  0: in the cluster, seen  -1: in the cluster, not seen
00149    std::vector<int> nearpts; //a way of marking the points we have looked at
00150    std::vector<int> temp;
00151    for(uint i=0;i<cloudpts.size(); ++i) inds[cloudpts[i]]=-1;
00152    for(uint i=0;i<cloudpts.size(); ++i){
00153       if(inds[cloudpts[i]]==-1){
00154          NNN(cloud,cloud.points[cloudpts[i]],temp, .05);
00155                mapping_msgs::PolygonalMap pmap;
00156                geometry_msgs::Polygon p;
00157          for(uint j=0;j<temp.size(); ++j){
00158             if(inds[temp[j]]==1){
00159                nearpts.push_back(temp[j]);
00160                inds[temp[j]]=2;
00161             }
00162             else
00163                inds[temp[j]]=-2;
00164          }
00165       }
00166    }
00167    //TODO: check if we are really just seeing the other hand:
00168    //       remove any points that do not have a point w/in 1cm
00169    if(nearpts.size())
00170    //now find the centroid of the nearcloud:
00171       pcl::compute3DCentroid(cloud,nearpts,centroid);
00172    else
00173       return false;
00174    return true;
00175 }
00176 
00177 
00178 
00179 bool getNearBlobs2(pcl::PointCloud<pcl::PointXYZ> &cloud, std::vector<pcl::PointCloud<pcl::PointXYZ> > &clouds, std::vector< Eigen::Vector4f> &nearcents ){
00180         pcl::PointCloud<pcl::PointXYZ> cloudout;
00181    pcl::PointXYZ pt,pt1,pt2; pt.x=pt.y=pt.z=0;
00182    std::vector<int> inds1,inds2,inds3(cloud.size(),1);
00183    std::vector<float> dists;
00184    Eigen::Vector4f centroid1,centroid2,nearcent1;
00185 //   bool foundarm=false;
00186 
00187    //for debugging delays:
00188    TimeEvaluator te("getNearBlobs2: ");
00189 //----------FIND FIRST HAND--------------------------
00190 
00191    //find closest pt to camera:
00192    NNN(cloud,pt,inds1,dists, 1.0);
00193    int ind=0; double smallestdist;
00194    for(uint i=0;i<dists.size(); ++i){
00195       if(dists[i]<smallestdist || i==0 ){
00196          ind=inds1[i];
00197          smallestdist=dists[i];
00198       }
00199    }
00200    smallestdist=sqrt(smallestdist);
00201    pt1=cloud.points[ind];
00202 
00203    te.mark("closest pt");
00204 
00205    //find points near that the closest point
00206    NNN(cloud,pt1,inds2, .1);
00207 
00208    //if there is nothing near that point, we're probably seeing noise.  just give up
00209    if(inds2.size() < 100){
00210            std::cout<<"very few points ";
00211            return false;
00212    }
00213 
00214    te.mark("nearby pts");
00215    //Iterate the following:
00216    //    find centroid of current cluster
00217    //    add a little height, to drive the cluster away from the arm
00218    //    search again around the centroid to redefine our cluster
00219 
00220    pcl::compute3DCentroid(cloud,inds2,centroid1);
00221    pt2.x=centroid1(0); pt2.y=centroid1(1)-.02; pt2.z=centroid1(2);
00222    NNN(cloud,pt2,inds2, .1);
00223 
00224    //in the middle of everything, locate where the arms is:
00225    std::vector<int> temp;
00226    NNN(cloud,pt2,temp, .15);
00227    //finding the arms is really reliable. we'll just throw out anytime when we can't find it.
00228    if(!findNearbyPts(cloud,temp,nearcent1))
00229       return false;
00230 
00231 
00232    te.mark("find arm");
00233 
00234    pcl::compute3DCentroid(cloud,inds2,centroid1);
00235    pt2.x=centroid1(0); pt2.y=centroid1(1)-.01; pt2.z=centroid1(2);
00236    NNN(cloud,pt2,inds2, .1);
00237 
00238    //save this cluster as a separate cloud.
00239    getSubCloud(cloud,inds2,cloudout);
00240 
00241 
00242    te.mark("save sub ");
00243 
00244    //-------Decide whether we are looking at potential hands:
00245    //try to classify whether this is actually a hand, or just a random object (like a face)
00246    //if there are many points at the same distance that we did not grab, then the object is not "out in front"
00247    for(uint i=0;i<inds2.size(); ++i) inds3[inds2[i]]=0; //mark in inds3 all the points in the potential hand
00248    pcl::compute3DCentroid(cloudout,centroid1);
00249    int s1,s2=0;
00250    s1=inds2.size();
00251    //search for all points in the cloud that are as close as the center of the potential hand:
00252    NNN(cloud,pt,inds2, centroid1.norm());
00253    for(uint i=0;i<inds2.size(); ++i){
00254       if(inds3[inds2[i]]) ++s2;
00255    }
00256    if(((float)s2)/((float)s1) > .3){
00257       std::cout<<"No hands detected ";
00258       return false;
00259    }
00260 
00261 
00262    te.mark("classify ");
00263 //   te.print();
00264    //OK, we have decided that there is at least one hand.
00265    clouds.push_back(cloudout);
00266 //   if(!foundarm) //if we never found the arm, use the centroid
00267     nearcents.push_back(nearcent1);
00268 
00269    //-----------------FIND SECOND HAND---------------------
00270    //find next smallest point
00271    smallestdist+=.3;
00272    smallestdist*=smallestdist;
00273 //   double thresh=smallestdist;
00274    bool foundpt=false;
00275    for(uint i=0;i<dists.size(); ++i){
00276       //a point in the second had must be:
00277       //   dist to camera must be within 30 cm of the first hand's closest dist
00278       //   more than 20 cm from the center of the first hand
00279       //   more than 30 cm from the center of the arm
00280 
00281       if(dists[i]<smallestdist && inds3[i] && gdist(cloud.points[inds1[i]],centroid1) > .2  && gdist(cloud.points[inds1[i]],nearcent1) >.3){
00282 //         printf("found second hand point %.03f  hand dist = %.03f, arm dist = %.03f \n",
00283 //               dists[i],gdist(cloud.points[inds1[i]],centroid1),gdist(cloud.points[inds1[i]],nearcent1));
00284          ind=inds1[i];
00285          smallestdist=dists[i];
00286          foundpt=true;
00287       }
00288    }
00289 
00290    if(foundpt){
00291 //         cout<<" 2nd run: "<<thresh-smallestdist;
00292            pcl::PointCloud<pcl::PointXYZ> cloudout2;
00293            NNN(cloud,cloud.points[ind],inds2, .1);
00294            pcl::compute3DCentroid(cloud,inds2,centroid2);
00295            pt2.x=centroid2(0); pt2.y=centroid2(1)-.02; pt2.z=centroid2(2);
00296            NNN(cloud,pt2,inds2, .1);
00297            pcl::compute3DCentroid(cloud,inds2,centroid2);
00298            pt2.x=centroid2(0); pt2.y=centroid2(1)-.01; pt2.z=centroid2(2);
00299            NNN(cloud,pt2,inds2, .1);
00300 
00301            //if too few points in the second hand, discard
00302            if(inds2.size()<100) return true;
00303 
00304            //check for overlapping points. if there are any, we don't want it!
00305 //         int overlap=0;
00306            for(uint i=0;i<inds2.size(); ++i)
00307                    if(inds3[inds2[i]]==0)
00308                       return true;
00309 
00310            NNN(cloud,pt2,temp, .15);
00311            //finding the arms is really reliable. we'll just throw out anytime when we can't find it.
00312            if(!findNearbyPts(cloud,temp,nearcent1))
00313               return true;
00314 
00315            getSubCloud(cloud,inds2,cloudout2);
00316            clouds.push_back(cloudout2);
00317            nearcents.push_back(nearcent1);
00318 
00319    }
00320 
00321 
00322 
00323    return true;
00324 
00325 }
00326 
00327 
00328 class HandDetector
00329 {
00330 
00331 private:
00332   ros::NodeHandle n_;
00333   ros::Publisher cloudpub_[2],handspub_;
00334   ros::Subscriber sub_;
00335   std::string fixedframe;
00336 
00337 public:
00338 
00339   HandDetector()
00340   {
00341    handspub_ = n_.advertise<body_msgs::Hands> ("hands", 1);
00342    cloudpub_[0] = n_.advertise<sensor_msgs::PointCloud2> ("hand0_cloud", 1);
00343    cloudpub_[1] = n_.advertise<sensor_msgs::PointCloud2> ("hand1_cloud", 1);
00344     sub_=n_.subscribe("/camera/rgb/points", 1, &HandDetector::cloudcb, this);
00345 
00346   }
00347 
00348 
00349 
00350 
00351   void makeHand(pcl::PointCloud<pcl::PointXYZ> &cloud,Eigen::Vector4f &_arm,  body_msgs::Hand &handmsg){
00352     Eigen::Vector4f centroid;
00353     handmsg.thumb=-1; //because we have not processed the hand...
00354     handmsg.stamp=cloud.header.stamp;
00355     pcl::compute3DCentroid (cloud, centroid);
00356     handmsg.arm=eigenToMsgPoint(_arm);
00357     handmsg.state="unprocessed";
00358     handmsg.palm.translation.x=centroid(0);
00359     handmsg.palm.translation.y=centroid(1);
00360     handmsg.palm.translation.z=centroid(2);
00361     pcl::toROSMsg(cloud,handmsg.handcloud);
00362     handmsg.handcloud.header=cloud.header;
00363     //TODO: do tracking seq
00364   }
00365 
00366 
00367   void cloudcb(const sensor_msgs::PointCloud2ConstPtr &scan){
00368           timeval t0=g_tick();
00369      sensor_msgs::PointCloud2 cloud2;
00370      pcl::PointCloud<pcl::PointXYZ> cloud;
00371      pcl::fromROSMsg(*scan,cloud);
00372      std::vector<Eigen::Vector4f> arm_center;
00373            std::vector<pcl::PointCloud<pcl::PointXYZ> > initialclouds;
00374       std::cout<<" pre blob time:  "<<g_tock(t0)<<"  ";
00375                 if(!getNearBlobs2(cloud,initialclouds,arm_center)){
00376                    std::cout<<" no hands detected "<<std::endl;
00377                    return;
00378                 }
00379       std::cout<<" blob time:  "<<g_tock(t0)<<"  ";
00380 
00381                 body_msgs::Hand hand1,hand2;
00382                 body_msgs::Hands hands;
00383 
00384                 // Publish hands
00385                 if(initialclouds.size()==1){
00386                   makeHand(initialclouds[0],arm_center[0],hand1);
00387         hands.hands.push_back(hand1);
00388         cloudpub_[0].publish(hand1.handcloud);
00389         handspub_.publish(hands);
00390                 }
00391 
00392 
00393       if(initialclouds.size()==2){
00394          makeHand(initialclouds[0],arm_center[0],hand1);
00395          makeHand(initialclouds[1],arm_center[1],hand2);
00396          //decide which is the left hand, which goes first:
00397          if(hand1.palm.translation.x < hand2.palm.translation.x){ //TODO: make sure this is right!
00398             hands.hands.push_back(hand1);
00399             hands.hands.push_back(hand2);
00400          }
00401          else{
00402             hands.hands.push_back(hand2);
00403             hands.hands.push_back(hand1);
00404          }
00405          cloudpub_[0].publish(hands.hands[0].handcloud);
00406          cloudpub_[1].publish(hands.hands[1].handcloud);
00407          handspub_.publish(hands);
00408       }
00409 
00410 
00411       std::cout<<" total time:  "<<g_tock(t0)<<std::endl;
00412   }
00413 
00414 } ;
00415 
00416 
00417 
00418 
00419 int main(int argc, char **argv)
00420 {
00421   ros::init(argc, argv, "hand_detector");
00422   ros::NodeHandle n;
00423   HandDetector detector;
00424   ros::spin();
00425   return 0;
00426 }


labust_kinect
Author(s): LABUST
autogenerated on Fri Feb 7 2014 11:37:27