object_singulation_node.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2012, Georgia Institute of Technology
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 of the Georgia Institute of Technology nor the names of
00018  *     its 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 // ROS
00035 #include <ros/ros.h>
00036 
00037 #include <std_msgs/Header.h>
00038 #include <geometry_msgs/PointStamped.h>
00039 #include <geometry_msgs/PoseStamped.h>
00040 #include <sensor_msgs/Image.h>
00041 #include <sensor_msgs/PointCloud2.h>
00042 #include <sensor_msgs/CameraInfo.h>
00043 
00044 #include <message_filters/subscriber.h>
00045 #include <message_filters/synchronizer.h>
00046 #include <message_filters/sync_policies/approximate_time.h>
00047 #include <cv_bridge/cv_bridge.h>
00048 
00049 // TF
00050 #include <tf/transform_listener.h>
00051 
00052 // PCL
00053 #include <pcl16/point_cloud.h>
00054 #include <pcl16/point_types.h>
00055 #include <pcl16/common/common.h>
00056 #include <pcl16/common/eigen.h>
00057 #include <pcl16/common/centroid.h>
00058 #include <pcl16/io/io.h>
00059 #include <pcl16_ros/transforms.h>
00060 #include <pcl16/ros/conversions.h>
00061 #include <pcl16/ModelCoefficients.h>
00062 #include <pcl16/sample_consensus/method_types.h>
00063 #include <pcl16/sample_consensus/model_types.h>
00064 #include <pcl16/segmentation/sac_segmentation.h>
00065 #include <pcl16/filters/extract_indices.h>
00066 
00067 // OpenCV
00068 #include <opencv2/core/core.hpp>
00069 #include <opencv2/imgproc/imgproc.hpp>
00070 #include <opencv2/highgui/highgui.hpp>
00071 
00072 // Boost
00073 #include <boost/shared_ptr.hpp>
00074 
00075 // cpl_visual_features
00076 #include <cpl_visual_features/helpers.h>
00077 #include <cpl_visual_features/features/edges.h>
00078 
00079 // tabletop_pushing
00080 #include <tabletop_pushing/SingulationPush.h>
00081 #include <tabletop_pushing/LocateTable.h>
00082 #include <tabletop_pushing/point_cloud_segmentation.h>
00083 
00084 // STL
00085 #include <vector>
00086 #include <set>
00087 #include <string>
00088 #include <sstream>
00089 #include <iostream>
00090 #include <utility>
00091 #include <float.h>
00092 #include <math.h>
00093 #include <time.h> // for srand(time(NULL))
00094 #include <cstdlib> // for MAX_RAND
00095 
00096 // Debugging IFDEFS
00097 #define DISPLAY_INPUT_COLOR 1
00098 // #define DISPLAY_INPUT_DEPTH 1
00099 // #define DISPLAY_WORKSPACE_MASK 1
00100 #define DISPLAY_PROJECTED_OBJECTS 1
00101 // #define DISPLAY_LINKED_EDGES 1
00102 #define DISPLAY_CHOSEN_BOUNDARY 1
00103 #define DISPLAY_3D_BOUNDARIES 1
00104 #define DISPLAY_PUSH_VECTOR 1
00105 #define DISPLAY_WAIT 1
00106 #define DEBUG_PUSH_HISTORY 1
00107 #define randf() static_cast<float>(rand())/RAND_MAX
00108 
00109 using boost::shared_ptr;
00110 using tabletop_pushing::SingulationPush;
00111 using tabletop_pushing::LocateTable;
00112 using geometry_msgs::PoseStamped;
00113 using geometry_msgs::PointStamped;
00114 typedef pcl16::PointCloud<pcl16::PointXYZ> XYZPointCloud;
00115 typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::Image,
00116                                                         sensor_msgs::Image,
00117                                                         sensor_msgs::PointCloud2> MySyncPolicy;
00118 typedef SingulationPush::Response PushVector;
00119 using tabletop_pushing::PointCloudSegmentation;
00120 using tabletop_pushing::ProtoObject;
00121 using tabletop_pushing::ProtoObjects;
00122 using cpl_visual_features::upSample;
00123 using cpl_visual_features::downSample;
00124 using cpl_visual_features::LinkEdges;
00125 
00126 struct Boundary : public std::vector<cv::Point>
00127 {
00128   std::vector<pcl16::PointXYZ> points3D;
00129   int object_idx;
00130   double ort;
00131   double xyLength3D;
00132   bool external;
00133   bool too_short;
00134   double push_angle;
00135   Eigen::Vector3f push_dir;
00136   ProtoObjects splits;
00137 };
00138 
00139 struct PushOpt
00140 {
00141   PushOpt(ProtoObject& _obj, double _push_angle,
00142           Eigen::Vector3f _push_vec, unsigned int _obj_idx,
00143           unsigned int _split_id, double _push_dist, double _split_score,
00144           int bin) :
00145       obj(_obj), push_angle(_push_angle), push_unit_vec(_push_vec),
00146       object_idx(_obj_idx), split_id(_split_id), push_dist(_push_dist),
00147       will_collide(false), will_leave(false), will_leave_table(false),
00148       start_collides(false), start_leaves(false), bad_angle(false),
00149       prev_bin_filled(false), next_bin_filled(false), split_score(_split_score),
00150       push_bin(bin), boundary_idx(0)
00151   {
00152   }
00153 
00154   Eigen::Vector4f getMovedPoint(geometry_msgs::Point p)
00155   {
00156     Eigen::Vector4f moved;
00157     moved[0] = p.x + push_unit_vec[0]*push_dist;
00158     moved[1] = p.y + push_unit_vec[1]*push_dist;
00159     moved[2] = p.z + push_unit_vec[2]*push_dist;
00160     moved[3] = 1.0f;
00161     return moved;
00162   }
00163 
00164   Eigen::Vector4f getMovedCentroid()
00165   {
00166     Eigen::Vector4f moved;
00167     moved[0] = obj.centroid[0] + push_unit_vec[0]*push_dist;
00168     moved[1] = obj.centroid[1] + push_unit_vec[1]*push_dist;
00169     moved[2] = obj.centroid[2] + push_unit_vec[2]*push_dist;
00170     moved[3] = 1.0f;
00171     return moved;
00172   }
00173 
00174   PushVector getPushVector()
00175   {
00176     PushVector push;
00177     push.start_point = start_point;
00178     push.push_angle = push_angle;
00179     push.push_dist = push_dist;
00180     push.object_idx = object_idx;
00181     push.no_push = false;
00182     push.push_bin = push_bin;
00183     return push;
00184   }
00185 
00186   XYZPointCloud pushPointCloud(XYZPointCloud& cloud_in)
00187   {
00188     XYZPointCloud cloud_out;
00189     cloud_out.header = cloud_in.header;
00190     cloud_out.resize(cloud_in.size());
00191     const double delta_x = push_unit_vec[0]*push_dist;
00192     const double delta_y = push_unit_vec[1]*push_dist;
00193     for (unsigned int i = 0; i < cloud_in.size(); ++i)
00194     {
00195       cloud_out.at(i) = cloud_in.at(i);
00196       cloud_out.at(i).x += delta_x;
00197       cloud_out.at(i).y += delta_y;
00198     }
00199     return cloud_out;
00200   }
00201 
00210   static bool compareOpts(PushOpt a, PushOpt b)
00211   {
00212     // Worst if push off the table
00213     if (a.will_leave_table)
00214     {
00215       if (!b.will_leave_table)
00216         return false;
00217     }
00218     else if (b.will_leave_table)
00219     {
00220       return true;
00221     }
00222     // Don't push into other stuff
00223     if (a.will_collide)
00224     {
00225       if (!b.will_collide)
00226         return false;
00227     }
00228     else if (b.will_collide)
00229     {
00230       return true;
00231     }
00232     // Don't start running into other stuff
00233     if (a.start_collides)
00234     {
00235       if (!b.start_collides)
00236         return false;
00237     }
00238     else if (b.start_collides)
00239     {
00240       return true;
00241     }
00242     // Dont leave the workspace
00243     if (a.will_leave)
00244     {
00245       if (!b.will_leave)
00246         return false;
00247     }
00248     else if (b.will_leave)
00249     {
00250       return true;
00251     }
00252     // Don't start outside the workspace
00253     if (a.start_leaves)
00254     {
00255       if (!b.start_leaves)
00256         return false;
00257     }
00258     else if (b.start_leaves)
00259     {
00260       return true;
00261     }
00262     // Split based on point cloud ratios
00263     if (a.split_score == b.split_score)
00264     {
00265       return (a.start_point.x < b.start_point.x);
00266     }
00267     return (a.split_score > b.split_score);
00268   }
00269 
00270   // Members
00271   ProtoObject obj;
00272   double push_angle;
00273   Eigen::Vector3f push_unit_vec;
00274   unsigned int object_idx;
00275   unsigned int split_id;
00276   double push_dist;
00277   bool will_collide;
00278   bool will_leave;
00279   bool will_leave_table;
00280   bool start_collides;
00281   bool start_leaves;
00282   bool bad_angle;
00283   bool prev_bin_filled;
00284   bool next_bin_filled;
00285   geometry_msgs::Point start_point;
00286   double split_score;
00287   unsigned int push_bin;
00288   unsigned int boundary_idx;
00289 };
00290 
00291 typedef std::vector<PushOpt> PushOpts;
00292 
00293 class ObjectSingulation
00294 {
00295  public:
00296   ObjectSingulation(shared_ptr<PointCloudSegmentation> pcl_segmenter) :
00297       pcl_segmenter_(pcl_segmenter), callback_count_(0), next_id_(0),
00298       initialized_(false), merged_(false), split_(false)
00299   {
00300     // Create derivative kernels for edge calculation
00301     cv::getDerivKernels(dy_kernel_, dx_kernel_, 1, 0, CV_SCHARR, true, CV_32F);
00302     cv::flip(dy_kernel_, dy_kernel_, -1);
00303     cv::transpose(dy_kernel_, dx_kernel_);
00304   }
00305 
00306   bool initialize(cv::Mat& color_img, cv::Mat& depth_img,
00307                   XYZPointCloud& cloud, cv::Mat& workspace_mask)
00308   {
00309     callback_count_ = 0;
00310     next_id_ = 0;
00311     ProtoObjects objs = calcProtoObjects(cloud);
00312     initialized_ = true;
00313     callback_count_++;
00314 
00315     PushVector push_vector;
00316     push_vector.object_idx = 0;
00317     push_vector.object_id = 0;
00318     push_vector.no_push = true;
00319     push_vector.num_objects = 0;
00320     prev_push_vector_ = push_vector;
00321     return true;
00322   }
00323 
00334   PushVector getPushVector(bool no_push_calc, cv::Mat& color_img,
00335                            cv::Mat& depth_img, XYZPointCloud& cloud,
00336                            cv::Mat& workspace_mask, bool use_guided=true)
00337   {
00338     if (!initialized_)
00339     {
00340       ROS_WARN_STREAM("Trying to get a push vector before initializing.");
00341       PushVector push_vector;
00342       push_vector.object_idx = 0;
00343       push_vector.object_id = 0;
00344       push_vector.no_push = true;
00345       push_vector.num_objects = 0;
00346       return push_vector;
00347     }
00348     // Move current proto objects to prev proto objects
00349     prev_proto_objs_.clear();
00350     for (unsigned int i = 0; i < cur_proto_objs_.size(); ++i)
00351     {
00352       prev_proto_objs_.push_back(cur_proto_objs_[i]);
00353     }
00354 
00355     calcProtoObjects(cloud, use_guided);
00356 
00357     if (no_push_calc)
00358     {
00359       PushVector push_vector;
00360       push_vector.no_push = true;
00361       push_vector.num_objects = cur_proto_objs_.size();
00362       ++callback_count_;
00363       prev_push_vector_ = push_vector;
00364       return push_vector;
00365     }
00366 
00367     if (!use_guided)
00368     {
00369       PushVector push_vector = findRandomPushVector(cur_proto_objs_, cloud,
00370                                                     color_img);
00371       prev_push_vector_ = push_vector;
00372       ++callback_count_;
00373       return push_vector;
00374     }
00375 
00376     cv::Mat boundary_img;
00377     std::vector<Boundary> boundaries = getObjectBoundaryStrengths(
00378         color_img, depth_img, workspace_mask, boundary_img);
00379     PushVector push_vector = determinePushPose(boundary_img, cur_proto_objs_,
00380                                                boundaries, cloud);
00381     push_vector.num_objects = cur_proto_objs_.size();
00382     prev_push_vector_ = push_vector;
00383 #ifdef DISPLAY_3D_BOUNDARIES
00384     if (use_displays_ || write_to_disk_)
00385     {
00386       draw3DBoundaries(boundaries, color_img, cur_proto_objs_.size());
00387     }
00388 #endif // DISPLAY_3D_BOUNDARIES
00389 #ifdef DEBUG_PUSH_HISTORY
00390     drawObjectHists(color_img, cur_proto_objs_, push_vector.push_bin,
00391                     push_vector.object_idx);
00392 #endif // DEBUG_PUSH_HISTORY
00393     if (push_vector.object_idx == cur_proto_objs_.size())
00394     {
00395       ROS_WARN_STREAM("No push vector selected." << std::endl);
00396       push_vector.no_push = true;
00397       ++callback_count_;
00398       return push_vector;
00399     }
00400 
00401     ++callback_count_;
00402     prev_push_vector_ = push_vector;
00403     return push_vector;
00404   }
00405 
00414   PushVector findRandomPushPose(XYZPointCloud& input_cloud)
00415   {
00416     ProtoObjects objs;
00417     pcl_segmenter_->findTabletopObjects(input_cloud, objs);
00418     prev_proto_objs_ = cur_proto_objs_;
00419     cur_proto_objs_ = objs;
00420 
00421     ROS_INFO_STREAM("Found " << objs.size() << " objects.");
00422 
00423     std::vector<int> pushable_obj_idx;
00424     pushable_obj_idx.clear();
00425     for (unsigned int i = 0; i < objs.size(); ++i)
00426     {
00427       if (objs[i].centroid[0] > min_pushing_x_ &&
00428           objs[i].centroid[0] < max_pushing_x_ &&
00429           objs[i].centroid[1] > min_pushing_y_ &&
00430           objs[i].centroid[1] < max_pushing_y_)
00431       {
00432         pushable_obj_idx.push_back(i);
00433       }
00434     }
00435     PushVector p;
00436     p.header.frame_id = workspace_frame_;
00437 
00438     if (pushable_obj_idx.size() == 0)
00439     {
00440       ROS_WARN_STREAM("No object clusters found! Returning empty push_pose");
00441       p.no_push = true;
00442       callback_count_++;
00443       return p;
00444     }
00445     ROS_INFO_STREAM("Found " << pushable_obj_idx.size()
00446                     << " pushable proto objects");
00447     int rand_idx = pushable_obj_idx[rand() % pushable_obj_idx.size()];
00448     // Choose a random orientation
00449     double push_angle = (randf()* (max_push_angle_- min_push_angle_) +
00450                          min_push_angle_);
00451     double push_dist = 0.0;
00452     Eigen::Vector3f push_unit_vec(cos(push_angle), sin(push_angle), 0.0f);
00453     p.start_point = determineStartPoint(objs[rand_idx].cloud,
00454                                         objs[rand_idx].centroid,
00455                                         push_unit_vec,
00456                                         push_angle, push_dist);
00457     p.push_angle = push_angle;
00458     p.push_dist = push_dist;
00459     p.num_objects = objs.size();
00460     ROS_INFO_STREAM("Chosen push pose is at: (" << p.start_point.x << ", "
00461                     << p.start_point.y << ", " << p.start_point.z
00462                     << ") with orientation of: " << p.push_angle);
00463     displayPushVector(objs[rand_idx], p, push_unit_vec);
00464     callback_count_++;
00465     return p;
00466   }
00467 
00468  protected:
00477   ProtoObjects calcProtoObjects(XYZPointCloud& cloud, bool use_guided=true)
00478   {
00479     XYZPointCloud objs_cloud;
00480     XYZPointCloud table_cloud;
00481     ProtoObjects objs;
00482     pcl_segmenter_->findTabletopObjects(cloud, objs, objs_cloud, table_cloud);
00483     cur_table_cloud_ = table_cloud;
00484     ROS_INFO_STREAM("Found " << objs.size() << " objects!");
00485     for (unsigned int i = 0; i < objs.size(); ++i)
00486     {
00487       if (use_guided)
00488       {
00489         objs[i].push_history.resize(num_angle_bins_, 0);
00490       }
00491       else
00492       {
00493         objs[i].push_history.resize(1, 0);
00494       }
00495     }
00496     XYZPointCloud cur_objs_down;
00497     pcl_segmenter_->downsampleCloud(objs_cloud, cur_objs_down);
00498     ProtoObjects cur_objs;
00499     if (callback_count_ > 0)
00500     {
00501       // Determine where stuff has moved
00502       ProtoObjects moved_regions;
00503       pcl_segmenter_->getMovedRegions(prev_objs_down_, cur_objs_down, moved_regions);
00504       // Match these moved regions to the previous objects
00505       pcl_segmenter_->matchMovedRegions(prev_proto_objs_, moved_regions);
00506       // Match the moved objects to their new locations
00507       updateMovedObjs(objs, prev_proto_objs_, use_guided);
00508     }
00509     else
00510     {
00511       // Initialize IDs
00512       for (unsigned int i = 0; i < objs.size(); ++i)
00513       {
00514         objs[i].id = getNextID();
00515       }
00516       // TODO: Should get the boundary of the point cloud instead of just max x
00517       // and y
00518       min_table_x_ = FLT_MAX;
00519       max_table_x_ = -FLT_MAX;
00520       min_table_y_ = FLT_MAX;
00521       max_table_y_ = -FLT_MAX;
00522       for (unsigned int i = 0; i < cur_table_cloud_.size(); ++i)
00523       {
00524         if (cur_table_cloud_.at(i).x < min_table_x_)
00525         {
00526           min_table_x_ = cur_table_cloud_.at(i).x;
00527         }
00528         if (cur_table_cloud_.at(i).y < min_table_y_)
00529         {
00530           min_table_y_ = cur_table_cloud_.at(i).y;
00531         }
00532         if (cur_table_cloud_.at(i).x > max_table_x_)
00533         {
00534           max_table_x_ = cur_table_cloud_.at(i).x;
00535         }
00536         if (cur_table_cloud_.at(i).y > max_table_y_)
00537         {
00538           max_table_y_ = cur_table_cloud_.at(i).y;
00539         }
00540       }
00541     }
00542     prev_objs_down_ = cur_objs_down;
00543     cur_proto_objs_.clear();
00544     cur_proto_objs_ = objs;
00545     return objs;
00546   }
00547 
00557   void updateMovedObjs(ProtoObjects& cur_objs, ProtoObjects& prev_objs,
00558                        bool use_guided = true)
00559   {
00560     merged_ = cur_objs.size()  < prev_objs.size();
00561     split_ = cur_objs.size()  > prev_objs.size();
00562     if (split_ || merged_ )
00563     {
00564       if (merged_)
00565       {
00566         ROS_WARN_STREAM("Objects merged from " << prev_objs.size() << " to " <<
00567                         cur_objs.size());
00568       }
00569       else
00570       {
00571         ROS_WARN_STREAM("Objects split from " << prev_objs.size() << " to " <<
00572                         cur_objs.size());
00573       }
00574       int num_moved = 0;
00575       int num_unmoved = 0;
00576       for (unsigned int i = 0; i < prev_objs.size(); ++i)
00577       {
00578         if (prev_objs[i].moved)
00579         {
00580           num_moved++;
00581         }
00582         else
00583         {
00584           num_unmoved++;
00585         }
00586       }
00587       ROS_INFO_STREAM("Prev objs num_moved: " << num_moved);
00588       ROS_INFO_STREAM("Prev objs num_unmoved: " << num_unmoved);
00589     }
00590     else
00591     {
00592       ROS_DEBUG_STREAM("Same number of objects: " << prev_objs.size());
00593     }
00594 
00595     // Match stuff
00596     std::vector<bool> matched = matchUnmoved(cur_objs, prev_objs);
00597     matchMoved(cur_objs, prev_objs, matched, split_, merged_, use_guided);
00598 
00599     // Update push histories
00600     if (!prev_push_vector_.no_push)
00601     {
00602       for (unsigned int i = 0; i < cur_objs.size(); ++i)
00603       {
00604         if (cur_objs[i].id == prev_push_vector_.object_id)
00605         {
00606           if (!use_guided && !use_unguided_icp_ && !split_)
00607           {
00608             cur_objs[i].push_history[0]++;
00609             ROS_INFO_STREAM("Updating the push history for object " << i <<
00610                             " to " << cur_objs[i].push_history[0]);
00611             continue;
00612           }
00613           // Check if push failed (nothing moved or wrong object moved)
00614           if (!cur_objs[i].moved)
00615           {
00616             ROS_WARN_STREAM("Intended object to push did not move, not " <<
00617                             " updating push history.");
00618           }
00619           else if (split_)
00620           {
00621             ROS_WARN_STREAM("Not updating push history because of split");
00622           }
00623           else if (merged_)
00624           {
00625             ROS_WARN_STREAM("Not updating push history because of merge");
00626           }
00627           else if (!use_guided)
00628           {
00629             cur_objs[i].push_history[0]++;
00630             ROS_INFO_STREAM("Updating the push history for object " << i <<
00631                             " to " << cur_objs[i].push_history[0]);
00632             continue;
00633           }
00634           else if (cur_objs[i].icp_score <= bad_icp_score_limit_)
00635           {
00636             // Only increment if good ICP score
00637             cur_objs[i].push_history[prev_push_vector_.push_bin]++;
00638           }
00639           else
00640           {
00641             ROS_WARN_STREAM("Not updating push history because of bad push.");
00642           }
00643         }
00644         else if (cur_objs[i].moved)
00645         {
00646           ROS_WARN_STREAM("Object " << cur_objs[i].id <<
00647                           " unintentionally moved. Intented to move " <<
00648                           prev_push_vector_.object_id);
00649         }
00650       }
00651     }
00652   }
00653 
00654   std::vector<bool> matchUnmoved(ProtoObjects& cur_objs,
00655                                  ProtoObjects& prev_objs)
00656   {
00657     std::vector<bool> matched(cur_objs.size(), false);
00658     // First match the unmoved objects
00659     for (unsigned int i = 0; i < prev_objs.size(); ++i)
00660     {
00661       if (!prev_objs[i].moved)
00662       {
00663         double min_score = FLT_MAX;
00664         unsigned int min_idx = cur_objs.size();
00665         // Update the ID in cur_objs of the closest centroid in previous objects
00666         for (unsigned int j = 0; j < cur_objs.size(); ++j)
00667         {
00668           double score = pcl_segmenter_->sqrDist(prev_objs[i].centroid,
00669                                                  cur_objs[j].centroid);
00670           if (score < min_score)
00671           {
00672             min_idx = j;
00673             min_score = score;
00674           }
00675         }
00676         if (min_idx < cur_objs.size())
00677         {
00678           ROS_INFO_STREAM("Prev unmoved obj: " << prev_objs[i].id << ", " << i
00679                           << " maps to cur " << min_idx << " : " << min_score);
00680           if (!matched[min_idx])
00681           {
00682             cur_objs[min_idx].id = prev_objs[i].id;
00683             cur_objs[min_idx].singulated = prev_objs[i].singulated;
00684             cur_objs[min_idx].push_history = prev_objs[i].push_history;
00685             cur_objs[min_idx].transform = prev_objs[i].transform;
00686             cur_objs[min_idx].icp_score = 0.0;
00687             cur_objs[min_idx].moved = false;
00688           }
00689           matched[min_idx] = true;
00690         }
00691       }
00692     }
00693     return matched;
00694   }
00695 
00696   void matchMoved(ProtoObjects& cur_objs, ProtoObjects& prev_objs,
00697                   std::vector<bool> matched, bool split, bool merged,
00698                   bool use_guided)
00699   {
00700     for (unsigned int i = 0; i < prev_objs.size(); ++i)
00701     {
00702       if (prev_objs[i].moved)
00703       {
00704         double min_score = std::numeric_limits<double>::max();
00705         unsigned int min_idx = cur_objs.size();
00706         // Match the moved objects to their new locations
00707         Eigen::Matrix4f min_transform;
00708         for (unsigned int j = 0; j < cur_objs.size(); ++j)
00709         {
00710           if (!matched[j])
00711           {
00712             // Run ICP to match between frames
00713             Eigen::Matrix4f transform;
00714             ROS_INFO_STREAM("ICP of " << i << " to " << j);
00715             double cur_score = pcl_segmenter_->ICPProtoObjects(prev_objs[i],
00716                                                                cur_objs[j],
00717                                                                transform);
00718             if (cur_score < min_score)
00719             {
00720               min_score = cur_score;
00721               min_idx = j;
00722               min_transform = transform;
00723             }
00724           }
00725         }
00726         if (min_idx < cur_objs.size())
00727         {
00728           ROS_INFO_STREAM("Prev moved obj: " << prev_objs[i].id  << ", " << i
00729                           << " maps to cur " << min_idx << " : " << min_score);
00730           if (!matched[min_idx])
00731           {
00732             // Examine bad fits of ICP
00733             bool bad_icp = (min_score > bad_icp_score_limit_);
00734             cur_objs[min_idx].id = prev_objs[i].id;
00735             cur_objs[min_idx].icp_score = min_score;
00736             cur_objs[min_idx].moved = true;
00737             cur_objs[min_idx].singulated = false;
00738             matched[min_idx] = true;
00739             if (split && (bad_icp || cur_objs.size() == 2))
00740             {
00741               ROS_INFO_STREAM("Bad score on split, resetting history of " <<
00742                               min_idx << "!");
00743               if(use_guided)
00744               {
00745                 cur_objs[min_idx].push_history.resize(num_angle_bins_, 0);
00746               }
00747               else
00748               {
00749                 cur_objs[min_idx].push_history.resize(1, 0);
00750               }
00751               cur_objs[min_idx].transform =  Eigen::Matrix4f::Identity();
00752             }
00753             else if (merged && (bad_icp || cur_objs.size() == 1))
00754             {
00755               ROS_INFO_STREAM("Bad score on merge, resetting history of " <<
00756                               min_idx << "!");
00757               if(use_guided)
00758               {
00759                 cur_objs[min_idx].push_history.resize(num_angle_bins_, 0);
00760               }
00761               else
00762               {
00763                 cur_objs[min_idx].push_history.resize(1, 0);
00764               }
00765               cur_objs[min_idx].singulated = false;
00766               cur_objs[min_idx].transform =  Eigen::Matrix4f::Identity();
00767             }
00768             else
00769             {
00770               cur_objs[min_idx].push_history = prev_objs[i].push_history;
00771               cur_objs[min_idx].transform = min_transform*prev_objs[i].transform;
00772             }
00773           }
00774         }
00775         else
00776         {
00777           ROS_WARN_STREAM("No match for moved previus object: "
00778                           << prev_objs[i].id);
00779         }
00780       }
00781     }
00782     for (unsigned int i = 0; i < matched.size(); ++i)
00783     {
00784       if (!matched[i])
00785       {
00786         cur_objs[i].id = getNextID();
00787         cur_objs[i].moved = false;
00788       }
00789     }
00790   }
00791 
00800   PushVector determinePushPose(cv::Mat& boundary_img, ProtoObjects& objs,
00801                                std::vector<Boundary>& boundaries,
00802                                XYZPointCloud& cloud)
00803   {
00804     cv::Mat obj_lbl_img = pcl_segmenter_->projectProtoObjectsIntoImage(
00805         objs, boundary_img.size(), workspace_frame_);
00806 
00807 #ifdef DISPLAY_PROJECTED_OBJECTS
00808     if (use_displays_ || write_to_disk_)
00809     {
00810       drawObjectTransformAxises(obj_lbl_img, objs);
00811     }
00812 #endif // DISPLAY_PROJECTED_OBJECTS
00813 
00814     get3DBoundaries(boundaries, cloud);
00815     associate3DBoundaries(boundaries, objs, obj_lbl_img);
00816 
00817     std::vector<unsigned int> singulated_ids = checkSingulated(boundaries, objs);
00818     if (singulated_ids.size() == objs.size())
00819     {
00820       ROS_WARN_STREAM("All objects singulated!");
00821       PushVector push_pose;
00822       push_pose.object_idx = objs.size();
00823       push_pose.no_push = true;
00824       push_pose.singulated = singulated_ids;
00825       return push_pose;
00826     }
00827 
00828     PushVector push = determinePushVector(boundaries, objs, obj_lbl_img);
00829     push.singulated = singulated_ids;
00830     ROS_INFO_STREAM("Push object: " << push.object_idx);
00831     ROS_INFO_STREAM("Push dist: " << push.push_dist);
00832     ROS_INFO_STREAM("Push angle: " << push.push_angle);
00833     ROS_INFO_STREAM("Push angle bin: " << push.push_bin << std::endl);
00834     return push;
00835   }
00836 
00837   PushVector determinePushVector(std::vector<Boundary>& boundaries,
00838                                  ProtoObjects& objs, cv::Mat& obj_lbl_img)
00839   {
00840     // Check if the previously pushed object is singulated
00841     bool force_obj_id = false;
00842     int forced_idx = -1;
00843     if (!prev_push_vector_.no_push && !split_)
00844     {
00845       for (unsigned int i = 0; i < objs.size(); ++i)
00846       {
00847         if (objs[i].singulated)
00848         {
00849           continue;
00850         }
00851         if (objs[i].id == prev_push_vector_.object_id)
00852         {
00853           force_obj_id = true;
00854           forced_idx = i;
00855           break;
00856         }
00857       }
00858     }
00859     PushOpts push_opts;
00860     ROS_INFO_STREAM("Evaluating boundaries");
00861     for (unsigned int b = 0; b < boundaries.size(); ++b)
00862     {
00863       // Ignore small boundaries or those not on objects
00864       if (boundaries[b].object_idx == objs.size() ||
00865           boundaries[b].external || boundaries[b].too_short)
00866       {
00867         continue;
00868       }
00869       const int i = boundaries[b].object_idx;
00870       const unsigned int bin = quantizeAngle(boundaries[b].ort);
00871       // Only choose from boundaries associated with unpushed directions
00872       if (objs[i].push_history[bin] == 0)
00873       {
00874         // If previous object is unsingulated, then choose from it only
00875         if (force_obj_id && boundaries[b].object_idx != forced_idx)
00876         {
00877           continue;
00878         }
00879         PushOpts split_opts = generatePushOpts(boundaries[b]);
00880         evaluatePushOpts(split_opts, boundaries[b], objs);
00881         for (unsigned int s = 0; s < split_opts.size(); ++s)
00882         {
00883           if (!split_opts[s].bad_angle)
00884           {
00885             split_opts[s].boundary_idx = b;
00886             push_opts.push_back(split_opts[s]);
00887           }
00888         }
00889       }
00890     }
00891     if (push_opts.size() == 0)
00892     {
00893       ROS_WARN_STREAM("No viable pushing options found");
00894       PushVector push_pose;
00895       push_pose.object_idx = objs.size();
00896       push_pose.no_push = true;
00897       return push_pose;
00898     }
00899     // TODO: Get highest ranked pushes for each object
00900     // TODO: Prefer pushes in directions with full neighboring bins
00901     // TODO: Get higher ranked pushes for each direction (bin)
00902     // Sort ranks...
00903     if (push_opts.size() > 1)
00904     {
00905       ROS_INFO_STREAM("Sorting push options.");
00906       std::sort(push_opts.begin(), push_opts.end(), PushOpt::compareOpts);
00907     }
00908 
00909     // TODO: store options for next attempt if necessary
00910     ROS_INFO_STREAM("Getting best push_vector of " << push_opts.size() <<
00911                     " options");
00912     PushVector push_pose = push_opts[0].getPushVector();
00913     ROS_INFO_STREAM("Chosen push score: " << push_opts[0].split_score);
00914     ROS_INFO_STREAM("Chosen push_dir: [" <<
00915                     push_opts[0].push_unit_vec[0] << ", " <<
00916                     push_opts[0].push_unit_vec[1] << ", " <<
00917                     push_opts[0].push_unit_vec[2] << "] : " <<
00918                     push_opts[0].push_unit_vec.norm());
00919     ROS_INFO_STREAM("Chosen start_point: (" <<
00920                     push_opts[0].start_point.x << ", " <<
00921                     push_opts[0].start_point.y << ", " <<
00922                     push_opts[0].start_point.z << ")");
00923     push_pose.header.frame_id = workspace_frame_;
00924     push_pose.object_id = objs[push_pose.object_idx].id;
00925     if (push_opts[0].start_collides)
00926     {
00927       ROS_WARN_STREAM("Chosen PushOpt start collides with another object.");
00928     }
00929     if (push_opts[0].will_collide)
00930     {
00931       ROS_WARN_STREAM("Chosen PushOpt collides with another object.");
00932     }
00933     if (push_opts[0].start_leaves)
00934     {
00935       ROS_WARN_STREAM("Chosen PushOpt starts outside the workpsace");
00936     }
00937     if (push_opts[0].will_leave)
00938     {
00939       ROS_WARN_STREAM("Chosen PushOpt leaves the workpsace");
00940     }
00941     if (push_opts[0].will_leave_table)
00942     {
00943       ROS_WARN_STREAM("Chosen PushOpt leaves the table");
00944     }
00945 #ifdef DISPLAY_PUSH_VECTOR
00946     displayPushVector(obj_lbl_img, boundaries[push_opts[0].boundary_idx],
00947                       push_pose, push_opts[0]);
00948 #endif // DISPLAY_PUSH_VECTOR
00949     return push_pose;
00950   }
00951 
00960   PushVector findRandomPushVector(ProtoObjects& objs, XYZPointCloud& cloud,
00961                                   cv::Mat& color_img)
00962   {
00963     std::vector<int> pushable_obj_idx;
00964     std::vector<unsigned int> singulated_ids;
00965     std::stringstream sing_stream;
00966     pushable_obj_idx.clear();
00967     // Force to keep pushing the previous object if unsingulated
00968     int forced_idx = -1;
00969     for (unsigned int i = 0; i < objs.size(); ++i)
00970     {
00971       if (objs[i].push_history[0] < per_object_rand_push_count_)
00972       {
00973         if (objs[i].id == prev_push_vector_.object_id)
00974         {
00975           forced_idx = i;
00976         }
00977         pushable_obj_idx.push_back(i);
00978       }
00979       else
00980       {
00981         singulated_ids.push_back(i);
00982         sing_stream << i << " ";
00983       }
00984     }
00985     if (singulated_ids.size() > 0)
00986     {
00987       ROS_INFO_STREAM("The following objects are singulated: " <<
00988                       sing_stream.str());
00989     }
00990 
00991 #ifdef DISPLAY_PROJECTED_OBJECTS
00992     cv::Mat obj_lbl_img = pcl_segmenter_->projectProtoObjectsIntoImage(
00993         objs, color_img.size(), workspace_frame_);
00994 
00995     if (use_displays_ || write_to_disk_)
00996     {
00997       drawObjectTransformAxises(obj_lbl_img, objs);
00998     }
00999 #endif // DISPLAY_PROJECTED_OBJECTS
01000     // TODO: Display push histories
01001     PushVector p;
01002     p.header.frame_id = workspace_frame_;
01003     p.singulated = singulated_ids;
01004     p.num_objects = objs.size();
01005     if (pushable_obj_idx.size() == 0)
01006     {
01007       if (use_displays_)
01008       {
01009         drawUnguidedHists(color_img, cur_proto_objs_, -1);
01010       }
01011       ROS_WARN_STREAM("All objects singulated! Returning empty push_pose");
01012       p.no_push = true;
01013       return p;
01014     }
01015     p.no_push = false;
01016 
01017     ROS_INFO_STREAM("Found " << pushable_obj_idx.size()
01018                     << " pushable proto objects");
01019 
01020     int rand_idx = pushable_obj_idx[rand() % pushable_obj_idx.size()];
01021     int chosen_idx;
01022     if (forced_idx < 0)
01023     {
01024       chosen_idx = rand_idx;
01025     }
01026     else
01027     {
01028       chosen_idx = forced_idx;
01029     }
01030     // Choose a random orientation
01031     double push_angle = 0.0;
01032     double push_dist = 0.0;
01033     Eigen::Vector3f push_unit_vec(0.0f, 0.0f, 0.0f);
01034     int push_guess_count = 0;
01035     bool will_collide = false;
01036     bool will_leave = false;
01037     do
01038     {
01039       push_angle = (randf()* (max_push_angle_- min_push_angle_) +
01040                     min_push_angle_);
01041       push_unit_vec[0] = cos(push_angle);
01042       push_unit_vec[1] = sin(push_angle);
01043       p.start_point = determineStartPoint(objs[chosen_idx].cloud,
01044                                           objs[chosen_idx].centroid,
01045                                           push_unit_vec,
01046                                           push_angle, push_dist);
01047       PushOpt po(objs[chosen_idx], push_angle, push_unit_vec, chosen_idx,
01048                  0, push_dist, 1.0, 0);
01049       will_collide = pushCollidesWithObject(po, objs);
01050       will_leave = pushLeavesWorkspace(po, objs[chosen_idx]);
01051       push_guess_count++;
01052     } while ((will_collide || will_leave) &&
01053              push_guess_count < push_guess_limit_);
01054     p.push_angle = push_angle;
01055     p.push_dist = push_dist;
01056     p.object_idx = chosen_idx;
01057     p.object_id = objs[chosen_idx].id;
01058     if (will_collide)
01059     {
01060       ROS_WARN_STREAM("Chosen push is expected to collide.");
01061     }
01062     if (will_leave)
01063     {
01064       ROS_WARN_STREAM("Chosen push is expected to leave the workspace.");
01065     }
01066     if (use_displays_)
01067     {
01068       displayPushVector(objs[chosen_idx], p, push_unit_vec);
01069       drawUnguidedHists(color_img, cur_proto_objs_, chosen_idx);
01070     }
01071     ROS_INFO_STREAM("Pushing object: " << chosen_idx);
01072     ROS_INFO_STREAM("Chosen push pose is at: (" << p.start_point.x << ", "
01073                     << p.start_point.y << ", " << p.start_point.z
01074                     << ") with orientation of: " << p.push_angle << "\n");
01075     return p;
01076   }
01077 
01088   std::vector<Boundary> getObjectBoundaryStrengths(cv::Mat& color_img,
01089                                                    cv::Mat& depth_img,
01090                                                    cv::Mat& workspace_mask,
01091                                                    cv::Mat& combined_edges)
01092   {
01093     cv::Mat tmp_bw(color_img.size(), CV_8UC1);
01094     cv::Mat bw_img(color_img.size(), CV_32FC1);
01095     cv::Mat Ix(bw_img.size(), CV_32FC1);
01096     cv::Mat Iy(bw_img.size(), CV_32FC1);
01097     cv::Mat Ix_d(bw_img.size(), CV_32FC1);
01098     cv::Mat Iy_d(bw_img.size(), CV_32FC1);
01099     cv::Mat edge_img(color_img.size(), CV_32FC1);
01100     cv::Mat depth_edge_img(color_img.size(), CV_32FC1);
01101     cv::Mat edge_img_masked(edge_img.size(), CV_32FC1, cv::Scalar(0.0));
01102     cv::Mat depth_edge_img_masked(edge_img.size(), CV_32FC1, cv::Scalar(0.0));
01103 
01104     // Convert to grayscale
01105     cv::cvtColor(color_img, tmp_bw, CV_BGR2GRAY);
01106     tmp_bw.convertTo(bw_img, CV_32FC1, 1.0/255);
01107 
01108     // Get image derivatives
01109     cv::filter2D(bw_img, Ix, CV_32F, dx_kernel_);
01110     cv::filter2D(bw_img, Iy, CV_32F, dy_kernel_);
01111     cv::filter2D(depth_img, Ix_d, CV_32F, dx_kernel_);
01112     cv::filter2D(depth_img, Iy_d, CV_32F, dy_kernel_);
01113 
01114     // Create magintude image
01115     for (int r = 0; r < edge_img.rows; ++r)
01116     {
01117       float* mag_row = edge_img.ptr<float>(r);
01118       float* Ix_row = Ix.ptr<float>(r);
01119       float* Iy_row = Iy.ptr<float>(r);
01120       for (int c = 0; c < edge_img.cols; ++c)
01121       {
01122         mag_row[c] = sqrt(Ix_row[c]*Ix_row[c] + Iy_row[c]*Iy_row[c]);
01123       }
01124     }
01125     for (int r = 0; r < depth_edge_img.rows; ++r)
01126     {
01127       float* mag_row = depth_edge_img.ptr<float>(r);
01128       float* Ix_row = Ix_d.ptr<float>(r);
01129       float* Iy_row = Iy_d.ptr<float>(r);
01130       for (int c = 0; c < depth_edge_img.cols; ++c)
01131       {
01132         mag_row[c] = sqrt(Ix_row[c]*Ix_row[c] + Iy_row[c]*Iy_row[c]);
01133       }
01134     }
01135 
01136     // Remove stuff from the image
01137     edge_img.copyTo(edge_img_masked, workspace_mask);
01138     depth_edge_img.copyTo(depth_edge_img_masked, workspace_mask);
01139     if (threshold_edges_)
01140     {
01141       cv::Mat bin_depth_edges;
01142       cv::threshold(depth_edge_img_masked, bin_depth_edges,
01143                     depth_edge_weight_thresh_, depth_edge_weight_,
01144                     cv::THRESH_BINARY);
01145       cv::Mat bin_img_edges;
01146       cv::threshold(edge_img_masked, bin_img_edges, edge_weight_thresh_,
01147                     (1.0-depth_edge_weight_), cv::THRESH_BINARY);
01148       combined_edges = bin_depth_edges + bin_img_edges;
01149       double edge_max = 1.0;
01150       double edge_min = 1.0;
01151       cv::minMaxLoc(edge_img_masked, &edge_min, &edge_max);
01152       double depth_max = 1.0;
01153       double depth_min = 1.0;
01154       cv::minMaxLoc(depth_edge_img_masked, &depth_min, &depth_max);
01155     }
01156     else
01157     {
01158       combined_edges = cv::max(edge_img_masked, depth_edge_img_masked);
01159     }
01160 
01161     // Link edges into object boundary hypotheses
01162     std::vector<std::vector<cv::Point> > edges = LinkEdges::edgeLink(
01163         combined_edges, min_edge_length_, false);
01164     std::vector<Boundary> boundaries;
01165     for (unsigned int i = 0; i < edges.size(); ++i)
01166     {
01167       Boundary b;
01168       b.assign(edges[i].begin(), edges[i].end());
01169       boundaries.push_back(b);
01170     }
01171     return boundaries;
01172   }
01173 
01174   void get3DBoundaries(std::vector<Boundary>& boundaries, XYZPointCloud& cloud)
01175   {
01176     for (unsigned int b = 0; b < boundaries.size(); ++b)
01177     {
01178       get3DBoundary(boundaries[b], cloud);
01179     }
01180   }
01181 
01182   void get3DBoundary(Boundary& b, XYZPointCloud& cloud)
01183   {
01184     b.points3D.clear();
01185     for (unsigned int i = 0; i < b.size(); ++i)
01186     {
01187       // NOTE: I don't think this is what it should be, lets try though...
01188       // NOTE: need to upsample the indices here
01189       pcl16::PointXYZ p = cloud.at(b[i].x*upscale_, b[i].y*upscale_);
01190       // Don't add empty points
01191       if ((p.x == 0.0 && p.y == 0.0 && p.z == 0.0 ) || isnan(p.x) ||
01192           isnan(p.y) || isnan(p.z)) continue;
01193       b.points3D.push_back(p);
01194     }
01195   }
01196 
01197   void associate3DBoundaries(std::vector<Boundary>& boundaries,
01198                              ProtoObjects& objs, cv::Mat& obj_lbl_img)
01199   {
01200     // Clear boundary_angle_dist for all objects
01201     for (unsigned int o = 0; o < objs.size(); ++o)
01202     {
01203       objs[o].boundary_angle_dist.clear();
01204       objs[o].boundary_angle_dist.resize(num_angle_bins_, 0);
01205     }
01206     int no_overlap_count = 0;
01207     for (unsigned int b = 0; b < boundaries.size(); ++b)
01208     {
01209       // NOTE: default to no match, only update if all criterian are met
01210       boundaries[b].object_idx = objs.size();
01211       std::vector<int> obj_overlaps(objs.size(), 0);
01212       for (unsigned int i = 0; i < boundaries[b].size(); ++i)
01213       {
01214         unsigned int id = obj_lbl_img.at<uchar>(boundaries[b][i].y,
01215                                                 boundaries[b][i].x);
01216         if (id > 0)
01217         {
01218           obj_overlaps[id-1]++;
01219         }
01220       }
01221       int max_overlap = 0;
01222       unsigned int max_idx = objs.size();
01223       for (unsigned int o = 0; o < objs.size(); ++o)
01224       {
01225         if (obj_overlaps[o] > max_overlap)
01226         {
01227           max_overlap = obj_overlaps[o];
01228           max_idx = o;
01229         }
01230       }
01231       if (max_idx == objs.size())
01232       {
01233         no_overlap_count++;
01234       }
01235       else
01236       {
01237         boundaries[b].object_idx = max_idx;
01238         // Don't add short boundaries
01239         if (boundaries[b].points3D.size() >= min_boundary_length_)
01240         {
01241           boundaries[b].too_short = false;
01242           // HACK: we now also set the push dir and push angle inside this
01243           // method
01244           ProtoObjects pos = splitObject3D(boundaries[b], objs[max_idx],
01245                                            true);
01246           const unsigned int s0 = pos[0].cloud.size();
01247           const unsigned int s1 = pos[1].cloud.size();
01248 
01249           // NOTE: Don't add external object boundaries
01250           if (s0 > min_cluster_size_ && s1 > min_cluster_size_)
01251           {
01252             boundaries[b].ort = getObjFrameBoundaryOrientation(
01253                 boundaries[b], objs[max_idx].transform);
01254             boundaries[b].xyLength3D = getBoundaryLengthXYMeters(boundaries[b]);
01255             int angle_idx = quantizeAngle(boundaries[b].ort);
01256             objs[max_idx].boundary_angle_dist[angle_idx]++;
01257             boundaries[b].external = false;
01258             boundaries[b].splits = pos;
01259           }
01260           else
01261           {
01262             boundaries[b].external = true;
01263           }
01264         }
01265         else
01266         {
01267           boundaries[b].too_short = true;
01268           boundaries[b].external = false;
01269         }
01270       }
01271     }
01272   }
01273 
01274   float getObjFrameBoundaryOrientation(Boundary& b, Eigen::Matrix4f& t)
01275   {
01276     Eigen::Matrix3f rot = t.block<3,3>(0,0);
01277     Eigen::Vector3f b_vect_obj = rot.transpose()*b.push_dir;
01278     float angle = std::atan2(b_vect_obj[1], b_vect_obj[0]);
01279 
01280     return angle;
01281   }
01282 
01283   float getObjFrameAngleFromWorldFrame(float theta, Eigen::Matrix4f& t)
01284   {
01285     // Rotate based on object transform history
01286     Eigen::Vector3f b_vect(cos(theta), sin(theta), 0.0f);
01287     Eigen::Matrix3f rot = t.block<3,3>(0,0);
01288     Eigen::Vector3f b_vect_obj = rot.transpose()*b_vect;
01289     float angle = std::atan2(b_vect_obj[1], b_vect_obj[0]);
01290     return angle;
01291   }
01292 
01293   int quantizeAngle(float angle)
01294   {
01295     angle = subHalfPiAngle(angle);
01296     int bin = static_cast<int>(((angle + M_PI/2.0)/M_PI)*num_angle_bins_);
01297     return std::max(std::min(bin, num_angle_bins_-1), 0);
01298   }
01299 
01300   float subHalfPiAngle(float angle)
01301   {
01302     // NOTE: All angles should be between -pi/2 and pi/2 (only want gradient)
01303     while ( angle < -M_PI/2 )
01304     {
01305       angle += M_PI;
01306     }
01307     while ( angle > M_PI/2 )
01308     {
01309       angle -= M_PI;
01310     }
01311     return angle;
01312   }
01313 
01314   Eigen::Vector3f getRANSACXYVector(Boundary& b)
01315   {
01316     Eigen::Vector3f l_pt;
01317     return getRANSACXYVector(b, l_pt);
01318   }
01319 
01320   Eigen::Vector3f getRANSACXYVector(Boundary& b, Eigen::Vector3f& l_pt)
01321   {
01322     XYZPointCloud cloud;
01323     cloud.resize(b.points3D.size());
01324     for (unsigned int i = 0; i < b.points3D.size(); ++i)
01325     {
01326       cloud.at(i) = b.points3D[i];
01327       // NOTE: This helps with visualization if the line is at the approximate
01328       // height in the world
01329       cloud.at(i).z = b.points3D[0].z;
01330     }
01331     pcl16::ModelCoefficients c;
01332     pcl16::PointIndices line_inliers;
01333     pcl16::SACSegmentation<pcl16::PointXYZ> line_seg;
01334     line_seg.setOptimizeCoefficients(true);
01335     line_seg.setModelType(pcl16::SACMODEL_LINE);
01336     line_seg.setMethodType(pcl16::SAC_RANSAC);
01337     line_seg.setDistanceThreshold(boundary_ransac_thresh_);
01338     line_seg.setInputCloud(cloud.makeShared());
01339     line_seg.segment(line_inliers, c);
01340 
01341     Eigen::Vector3f l_vector(c.values[3], c.values[4], 0.0);
01342     l_vector /= l_vector.norm();
01343     l_pt[0] = c.values[0];
01344     l_pt[1] = c.values[1];
01345     l_pt[2] = c.values[2];
01346     return l_vector;
01347   }
01348 
01349   Eigen::Vector4f splitPlaneVertical(Boundary& b, bool update_boundary=false,
01350                                      bool use_boundary=false)
01351   {
01352     Eigen::Vector3f l_pt;
01353     Eigen::Vector3f l_dir = getRANSACXYVector(b, l_pt);
01354     if (update_boundary)
01355     {
01356       b.push_dir = l_dir;
01357       b.push_angle = atan2(l_dir[1], l_dir[0]);
01358     }
01359     if (use_boundary)
01360     {
01361       l_dir = b.push_dir;
01362     }
01363     const Eigen::Vector3f z_axis(0, 0, 1);
01364     Eigen::Vector3f n = l_dir.cross(z_axis);
01365     n = n/n.norm();
01366     float p = -(n[0]*l_pt[0]+n[1]*l_pt[1]+n[2]*l_pt[2]);
01367     Eigen::Vector4f hessian(n[0], n[1], n[2], p);
01368     return hessian;
01369   }
01370 
01371   std::vector<unsigned int> checkSingulated(std::vector<Boundary>& boundaries,
01372                                             ProtoObjects& objs)
01373   {
01374     std::set<unsigned int> has_pushes;
01375     for (unsigned int b = 0; b < boundaries.size(); ++b)
01376     {
01377       // Ignore small boundaries or those not on objects
01378       if (boundaries[b].object_idx == objs.size() ||
01379           boundaries[b].external || boundaries[b].too_short)
01380       {
01381         continue;
01382       }
01383       const int i = boundaries[b].object_idx;
01384       const unsigned int bin = quantizeAngle(boundaries[b].ort);
01385       // Only choose from boundaries associated with unpushed directions
01386       if (objs[i].push_history[bin] == 0)
01387       {
01388         has_pushes.insert(i);
01389       }
01390     }
01391     std::vector<unsigned int> singulated_ids;
01392     std::stringstream sing_stream;
01393     for (unsigned int i = 0; i < objs.size(); ++i)
01394     {
01395       // NOTE: We force something that has been called singulated once to
01396       // remain singulated
01397       if (has_pushes.count(i) == 0 ||
01398           (objs[i].singulated && force_remain_singulated_))
01399       {
01400         singulated_ids.push_back(i);
01401         objs[i].singulated = true;
01402         sing_stream << i << " ";
01403       }
01404       else
01405       {
01406         objs[i].singulated = false;
01407       }
01408     }
01409     if (singulated_ids.size() > 0)
01410     {
01411       ROS_INFO_STREAM("The following objects are singulated: " <<
01412                       sing_stream.str());
01413     }
01414     return singulated_ids;
01415   }
01416 
01417   PushOpts generatePushOpts(Boundary& boundary)
01418   {
01419     const unsigned int bin = quantizeAngle(boundary.ort);
01420     double push_dist = std::min(std::max(boundary.xyLength3D+
01421                                          push_dist_inflation_, min_push_dist_),
01422                                 max_push_dist_);
01423     double push_angle_pos = 0.0;
01424     double push_angle_neg = 0.0;
01425     Eigen::Vector3f push_vec_pos;
01426     Eigen::Vector3f push_vec_neg;
01427     if (boundary.push_angle > 0.0)
01428     {
01429       push_angle_pos = boundary.push_angle;
01430       push_angle_neg = boundary.push_angle - M_PI;
01431       push_vec_pos[0] = boundary.push_dir[0];
01432       push_vec_pos[1] = boundary.push_dir[1];
01433       push_vec_pos[2] = 0.0f;
01434       push_vec_neg[0] = -boundary.push_dir[0];
01435       push_vec_neg[1] = -boundary.push_dir[1];
01436       push_vec_pos[2] = 0.0f;
01437     }
01438     else
01439     {
01440       push_angle_neg = boundary.push_angle;
01441       push_angle_pos = boundary.push_angle + M_PI;
01442       push_vec_neg[0] = boundary.push_dir[0];
01443       push_vec_neg[1] = boundary.push_dir[1];
01444       push_vec_neg[2] = 0.0f;
01445       push_vec_pos[0] = -boundary.push_dir[0];
01446       push_vec_pos[1] = -boundary.push_dir[1];
01447       push_vec_pos[2] = 0.0f;
01448     }
01449     double split_score = (std::min(boundary.splits[0].cloud.size(),
01450                                    boundary.splits[1].cloud.size()) /
01451                           static_cast<double>(
01452                               std::max(boundary.splits[0].cloud.size(),
01453                                        boundary.splits[1].cloud.size())));
01454     std::vector<PushOpt> split_opts;
01455     split_opts.push_back(PushOpt(boundary.splits[0], push_angle_pos,
01456                                  push_vec_pos, boundary.object_idx, 0,
01457                                  push_dist, split_score, bin));
01458     split_opts.push_back(PushOpt(boundary.splits[0], push_angle_neg,
01459                                  push_vec_neg, boundary.object_idx, 0,
01460                                  push_dist, split_score, bin));
01461     split_opts.push_back(PushOpt(boundary.splits[1], push_angle_pos,
01462                                  push_vec_pos, boundary.object_idx, 1,
01463                                  push_dist, split_score, bin));
01464     split_opts.push_back(PushOpt(boundary.splits[1], push_angle_neg,
01465                                  push_vec_neg, boundary.object_idx, 1,
01466                                  push_dist, split_score, bin));
01467     return split_opts;
01468   }
01469 
01470   void evaluatePushOpts(PushOpts& split_opts, Boundary& boundary,
01471                         ProtoObjects& objs)
01472   {
01473     XYZPointCloud s0_int;
01474     pcl_segmenter_->lineCloudIntersection(
01475         boundary.splits[0].cloud, split_opts[0].push_unit_vec,
01476         boundary.splits[0].centroid, s0_int);
01477     XYZPointCloud s1_int;
01478     pcl_segmenter_->lineCloudIntersection(
01479         boundary.splits[1].cloud, split_opts[1].push_unit_vec,
01480         boundary.splits[1].centroid, s1_int);
01481     for (unsigned int i = 0; i < split_opts.size(); ++i)
01482     {
01483       if (split_opts[i].split_id == 0 && s0_int.size() > 0)
01484       {
01485         split_opts[i].start_point = determineStartPoint(s0_int, split_opts[i]);
01486       }
01487       else if (s1_int.size() > 0)
01488       {
01489         split_opts[i].start_point = determineStartPoint(s1_int, split_opts[i]);
01490       }
01491       split_opts[i].will_collide = pushCollidesWithObject(split_opts[i], objs);
01492       split_opts[i].start_collides = startCollidesWithObject(split_opts[i],
01493                                                              objs);
01494       split_opts[i].start_leaves = startLeavesWorkspace(split_opts[i]);
01495       split_opts[i].will_leave = pushLeavesWorkspace(
01496           split_opts[i], objs[split_opts[i].object_idx]);
01497       split_opts[i].will_leave_table = pushLeavesTable(split_opts[i]);
01498     }
01499   }
01500 
01501   void displayPushVector(cv::Mat& lbl_img, Boundary& boundary,
01502                          PushVector& push, PushOpt& split)
01503   {
01504     cv::Mat split_img = pcl_segmenter_->projectProtoObjectsIntoImage(
01505         boundary.splits, lbl_img.size(), workspace_frame_);
01506     cv::Mat disp_img = pcl_segmenter_->displayObjectImage(
01507         split_img, "3D Split", false);
01508 #ifdef DISPLAY_CHOSEN_BOUNDARY
01509     if (use_displays_ || write_to_disk_)
01510     {
01511       // if (use_displays_)
01512       // {
01513       //   displayBoundaryOrientation(disp_img, boundary, "chosen debug");
01514       // }
01515       highlightBoundaryOrientation(disp_img, boundary, "chosen");
01516     }
01517 #endif // DISPLAY_CHOSEN_BOUNDARY
01518 
01519     const Eigen::Vector4f moved_start = split.getMovedPoint(push.start_point);
01520     PointStamped start_point;
01521     start_point.point = push.start_point;
01522     start_point.header.frame_id = workspace_frame_;
01523     PointStamped end_point;
01524     end_point.point.x = moved_start[0];
01525     end_point.point.y = moved_start[1];
01526     end_point.point.z = moved_start[2];
01527     end_point.header.frame_id = workspace_frame_;
01528 
01529     cv::Point img_start_point = pcl_segmenter_->projectPointIntoImage(
01530         start_point);
01531     cv::Point img_end_point = pcl_segmenter_->projectPointIntoImage(
01532         end_point);
01533     cv::line(disp_img, img_start_point, img_end_point, cv::Scalar(0,0.0,0.0),3);
01534     cv::line(disp_img, img_start_point, img_end_point, cv::Scalar(0,1.0,0.0));
01535     cv::circle(disp_img, img_end_point, 4, cv::Scalar(0,0.0,0.0),3);
01536     cv::circle(disp_img, img_end_point, 4, cv::Scalar(0,1.0,0.0));
01537 
01538     if (use_displays_)
01539     {
01540       cv::imshow("push_vector", disp_img);
01541     }
01542     if (write_to_disk_)
01543     {
01544       // Write to disk to create video output
01545       std::stringstream push_out_name;
01546       push_out_name << base_output_path_ << "push_vector" << callback_count_
01547                     << ".png";
01548       cv::Mat push_out_img(disp_img.size(), CV_8UC3);
01549       disp_img.convertTo(push_out_img, CV_8UC3, 255);
01550       cv::imwrite(push_out_name.str(), push_out_img);
01551     }
01552 
01553   }
01554 
01555 
01556   void displayPushVector(ProtoObject& obj, PushVector& push,
01557                          Eigen::Vector3f push_unit_vec)
01558   {
01559     ProtoObjects objs;
01560     objs.push_back(obj);
01561     cv::Mat obj_img = pcl_segmenter_->projectProtoObjectsIntoImage(
01562         objs, cv::Size(320, 240), workspace_frame_);
01563     cv::Mat disp_img = pcl_segmenter_->displayObjectImage(
01564         obj_img, "3D Split", false);
01565 
01566     PointStamped start_point;
01567     start_point.point = push.start_point;
01568     start_point.header.frame_id = workspace_frame_;
01569     PointStamped end_point;
01570     end_point.point.x = push.start_point.x + push_unit_vec[0]*push.push_dist;
01571     end_point.point.y = push.start_point.y + push_unit_vec[1]*push.push_dist;
01572     end_point.point.z = push.start_point.z;
01573     end_point.header.frame_id = workspace_frame_;
01574 
01575     cv::Point img_start_point = pcl_segmenter_->projectPointIntoImage(
01576         start_point);
01577     cv::Point img_end_point = pcl_segmenter_->projectPointIntoImage(
01578         end_point);
01579     cv::line(disp_img, img_start_point, img_end_point, cv::Scalar(0,0.0,0.0),3);
01580     cv::line(disp_img, img_start_point, img_end_point, cv::Scalar(0,1.0,0.0));
01581     cv::circle(disp_img, img_end_point, 4, cv::Scalar(0,0.0,0.0),3);
01582     cv::circle(disp_img, img_end_point, 4, cv::Scalar(0,1.0,0.0));
01583 
01584     if (use_displays_)
01585     {
01586       cv::imshow("push_vector", disp_img);
01587     }
01588     if (write_to_disk_)
01589     {
01590       // Write to disk to create video output
01591       std::stringstream push_out_name;
01592       push_out_name << base_output_path_ << "push_vector" << callback_count_
01593                     << ".png";
01594       cv::Mat push_out_img(disp_img.size(), CV_8UC3);
01595       disp_img.convertTo(push_out_img, CV_8UC3, 255);
01596       cv::imwrite(push_out_name.str(), push_out_img);
01597     }
01598   }
01599 
01600   bool pushCollidesWithObject(PushOpt& po, ProtoObjects& objs)
01601   {
01602     // transform point cloud for po.object_idx
01603     XYZPointCloud moved = po.pushPointCloud(objs[po.object_idx].cloud);
01604     // check if transformed point cloud intersects with any other object
01605     for (unsigned int i = 0; i < objs.size(); ++i)
01606     {
01607       if (i == po.object_idx) continue;
01608       if (pcl_segmenter_->cloudsIntersect(moved, objs[i].cloud,
01609                                           push_collision_intersection_thresh_))
01610         return true;
01611     }
01612     return false;
01613   }
01614 
01615   bool startCollidesWithObject(PushOpt& po, ProtoObjects& objs)
01616   {
01617     // check if transformed point cloud intersects with any other object
01618     for (unsigned int i = 0; i < objs.size(); ++i)
01619     {
01620       if (i == po.object_idx) continue;
01621       if (pcl_segmenter_->pointIntersectsCloud(objs[i].cloud, po.start_point,
01622                                                start_collision_thresh_))
01623         return true;
01624     }
01625     return false;
01626   }
01627 
01628   int pushCollidesWithWhat(PushOpt& po, ProtoObjects& objs)
01629   {
01630     // transform point cloud for po.object_idx
01631     XYZPointCloud moved = po.pushPointCloud(objs[po.object_idx].cloud);
01632     // check if transformed point cloud intersects with any other object
01633     for (unsigned int i = 0; i < objs.size(); ++i)
01634     {
01635       if (i == po.object_idx)
01636       {
01637         continue;
01638       }
01639       if (pcl_segmenter_->cloudsIntersect(moved, objs[i].cloud,
01640                                           push_collision_intersection_thresh_))
01641       {
01642         cv::Mat moved_img(240, 320, CV_8UC1, cv::Scalar(0));
01643         pcl_segmenter_->projectPointCloudIntoImage(moved, moved_img);
01644         cv::Mat collied_img(240, 320, CV_8UC1, cv::Scalar(0));
01645         pcl_segmenter_->projectPointCloudIntoImage(objs[i].cloud, collied_img);
01646         cv::imshow("moved push cloud", moved_img*128+collied_img*64);
01647         return i;
01648       }
01649     }
01650     return -1;
01651   }
01652 
01653   bool pushLeavesWorkspace(PushOpt& po, ProtoObject& obj)
01654   {
01655     XYZPointCloud moved = po.pushPointCloud(obj.cloud);
01656     for (unsigned int i = 0; i < moved.size(); ++i)
01657     {
01658       const pcl16::PointXYZ pt = moved.at(i);
01659       if  (pt.x > max_pushing_x_ || pt.x < min_pushing_x_ ||
01660            pt.y > max_pushing_y_ || pt.y < min_pushing_y_)
01661       {
01662         return true;
01663       }
01664     }
01665     return false;
01666   }
01667 
01668   bool startLeavesWorkspace(PushOpt& po)
01669   {
01670     return (po.start_point.x < min_pushing_x_ ||
01671             po.start_point.y < min_pushing_y_ ||
01672             po.start_point.x > max_pushing_x_ ||
01673             po.start_point.y > max_pushing_y_);
01674   }
01675 
01676   bool pushLeavesTable(PushOpt& po)
01677   {
01678     Eigen::Vector4f moved = po.getMovedCentroid();
01679     return (moved[0] > max_table_x_ || moved[0] < min_table_x_ ||
01680             moved[1] > max_table_y_ || moved[1] < min_table_y_);
01681   }
01682 
01683   double getBoundaryLengthXYMeters(Boundary& b)
01684   {
01685     double max_dist = 0.0;
01686     for (unsigned int i = 0; i < b.points3D.size()-1; ++i)
01687     {
01688       for (unsigned int j = i+1; j < b.points3D.size(); ++j)
01689       {
01690         double dist = pcl_segmenter_->sqrDistXY(b.points3D[i], b.points3D[j]);
01691         if (dist > max_dist)
01692         {
01693           max_dist = dist;
01694         }
01695       }
01696     }
01697     max_dist = std::sqrt(max_dist);
01698     return max_dist;
01699   }
01700 
01701   geometry_msgs::Point determineStartPoint(XYZPointCloud& pts, PushOpt& opt)
01702   {
01703     unsigned int min_idx = pts.size();
01704     unsigned int max_idx = pts.size();
01705     float min_y = FLT_MAX;
01706     float max_y = -FLT_MAX;
01707     for (unsigned int i = 0; i < pts.size(); ++i)
01708     {
01709       if (pts.at(i).y < min_y)
01710       {
01711         min_y = pts.at(i).y;
01712         min_idx = i;
01713       }
01714       if (pts.at(i).y > max_y)
01715       {
01716         max_y = pts.at(i).y;
01717         max_idx = i;
01718       }
01719     }
01720 
01721     geometry_msgs::Point p;
01722     // NOTE: greater than 0 implies pushing to the left, want right extreme,
01723     // hence min y value
01724     if (opt.push_angle > 0)
01725     {
01726       p.x = pts.at(min_idx).x;
01727       p.y = pts.at(min_idx).y;
01728       p.z = pts.at(min_idx).z;
01729     }
01730     else
01731     {
01732       p.x = pts.at(max_idx).x;
01733       p.y = pts.at(max_idx).y;
01734       p.z = pts.at(max_idx).z;
01735     }
01736     // HACK: This should be made explicit that we are chaning this inside this
01737     // method but I don't want to refactor right now.
01738     double push_dist2 = std::sqrt(pcl_segmenter_->sqrDistXY(pts.at(max_idx),
01739                                                             pts.at(min_idx)))+
01740         push_dist_inflation_;
01741     // Clip push dist
01742     push_dist2 = std::min(std::max(push_dist2, min_push_dist_), max_push_dist_);
01743 
01744     if (push_dist2 > opt.push_dist)
01745     {
01746       opt.push_dist = push_dist2;
01747     }
01748     return p;
01749   }
01750 
01751   geometry_msgs::Point determineStartPoint(XYZPointCloud& obj_cloud,
01752                                            Eigen::Vector4f centroid,
01753                                            Eigen::Vector3f push_unit_vec,
01754                                            double& push_angle,
01755                                            double& push_dist)
01756   {
01757     XYZPointCloud pts;
01758     pcl_segmenter_->lineCloudIntersection(obj_cloud, push_unit_vec, centroid, pts);
01759     unsigned int min_idx = pts.size();
01760     unsigned int max_idx = pts.size();
01761     float min_y = FLT_MAX;
01762     float max_y = -FLT_MAX;
01763     for (unsigned int i = 0; i < pts.size(); ++i)
01764     {
01765       if (pts.at(i).y < min_y)
01766       {
01767         min_y = pts.at(i).y;
01768         min_idx = i;
01769       }
01770       if (pts.at(i).y > max_y)
01771       {
01772         max_y = pts.at(i).y;
01773         max_idx = i;
01774       }
01775     }
01776     geometry_msgs::Point p;
01777     if (pts.size() == 0)
01778     {
01779       p.x = centroid[0];
01780       p.y = centroid[1];
01781       p.z = centroid[2];
01782       push_dist = min_push_dist_;
01783       ROS_WARN_STREAM("No intersecting points on the line, so returning centroid as start point.");
01784       return p;
01785     }
01786     // NOTE: greater than 0 implies pushing to the left, want right extreme,
01787     // hence min y value
01788     if (push_angle > 0)
01789     {
01790       p.x = pts.at(min_idx).x;
01791       p.y = pts.at(min_idx).y;
01792       p.z = pts.at(min_idx).z;
01793     }
01794     else
01795     {
01796       p.x = pts.at(max_idx).x;
01797       p.y = pts.at(max_idx).y;
01798       p.z = pts.at(max_idx).z;
01799     }
01800     // HACK: This should be made explicit that we are chaning this inside this
01801     // method but I don't want to refactor right now.
01802     push_dist = std::sqrt(pcl_segmenter_->sqrDistXY(pts.at(max_idx),
01803                                                             pts.at(min_idx)))+
01804         push_dist_inflation_;
01805     // Clip push dist
01806     push_dist = std::min(std::max(push_dist, min_push_dist_), max_push_dist_);
01807     return p;
01808   }
01809 
01810   ProtoObjects splitObject3D(Boundary& boundary, ProtoObject& to_split,
01811                              bool update_boundary=false)
01812   {
01813     // Get plane containing the boundary
01814     Eigen::Vector4f hessian = splitPlaneVertical(boundary, update_boundary);
01815     // Split based on the plane
01816     return splitObject3D(hessian, to_split);
01817   }
01818 
01819   ProtoObjects splitObject3D(Eigen::Vector4f& hessian,
01820                              ProtoObject& to_split)
01821   {
01822     // Split the point clouds based on the half plane distance test
01823     pcl16::PointIndices p1;
01824     for (unsigned int i = 0; i < to_split.cloud.size(); ++i)
01825     {
01826       const pcl16::PointXYZ x = to_split.cloud.at(i);
01827       const float D = hessian[0]*x.x + hessian[1]*x.y + hessian[2]*x.z +
01828           hessian[3];
01829       if (D > 0)
01830       {
01831         p1.indices.push_back(i);
01832       }
01833     }
01834     // Extract indices
01835     ProtoObjects split;
01836     ProtoObject po1;
01837     ProtoObject po2;
01838     pcl16::ExtractIndices<pcl16::PointXYZ> extract;
01839     extract.setInputCloud(to_split.cloud.makeShared());
01840     extract.setIndices(boost::make_shared<pcl16::PointIndices>(p1));
01841     extract.filter(po1.cloud);
01842     extract.setNegative(true);
01843     extract.filter(po2.cloud);
01844     split.push_back(po1);
01845     split.push_back(po2);
01846     for (unsigned int i = 0; i < split.size(); ++i)
01847     {
01848       pcl16::compute3DCentroid(split[i].cloud, split[i].centroid);
01849     }
01850 
01851     return split;
01852   }
01853 
01854   //
01855   // I/O Methods
01856   //
01857   void displayBoundaryOrientation(cv::Mat& obj_img, Boundary& boundary,
01858                                   std::string title)
01859   {
01860     cv::Mat obj_disp_img(obj_img.size(), CV_32FC3);
01861     if (obj_img.depth() == CV_8U)
01862     {
01863       cv::Mat obj_img_f;
01864       obj_img.convertTo(obj_img_f, CV_32FC1, 30.0/255);
01865       cv::cvtColor(obj_img_f, obj_disp_img, CV_GRAY2BGR);
01866     }
01867     else if (obj_img.type() == CV_32FC1)
01868     {
01869       cv::cvtColor(obj_img, obj_disp_img, CV_GRAY2BGR);
01870     }
01871     else
01872     {
01873       obj_img.copyTo(obj_disp_img);
01874     }
01875     Eigen::Vector3f l_pt(boundary.points3D[0].x, boundary.points3D[0].y,
01876                          boundary.points3D[0].z);
01877     Eigen::Vector3f l_dir = boundary.push_dir;
01878     Eigen::Vector4f n = splitPlaneVertical(boundary, false, true);
01879     const cv::Scalar red(0.0f, 0.0f, 1.0f);
01880     const cv::Scalar blue(1.0f, 0.0f, 0.0f);
01881     const cv::Scalar cyan(1.0f, 1.0f, 0.0f);
01882     const cv::Scalar green(0.0f, 1.0f, 0.0f);
01883     for (unsigned int i = 0; i < boundary.points3D.size(); ++i)
01884     {
01885       PointStamped start_pt;
01886       start_pt.header.frame_id = workspace_frame_;
01887       start_pt.point.x = boundary.points3D[i].x;
01888       start_pt.point.y = boundary.points3D[i].y;
01889       start_pt.point.z = boundary.points3D[i].z;
01890       PointStamped end_pt;
01891       end_pt.header.frame_id = workspace_frame_;
01892       end_pt.point.x = start_pt.point.x + n[0]*0.10;
01893       end_pt.point.y = start_pt.point.y + n[1]*0.10;
01894       end_pt.point.z = start_pt.point.z + n[2]*0.10;
01895 
01896       cv::Point img_start_pt = pcl_segmenter_->projectPointIntoImage(start_pt);
01897       cv::Point img_end_pt = pcl_segmenter_->projectPointIntoImage(end_pt);
01898       cv::line(obj_disp_img, img_start_pt, img_end_pt, cv::Scalar(0.0,0.0,0.0), 3);
01899       cv::line(obj_disp_img, img_start_pt, img_end_pt, cyan);
01900       cv::circle(obj_disp_img, img_end_pt, 4, cv::Scalar(0.0,0.0,0.0), 3);
01901       cv::circle(obj_disp_img, img_end_pt, 4, blue);
01902     }
01903     PointStamped l_point;
01904     l_point.header.frame_id = workspace_frame_;
01905     l_point.point.x = l_pt[0];
01906     l_point.point.y = l_pt[1];
01907     l_point.point.z = l_pt[2];
01908     PointStamped l_end;
01909     l_end.header.frame_id = workspace_frame_;
01910     l_end.point.x = l_pt[0] + l_dir[0]*0.10;
01911     l_end.point.y = l_pt[1] + l_dir[1]*0.10;
01912     l_end.point.z = l_pt[2] + l_dir[2]*0.10;
01913     cv::Point img_l_pt = pcl_segmenter_->projectPointIntoImage(l_point);
01914     cv::Point img_l_end = pcl_segmenter_->projectPointIntoImage(l_end);
01915     cv::circle(obj_disp_img, img_l_pt, 6, cv::Scalar(0.0,0.0,0.0), 3);
01916     cv::circle(obj_disp_img, img_l_pt, 6, green);
01917     cv::line(obj_disp_img, img_l_pt, img_l_end, cv::Scalar(0.0,0.0,0.0), 3);
01918     cv::line(obj_disp_img, img_l_pt, img_l_end, green);
01919     const cv::Vec3f green_v(0.0f, 1.0f, 0.0f);
01920     for (unsigned int i = 0; i < boundary.size(); ++i)
01921     {
01922       obj_disp_img.at<cv::Vec3f>(boundary[i].y, boundary[i].x) = green_v;
01923     }
01924     cv::imshow(title, obj_disp_img);
01925   }
01926 
01927   void highlightBoundaryOrientation(cv::Mat& obj_img, Boundary& boundary,
01928                                     std::string title)
01929   {
01930     cv::Mat obj_disp_img(obj_img.size(), CV_32FC3);
01931     if (obj_img.depth() == CV_8U)
01932     {
01933       cv::Mat obj_img_f;
01934       obj_img.convertTo(obj_img_f, CV_32FC1, 30.0/255);
01935       cv::cvtColor(obj_img_f, obj_disp_img, CV_GRAY2BGR);
01936     }
01937     else if (obj_img.type() == CV_32FC1)
01938     {
01939       cv::cvtColor(obj_img, obj_disp_img, CV_GRAY2BGR);
01940     }
01941     else
01942     {
01943       obj_img.copyTo(obj_disp_img);
01944     }
01945     cv::Vec3f green(0.0,1.0,0.0);
01946     for (unsigned int i = 0; i < boundary.size(); ++i)
01947     {
01948       obj_disp_img.at<cv::Vec3f>(boundary[i].y, boundary[i].x) = green;
01949     }
01950     if (use_displays_)
01951     {
01952       cv::imshow(title, obj_disp_img);
01953     }
01954     if (write_to_disk_)
01955     {
01956       // Write to disk to create video output
01957       std::stringstream bound_out_name;
01958       bound_out_name << base_output_path_ << "chosen" << callback_count_
01959                      << ".png";
01960       cv::Mat bound_out_img(obj_disp_img.size(), CV_8UC3);
01961       obj_disp_img.convertTo(bound_out_img, CV_8UC3, 255);
01962       cv::imwrite(bound_out_name.str(), bound_out_img);
01963     }
01964   }
01965 
01966   void draw3DBoundaries(std::vector<Boundary> boundaries, cv::Mat& img,
01967                         unsigned int objs_size, bool is_obj_img=false)
01968   {
01969     cv::Mat disp_img(img.size(), CV_32FC3, cv::Scalar(0.0,0.0,0.0));
01970 
01971     if (is_obj_img)
01972     {
01973       for (int r = 0; r < img.rows; ++r)
01974       {
01975         for (int c = 0; c < img.cols; ++c)
01976         {
01977           unsigned int id = img.at<uchar>(r,c);
01978           if (id > 0)
01979           {
01980             disp_img.at<cv::Vec3f>(r,c) = pcl_segmenter_->colors_[id-1];
01981           }
01982         }
01983       }
01984     }
01985     else
01986     {
01987       if (img.type() == CV_32FC3)
01988       {
01989         img.copyTo(disp_img);
01990       }
01991       else if (img.type() == CV_8UC3)
01992       {
01993         img.convertTo(disp_img, CV_32FC3, 1.0/255.0);
01994       }
01995       else if (img.type() == CV_32FC1)
01996       {
01997         cv::cvtColor(img, disp_img, CV_GRAY2BGR);
01998       }
01999       else if (img.type() == CV_8UC3)
02000       {
02001         cv::Mat tmp_img;
02002         img.convertTo(tmp_img, CV_32FC1, 1.0/255.0);
02003         cv::cvtColor(tmp_img, disp_img, CV_GRAY2BGR);
02004       }
02005     }
02006     cv::Vec3f no_obj(0.0, 0.0, 1.0);
02007     cv::Vec3f short_col(1.0, 0.0, 0.0);
02008     cv::Vec3f good(0.0, 1.0, 0.0);
02009     cv::Vec3f external(0.0, 1.0, 1.0);
02010     for (unsigned int b = 0; b < boundaries.size(); ++b)
02011     {
02012       cv::Vec3f color;
02013       if (boundaries[b].object_idx < objs_size)
02014       {
02015         if (boundaries[b].too_short)
02016         {
02017           color = short_col;
02018         }
02019         else if (boundaries[b].external)
02020         {
02021           color = external;
02022         }
02023         else
02024         {
02025           color = good;
02026         }
02027       }
02028       else
02029       {
02030         color = no_obj;
02031       }
02032       for (unsigned int i = 0; i < boundaries[b].size(); ++i)
02033       {
02034         disp_img.at<cv::Vec3f>(boundaries[b][i].y,
02035                                boundaries[b][i].x) = color;
02036       }
02037     }
02038     if (use_displays_)
02039     {
02040       cv::imshow("3D boundaries", disp_img);
02041     }
02042     if (write_to_disk_)
02043     {
02044       // Write to disk to create video output
02045       std::stringstream bound_out_name;
02046       bound_out_name << base_output_path_ << "bound3D" << callback_count_
02047                      << ".png";
02048       cv::Mat bound_out_img(disp_img.size(), CV_8UC3);
02049       disp_img.convertTo(bound_out_img, CV_8UC3, 255);
02050       cv::imwrite(bound_out_name.str(), bound_out_img);
02051     }
02052   }
02053 
02054   void drawObjectTransformAxises(cv::Mat& obj_img, ProtoObjects& objs)
02055   {
02056     cv::Mat obj_disp_img(obj_img.size(), CV_32FC3, cv::Scalar(0.0,0.0,0.0));
02057 
02058     for (int r = 0; r < obj_img.rows; ++r)
02059     {
02060       for (int c = 0; c < obj_img.cols; ++c)
02061       {
02062         unsigned int id = obj_img.at<uchar>(r,c);
02063         if (id > 0)
02064         {
02065           obj_disp_img.at<cv::Vec3f>(r,c) = pcl_segmenter_->colors_[id-1];
02066         }
02067       }
02068     }
02069     cv::Scalar green(0.0, 1.0, 0.0);
02070     cv::Scalar red(0.0, 0.0, 1.0);
02071     cv::Scalar cyan(1.0, 1.0, 0.0);
02072     cv::Scalar black(0.0, 0.0, 0.0);
02073     const Eigen::Vector3f x_axis(0.1, 0.0, 0.0);
02074     const Eigen::Vector3f y_axis(0.0, 0.1, 0.0);
02075     const Eigen::Vector3f z_axis(0.0, 0.0, 0.1);
02076 
02077     for (unsigned int i = 0; i < objs.size(); ++i)
02078     {
02079       Eigen::Matrix3f rot = objs[i].transform.block<3,3>(0,0);
02080       Eigen::Vector3f trans = objs[i].transform.block<3,1>(0,3);
02081 
02082       // Transform axises into current frame
02083       Eigen::Vector3f x_t = rot*x_axis;
02084       Eigen::Vector3f y_t = rot*y_axis;
02085       Eigen::Vector3f z_t = rot*z_axis;
02086       x_t /= x_t.norm();
02087       y_t /= y_t.norm();
02088       z_t /= z_t.norm();
02089 
02090       // Project axises into image
02091       PointStamped start_point;
02092       start_point.point.x = objs[i].centroid[0];
02093       start_point.point.y = objs[i].centroid[1];
02094       start_point.point.z = objs[i].centroid[2];
02095       start_point.header.frame_id = workspace_frame_;
02096 
02097       PointStamped end_point_x;
02098       end_point_x.point.x = start_point.point.x + 0.1*x_t[0];
02099       end_point_x.point.y = start_point.point.y + 0.1*x_t[1];
02100       end_point_x.point.z = start_point.point.z + 0.1*x_t[2];
02101       end_point_x.header.frame_id = workspace_frame_;
02102 
02103       PointStamped end_point_y;
02104       end_point_y.point.x = start_point.point.x + 0.1*y_t[0];
02105       end_point_y.point.y = start_point.point.y + 0.1*y_t[1];
02106       end_point_y.point.z = start_point.point.z + 0.1*y_t[2];
02107       end_point_y.header.frame_id = workspace_frame_;
02108 
02109       PointStamped end_point_z;
02110       end_point_z.point.x = start_point.point.x + 0.1*z_t[0];
02111       end_point_z.point.y = start_point.point.y + 0.1*z_t[1];
02112       end_point_z.point.z = start_point.point.z + 0.1*z_t[2];
02113       end_point_z.header.frame_id = workspace_frame_;
02114 
02115       cv::Point img_start_point = pcl_segmenter_->projectPointIntoImage(
02116           start_point);
02117       cv::Point img_end_point_x = pcl_segmenter_->projectPointIntoImage(
02118           end_point_x);
02119       cv::Point img_end_point_y = pcl_segmenter_->projectPointIntoImage(
02120           end_point_y);
02121       cv::Point img_end_point_z = pcl_segmenter_->projectPointIntoImage(
02122           end_point_z);
02123 
02124       // Draw axises on image
02125       cv::line(obj_disp_img, img_start_point, img_end_point_x, black, 3);
02126       cv::line(obj_disp_img, img_start_point, img_end_point_y, black, 3);
02127       cv::line(obj_disp_img, img_start_point, img_end_point_z, black, 3);
02128 
02129       cv::line(obj_disp_img, img_start_point, img_end_point_x, red);
02130       cv::line(obj_disp_img, img_start_point, img_end_point_y, green);
02131       cv::line(obj_disp_img, img_start_point, img_end_point_z, cyan);
02132     }
02133     if (use_displays_)
02134     {
02135       cv::imshow("Object axis", obj_disp_img);
02136     }
02137     if (write_to_disk_)
02138     {
02139       // Write to disk to create video output
02140       std::stringstream axis_out_name;
02141       axis_out_name << base_output_path_ << "axis" << callback_count_
02142                     << ".png";
02143       cv::Mat axis_out_img(obj_disp_img.size(), CV_8UC3);
02144       obj_disp_img.convertTo(axis_out_img, CV_8UC3, 255);
02145       cv::imwrite(axis_out_name.str(), axis_out_img);
02146     }
02147   }
02148 
02149   int getNextID()
02150   {
02151     ROS_DEBUG_STREAM("Getting next ID: " << next_id_);
02152     return next_id_++;
02153   }
02154 
02155   void drawObjectHists(cv::Mat& img, ProtoObjects& objs, int highlight_bin,
02156                        int highlight_obj, bool is_obj_img=false)
02157   {
02158     cv::Mat disp_img(img.size(), CV_32FC3, cv::Scalar(0.0,0.0,0.0));
02159     if (is_obj_img)
02160     {
02161       for (int r = 0; r < img.rows; ++r)
02162       {
02163         for (int c = 0; c < img.cols; ++c)
02164         {
02165           unsigned int id = img.at<uchar>(r,c);
02166           if (id > 0)
02167           {
02168             disp_img.at<cv::Vec3f>(r,c) = pcl_segmenter_->colors_[id-1];
02169           }
02170         }
02171       }
02172     }
02173     else
02174     {
02175       img.convertTo(disp_img, CV_32FC3, 1.0/255.0);
02176     }
02177 
02178     cv::Mat disp_img_hist;
02179     disp_img.copyTo(disp_img_hist);
02180     // const Eigen::Vector4f x_axis(0.1, 0.0, 0.0, 1.0);
02181     const Eigen::Vector3f x_axis(0.1, 0.0, 0.0);
02182     // const int w = 5;
02183     // const int h = 30;
02184     const int w = histogram_bin_width_;
02185     const int h = histogram_bin_height_;
02186     const cv::Scalar est_line_color(0.0, 0.0, 0.0);
02187     const cv::Scalar est_fill_color(0.0, 0.0, 0.7);
02188     const cv::Scalar history_line_color(0.0, 0.0, 0.0);
02189     const cv::Scalar history_fill_color(0.0, 0.7, 0.0);
02190     const cv::Scalar highlight_color(0.7, 0.0, 0.0);
02191     for (unsigned int i = 0; i < objs.size(); ++i)
02192     {
02193       // const Eigen::Vector4f x_t = objs[i].transform.transpose()*x_axis;
02194       Eigen::Matrix3f rot = objs[i].transform.block<3,3>(0,0);
02195       Eigen::Vector3f trans = objs[i].transform.block<3,1>(0,3);
02196       const Eigen::Vector3f x_t = rot*x_axis;
02197       // NOTE: In degrees!
02198       const float start_angle = (-atan2(x_t[1], x_t[0])+ M_PI)*180.0/M_PI;
02199       // Get the locations from object centroids
02200       PointStamped center3D;
02201       center3D.point.x = objs[i].centroid[0];
02202       center3D.point.y = objs[i].centroid[1];
02203       center3D.point.z = objs[i].centroid[2];
02204       center3D.header.frame_id = workspace_frame_;
02205       cv::Point center = pcl_segmenter_->projectPointIntoImage(center3D);
02206       drawSemicircleHist(objs[i].boundary_angle_dist, disp_img, center, w, h,
02207                          est_line_color, est_fill_color, highlight_color,
02208                          start_angle);
02209       int to_highlight = -1;
02210       if (i == highlight_obj)
02211       {
02212         to_highlight = highlight_bin;
02213       }
02214       drawSemicircleHist(objs[i].push_history, disp_img_hist, center, w, h,
02215                          history_line_color, history_fill_color,
02216                          highlight_color, start_angle, to_highlight);
02217     }
02218     if (use_displays_)
02219     {
02220       cv::imshow("Boundary Estimate Distributions", disp_img);
02221       cv::imshow("Pushing History Distributions", disp_img_hist);
02222     }
02223     if (write_to_disk_)
02224     {
02225       // Write to disk to create video output
02226       std::stringstream est_out_name;
02227       std::stringstream hist_out_name;
02228       est_out_name << base_output_path_ << "bound_est" << callback_count_
02229                    << ".png";
02230       hist_out_name << base_output_path_ << "hist_est" << callback_count_
02231                     << ".png";
02232       cv::Mat est_out_img(disp_img.size(), CV_8UC3);
02233       cv::Mat hist_out_img(disp_img.size(), CV_8UC3);
02234       disp_img.convertTo(est_out_img, CV_8UC3, 255);
02235       disp_img_hist.convertTo(hist_out_img, CV_8UC3, 255);
02236       cv::imwrite(est_out_name.str(), est_out_img);
02237       cv::imwrite(hist_out_name.str(), hist_out_img);
02238     }
02239   }
02240 
02241   void drawUnguidedHists(cv::Mat& img, ProtoObjects& objs, int highlight_obj)
02242   {
02243     cv::Mat disp_img(img.size(), CV_32FC3, cv::Scalar(0.0,0.0,0.0));
02244     img.convertTo(disp_img, CV_32FC3, 1.0/255.0);
02245 
02246     const Eigen::Vector3f x_axis(0.1, 0.0, 0.0);
02247     const int w = histogram_bin_width_;
02248     const int h = histogram_bin_height_;
02249     const cv::Scalar est_line_color(0.0, 0.0, 0.0);
02250     const cv::Scalar est_fill_color(0.0, 0.0, 0.7);
02251     const cv::Scalar history_line_color(0.0, 0.0, 0.0);
02252     const cv::Scalar history_fill_color(0.0, 0.7, 0.0);
02253     const cv::Scalar highlight_color(0.7, 0.0, 0.0);
02254     for (unsigned int i = 0; i < objs.size(); ++i)
02255     {
02256       // const Eigen::Vector4f x_t = objs[i].transform.transpose()*x_axis;
02257       Eigen::Matrix3f rot = objs[i].transform.block<3,3>(0,0);
02258       Eigen::Vector3f trans = objs[i].transform.block<3,1>(0,3);
02259       const Eigen::Vector3f x_t = rot*x_axis;
02260       // NOTE: In degrees!
02261       const float start_angle = (-atan2(x_t[1], x_t[0])+ M_PI)*180.0/M_PI;
02262       // Get the locations from object centroids
02263       PointStamped center3D;
02264       center3D.point.x = objs[i].centroid[0];
02265       center3D.point.y = objs[i].centroid[1];
02266       center3D.point.z = objs[i].centroid[2];
02267       center3D.header.frame_id = workspace_frame_;
02268       cv::Point center = pcl_segmenter_->projectPointIntoImage(center3D);
02269       int to_highlight = -1;
02270       if (i == highlight_obj)
02271       {
02272         to_highlight = 2;
02273       }
02274       std::vector<int> fake_push_history(5,0);
02275       fake_push_history[2] = objs[i].push_history[0];
02276       drawUnguidedHist(fake_push_history, disp_img, center, w, h,
02277                        history_line_color, history_fill_color,
02278                        highlight_color, start_angle, to_highlight);
02279     }
02280     if (use_displays_)
02281     {
02282       cv::imshow("Pushing History Distributions", disp_img);
02283     }
02284     if (write_to_disk_)
02285     {
02286       // Write to disk to create video output
02287       std::stringstream hist_out_name;
02288       hist_out_name << base_output_path_ << "hist_est" << callback_count_
02289                     << ".png";
02290       cv::Mat hist_out_img(disp_img.size(), CV_8UC3);
02291       disp_img.convertTo(hist_out_img, CV_8UC3, 255);
02292       cv::imwrite(hist_out_name.str(), hist_out_img);
02293     }
02294   }
02295 
02296   void drawSemicircleHist(std::vector<int>& hist, cv::Mat& disp_img,
02297                           const cv::Point center, int w, int h,
02298                           const cv::Scalar line_color,
02299                           const cv::Scalar fill_color,
02300                           const cv::Scalar highlight_color,
02301                           const float start_rot, int highlight_bin=-1)
02302   {
02303     int half_circ = w*hist.size();
02304     const int r0 = half_circ/M_PI;
02305     const cv::Size s0(r0, r0);
02306     const float angle_inc = 180.0/(hist.size());
02307     float hist_max = 0.0;
02308     const float deg_precision = 1.0;
02309     for (unsigned int i = 0; i < hist.size(); ++i)
02310     {
02311       if (hist[i] > hist_max)
02312       {
02313         hist_max = hist[i];
02314       }
02315     }
02316     // Draw all fills
02317     float rot = start_rot;
02318     for (unsigned int i = 0; i < hist.size(); ++i)
02319     {
02320       // NOTE: need to flip histogram order to correctly display
02321       if (hist[hist.size()-i-1] > 0 || (hist.size()-i-1) == highlight_bin)
02322       {
02323         int d_y = 0;
02324         if (hist_max != 0)
02325         {
02326           d_y = h * hist[hist.size()-i-1] / hist_max;
02327         }
02328         if ((hist.size()-i-1) == highlight_bin && d_y == 0)
02329         {
02330           d_y = h;
02331         }
02332         const int r1 = r0+d_y;
02333         const cv::Size s1(r1, r1);
02334         std::vector<cv::Point> out_arc_pts;
02335         float start_angle = 0.0;
02336         float end_angle = angle_inc;
02337 
02338         cv::ellipse2Poly(center, s1, rot, start_angle, end_angle, deg_precision,
02339                          out_arc_pts);
02340         std::vector<cv::Point> in_arc_pts;
02341         cv::ellipse2Poly(center, s0, rot, start_angle, end_angle, deg_precision,
02342                          in_arc_pts);
02343         std::vector<cv::Point> poly_vect;
02344         for (unsigned int j = 0; j < in_arc_pts.size(); ++j)
02345         {
02346           poly_vect.push_back(in_arc_pts[j]);
02347         }
02348         for (unsigned int j = out_arc_pts.size()-1; j > 0; --j)
02349         {
02350           poly_vect.push_back(out_arc_pts[j]);
02351         }
02352 
02353         int npts[1] = {poly_vect.size()};
02354         cv::Point* poly = new cv::Point[poly_vect.size()];
02355         for (unsigned int j = 0; j < poly_vect.size(); ++j)
02356         {
02357           poly[j] = poly_vect[j];
02358         }
02359         const cv::Point* pts[1] = {poly};
02360         // fill bin
02361         if ((hist.size()-i-1) == highlight_bin)
02362         {
02363           cv::fillPoly(disp_img, pts, npts, 1, highlight_color);
02364         }
02365         else
02366         {
02367           cv::fillPoly(disp_img, pts, npts, 1, fill_color);
02368         }
02369         delete poly;
02370       }
02371       rot += angle_inc;
02372     }
02373     // Draw inner circle
02374     cv::ellipse(disp_img, center, s0, start_rot, 0.0, 180.0, line_color);
02375     std::vector<cv::Point> arc_pts;
02376     cv::ellipse2Poly(center, s0, start_rot, 0.0, 180.0, deg_precision, arc_pts);
02377     // Draw diameter line
02378     cv::line(disp_img, arc_pts.front(), arc_pts.back(), line_color);
02379 
02380     // Draw all lines
02381     rot = start_rot;
02382     for (unsigned int i = 0; i < hist.size(); ++i)
02383     {
02384       if (hist[hist.size()-i-1] > 0 || (hist.size()-i-1) == highlight_bin)
02385       {
02386         int d_y = 0;
02387         if (hist_max != 0)
02388         {
02389           d_y = h * hist[hist.size()-i-1] / hist_max;
02390         }
02391         if ((hist.size()-i-1) == highlight_bin && d_y == 0)
02392         {
02393           d_y = h;
02394         }
02395 
02396         const int r1 = r0+d_y;
02397         const cv::Size s1(r1, r1);
02398         float start_angle = 0.0;
02399         float end_angle = angle_inc;
02400 
02401         cv::ellipse(disp_img, center, s1, rot, start_angle, end_angle,
02402                     line_color);
02403         std::vector<cv::Point> out_arc_pts;
02404         cv::ellipse2Poly(center, s1, rot, start_angle, end_angle, deg_precision,
02405                          out_arc_pts);
02406         std::vector<cv::Point> in_arc_pts;
02407         cv::ellipse2Poly(center, s0, rot, start_angle, end_angle, deg_precision,
02408                          in_arc_pts);
02409         // Draw bin edge lines
02410         cv::line(disp_img, in_arc_pts.front(), out_arc_pts.front(), line_color);
02411         cv::line(disp_img, in_arc_pts.back(), out_arc_pts.back(), line_color);
02412       }
02413       rot += angle_inc;
02414     }
02415   }
02416 
02417   void drawUnguidedHist(std::vector<int>& hist, cv::Mat& disp_img,
02418                         const cv::Point center, int w, int h,
02419                         const cv::Scalar line_color,
02420                         const cv::Scalar fill_color,
02421                         const cv::Scalar highlight_color,
02422                         const float start_rot, int highlight_bin=-1)
02423   {
02424     int half_circ = w*hist.size();
02425     const int r0 = half_circ/M_PI;
02426     const cv::Size s0(r0, r0);
02427     const float angle_inc = 180.0/(hist.size());
02428     float hist_max = per_object_rand_push_count_;
02429     const float deg_precision = 1.0;
02430     // Draw all fills
02431     float rot = start_rot;
02432     for (unsigned int i = 0; i < hist.size(); ++i)
02433     {
02434       // NOTE: need to flip histogram order to correctly display
02435       if (hist[hist.size()-i-1] > 0 || (hist.size()-i-1) == highlight_bin)
02436       {
02437         int d_y = 0.0;
02438         if (hist_max != 0)
02439         {
02440           d_y = h * hist[hist.size()-i-1] / hist_max;
02441         }
02442         if ((hist.size()-i-1) == highlight_bin)
02443         {
02444           d_y += h/hist_max;
02445         }
02446         const int r1 = r0+d_y;
02447         const cv::Size s1(r1, r1);
02448         std::vector<cv::Point> out_arc_pts;
02449         float start_angle = 0.0;
02450         float end_angle = angle_inc;
02451 
02452         cv::ellipse2Poly(center, s1, rot, start_angle, end_angle, deg_precision,
02453                          out_arc_pts);
02454         std::vector<cv::Point> in_arc_pts;
02455         cv::ellipse2Poly(center, s0, rot, start_angle, end_angle, deg_precision,
02456                          in_arc_pts);
02457         std::vector<cv::Point> poly_vect;
02458         for (unsigned int j = 0; j < in_arc_pts.size(); ++j)
02459         {
02460           poly_vect.push_back(in_arc_pts[j]);
02461         }
02462         for (unsigned int j = out_arc_pts.size()-1; j > 0; --j)
02463         {
02464           poly_vect.push_back(out_arc_pts[j]);
02465         }
02466 
02467         int npts[1] = {poly_vect.size()};
02468         cv::Point* poly = new cv::Point[poly_vect.size()];
02469         for (unsigned int j = 0; j < poly_vect.size(); ++j)
02470         {
02471           poly[j] = poly_vect[j];
02472         }
02473         const cv::Point* pts[1] = {poly};
02474         // fill bin
02475         if ((hist.size()-i-1) == highlight_bin)
02476         {
02477           cv::fillPoly(disp_img, pts, npts, 1, highlight_color);
02478         }
02479         else
02480         {
02481           cv::fillPoly(disp_img, pts, npts, 1, fill_color);
02482         }
02483         delete poly;
02484       }
02485       rot += angle_inc;
02486     }
02487     // Draw inner circle
02488     cv::ellipse(disp_img, center, s0, start_rot, 0.0, 180.0, line_color);
02489     std::vector<cv::Point> arc_pts;
02490     cv::ellipse2Poly(center, s0, start_rot, 0.0, 180.0, deg_precision, arc_pts);
02491     // Draw diameter line
02492     cv::line(disp_img, arc_pts.front(), arc_pts.back(), line_color);
02493 
02494     // Draw all lines
02495     rot = start_rot;
02496     for (unsigned int i = 0; i < hist.size(); ++i)
02497     {
02498       if (hist[hist.size()-i-1] > 0 || (hist.size()-i-1) == highlight_bin)
02499       {
02500         int d_y = h * hist[hist.size()-i-1] / hist_max;
02501         if ((hist.size()-i-1) == highlight_bin)
02502         {
02503           d_y += h/hist_max;
02504         }
02505 
02506         const int r1 = r0+d_y;
02507         const cv::Size s1(r1, r1);
02508         float start_angle = 0.0;
02509         float end_angle = angle_inc;
02510 
02511         cv::ellipse(disp_img, center, s1, rot, start_angle, end_angle,
02512                     line_color);
02513         std::vector<cv::Point> out_arc_pts;
02514         cv::ellipse2Poly(center, s1, rot, start_angle, end_angle, deg_precision,
02515                          out_arc_pts);
02516         std::vector<cv::Point> in_arc_pts;
02517         cv::ellipse2Poly(center, s0, rot, start_angle, end_angle, deg_precision,
02518                          in_arc_pts);
02519         // Draw bin edge lines
02520         cv::line(disp_img, in_arc_pts.front(), out_arc_pts.front(), line_color);
02521         cv::line(disp_img, in_arc_pts.back(), out_arc_pts.back(), line_color);
02522       }
02523       rot += angle_inc;
02524     }
02525   }
02526 
02527  public:
02528   //
02529   // Getters and setters
02530   //
02531 
02532   bool isInitialized() const { return initialized_; }
02533 
02534   void unInitialize() { initialized_ = false; }
02535 
02536   //
02537   // Class member variables
02538   //
02539  protected:
02540   cv::Mat dx_kernel_;
02541   cv::Mat dy_kernel_;
02542   cv::Mat g_kernel_;
02543   shared_ptr<PointCloudSegmentation> pcl_segmenter_;
02544   XYZPointCloud prev_cloud_down_;
02545   XYZPointCloud prev_objs_down_;
02546   ProtoObjects prev_proto_objs_;
02547   ProtoObjects cur_proto_objs_;
02548   PushVector prev_push_vector_;
02549   int callback_count_;
02550   int next_id_;
02551   bool initialized_;
02552   bool merged_;
02553   bool split_;
02554   XYZPointCloud cur_table_cloud_;
02555 
02556  public:
02557   double min_pushing_x_;
02558   double max_pushing_x_;
02559   double min_pushing_y_;
02560   double max_pushing_y_;
02561   double min_table_x_;
02562   double max_table_x_;
02563   double min_table_y_;
02564   double max_table_y_;
02565   double push_collision_intersection_thresh_;
02566   double start_collision_thresh_;
02567   std::string workspace_frame_;
02568   ros::Publisher obj_push_pub_;
02569   bool threshold_edges_;
02570   double depth_edge_weight_;
02571   double edge_weight_thresh_;
02572   double depth_edge_weight_thresh_;
02573   double max_push_angle_;
02574   double min_push_angle_;
02575   double boundary_ransac_thresh_;
02576   int min_edge_length_;
02577   int num_angle_bins_;
02578   int num_downsamples_;
02579   int upscale_;
02580   bool use_displays_;
02581   bool write_to_disk_;
02582   int min_cluster_size_;
02583   int min_boundary_length_;
02584   std::string base_output_path_;
02585   int histogram_bin_width_;
02586   int histogram_bin_height_;
02587   double min_push_dist_;
02588   double max_push_dist_;
02589   double push_dist_inflation_;
02590   double bad_icp_score_limit_;
02591   bool force_remain_singulated_;
02592   int per_object_rand_push_count_;
02593   bool use_unguided_icp_;
02594   int push_guess_limit_;
02595 };
02596 
02597 class ObjectSingulationNode
02598 {
02599  public:
02600   ObjectSingulationNode(ros::NodeHandle &n) :
02601       n_(n), n_private_("~"),
02602       image_sub_(n, "color_image_topic", 1),
02603       depth_sub_(n, "depth_image_topic", 1),
02604       cloud_sub_(n, "point_cloud_topic", 1),
02605       sync_(MySyncPolicy(15), image_sub_, depth_sub_, cloud_sub_),
02606       have_depth_data_(false), tracking_(false),
02607       tracker_initialized_(false),
02608       camera_initialized_(false), record_count_(0), recording_input_(false)
02609   {
02610     tf_ = shared_ptr<tf::TransformListener>(new tf::TransformListener());
02611     pcl_segmenter_ = shared_ptr<PointCloudSegmentation>(
02612         new PointCloudSegmentation(tf_));
02613     os_ = shared_ptr<ObjectSingulation>(new ObjectSingulation(pcl_segmenter_));
02614     // Get parameters from the server
02615     n_private_.param("crop_min_x", crop_min_x_, 0);
02616     n_private_.param("crop_max_x", crop_max_x_, 640);
02617     n_private_.param("crop_min_y", crop_min_y_, 0);
02618     n_private_.param("crop_max_y", crop_max_y_, 480);
02619     n_private_.param("display_wait_ms", display_wait_ms_, 3);
02620     n_private_.param("use_displays", use_displays_, false);
02621     os_->use_displays_ = use_displays_;
02622     n_private_.param("write_to_disk", os_->write_to_disk_, false);
02623     n_private_.param("write_input_to_disk", write_input_to_disk_, false);
02624     n_private_.param("min_workspace_x", min_workspace_x_, 0.0);
02625     n_private_.param("min_workspace_y", min_workspace_y_, 0.0);
02626     n_private_.param("min_workspace_z", min_workspace_z_, 0.0);
02627     n_private_.param("max_workspace_x", max_workspace_x_, 0.0);
02628     n_private_.param("max_workspace_y", max_workspace_y_, 0.0);
02629     n_private_.param("max_workspace_z", max_workspace_z_, 0.0);
02630     n_private_.param("min_pushing_x", os_->min_pushing_x_, 0.0);
02631     n_private_.param("min_pushing_y", os_->min_pushing_y_, 0.0);
02632     n_private_.param("max_pushing_x", os_->max_pushing_x_, 0.0);
02633     n_private_.param("max_pushing_y", os_->max_pushing_y_, 0.0);
02634     std::string default_workspace_frame = "/torso_lift_link";
02635     n_private_.param("workspace_frame", workspace_frame_,
02636                      default_workspace_frame);
02637     os_->workspace_frame_ = workspace_frame_;
02638 
02639     n_private_.param("threshold_edges", os_->threshold_edges_, false);
02640     n_private_.param("edge_weight_thresh", os_->edge_weight_thresh_, 0.5);
02641     n_private_.param("depth_edge_weight_thresh", os_->depth_edge_weight_thresh_,
02642                      0.5);
02643     n_private_.param("depth_edge_weight", os_->depth_edge_weight_, 0.75);
02644     n_private_.param("max_pushing_angle", os_->max_push_angle_, M_PI);
02645     n_private_.param("min_pushing_angle", os_->min_push_angle_, -M_PI);
02646     n_private_.param("boundary_ransac_thresh", os_->boundary_ransac_thresh_,
02647                      0.01);
02648     n_private_.param("min_edge_length", os_->min_edge_length_, 3);
02649     n_private_.param("num_angle_bins", os_->num_angle_bins_, 8);
02650     n_private_.param("os_min_cluster_size", os_->min_cluster_size_, 20);
02651     n_private_.param("os_min_boundary_length", os_->min_boundary_length_, 3);
02652     // NOTE: Must be at least 3 for mathematical reasons
02653     os_->min_boundary_length_ = std::max(os_->min_boundary_length_, 3);
02654     n_private_.param("min_push_dist", os_->min_push_dist_, 0.1);
02655     n_private_.param("max_push_dist", os_->max_push_dist_, 0.5);
02656     n_private_.param("push_dist_inflation", os_->push_dist_inflation_, 0.0);
02657     os_->min_boundary_length_ = std::max(os_->min_boundary_length_, 3);
02658     n_private_.param("os_hist_bin_width", os_->histogram_bin_width_, 5);
02659     n_private_.param("os_hist_bin_height", os_->histogram_bin_height_, 30);
02660     n_private_.param("bad_icp_score_limit", os_->bad_icp_score_limit_, 0.0015);
02661     n_private_.param("push_collision_thresh",
02662                      os_->push_collision_intersection_thresh_, 0.03);
02663     n_private_.param("start_collision_thresh",
02664                      os_->start_collision_thresh_, 0.05);
02665     n_private_.param("force_remain_singulated",
02666                      os_->force_remain_singulated_, true);
02667     n_private_.param("per_object_rand_push_count",
02668                      os_->per_object_rand_push_count_, 3);
02669     n_private_.param("use_unguided_icp", os_->use_unguided_icp_, true);
02670     n_private_.param("push_guess_limit", os_->push_guess_limit_, 1);
02671     std::string output_path_def = "~";
02672     n_private_.param("img_output_path", base_output_path_, output_path_def);
02673     os_->base_output_path_ = base_output_path_;
02674 
02675     n_private_.param("min_table_z", pcl_segmenter_->min_table_z_, -0.5);
02676     n_private_.param("max_table_z", pcl_segmenter_->max_table_z_, 1.5);
02677     pcl_segmenter_->min_workspace_x_ = min_workspace_x_;
02678     pcl_segmenter_->max_workspace_x_ = max_workspace_x_;
02679     pcl_segmenter_->min_workspace_z_ = min_workspace_z_;
02680     pcl_segmenter_->max_workspace_z_ = max_workspace_z_;
02681     n_private_.param("moved_count_thresh", pcl_segmenter_->moved_count_thresh_,
02682                      1);
02683 
02684     n_private_.param("autostart_tracking", tracking_, false);
02685     n_private_.param("autostart_pcl_segmentation", autorun_pcl_segmentation_,
02686                      false);
02687     n_private_.param("use_guided_pushes", use_guided_pushes_, true);
02688 
02689     n_private_.param("num_downsamples", num_downsamples_, 2);
02690     pcl_segmenter_->num_downsamples_ = num_downsamples_;
02691     os_->num_downsamples_ = num_downsamples_;
02692     os_->upscale_ = std::pow(2,num_downsamples_);
02693 
02694     std::string cam_info_topic_def = "/kinect_head/rgb/camera_info";
02695     n_private_.param("cam_info_topic", cam_info_topic_,
02696                      cam_info_topic_def);
02697     n_private_.param("table_ransac_thresh", pcl_segmenter_->table_ransac_thresh_,
02698                      0.01);
02699     n_private_.param("table_ransac_angle_thresh",
02700                      pcl_segmenter_->table_ransac_angle_thresh_, 30.0);
02701     n_private_.param("pcl_cluster_tolerance", pcl_segmenter_->cluster_tolerance_,
02702                      0.25);
02703     n_private_.param("pcl_difference_thresh", pcl_segmenter_->cloud_diff_thresh_,
02704                      0.01);
02705     n_private_.param("pcl_min_cluster_size", pcl_segmenter_->min_cluster_size_,
02706                      100);
02707     n_private_.param("pcl_max_cluster_size", pcl_segmenter_->max_cluster_size_,
02708                      2500);
02709     n_private_.param("pcl_voxel_downsample_res", pcl_segmenter_->voxel_down_res_,
02710                      0.005);
02711     n_private_.param("pcl_cloud_intersect_thresh",
02712                      pcl_segmenter_->cloud_intersect_thresh_, 0.005);
02713     n_private_.param("pcl_concave_hull_alpha", pcl_segmenter_->hull_alpha_,
02714                      0.1);
02715     n_private_.param("use_pcl_voxel_downsample",
02716                      pcl_segmenter_->use_voxel_down_, true);
02717     n_private_.param("icp_max_iters", pcl_segmenter_->icp_max_iters_, 100);
02718     n_private_.param("icp_transform_eps", pcl_segmenter_->icp_transform_eps_,
02719                      0.0);
02720     n_private_.param("icp_max_cor_dist",
02721                      pcl_segmenter_->icp_max_cor_dist_, 1.0);
02722     n_private_.param("icp_ransac_thresh",
02723                      pcl_segmenter_->icp_ransac_thresh_, 0.015);
02724 
02725     // Setup ros node connections
02726     sync_.registerCallback(&ObjectSingulationNode::sensorCallback,
02727                            this);
02728     push_pose_server_ = n_.advertiseService(
02729         "get_singulation_push_vector", &ObjectSingulationNode::getPushPose, this);
02730     table_location_server_ = n_.advertiseService(
02731         "get_table_location", &ObjectSingulationNode::getTableLocation,
02732         this);
02733     os_->obj_push_pub_ = n_.advertise<sensor_msgs::PointCloud2>(
02734         "object_singulation_cloud", 1000);
02735   }
02736 
02737   void sensorCallback(const sensor_msgs::ImageConstPtr& img_msg,
02738                       const sensor_msgs::ImageConstPtr& depth_msg,
02739                       const sensor_msgs::PointCloud2ConstPtr& cloud_msg)
02740   {
02741     if (!camera_initialized_)
02742     {
02743       cam_info_ = *ros::topic::waitForMessage<sensor_msgs::CameraInfo>(
02744           cam_info_topic_, n_, ros::Duration(5.0));
02745       camera_initialized_ = true;
02746       pcl_segmenter_->cam_info_ = cam_info_;
02747     }
02748     // Convert images to OpenCV format
02749     cv::Mat color_frame;
02750     cv::Mat depth_frame;
02751     cv_bridge::CvImagePtr color_cv_ptr = cv_bridge::toCvCopy(img_msg);
02752     cv_bridge::CvImagePtr depth_cv_ptr = cv_bridge::toCvCopy(depth_msg);
02753     color_frame = color_cv_ptr->image;
02754     depth_frame = depth_cv_ptr->image;
02755 
02756     // Swap kinect color channel order
02757     // cv::cvtColor(color_frame, color_frame, CV_RGB2BGR);
02758 
02759     // Transform point cloud into the correct frame and convert to PCL struct
02760     XYZPointCloud cloud;
02761     pcl16::fromROSMsg(*cloud_msg, cloud);
02762     tf_->waitForTransform(workspace_frame_, cloud.header.frame_id,
02763                           cloud.header.stamp, ros::Duration(0.5));
02764     pcl16_ros::transformPointCloud(workspace_frame_, cloud, cloud, *tf_);
02765 
02766     // Convert nans to zeros
02767     for (int r = 0; r < depth_frame.rows; ++r)
02768     {
02769       float* depth_row = depth_frame.ptr<float>(r);
02770       for (int c = 0; c < depth_frame.cols; ++c)
02771       {
02772         float cur_d = depth_row[c];
02773         if (isnan(cur_d))
02774         {
02775           depth_row[c] = 0.0;
02776         }
02777       }
02778     }
02779 
02780     cv::Mat workspace_mask(color_frame.rows, color_frame.cols, CV_8UC1,
02781                            cv::Scalar(255));
02782     // Black out pixels in color and depth images outside of workspace
02783     // As well as outside of the crop window
02784     for (int r = 0; r < color_frame.rows; ++r)
02785     {
02786       uchar* workspace_row = workspace_mask.ptr<uchar>(r);
02787       for (int c = 0; c < color_frame.cols; ++c)
02788       {
02789         // NOTE: Cloud is accessed by at(column, row)
02790         pcl16::PointXYZ cur_pt = cloud.at(c, r);
02791         if (cur_pt.x < min_workspace_x_ || cur_pt.x > max_workspace_x_ ||
02792             cur_pt.y < min_workspace_y_ || cur_pt.y > max_workspace_y_ ||
02793             cur_pt.z < min_workspace_z_ || cur_pt.z > max_workspace_z_ ||
02794             r < crop_min_y_ || c < crop_min_x_ || r > crop_max_y_ ||
02795             c > crop_max_x_ )
02796         {
02797           workspace_row[c] = 0;
02798         }
02799       }
02800     }
02801 
02802     // Downsample everything first
02803     cv::Mat color_frame_down = downSample(color_frame, num_downsamples_);
02804     cv::Mat depth_frame_down = downSample(depth_frame, num_downsamples_);
02805     cv::Mat workspace_mask_down = downSample(workspace_mask, num_downsamples_);
02806 
02807     // Save internally for use in the service callback
02808     prev_color_frame_ = cur_color_frame_.clone();
02809     prev_depth_frame_ = cur_depth_frame_.clone();
02810     prev_workspace_mask_ = cur_workspace_mask_.clone();
02811     prev_camera_header_ = cur_camera_header_;
02812 
02813     // Update the current versions
02814     cur_color_frame_ = color_frame_down.clone();
02815     cur_depth_frame_ = depth_frame_down.clone();
02816     cur_workspace_mask_ = workspace_mask_down.clone();
02817     cur_point_cloud_ = cloud;
02818     have_depth_data_ = true;
02819     cur_camera_header_ = img_msg->header;
02820     pcl_segmenter_->cur_camera_header_ = cur_camera_header_;
02821 
02822     // Debug stuff
02823     if (autorun_pcl_segmentation_)
02824     {
02825       getPushPose(use_guided_pushes_);
02826       if (!os_->isInitialized())
02827       {
02828         ROS_INFO_STREAM("Calling initialize.");
02829         os_->initialize(cur_color_frame_, cur_depth_frame_, cur_point_cloud_,
02830                         cur_workspace_mask_);
02831       }
02832     }
02833 
02834     // Display junk
02835 #ifdef DISPLAY_INPUT_COLOR
02836     if (use_displays_)
02837     {
02838       cv::imshow("color", cur_color_frame_);
02839     }
02840     // Way too much disk writing!
02841     if (write_input_to_disk_ && recording_input_)
02842     {
02843       std::stringstream out_name;
02844       out_name << base_output_path_ << "input" << record_count_ << ".png";
02845       record_count_++;
02846       cv::imwrite(out_name.str(), cur_color_frame_);
02847     }
02848 #endif // DISPLAY_INPUT_COLOR
02849 #ifdef DISPLAY_INPUT_DEPTH
02850     if (use_displays_)
02851     {
02852       double depth_max = 1.0;
02853       cv::minMaxLoc(cur_depth_frame_, NULL, &depth_max);
02854       cv::Mat depth_display = cur_depth_frame_.clone();
02855       depth_display /= depth_max;
02856       cv::imshow("input_depth", depth_display);
02857     }
02858 #endif // DISPLAY_INPUT_DEPTH
02859 #ifdef DISPLAY_WORKSPACE_MASK
02860     if (use_displays_)
02861     {
02862       cv::imshow("workspace_mask", cur_workspace_mask_);
02863     }
02864 #endif // DISPLAY_WORKSPACE_MASK
02865 #ifdef DISPLAY_WAIT
02866     if (use_displays_)
02867     {
02868       cv::waitKey(display_wait_ms_);
02869     }
02870 #endif // DISPLAY_WAIT
02871   }
02872 
02882   bool getPushPose(SingulationPush::Request& req, SingulationPush::Response& res)
02883   {
02884     if ( have_depth_data_ )
02885     {
02886       if (req.initialize)
02887       {
02888         record_count_ = 0;
02889         os_->unInitialize();
02890         os_->initialize(cur_color_frame_, cur_depth_frame_,
02891                         cur_point_cloud_, cur_workspace_mask_);
02892         res.no_push = true;
02893         recording_input_ = false;
02894         return true;
02895       }
02896       else
02897       {
02898         if ( ! recording_input_)
02899         {
02900           recording_input_ = true;
02901           ROS_INFO_STREAM("Starting input recording.");
02902         }
02903         if ( req.no_push_calc)
02904         {
02905           recording_input_ = false;
02906           ROS_INFO_STREAM("Stopping input recording.");
02907         }
02908         res = getPushPose(req.use_guided, req.no_push_calc);
02909         if ( res.no_push)
02910         {
02911           recording_input_ = false;
02912           ROS_INFO_STREAM("Stopping input recording.");
02913         }
02914 
02915       }
02916     }
02917     else
02918     {
02919       ROS_ERROR_STREAM("Calling getPushPose prior to receiving sensor data.");
02920       recording_input_ = false;
02921       res.no_push = true;
02922       return false;
02923     }
02924     return true;
02925   }
02926 
02935   PushVector getPushPose(bool use_guided=true, bool no_push_calc=false)
02936   {
02937     return os_->getPushVector(no_push_calc, cur_color_frame_,
02938                               cur_depth_frame_, cur_point_cloud_,
02939                               cur_workspace_mask_, use_guided);
02940   }
02941 
02951   bool getTableLocation(LocateTable::Request& req, LocateTable::Response& res)
02952   {
02953     if ( have_depth_data_ )
02954     {
02955       res.table_centroid = getTablePlane(cur_point_cloud_);
02956       if ((res.table_centroid.pose.position.x == 0.0 &&
02957            res.table_centroid.pose.position.y == 0.0 &&
02958            res.table_centroid.pose.position.z == 0.0) ||
02959           res.table_centroid.pose.position.x < 0.0)
02960       {
02961         ROS_ERROR_STREAM("No plane found, leaving");
02962         res.found_table = false;
02963         return false;
02964       }
02965       res.found_table = true;
02966     }
02967     else
02968     {
02969       ROS_ERROR_STREAM("Calling getTableLocation prior to receiving sensor data.");
02970       res.found_table = false;
02971       return false;
02972     }
02973     return true;
02974   }
02975 
02983   PoseStamped getTablePlane(XYZPointCloud& cloud)
02984   {
02985     XYZPointCloud obj_cloud, table_cloud;
02986     // TODO: Comptue the hull on the first call
02987     Eigen::Vector4f table_centroid;
02988     pcl_segmenter_->getTablePlane(cloud, obj_cloud, table_cloud, table_centroid/*, true*/);
02989     PoseStamped p;
02990     p.pose.position.x = table_centroid[0];
02991     p.pose.position.y = table_centroid[1];
02992     p.pose.position.z = table_centroid[2];
02993     p.header = cloud.header;
02994     ROS_INFO_STREAM("Table centroid is: ("
02995                     << p.pose.position.x << ", "
02996                     << p.pose.position.y << ", "
02997                     << p.pose.position.z << ")");
02998     return p;
02999   }
03000 
03004   void spin()
03005   {
03006     while(n_.ok())
03007     {
03008       ros::spinOnce();
03009     }
03010   }
03011 
03012  protected:
03013   ros::NodeHandle n_;
03014   ros::NodeHandle n_private_;
03015   message_filters::Subscriber<sensor_msgs::Image> image_sub_;
03016   message_filters::Subscriber<sensor_msgs::Image> depth_sub_;
03017   message_filters::Subscriber<sensor_msgs::PointCloud2> cloud_sub_;
03018   message_filters::Synchronizer<MySyncPolicy> sync_;
03019   sensor_msgs::CameraInfo cam_info_;
03020   shared_ptr<tf::TransformListener> tf_;
03021   ros::ServiceServer push_pose_server_;
03022   ros::ServiceServer table_location_server_;
03023   cv::Mat cur_color_frame_;
03024   cv::Mat cur_depth_frame_;
03025   cv::Mat cur_workspace_mask_;
03026   cv::Mat prev_color_frame_;
03027   cv::Mat prev_depth_frame_;
03028   cv::Mat prev_workspace_mask_;
03029   std_msgs::Header cur_camera_header_;
03030   std_msgs::Header prev_camera_header_;
03031   XYZPointCloud cur_point_cloud_;
03032   shared_ptr<PointCloudSegmentation> pcl_segmenter_;
03033   shared_ptr<ObjectSingulation> os_;
03034   bool have_depth_data_;
03035   int crop_min_x_;
03036   int crop_max_x_;
03037   int crop_min_y_;
03038   int crop_max_y_;
03039   int display_wait_ms_;
03040   bool use_displays_;
03041   bool write_input_to_disk_;
03042   std::string base_output_path_;
03043   double min_workspace_x_;
03044   double max_workspace_x_;
03045   double min_workspace_y_;
03046   double max_workspace_y_;
03047   double min_workspace_z_;
03048   double max_workspace_z_;
03049   int num_downsamples_;
03050   std::string workspace_frame_;
03051   PoseStamped table_centroid_;
03052   bool tracking_;
03053   bool tracker_initialized_;
03054   bool camera_initialized_;
03055   std::string cam_info_topic_;
03056   int record_count_;
03057   bool autorun_pcl_segmentation_;
03058   bool use_guided_pushes_;
03059   bool recording_input_;
03060 };
03061 
03062 int main(int argc, char ** argv)
03063 {
03064   int seed = time(NULL);
03065   srand(seed);
03066   std::cout << "Rand seed is: " << seed << std::endl;
03067   ros::init(argc, argv, "object_singulation_node");
03068   ros::NodeHandle n;
03069   ObjectSingulationNode singulation_node(n);
03070   singulation_node.spin();
03071   return 0;
03072 }


tabletop_pushing
Author(s): Tucker Hermans
autogenerated on Wed Nov 27 2013 11:59:44