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 }