00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include <cassert>
00013 #include <limits>
00014 #include <vector>
00015 #include <sstream>
00016
00017 #include <boost/foreach.hpp>
00018 #define foreach BOOST_FOREACH
00019 #include <boost/thread.hpp>
00020
00021 #include "ros/ros.h"
00022 #include "tf/transform_listener.h"
00023 #include "tf/transform_broadcaster.h"
00024 #include "semanticmodel/blob_store.h"
00025 #include "pcl16/point_types.h"
00026 #include "pcl16_ros/point_cloud.h"
00027 #include "pcl16_ros/transforms.h"
00028 #include "pcl16/surface/convex_hull.h"
00029 #include "pcl16/segmentation/extract_clusters.h"
00030 #include "pcl16/segmentation/sac_segmentation.h"
00031 #include "pcl16/filters/extract_indices.h"
00032 #include "pcl16/sample_consensus/method_types.h"
00033 #include "pcl16/sample_consensus/model_types.h"
00034 #include "image_geometry/pinhole_camera_model.h"
00035 #include "opencv2/opencv.hpp"
00036
00037 #include "semanticmodel/centroid.hh"
00038 #include "semanticmodel/miniatureoccupancygrid.hh"
00039 #include "semanticmodel/Blobs.h"
00040 #include "semanticmodel/Segment.h"
00041 #include "semanticmodel/blob.hh"
00042 #include "semanticmodel/GetCollectionNamespace.h"
00043
00044 #include "visualization_msgs/MarkerArray.h"
00045
00046 #include "dynamic_reconfigure/server.h"
00047 #include "semanticmodel/ObjectTrackerConfig.h"
00048
00049 using namespace std;
00050 using visualization_msgs::Marker;
00051 using visualization_msgs::MarkerArray;
00052 using semanticmodel::Blob;
00053 using semanticmodel::Blobs;
00054 using semanticmodel::Segment;
00055 using semanticmodel::GetCollectionNamespace;
00056 using sensor_msgs::PointCloud2;
00057 using semanticmodel::MiniatureOccupancyGrid;
00058
00059 typedef pcl16::PointXYZRGB Point;
00060 typedef pcl16::PointCloud<Point> PointCloud;
00061
00062 semanticmodel::ObjectTrackerConfig config;
00063 void config_cb(const semanticmodel::ObjectTrackerConfig& _config, uint32_t i)
00064 {
00065
00066 config = _config;
00067 }
00068
00069
00070 vector<Blob*> objects;
00071
00072
00073 boost::shared_ptr<semanticmodel::BlobStore> blob_store;
00074
00075
00076
00077 MiniatureOccupancyGrid OG(0.05);
00078
00079
00080
00081
00082 vector<int> colors;
00083
00084 cv::RNG prng(2);
00085
00086
00087 ros::Subscriber cluster_sub;
00088 ros::Subscriber full_cloud_sub;
00089 ros::Publisher all_objects_pub;
00090 ros::Publisher flat_frames_publisher;
00091 ros::Publisher viz_pub;
00092 ros::Publisher og_pub;
00093
00094
00095 boost::shared_ptr<tf::TransformListener> listener;
00096 boost::shared_ptr<tf::TransformBroadcaster> broadcaster;
00097
00098 void push_one_frame_segment(const Blob* blob,
00099 const sensor_msgs::Image& rgb_img,
00100 const sensor_msgs::CameraInfo& rgb_info,
00101 const sensor_msgs::Image& depth_img,
00102 const sensor_msgs::CameraInfo& depth_info);
00103
00104
00105 void cluster_objects(const PointCloud::ConstPtr& aboves,
00106 std::vector<PointCloud::Ptr>& clusters)
00107 {
00108 if (!aboves)
00109 return;
00110 if (aboves->points.size() < 100)
00111 return;
00112 if (!ros::ok())
00113 return;
00114
00115 clusters.clear();
00116 pcl16::search::KdTree<Point>::Ptr tree(new pcl16::search::KdTree<Point>());
00117 tree->setInputCloud(aboves);
00118
00119 pcl16::EuclideanClusterExtraction<Point> euclid;
00120 std::vector<pcl16::PointIndices> indices;
00121 euclid.setClusterTolerance(config.cluster_tolerance);
00122 euclid.setMinClusterSize(config.min_cluster_size);
00123 euclid.setMaxClusterSize(config.max_cluster_size);
00124 euclid.setSearchMethod(tree);
00125 euclid.setInputCloud(aboves);
00126 ROS_DEBUG_NAMED("cluster",
00127 "About to cluster %zu points", aboves->points.size());
00128 euclid.extract(indices);
00129
00130
00131 pcl16::ExtractIndices<Point> extractor;
00132 foreach(pcl16::PointIndices& index, indices)
00133 {
00134 if (!ros::ok())
00135 return;
00136 PointCloud::Ptr new_cluster(new PointCloud());
00137 extractor.setInputCloud(aboves);
00138 pcl16::PointIndices::Ptr indexptr(new pcl16::PointIndices(index));
00139 extractor.setIndices(indexptr);
00140 extractor.filter(*new_cluster);
00141 clusters.push_back(new_cluster);
00142 }
00143 }
00144
00145 void deplane_objects(std::vector<PointCloud::Ptr>& clusters)
00146 {
00147 if (!ros::ok())
00148 return;
00149
00150 std::vector<PointCloud::Ptr> newclusters;
00151
00152
00153 pcl16::SACSegmentation<Point> segmenter;
00154 segmenter.setOptimizeCoefficients(true);
00155 segmenter.setModelType(pcl16::SACMODEL_PLANE);
00156 segmenter.setMethodType(pcl16::SAC_RANSAC);
00157 segmenter.setDistanceThreshold(config.plane_tolerance);
00158 segmenter.setMaxIterations(config.object_ransac_iterations);
00159
00160 foreach(PointCloud::Ptr& cluster, clusters)
00161 {
00162 if (cluster->points.size() == 0)
00163 continue;
00164 if (!ros::ok())
00165 return;
00166
00167 pcl16::ModelCoefficients::Ptr model(new pcl16::ModelCoefficients());
00168 pcl16::PointIndices::Ptr indices(new pcl16::PointIndices());
00169 segmenter.setInputCloud(cluster);
00170 segmenter.segment(*indices, *model);
00171
00172 double fraction = (float)indices->indices.size() / cluster->points.size();
00173
00174
00175
00176
00177
00178
00179
00180
00181 if (fraction <= config.object_plane_percentage)
00182 {
00183 newclusters.push_back(cluster);
00184 ROS_DEBUG_NAMED("object_tracker", "Kept cluster with %zu points.",
00185 indices->indices.size());
00186 }
00187 else
00188 {
00189 ROS_DEBUG_NAMED("object_tracker", "Rejected cluster with %zu points.",
00190 indices->indices.size());
00191 }
00192 }
00193
00194
00195 std::swap(clusters, newclusters);
00196 }
00197
00198
00199 void fc_cb(const PointCloud::ConstPtr& input)
00200 {
00201 if (!ros::ok())
00202 return;
00203
00204 PointCloud::Ptr canonical(new PointCloud());
00205 if (!pcl16_ros::transformPointCloud(config.canonical_frame,
00206 *input,
00207 *canonical,
00208 *listener))
00209 return;
00210
00211
00212 boost::scoped_ptr<boost::mutex::scoped_lock> lock(OG.get_lock());
00213 foreach(const Point& p, *input)
00214 {
00215
00216 if (p.z >= config.occ_trick_low && p.z <= config.occ_trick_high)
00217 {
00218 OG.set(p.x, p.y, MiniatureOccupancyGrid::OCCUPIED);
00219 }
00220 }
00221 nav_msgs::OccupancyGrid og;
00222 OG.to_msg(og);
00223 og_pub.publish(og);
00224 }
00225
00226
00227
00228
00229 void occupancy_trick(PointCloud::Ptr& input)
00230 {
00231 if (!ros::ok())
00232 return;
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242 PointCloud::Ptr output(new PointCloud());
00243 output->header = input->header;
00244
00245
00246 boost::scoped_ptr<boost::mutex::scoped_lock> lock(OG.get_lock());
00247 foreach(const Point& p, *input)
00248 {
00249 if (OG.get(p.x, p.y) == MiniatureOccupancyGrid::EMPTY)
00250 output->points.push_back(p);
00251 }
00252
00253 std::swap(input, output);
00254 }
00255
00256 void blobs_cb(const Blobs::ConstPtr& blobs)
00257 {
00258
00259 ROS_ASSERT(blobs->blobs.size() == 1);
00260
00261
00262 PointCloud pcl16_cloud;
00263 pcl16::fromROSMsg(blobs->blobs[0], pcl16_cloud);
00264
00265
00266 PointCloud::Ptr cluster(new PointCloud());
00267 pcl16_ros::transformPointCloud(config.canonical_frame,
00268 pcl16_cloud,
00269 *cluster,
00270 *listener);
00271
00272
00273
00274
00275
00276 occupancy_trick(cluster);
00277 if (cluster->points.size() == 0 || !ros::ok())
00278 {
00279 ROS_INFO_NAMED("object_tracker", "Occupancy trick erased everything.");
00280 return;
00281 }
00282
00283
00284 vector<PointCloud::Ptr> clusters;
00285 cluster_objects(cluster, clusters);
00286 if (clusters.size() == 0 || !ros::ok())
00287 {
00288 ROS_INFO_NAMED("object_tracker", "Clustering went nowhere.");
00289 return;
00290 }
00291
00292
00293 deplane_objects(clusters);
00294 if (clusters.size() == 0 || !ros::ok())
00295 {
00296 ROS_INFO_NAMED("object_tracker", "Deplaning erased everything.");
00297 return;
00298 }
00299
00300 foreach(PointCloud::Ptr cluster, clusters)
00301 {
00302
00303 Blob* newblob = new Blob(cluster);
00304
00305 push_one_frame_segment(newblob,
00306 blobs->rgb_image,
00307 blobs->rgb_info,
00308 blobs->depth_image,
00309 blobs->depth_info);
00310
00311
00312 Blob::MergeBlobWithSet(objects, newblob);
00313 }
00314
00315
00316 if (!ros::ok())
00317 return;
00318
00319
00320
00321
00322 int id = 50;
00323 foreach(Blob* blob, objects)
00324 {
00325 Marker marker;
00326 marker.header.frame_id = blob->cloud->header.frame_id;
00327 marker.header.stamp = ros::Time::now();
00328 marker.id = id;
00329 marker.ns = "objects";
00330 marker.type = Marker::CUBE;
00331 marker.action = Marker::ADD;
00332
00333 Eigen::Vector4f center(semanticmodel::centroid(blob->cloud));
00334 marker.pose.position.x = center[0];
00335 marker.pose.position.y = center[1];
00336 marker.pose.position.z = center[2];
00337 marker.pose.orientation.x = 0.0;
00338 marker.pose.orientation.y = 0.0;
00339 marker.pose.orientation.z = 0.0;
00340 marker.pose.orientation.w = 1.0;
00341 blob->size(marker.scale.x, marker.scale.y, marker.scale.z);
00342 marker.color.a = 0.75;
00343 blob->RGB(marker.color.r, marker.color.g, marker.color.b);
00344 id++;
00345 if (config.send_boxes)
00346 viz_pub.publish(marker);
00347
00348 tf::Transform transform;
00349 transform.setOrigin(tf::Vector3(center[0], center[1], center[2]));
00350 transform.setRotation(tf::Quaternion(0, 0, 1.0, 0.0));
00351 std::string name = (boost::format("object_%02d") % blob->id).str();
00352 tf::StampedTransform trans(transform,
00353 blob->cloud->header.stamp,
00354 config.canonical_frame,
00355 name);
00356 broadcaster->sendTransform(trans);
00357 }
00358
00359
00360 while (colors.size() < 3 * objects.size())
00361 colors.push_back(int(prng.uniform(0.0, 1.0) * 255));
00362
00363
00364 blob_store->update(objects, blobs->rgb_image, blobs->rgb_info);
00365
00366
00367 if (objects.size() == 0 || !ros::ok())
00368 return;
00369
00370 PointCloud::Ptr all_objects(new PointCloud());
00371 all_objects->header.frame_id = objects.at(0)->cloud->header.frame_id;
00372 foreach(Blob* blob, objects)
00373 {
00374 (*all_objects) += *(blob->cloud);
00375 }
00376 all_objects_pub.publish(all_objects);
00377 }
00378
00379 int main(int argc, char** argv)
00380 {
00381 ros::init(argc, argv, "object_tracker");
00382 ros::NodeHandle node("~");
00383
00384 cluster_sub = node.subscribe<Blobs>("blobs", 100, blobs_cb);
00385 full_cloud_sub = node.subscribe<PointCloud>("/map_points", 100, fc_cb);
00386 all_objects_pub = node.advertise<PointCloud>("all_objects_cloud", 100, true);
00387 viz_pub = node.advertise<visualization_msgs::Marker>(
00388 "visualization_marker", 0);
00389
00390 flat_frames_publisher = node.advertise<Segment>("/flat_frames", 10);
00391 og_pub = node.advertise<nav_msgs::OccupancyGrid>("/og", 100);
00392 std::string camera_frame;
00393 bool found = node.getParam("camera_frame", camera_frame);
00394 ROS_ASSERT(found);
00395
00396
00397 dynamic_reconfigure::Server<semanticmodel::ObjectTrackerConfig> srv;
00398 dynamic_reconfigure::Server<semanticmodel::ObjectTrackerConfig>::
00399 CallbackType f;
00400 f = boost::bind(&config_cb, _1, _2);
00401 srv.setCallback(f);
00402
00403 ROS_INFO_STREAM("Waiting for service get_db_name");
00404 GetCollectionNamespace coll_ns_srv;
00405 ros::service::waitForService("get_collection_namespace");
00406 ros::service::call("get_collection_namespace", coll_ns_srv);
00407 ROS_INFO_STREAM ("Db name is " << coll_ns_srv.response.db_name << "/"
00408 << coll_ns_srv.response.collection_namespace);
00409 blob_store.reset(
00410 new semanticmodel::BlobStore(config.canonical_frame,
00411 camera_frame,
00412 coll_ns_srv.response.db_name,
00413 coll_ns_srv.response.collection_namespace));
00414
00415 listener.reset(new tf::TransformListener(ros::Duration(600)));
00416 broadcaster.reset(new tf::TransformBroadcaster());
00417 ros::spin();
00418
00419
00420 foreach(Blob* blob, objects)
00421 delete blob;
00422
00423 return 0;
00424 }
00425
00426 void push_one_frame_segment(const Blob* blob,
00427 const sensor_msgs::Image& rgb_img,
00428 const sensor_msgs::CameraInfo& rgb_info,
00429 const sensor_msgs::Image& depth_img,
00430 const sensor_msgs::CameraInfo& depth_info)
00431 {
00432
00433 Segment seg;
00434 seg.rgb_image = rgb_img;
00435 seg.rgb_info = rgb_info;
00436 seg.depth_image = depth_img;
00437 seg.depth_info = depth_info;
00438
00439
00440 image_geometry::PinholeCameraModel pcm;
00441 pcm.fromCameraInfo(rgb_info);
00442 PointCloud::Ptr camcloud(new PointCloud());
00443 pcl16_ros::transformPointCloud(rgb_img.header.frame_id,
00444 rgb_img.header.stamp,
00445 *(blob->cloud),
00446 blob->cloud->header.frame_id,
00447 *camcloud,
00448 *listener);
00449
00450 for (size_t i = 0 ; i < camcloud->size() ; ++i)
00451 {
00452 cv::Point3d cvpt(camcloud->points[i].x,
00453 camcloud->points[i].y,
00454 camcloud->points[i].z);
00455 cv::Point2d pt = pcm.project3dToPixel(cvpt);
00456 if (pt.x >= 0 && pt.x < rgb_img.width &&
00457 pt.y >= 0 && pt.y < rgb_img.height)
00458 {
00459 seg.coordinates.push_back(pt.y);
00460 seg.coordinates.push_back(pt.x);
00461 }
00462 }
00463 flat_frames_publisher.publish(seg);
00464 }