00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
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 
00050 #include <tf/transform_listener.h>
00051 
00052 
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 
00068 #include <opencv2/core/core.hpp>
00069 #include <opencv2/imgproc/imgproc.hpp>
00070 #include <opencv2/highgui/highgui.hpp>
00071 
00072 
00073 #include <boost/shared_ptr.hpp>
00074 
00075 
00076 #include <cpl_visual_features/helpers.h>
00077 #include <cpl_visual_features/features/edges.h>
00078 
00079 
00080 #include <tabletop_pushing/SingulationPush.h>
00081 #include <tabletop_pushing/LocateTable.h>
00082 #include <tabletop_pushing/point_cloud_segmentation.h>
00083 
00084 
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> 
00094 #include <cstdlib> 
00095 
00096 
00097 #define DISPLAY_INPUT_COLOR 1
00098 
00099 
00100 #define DISPLAY_PROJECTED_OBJECTS 1
00101 
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     
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     
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     
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     
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     
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     
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   
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     
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     
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     
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       
00502       ProtoObjects moved_regions;
00503       pcl_segmenter_->getMovedRegions(prev_objs_down_, cur_objs_down, moved_regions);
00504       
00505       pcl_segmenter_->matchMovedRegions(prev_proto_objs_, moved_regions);
00506       
00507       updateMovedObjs(objs, prev_proto_objs_, use_guided);
00508     }
00509     else
00510     {
00511       
00512       for (unsigned int i = 0; i < objs.size(); ++i)
00513       {
00514         objs[i].id = getNextID();
00515       }
00516       
00517       
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     
00596     std::vector<bool> matched = matchUnmoved(cur_objs, prev_objs);
00597     matchMoved(cur_objs, prev_objs, matched, split_, merged_, use_guided);
00598 
00599     
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           
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             
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     
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         
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         
00707         Eigen::Matrix4f min_transform;
00708         for (unsigned int j = 0; j < cur_objs.size(); ++j)
00709         {
00710           if (!matched[j])
00711           {
00712             
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             
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     
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       
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       
00872       if (objs[i].push_history[bin] == 0)
00873       {
00874         
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     
00900     
00901     
00902     
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     
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     
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     
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     
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     
01105     cv::cvtColor(color_img, tmp_bw, CV_BGR2GRAY);
01106     tmp_bw.convertTo(bw_img, CV_32FC1, 1.0/255);
01107 
01108     
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     
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     
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     
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       
01188       
01189       pcl16::PointXYZ p = cloud.at(b[i].x*upscale_, b[i].y*upscale_);
01190       
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     
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       
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         
01239         if (boundaries[b].points3D.size() >= min_boundary_length_)
01240         {
01241           boundaries[b].too_short = false;
01242           
01243           
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           
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     
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     
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       
01328       
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       
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       
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       
01396       
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       
01512       
01513       
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       
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       
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     
01603     XYZPointCloud moved = po.pushPointCloud(objs[po.object_idx].cloud);
01604     
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     
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     
01631     XYZPointCloud moved = po.pushPointCloud(objs[po.object_idx].cloud);
01632     
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     
01723     
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     
01737     
01738     double push_dist2 = std::sqrt(pcl_segmenter_->sqrDistXY(pts.at(max_idx),
01739                                                             pts.at(min_idx)))+
01740         push_dist_inflation_;
01741     
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     
01787     
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     
01801     
01802     push_dist = std::sqrt(pcl_segmenter_->sqrDistXY(pts.at(max_idx),
01803                                                             pts.at(min_idx)))+
01804         push_dist_inflation_;
01805     
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     
01814     Eigen::Vector4f hessian = splitPlaneVertical(boundary, update_boundary);
01815     
01816     return splitObject3D(hessian, to_split);
01817   }
01818 
01819   ProtoObjects splitObject3D(Eigen::Vector4f& hessian,
01820                              ProtoObject& to_split)
01821   {
01822     
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     
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   
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       
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       
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       
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       
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       
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       
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     
02181     const Eigen::Vector3f x_axis(0.1, 0.0, 0.0);
02182     
02183     
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       
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       
02198       const float start_angle = (-atan2(x_t[1], x_t[0])+ M_PI)*180.0/M_PI;
02199       
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       
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       
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       
02261       const float start_angle = (-atan2(x_t[1], x_t[0])+ M_PI)*180.0/M_PI;
02262       
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       
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     
02317     float rot = start_rot;
02318     for (unsigned int i = 0; i < hist.size(); ++i)
02319     {
02320       
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         
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     
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     
02378     cv::line(disp_img, arc_pts.front(), arc_pts.back(), line_color);
02379 
02380     
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         
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     
02431     float rot = start_rot;
02432     for (unsigned int i = 0; i < hist.size(); ++i)
02433     {
02434       
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         
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     
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     
02492     cv::line(disp_img, arc_pts.front(), arc_pts.back(), line_color);
02493 
02494     
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         
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   
02530   
02531 
02532   bool isInitialized() const { return initialized_; }
02533 
02534   void unInitialize() { initialized_ = false; }
02535 
02536   
02537   
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     
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     
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     
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     
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     
02757     
02758 
02759     
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     
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     
02783     
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         
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     
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     
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     
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     
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     
02835 #ifdef DISPLAY_INPUT_COLOR
02836     if (use_displays_)
02837     {
02838       cv::imshow("color", cur_color_frame_);
02839     }
02840     
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     
02987     Eigen::Vector4f table_centroid;
02988     pcl_segmenter_->getTablePlane(cloud, obj_cloud, table_cloud, table_centroid);
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 }