MotldNode.cpp
Go to the documentation of this file.
00001 #include <ros/ros.h>
00002 #include <sensor_msgs/Image.h>
00003 #include <image_transport/image_transport.h>
00004 #include <opencv/cv.h>
00005 #include <opencv/highgui.h>
00006 #include <cv_bridge/CvBridge.h>
00007 #include <boost/thread/mutex.hpp>
00008 
00009 // point cloud related includes
00010 #include <pcl/point_cloud.h>
00011 #include <pcl/point_types.h>
00012 #include <pcl_ros/impl/transforms.hpp>
00013 #include <sensor_msgs/PointCloud2.h>
00014 #include <tf/tfMessage.h>
00015 #include <tf/transform_broadcaster.h>
00016 // subscriber filters
00017 #include <image_transport/image_transport.h>
00018 #include <message_filters/subscriber.h>
00019 #include <message_filters/synchronizer.h>
00020 #include <message_filters/sync_policies/approximate_time.h>
00021 #include <image_transport/subscriber_filter.h>
00022 
00023 // motld messages
00024 #include <motld/BoundingBox.h>
00025 #include <motld/Save.h>
00026 #include <motld/Config.h>
00027 #include <motld/TrackedObjects.h>
00028 
00029 // other messages
00030 #include <geometry_msgs/PoseArray.h>
00031 
00032 // tld classes and functions
00033 #include <motld/MultiObjectTLD.h>
00034 #ifndef SQR
00035 #define SQR(X) ((X)*(X))
00036 #endif
00037 
00038 using namespace motld;
00039 
00040 // typedefs
00041 typedef pcl::PointXYZ Point_t;
00042 typedef pcl::PointCloud<Point_t> Cloud_t;
00043 typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::Image, sensor_msgs::PointCloud2> MySyncPolicy;
00044 
00045 struct CloudProperties {
00046   pcl::PointXYZ mean;
00047   float width;
00048   float height;
00049 };
00050 
00051 bool debug = false;
00052 
00053 // image parameters
00054 const int CHANNELS = 3;
00055 int width_ = 640;
00056 int height_ = 480;
00057 bool force_resize_ = true;
00058 float resize_factor_ = 1.f;
00059 
00060 // pointcloud processing related variables
00061 bool use_cloud_ = true;
00062 Cloud_t cloud_;
00063 std::string frame_id_;
00064 
00065 // working buffers for the image
00066 unsigned char *img_;
00067 IplImage *ipl_img_;
00068 std::string image_frame_;
00069 sensor_msgs::CvBridge bridge_;
00070 
00071 // for publishing the poses
00072 motld::TrackedObjects tracked_objects_;
00073 geometry_msgs::PoseArray debug_objects_;
00074 ros::Publisher poses_pub_;
00075 ros::Publisher debug_poses_pub_;
00076 
00077 // for sending a debug image
00078 // RGB channels of debug image
00079 Matrix maRed;
00080 Matrix maGreen;
00081 Matrix maBlue;
00082 // the actual ipl image and message
00083 IplImage *out_img_;
00084 sensor_msgs::Image image_msg_;
00085 image_transport::Publisher image_pub_;
00086 
00087 // motld instance and settings
00088 bool learningEnabled_ = true;
00089 MOTLDSettings settings = MOTLDSettings(COLOR_MODE_RGB);
00090 MultiObjectTLD tld_ = MultiObjectTLD(width_, height_, settings);
00091 boost::mutex tld_mutex_;
00092 
00093 // initialization
00094 bool initialized_ = false;
00095 bool have_initial_box_ = false;
00096 bool started_ = false;
00097 bool read_model_from_file_ = false;
00098 std::string model_file_("");
00099 std::string camera_topic_("/camera/rgb/image_color");
00100 std::string depth_topic_("/camera/depth_registered/points");
00101 
00102 CloudProperties calculateCloudProperties(ObjectBox box) {
00103   CloudProperties cp;
00104 
00105   int startX, startY;
00106   int endX, endY;
00107   // security check
00108   if (!initialized_ || !started_) {
00109     ROS_WARN("calculateCloudProperties called but tracker is not yet started");
00110     return cp;
00111   }
00112   // rescale box appropriately
00113   if (force_resize_) {
00114     box.x *= resize_factor_;
00115     box.y *= resize_factor_;
00116     box.width *= resize_factor_;
00117     box.height *= resize_factor_;
00118   }
00119   // TODO: this is a crude hack as it only supports equal resolution and
00120   //       half image resolution clouds that are registered with the image
00121   if (int(cloud_.width) != ipl_img_->width * resize_factor_) {
00122     startX = box.x/2;
00123     startY = box.y/2;
00124     endX = startX + box.width / 2;
00125     endY = startY + box.height / 2;
00126   } else {
00127     startX = box.x;
00128     startY = box.y;
00129     endX = startX + box.width;
00130     endY = startY + box.height;
00131   }
00132   //ROS_INFO("startX %d", startX);
00133   // compute center of mass of bounding box
00134   int count = 0;
00135   double mean_z = 0.;
00136   for (int x = startX; x < endX; ++x) {
00137     for (int y = startY; y < endY; ++y) {
00138       const pcl::PointXYZ &p = cloud_(x, y);
00139       if (std::isnan(p.x) || std::isnan(p.y) || std::isnan(p.z))
00140         continue;
00141       mean_z += p.z;
00142       ++count;
00143     }
00144   }
00145   mean_z /=count;
00146 
00147   double var_z = 0.;
00148   for (int x = startX; x < endX; ++x) {
00149     for (int y = startY; y < endY; ++y) {
00150       const pcl::PointXYZ &p = cloud_(x, y);
00151       if (std::isnan(p.x) || std::isnan(p.y) || std::isnan(p.z))
00152         continue;
00153       var_z += SQR(p.z - mean_z);
00154     }
00155   }
00156   var_z /= count;
00157   double std_dev_z = std::sqrt(var_z);
00158 
00159   // remove outliers, compute new mean and width/height
00160   float min_x = 1e7, max_x = -1e7;
00161   float min_y = 1e7, max_y = -1e7;
00162   count = 0;
00163   for (int x = startX; x < endX; ++x) {
00164     for (int y = startY; y < endY; ++y) {
00165       const pcl::PointXYZ &p = cloud_(x, y);
00166       if (std::isnan(p.x) || std::isnan(p.y) || std::isnan(p.z))
00167         continue;
00168       if (std::fabs(p.z - mean_z) > 2. * std_dev_z) {
00169         //ROS_INFO_STREAM("Removing depth of: " << p.z << " first mean was: " << mean_z << " std_dev: " << std_dev_z);
00170         // remove this point as it is too far away from the mean
00171         continue;
00172       }
00173       cp.mean.x += p.x;
00174       cp.mean.y += p.y;
00175       cp.mean.z += p.z;
00176       if (p.x < min_x)
00177         min_x = p.x;
00178       if (p.x > max_x)
00179         max_x = p.x;
00180       if (p.y < min_y)
00181         min_y = p.y;
00182       if (p.y > max_y)
00183         max_y = p.y;
00184       ++count;
00185     }
00186   }
00187   if (count != 0) {
00188     cp.mean.x /= count;
00189     cp.mean.y /= count;
00190     cp.mean.z /= count;
00191     cp.width  = max_x - min_x;
00192     cp.height = max_y - min_y;
00193   }
00194 
00195   if (cp.height < 0)
00196     cp.height = 0. ;
00197   if (cp.width < 0)
00198     cp.width = 0. ;
00199 
00200   return cp;
00201 }
00202 
00203 void init() {
00204   // create tld instance
00205   if (read_model_from_file_) 
00206   {
00207     tld_ = MultiObjectTLD::loadClassifier(model_file_.c_str());
00208     have_initial_box_ = true;
00209   }
00210 
00211   // allocate array holding all pixel values for computation
00212   img_ = new unsigned char[width_ * height_ * CHANNELS];
00213   // also allocate an ipimage for displaying debug info and resizing
00214   CvSize wsize = {width_, height_};
00215   ipl_img_ = cvCreateImage(wsize, IPL_DEPTH_8U, CHANNELS);
00216   out_img_ = cvCreateImage(wsize, IPL_DEPTH_8U, CHANNELS);
00217   initialized_ = true;
00218   ROS_INFO("done initializing");
00219 }
00220 
00221 void prepareForProcessing(IplImage *image) {
00222   if (!initialized_)
00223   {
00224     ROS_WARN("motld not initialized when calling prepareForProcessing()");
00225     return;
00226   }
00227   IplImage *copy_src = image;
00228   int size = width_ * height_;
00229   if(force_resize_)
00230   {
00231     cvResize(image, ipl_img_);
00232     copy_src = ipl_img_;
00233   }
00234   // copy into working buffer
00235   for(int i = 0; i < size; i++){
00236     img_[i] = copy_src->imageData[i*3+2];
00237     img_[i+size] = copy_src->imageData[i*3+1];
00238     img_[i+2*size] = copy_src->imageData[i*3];
00239   }
00240 }
00241 
00242 void processAndPublish() {
00243   if (!initialized_)
00244   {
00245     ROS_WARN("motld not initialized when calling processAndPublish()");
00246     return;
00247   }
00248   double start = ros::Time::now().toSec();
00249   tld_.processFrame(img_);
00250   double elapsed_time = ros::Time::now().toSec() - start;
00251   double fps = 1. / elapsed_time;
00252   ROS_DEBUG("MOTLD fps: %.2f", fps);
00253  
00254   // publish debug image if subscribers present
00255   if (image_pub_.getNumSubscribers() > 0)
00256   {
00257     tld_.getDebugImage(img_, maRed, maGreen, maBlue, 0);    
00258     for(int i = 0; i < width_*height_; ++i){
00259       out_img_->imageData[3*i+2] = maRed.data()[i];
00260       out_img_->imageData[3*i+1] = maGreen.data()[i];
00261       out_img_->imageData[3*i+0] = maBlue.data()[i];
00262     }
00263     bridge_.fromIpltoRosImage(out_img_, image_msg_);
00264     image_msg_.header.stamp = ros::Time::now();
00265     image_msg_.header.frame_id = image_frame_;
00266     image_pub_.publish(image_msg_);
00267   }
00268 }
00269 
00270 void processCloud() {
00271   // publish poses
00272   bool poses_pub_subscribed = poses_pub_.getNumSubscribers() > 0;
00273   bool debug_pub_subscribed = debug_poses_pub_.getNumSubscribers() > 0;
00274   if (poses_pub_subscribed || debug_pub_subscribed)
00275   {
00276     std::vector<ObjectBox> boxes = tld_.getObjectBoxes();
00277 
00278     // pose array
00279     tracked_objects_.pose.header.stamp = ros::Time::now();
00280     tracked_objects_.pose.header.frame_id = frame_id_;
00281     tracked_objects_.height.resize(boxes.size());
00282     tracked_objects_.width.resize(boxes.size());
00283     tracked_objects_.status.resize(boxes.size());
00284     tracked_objects_.name.resize(boxes.size());
00285     tracked_objects_.pose.poses.resize(boxes.size());
00286 
00287     // debug array 
00288     debug_objects_.header.stamp = ros::Time::now();
00289     debug_objects_.header.frame_id = frame_id_;
00290     debug_objects_.poses.resize(boxes.size());
00291 
00292     for (size_t o = 0; o < boxes.size(); ++o) 
00293     {
00294       int status = tld_.getStatus(boxes[o].objectId);
00295       // set name of object
00296       tracked_objects_.name[o] = boxes[o].objectId;
00297 
00298       // calculate cloud properties from cloud and publish
00299       CloudProperties cp  = calculateCloudProperties(boxes[o]);
00300       tracked_objects_.height[o] = cp.height;
00301       tracked_objects_.width[o] = cp.width;
00302       tracked_objects_.status[o] = status;
00303       
00304       tracked_objects_.pose.poses[o].position.x = cp.mean.x;
00305       tracked_objects_.pose.poses[o].position.y = cp.mean.y;
00306       tracked_objects_.pose.poses[o].position.z = cp.mean.z;
00307 
00308       // TODO: compute reasonable orientation from cloud here
00309       tracked_objects_.pose.poses[o].orientation.x = 0.f;
00310       tracked_objects_.pose.poses[o].orientation.y = 0.f;
00311       tracked_objects_.pose.poses[o].orientation.z = 0.f;
00312       tracked_objects_.pose.poses[o].orientation.w = 1.f;
00313 
00314       if (debug_pub_subscribed)
00315         {
00316           debug_objects_.poses[o].position.x = cp.mean.x;
00317           debug_objects_.poses[o].position.y = cp.mean.y;
00318           debug_objects_.poses[o].position.z = cp.mean.z;
00319 
00320           // TODO: compute reasonable orientation from cloud here
00321           debug_objects_.poses[o].orientation.x = 0.f;
00322           debug_objects_.poses[o].orientation.y = 0.f;
00323           debug_objects_.poses[o].orientation.z = 0.f;
00324           debug_objects_.poses[o].orientation.w = 1.f;
00325         }
00326     }
00327     
00328     if (poses_pub_subscribed)
00329         poses_pub_.publish(tracked_objects_);
00330 
00331     if (debug_pub_subscribed)
00332         debug_poses_pub_.publish(debug_objects_);
00333   }
00334 }
00335 
00340 void trackerCallback(const sensor_msgs::ImageConstPtr& msg) {
00341   // grab a frame
00342   IplImage *color_image = bridge_.imgMsgToCv(msg, "bgr8");
00343   image_frame_ = msg->header.frame_id;
00344 
00345   // first time initialization stuff
00346   if (!started_)
00347   {
00348     if (!force_resize_) {
00349       width_ = color_image->width;
00350       height_ = color_image->height;
00351     }
00352     else {
00353       resize_factor_ = color_image->width / width_;
00354     }
00355     started_ = true;
00356   }
00357 
00358   // prepare input
00359   prepareForProcessing(color_image);
00360   // lock and process
00361   {
00362     boost::mutex::scoped_lock(tld_mutex_);
00363     processAndPublish();
00364   }
00365 }
00366 
00367 void synchronizedCallback(const sensor_msgs::ImageConstPtr& msg, const sensor_msgs::PointCloud2ConstPtr& msg_cloud) {
00368   // grab the image and process
00369   trackerCallback(msg);
00370   // also grab the cloud
00371   pcl::fromROSMsg(*msg_cloud, cloud_);
00372   frame_id_ = cloud_.header.frame_id;
00373   // and process it
00374   processCloud();
00375 }
00376 
00377 void boundingBoxCallback(const motld::BoundingBoxConstPtr& msg) {
00378   if (!initialized_ || !started_) 
00379   {
00380     ROS_WARN("boundingBoxCallback called but tracker is not yet started");
00381     return;
00382   }
00383   ROS_INFO("boundingBoxCallback called");
00384   ObjectBox box;
00385   box.x = msg->x / resize_factor_;
00386   box.y = msg->y / resize_factor_;
00387   box.width = msg->width / resize_factor_;
00388   box.height = msg->height / resize_factor_;
00389   {
00390     boost::mutex::scoped_lock(tld_mutex_);
00391     tld_.addObject(box);
00392     have_initial_box_ = true;
00393   }
00394 }
00395 
00396 void saveModelCallback(const motld::SaveConstPtr& msg) {
00397   boost::unique_lock<boost::mutex> state_lock_(tld_mutex_);
00398   ROS_INFO("Saving model to %s", msg->filename.c_str());
00399   tld_.saveClassifier(msg->filename.c_str());
00400 }
00401 
00402 void configCallback(const motld::ConfigConstPtr& msg) {
00403   ROS_INFO("Got new config parameters");
00404   if (msg->learning != -1) {
00405     learningEnabled_ = msg->learning > 0;
00406   }
00407   
00408   // set the learning parameter in the tld instance
00409   {
00410     boost::mutex::scoped_lock tld_lock(tld_mutex_);
00411     tld_.enableLearning(learningEnabled_);
00412   }
00413 }
00414 
00415 
00419 int main(int argc, char **argv) {
00420   ros::init(argc, argv, "motld_node");
00421   ros::NodeHandle nh;
00422   ros::NodeHandle private_nh("~");
00423   image_transport::ImageTransport it(nh);
00424   // read parameters
00425   private_nh.param("width", width_, width_);
00426   private_nh.param("height", width_, width_);
00427   private_nh.param("force_resize", force_resize_, force_resize_);
00428   private_nh.param("learning_enabled", learningEnabled_, learningEnabled_);
00429   private_nh.param("model_file", model_file_, model_file_);
00430   private_nh.param("use_cloud", use_cloud_, use_cloud_);
00431   private_nh.param("camera_topic", camera_topic_, camera_topic_);
00432   private_nh.param("depth_topic", depth_topic_, depth_topic_);
00433   if (model_file_.compare("") != 0)
00434   {
00435     read_model_from_file_ = true;
00436   }
00437   // init subscribers
00438   image_transport::SubscriberFilter sub(it, camera_topic_, 1);
00439   image_transport::Subscriber sub_img; 
00440   message_filters::Subscriber< sensor_msgs::PointCloud2 > sub_cloud(nh, depth_topic_, 1);
00441   message_filters::Synchronizer< MySyncPolicy > sync(MySyncPolicy(30), sub, sub_cloud);
00442   if (!use_cloud_ || depth_topic_.compare("") == 0) 
00443   {
00444     ROS_INFO("not registering with cloud");
00445     ROS_INFO_STREAM("topic: " << camera_topic_);
00446     sub_img = it.subscribe(camera_topic_, 1, trackerCallback);
00447   }
00448   else
00449   {
00450     ROS_INFO("using kinect registered version");
00451     ROS_INFO_STREAM("topics: " << camera_topic_ << " " << depth_topic_);
00452     sync.registerCallback(synchronizedCallback);
00453   }
00454   
00455   ros::Subscriber sub_box = private_nh.subscribe<motld::BoundingBox>("tracker_start_box", 1, boundingBoxCallback);
00456   ros::Subscriber sub_config = private_nh.subscribe<motld::Config>("config_opentld", 1, configCallback);
00457   ros::Subscriber sub_save = private_nh.subscribe<motld::Save>("save_tld_model", 1, saveModelCallback);        
00458 
00459   // init publishers
00460   image_pub_ = it.advertise("tracking_image", 1);
00461   poses_pub_ = private_nh.advertise<motld::TrackedObjects>("tracked_objects", 1);
00462   debug_poses_pub_ = private_nh.advertise<geometry_msgs::PoseArray>("debug_poses", 1);
00463 
00464   // init motld
00465   init();
00466   // and run (everything is handled via the trackerCallback)
00467   while (ros::ok()) 
00468   {
00469     ros::spinOnce();
00470   }
00471 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines


motld
Author(s): Jost Tobias Springenberg, Jan Wuelfing
autogenerated on Wed Dec 26 2012 16:24:49