ShapeReconstruction.cpp
Go to the documentation of this file.
00001 #include "shape_reconstruction/ShapeReconstruction.h"
00002 
00003 #include <pcl/conversions.h>
00004 #include <pcl/io/pcd_io.h>
00005 #include <pcl/filters/filter_indices.h>
00006 #include <sensor_msgs/Image.h>
00007 
00008 #include <opencv2/imgproc/imgproc.hpp>
00009 #include <opencv2/video/tracking.hpp>
00010 #include <opencv2/highgui/highgui.hpp>
00011 #include <opencv2/video/tracking.hpp>
00012 
00013 #include <pcl_conversions/pcl_conversions.h>
00014 
00015 #include <ros/package.h>
00016 
00017 #include <cmath>
00018 
00019 #include <ros/console.h>
00020 
00021 #include "shape_reconstruction/RangeImagePlanar.hpp"
00022 
00023 #include "shape_reconstruction/Passthrough.hpp"
00024 
00025 #include <pcl/point_cloud.h>
00026 #include <pcl/point_types.h>
00027 #include <pcl/io/pcd_io.h>
00028 #include <pcl/visualization/pcl_visualizer.h>
00029 #include <pcl/filters/passthrough.h>
00030 
00031 #include <boost/lexical_cast.hpp>
00032 #include <sensor_msgs/image_encodings.h>
00033 
00034 #include <pcl/point_types_conversion.h>
00035 
00036 #include <pcl/registration/icp.h>
00037 
00038 #include <pcl/common/centroid.h>
00039 
00040 #include <pcl/surface/poisson.h>
00041 #include <pcl/surface/mls.h>
00042 
00043 #include <math.h>
00044 
00045 using namespace omip;
00046 
00047 ShapeReconstruction::ShapeReconstruction()
00048     :
00049       _accumulate_change_candidates(true),
00050       _min_depth_change(0.08),
00051       _remove_inconsistent_points(true),
00052       _t(0),
00053       _extend_to_neighbor_sv(false),
00054       _acc_candidates_of_current(IMG_HEIGHT, IMG_WIDTH, CV_8UC1),
00055       _similarity_in_normal(0),
00056       _similarity_in_h(0)
00057 {
00058     // Create auxiliary depth map image
00059     this->_acc_candidates_of_current.setTo(0);
00060 
00061     // Create range image planar object
00062     this->_rip_temp = pcl::RangeImagePlanar::Ptr(new pcl::RangeImagePlanar());
00063 
00064     // Create indices
00065     this->_moving_pts_of_current_idx_depth = pcl::PointIndices::Ptr(new pcl::PointIndices ());
00066     this->_moving_pts_of_previous_idx_depth = pcl::PointIndices::Ptr(new pcl::PointIndices ());
00067     this->_out_of_view_pts_of_current_idx= pcl::PointIndices::Ptr(new pcl::PointIndices ());
00068     this->_occluded_pts_of_current_idx= pcl::PointIndices::Ptr(new pcl::PointIndices ());
00069 
00070     this->_moving_pts_of_current_idx_color = pcl::PointIndices::Ptr(new pcl::PointIndices ());
00071     this->_moving_pts_of_previous_idx_color = pcl::PointIndices::Ptr(new pcl::PointIndices ());
00072 
00073     this->_knn = pcl::search::Search<omip::SRPoint>::Ptr(new pcl::search::KdTree<omip::SRPoint>);
00074 
00075     this->_rb_shape  =  omip::SRPointCloud::Ptr(new omip::SRPointCloud);
00076     this->_rb_shape->height=1;
00077 
00078     this->_supporting_features = pcl::PointCloud<pcl::PointXYZL>::Ptr(new pcl::PointCloud<pcl::PointXYZL>());
00079 
00080     this->_candidates = SRPointCloud::Ptr(new SRPointCloud);
00081     this->_candidates->height=1;
00082 
00083     this->_candidates_in_current = SRPointCloud::Ptr(new SRPointCloud);
00084 
00085     this->_rb_segment.reset(new SRPointCloud);
00086 
00090     this->_rb_polygon_data_ptr = vtkSmartPointer<vtkPolyData>::New ();
00091     this->_rb_normal_estimator_ptr.reset(new pcl::NormalEstimationOMP<omip::SRPoint, pcl::Normal>());
00092     this->_rb_normal_estimator_ptr->setKSearch (50);
00093     this->_rb_estimated_normals_pc_ptr.reset(new pcl::PointCloud<pcl::Normal>());
00094     this->_rb_tree_for_normal_estimation_ptr.reset(new pcl::search::KdTree<omip::SRPoint>());
00095     this->_rb_position_color_and_normals_pc_ptr.reset(new pcl::PointCloud<pcl::PointXYZRGBNormal>());
00096     this->_rb_tree_for_triangulation_ptr.reset(new pcl::search::KdTree<pcl::PointXYZRGBNormal>());
00097     this->_rb_greedy_projection_triangulator_ptr.reset(new pcl::GreedyProjectionTriangulation<pcl::PointXYZRGBNormal>());
00098     // Set typical values for the parameters
00099     this->_rb_greedy_projection_triangulator_ptr->setMu (2.5);
00100     this->_rb_greedy_projection_triangulator_ptr->setMaximumNearestNeighbors (100);
00101     this->_rb_greedy_projection_triangulator_ptr->setMaximumSurfaceAngle(M_PI); // 180 degrees
00102     this->_rb_greedy_projection_triangulator_ptr->setMinimumAngle(0.0); // 5 degrees
00103     this->_rb_greedy_projection_triangulator_ptr->setMaximumAngle(M_PI); // 180 degrees
00104     this->_rb_greedy_projection_triangulator_ptr->setNormalConsistency(true);
00105     this->_rb_greedy_projection_triangulator_ptr->setConsistentVertexOrdering(true);
00106     // Set the maximum distance between connected points (maximum edge length)
00107     this->_rb_greedy_projection_triangulator_ptr->setSearchRadius (0.1);
00108     this->_rb_polygon_writer_ptr = vtkSmartPointer<vtkSTLWriter>::New ();
00109     this->_rb_polygon_writer_ptr->SetFileTypeToBinary();
00110     this->_rb_triangulated_mesh_ptr.reset(new pcl::PolygonMesh());
00111 }
00112 
00113 void ShapeReconstruction::setInitialFullRGBDPCAndRBT(const SRPointCloud::Ptr &initial_pc_msg,
00114                                                      const geometry_msgs::TwistWithCovariance &rb_transformation_initial)
00115 {
00116     // Copy the initial point cloud
00117     pcl::copyPointCloud(*initial_pc_msg, *this->_initial_ffs._pc);
00118 
00119     // Get time stamp of the initial point cloud
00120     this->_initial_ffs._time.fromNSec(initial_pc_msg->header.stamp);
00121 
00122     // Clean the point cloud of nans
00123     pcl::removeNaNFromPointCloud<SRPoint>(*this->_initial_ffs._pc,*this->_initial_ffs._pc_without_nans, this->_initial_ffs._not_nan_indices);
00124 
00125     // Get the RGB image from the point cloud
00126     pcl::toROSMsg (*initial_pc_msg, this->_initial_ffs._rgb); //convert the cloud
00127 
00128     // Get the initial transformation
00129     this->_initial_ffs._transformation = geometry_msgs::TwistWithCovariance(rb_transformation_initial);
00130 
00131     // Get the depth map from the point cloud
00132     OrganizedPC2DepthMap(this->_initial_ffs._pc, this->_initial_ffs._dm->image);
00133 
00134     if(this->_depth_filling)
00135     {
00136         fillNaNsCBF(this->_initial_ffs._rgb, this->_initial_ffs._dm->image, this->_initial_ffs._dm_filled, this->_initial_ffs._time);
00137     }
00138 }
00139 
00140 void ShapeReconstruction::initialize()
00141 {
00142     std::stringstream pub_topic_name_ext_d_and_c;
00143     pub_topic_name_ext_d_and_c << "/shape_recons/shape_rb" << this->_rb_id;
00144     this->_rb_shape_pub = this->_node_handle.advertise<sensor_msgs::PointCloud2>(pub_topic_name_ext_d_and_c.str(), 1);
00145 
00146     std::stringstream pub_segment_topic_name;
00147     pub_segment_topic_name << "/shape_recons/segment_rb" << this->_rb_id;
00148     this->_rb_segment_pub = this->_node_handle.advertise<sensor_msgs::PointCloud2>(pub_segment_topic_name.str(), 1);
00149 
00150     std::stringstream pub_occlusion_topic_name;
00151     pub_occlusion_topic_name << "/shape_recons/pc_occlusions_rb" << this->_rb_id;
00152     this->_occlusions_pub = this->_node_handle.advertise<sensor_msgs::PointCloud2>(pub_occlusion_topic_name.str(), 1);
00153 
00154     std::stringstream pub_occlusion_t_topic_name;
00155     pub_occlusion_t_topic_name << "/shape_recons/pc_occlusions_transf_rb" << this->_rb_id;
00156     this->_occlusions_transformed_pub = this->_node_handle.advertise<sensor_msgs::PointCloud2>(pub_occlusion_t_topic_name.str(), 1);
00157 
00158     this->_approximate_voxel_grid_filter.setLeafSize(this->_leaf_size,
00159                                                      this->_leaf_size,
00160                                                      this->_leaf_size);
00161 
00162     // This filter should delete single super voxels. If the properties of the super voxels
00163     // change, or the leaf size of the voxel grid filter change, the radius and/or the min neighbors of
00164     // the outlier removal should change
00165     // set radius for neighbor search
00166     this->_radius_outlier_removal.setRadiusSearch (this->_ror_radius_search);
00167     // set threshold for minimum required neighbors neighbors
00168     this->_radius_outlier_removal.setMinNeighborsInRadius (this->_ror_min_neighbors);
00169 
00170     if(this->_record_videos)
00171     {
00172         std::string videos_path = ros::package::getPath("shape_reconstruction") + std::string("/videos/");
00173 
00174         // create folder if does not exist
00175         boost::filesystem::path videos_folder(videos_path);
00176         if (!boost::filesystem::exists(videos_folder)) {
00177             if (!boost::filesystem::create_directories(videos_folder)) {
00178                 ROS_ERROR_NAMED("ShapeReconstruction.ShapeReconstruction", "Output directory for video bag does not exist and cannot be created!");
00179                 return;
00180             }
00181         }
00182 
00183         this->_videos.open(videos_path + std::string("videos_rb") + boost::lexical_cast<std::string>(this->_rb_id)+std::string(".bag"),rosbag::bagmode::Write);
00184 
00185         sensor_msgs::ImagePtr initial_dm_msg;
00186         DepthImage2CvImage(this->_initial_ffs._dm->image, initial_dm_msg);
00187         this->_videos.write("original_dm", this->_initial_ffs._time, initial_dm_msg);
00188 
00189         if(this->_depth_filling)
00190         {
00191             sensor_msgs::ImagePtr initial_dm_filled_msg;
00192             DepthImage2CvImage(this->_initial_ffs._dm_filled, initial_dm_filled_msg);
00193             this->_videos.write("filled_dm", this->_initial_ffs._time, initial_dm_filled_msg);
00194         }
00195     }
00196 }
00197 
00198 ShapeReconstruction::ShapeReconstruction(const ShapeReconstruction &sr)
00199 {
00200     ROS_ERROR_STREAM_NAMED("ShapeReconstruction.ShapeReconstruction", "Do not use this copy constructor! It is not complete!");
00201 
00202     this->_rb_id = sr._rb_id;
00203 
00204     this->_to_initial = sr._to_initial;
00205     this->_depth_filling = sr._depth_filling;
00206 
00207     this->_initial_ffs = sr._initial_ffs.clone();
00208     this->_previous_ffs = sr._previous_ffs.clone();
00209     this->_current_ffs = sr._current_ffs.clone();
00210 
00211     this->_rip_temp = pcl::RangeImagePlanar::Ptr(new pcl::RangeImagePlanar(*sr._rip_temp));
00212 
00213     this->_moving_pts_of_current_idx_depth = pcl::PointIndices::Ptr(new pcl::PointIndices(*sr._moving_pts_of_current_idx_depth));
00214     this->_moving_pts_of_previous_idx_depth = pcl::PointIndices::Ptr(new pcl::PointIndices(*sr._moving_pts_of_previous_idx_depth));
00215     this->_out_of_view_pts_of_current_idx = pcl::PointIndices::Ptr(new pcl::PointIndices(*sr._out_of_view_pts_of_current_idx));
00216     this->_occluded_pts_of_current_idx = pcl::PointIndices::Ptr(new pcl::PointIndices(*sr._occluded_pts_of_current_idx));
00217 
00218 }
00219 
00220 ShapeReconstruction::~ShapeReconstruction()
00221 {
00222     if(this->_record_videos)
00223     {
00224         _videos.close();
00225     }
00226 }
00227 
00228 void ShapeReconstruction::setCameraInfo(const sensor_msgs::CameraInfo& camera_info)
00229 {
00230     this->_ci = sensor_msgs::CameraInfo(camera_info);
00231 }
00232 
00233 void ShapeReconstruction::setFullRGBDPCandRBT(const SRPointCloud::Ptr &pc_msg,
00234                                               const geometry_msgs::TwistWithCovariance &rb_transformation)
00235 {
00236     ros::Time t1 = ros::Time::now();
00237 
00238     _previous_k_indices.clear();
00239     _current_k_indices.clear();
00240     // t=0 means first point cloud AFTER initial received
00241     ROS_WARN_STREAM_NAMED("ShapeReconstruction.setFullRGBDPCandRBT", "[RB" << this->_rb_id << "]: t = " << _t);
00242 
00243 
00244     if(this->_current_ffs._pc->points.size() > 0 )
00245     {
00246         this->_previous_ffs = this->_current_ffs;
00247     }else{
00248         this->_previous_ffs = this->_initial_ffs;
00249     }
00250     this->_current_ffs.reset();
00251 
00252     ros::Time t2 = ros::Time::now();
00253 
00254     pcl::copyPointCloud(*pc_msg, *this->_current_ffs._pc);
00255 
00256     ros::Time t3 = ros::Time::now();
00257 
00258     // Clean the point cloud of nans
00259     this->_current_ffs._not_nan_indices.clear();
00260     pcl::removeNaNFromPointCloud<SRPoint>(*this->_current_ffs._pc,*this->_current_ffs._pc_without_nans, this->_current_ffs._not_nan_indices);
00261 
00262     ros::Time t4 = ros::Time::now();
00263 
00264     ROS_WARN_STREAM_NAMED("ShapeReconstruction.setFullRGBDPCandRBT", "[RB" << this->_rb_id << "]: " << "Point cloud time stamp: "
00265                           <<this->_current_ffs._pc->header.stamp <<  ", not nan points "
00266                           << this->_current_ffs._not_nan_indices.size() << "/" << this->_current_ffs._pc_without_nans->size());
00267 
00268 
00269     this->_current_ffs._time = pcl_conversions::fromPCL(pc_msg->header.stamp);
00270     pcl::toROSMsg (*pc_msg, this->_current_ffs._rgb); //convert the cloud
00271     this->_current_ffs._transformation = geometry_msgs::TwistWithCovariance(rb_transformation);
00272 
00273     ros::Time t5 = ros::Time::now();
00274 
00275     // Create a depth map from the current organized depth map
00276     OrganizedPC2DepthMap(this->_current_ffs._pc, this->_current_ffs._dm->image);
00277 
00278     ros::Time t6 = ros::Time::now();
00279 
00280     // -----------------------------------------------------------
00281     // main algorithm
00282 
00283     if(this->_depth_filling)
00284     {
00285         fillNaNsCBF(this->_current_ffs._rgb, this->_current_ffs._dm->image, this->_current_ffs._dm_filled, this->_current_ffs._time);
00286         this->_current_ffs._dm->image.copyTo(this->_current_ffs._dm_filled, this->_current_ffs._dm->image == this->_current_ffs._dm->image);
00287     }
00288 
00289     ros::Time t7 = ros::Time::now();
00290 
00291     if(this->_record_videos)
00292     {
00293         sensor_msgs::ImagePtr current_dm_msg;
00294         DepthImage2CvImage(this->_current_ffs._dm->image, current_dm_msg);
00295         this->_videos.write("original_dm", this->_current_ffs._time, current_dm_msg);
00296 
00297         if(this->_depth_filling)
00298         {
00299             sensor_msgs::ImagePtr current_dm_filled_msg;
00300             DepthImage2CvImage(this->_current_ffs._dm_filled, current_dm_filled_msg);
00301             this->_videos.write("filled_dm", this->_current_ffs._time, current_dm_filled_msg);
00302         }
00303     }
00304 
00305     // Segmentation method 1: motion segmentation based on DEPTH (memoryless)
00306     this->_DetectImageLocationsWhereDepthChanges();
00307     ros::Time t8 = ros::Time::now();
00308     this->_TestMotionCoherencyOfPointsInImageLocationsWhereDepthChanged();
00309     ros::Time t9 = ros::Time::now();
00310 
00311     // Segmentation method 2: motion segmentation based on COLOR (memoryless)
00312     this->_DetectImageLocationsWhereColorChanges();
00313     ros::Time t10 = ros::Time::now();
00314     this->_TestMotionCoherencyOfPointsInImageLocationsWhereColorChanged();
00315     ros::Time t11 = ros::Time::now();
00316 
00317     // Merge the segmentation results into the model(s)
00318     this->_MergeValidPointsIntoModels();
00319 
00320     ros::Time t12 = ros::Time::now();
00321 
00322     this->_RemoveInconsistentPointsFromModelsAndExtendToRegions();
00323 
00324     ros::Time t12b = ros::Time::now();
00325 
00326     this->_FilterModel();
00327 
00328     ros::Time t13 = ros::Time::now();
00329 
00330     _t++;
00331 
00332 //    std::cout << "Preprocessing: " << (t7-t1).toSec() << std::endl;
00333 //    std::cout << "Depth-based segmentation: " << (t9-t7).toSec() << std::endl;
00334 //    std::cout << "Color-based segmentation: " << (t11-t9).toSec() << std::endl;
00335 //    std::cout << "Remove inconsistent points and extend to regions: " << (t12b-t12).toSec() << std::endl;
00336 //    std::cout << "Filter model: " << (t13-t12b).toSec() << std::endl;
00337 //    std::cout << "Total processing RGBD frame (one RB): " << (t13-t1).toSec() << std::endl;
00338 }
00339 
00340 void ShapeReconstruction::_DetectImageLocationsWhereDepthChanges()
00341 {
00342     // Find the difference
00343     if(this->_depth_filling)
00344     {
00345         this->_difference_in_depth = this->_current_ffs._dm_filled - this->_previous_ffs._dm_filled;
00346     }else{
00347         this->_difference_in_depth = this->_current_ffs._dm->image - this->_previous_ffs._dm->image;
00348     }
00349 
00350     if(this->_record_videos)
00351     {
00352         sensor_msgs::ImagePtr dm_msg;
00353         DepthImage2CvImage(_difference_in_depth, dm_msg);
00354         this->_videos.write("changes_in_depth", this->_current_ffs._time, dm_msg);
00355     }
00356 
00357 
00358     // If the difference is negative (under some threshold) that means that the object occludes now what was behind and visible before
00359     // This part of the CURRENT depth map (of the CURRENT point cloud) should be added to the candidates of the RB
00360     cv::threshold(this->_difference_in_depth, this->_candidates_of_current, -_min_depth_change, 255, cv::THRESH_BINARY_INV);
00361 
00362     if(this->_record_videos)
00363     {
00364         sensor_msgs::ImagePtr dm_msg;
00365         DepthImage2CvImage(_candidates_of_current, dm_msg);
00366         this->_videos.write("candidates_of_current", this->_current_ffs._time, dm_msg);
00367     }
00368 
00369     // If the difference is positive (over some threshold) that means that the object is now not occluding what it was occluding before
00370     // This part of the PREVIOUS depth map (of the PREVIOUS point cloud) should be added to the candidates of the RB
00371     cv::threshold(this->_difference_in_depth, this->_candidates_of_previous, _min_depth_change, 255, cv::THRESH_BINARY);
00372 
00373     if(this->_record_videos)
00374     {
00375         sensor_msgs::ImagePtr dm_msg;
00376         DepthImage2CvImage(_candidates_of_previous, dm_msg);
00377         this->_videos.write("candidates_of_previous", this->_current_ffs._time, dm_msg);
00378     }
00379 
00380     this->_previous_depth_mask = this->_previous_ffs._dm->image > 1.5 | this->_previous_ffs._dm->image < 0.3 | this->_previous_ffs._dm->image != this->_previous_ffs._dm->image;
00381     this->_current_depth_mask = this->_current_ffs._dm->image > 1.5 | this->_current_ffs._dm->image < 0.3 | this->_current_ffs._dm->image != this->_current_ffs._dm->image;
00382 
00383     this->_candidates_of_current.convertTo(this->_candidates_of_current_8u, CV_8U);
00384     this->_candidates_of_previous.convertTo(this->_candidates_of_previous_8u, CV_8U);
00385 
00386     // accumulate candidates
00387     this->_acc_candidates_of_current = _acc_candidates_of_current | this->_candidates_of_current_8u;
00388     this->_acc_candidates_of_current.setTo(0,this->_candidates_of_previous_8u);
00389 
00390     if(this->_record_videos)
00391     {
00392         sensor_msgs::ImagePtr dm_msg;
00393         DepthImage2CvImage(_acc_candidates_of_current, dm_msg);
00394         this->_videos.write("acc_candidates_of_current", this->_current_ffs._time, dm_msg);
00395     }
00396 
00397     if (!this->_depth_filling)
00398     {
00399         ROS_INFO_STREAM_NAMED("ShapeReconstruction._DetectImageLocationsWhereDepthChanges",  "[RB" << this->_rb_id << "]: Removing nan points from accumulator");
00400         this->_acc_candidates_of_current.setTo(0, this->_current_ffs._dm->image != this->_current_ffs._dm->image);
00401     }
00402 
00403     if(this->_accumulate_change_candidates)
00404     {
00405         this->_acc_candidates_of_current.copyTo(this->_candidates_of_current_8u);
00406     }
00407 
00408     this->_candidates_of_current_8u.setTo(0, this->_current_depth_mask);
00409     this->_candidates_of_previous_8u.setTo(0, this->_previous_depth_mask);
00410 
00411     this->_moving_pts_of_current_idx_depth->indices.clear();
00412     this->_moving_pts_of_previous_idx_depth->indices.clear();
00413 
00414     Image8u2Indices(this->_candidates_of_current_8u, this->_moving_pts_of_current_idx_depth);
00415     Image8u2Indices(this->_candidates_of_previous_8u, this->_moving_pts_of_previous_idx_depth);
00416 }
00417 
00418 void ShapeReconstruction::_TestMotionCoherencyOfPointsInImageLocationsWhereDepthChanged()
00419 {
00420     // Estimate the transformations from the twists
00421     this->_EstimateTransformations();
00422 
00423     ROS_INFO_STREAM_NAMED("ShapeReconstruction._TestMotionCoherencyOfPointsInImageLocationsWhereDepthChanged",
00424                           "[RB" << this->_rb_id << "]: Estimating which of the points that changed DEPTH moved coherently with the RB motion.");
00425 
00426     // Filter the previous point cloud, transform only the filtered points and find the nearest neighbors in the current point cloud
00427     this->_sqrt_dist.clear();
00428     this->_FindCandidatesInPreviousPC(this->_previous_ffs,
00429                                       this->_current_ffs,
00430                                       this->_moving_pts_of_previous_idx_depth,
00431                                       this->_current_to_previous_HTransform,
00432                                       this->_current_k_indices,
00433                                       this->_sqrt_dist);
00434 
00435     // Filter the current point cloud, transform only the filtered points and find the nearest neighbors in the previous point cloud
00436     this->_sqrt_dist.clear();
00437     this->_FindCandidatesInCurrentPC(this->_previous_ffs,
00438                                      this->_current_ffs,
00439                                      this->_moving_pts_of_current_idx_depth,
00440                                      this->_previous_to_current_HTransform,
00441                                      this->_previous_k_indices,
00442                                      this->_sqrt_dist);
00443 
00444     std::vector<int > invalid_indices;
00445     // Add the valid points of the current point cloud to the model
00446     for (unsigned int i = 0; i < _previous_k_indices.size(); i++)
00447     {
00448         if (_previous_k_indices[i].size() == 0)
00449         {
00450             invalid_indices.push_back(_moving_pts_of_current_idx_depth->indices.at(i));
00451         }
00452     }
00453 
00454     cv::Mat not_coherent_points = cv::Mat(IMG_HEIGHT, IMG_WIDTH, CV_8UC1);
00455     Indices2Image8u(invalid_indices, not_coherent_points);
00456 
00457     this->_acc_candidates_of_current.setTo(0, not_coherent_points);
00458 }
00459 
00460 //#define DISPLAY_IMAGES_DILWCC
00461 void ShapeReconstruction::_DetectImageLocationsWhereColorChanges()
00462 {
00463     // Create matrices
00464     cv::Mat current_hsv_image, current_h_channel, current_s_channel, current_v_channel;
00465     cv::Mat previous_hsv_image, previous_h_channel, previous_s_channel, previous_v_channel;
00466     cv::Mat difference_h_channel, circular_difference_h_channel, thresholded_difference_h_channel, difference_h_channel_wo_black, thresholded_difference_h_channel_wo_black;
00467 
00468     // Convert ROS msg to opencv
00469     cv_bridge::CvImagePtr previous_rgb_cv;
00470     previous_rgb_cv = cv_bridge::toCvCopy(this->_previous_ffs._rgb);
00471 
00472     cv_bridge::CvImagePtr current_rgb_cv;
00473     current_rgb_cv = cv_bridge::toCvCopy(this->_current_ffs._rgb);
00474 
00475 #ifdef DISPLAY_IMAGES_DILWCC
00476     // Display original color images
00477     cv::imshow("color current", current_rgb_cv->image);
00478     cv::imshow("color previous", previous_rgb_cv->image);
00479 #endif
00480 
00481     // Convert color to HSV images
00482     cv::cvtColor(previous_rgb_cv->image, previous_hsv_image, CV_BGR2HSV);
00483     cv::cvtColor(current_rgb_cv->image, current_hsv_image, CV_BGR2HSV);
00484 
00485     // Smooth images
00486     //cv::GaussianBlur(previous_hsv_image, previous_hsv_image,cv::Size(5,5), 0,0);
00487     //cv::GaussianBlur(current_hsv_image, current_hsv_image,cv::Size(5,5), 0,0);
00488 
00489     // Extract channels
00490     cv::extractChannel(current_hsv_image,current_h_channel,0);
00491     cv::extractChannel(current_hsv_image,current_s_channel,1);
00492     cv::extractChannel(current_hsv_image,current_v_channel,2);
00493 
00494     cv::extractChannel(previous_hsv_image,previous_h_channel,0);
00495     cv::extractChannel(previous_hsv_image,previous_s_channel,1);
00496     cv::extractChannel(previous_hsv_image,previous_v_channel,2);
00497 
00498     // Difference of hue -> HUE values are between 0 and 179 and represent the angle
00499     // of a cylindrical coordinates system. That implies that 180=0.
00500     cv::absdiff(current_h_channel,  previous_h_channel, difference_h_channel );
00501     circular_difference_h_channel = 180.0 - difference_h_channel;
00502     circular_difference_h_channel.copyTo(difference_h_channel, difference_h_channel >89);
00503 
00504 #ifdef DISPLAY_IMAGES_DILWCC
00505     // Show difference of HUE
00506     cv::imshow("Circular difference of HUE", difference_h_channel);
00507 #endif
00508 
00509     // We do not consider pixels that change between different black hue colors, because black identification if really bad
00510     difference_h_channel.copyTo(difference_h_channel_wo_black);
00511     //difference_h_channel_wo_black.setTo(cv::Scalar(0), current_s_channel < 90 & current_v_channel < 50 & previous_s_channel < 90 & previous_v_channel < 50);
00512     difference_h_channel_wo_black.setTo(cv::Scalar(0), current_s_channel < 90 | previous_s_channel < 90 );
00513 
00514 #ifdef DISPLAY_IMAGES_DILWCC
00515     // Show pixels with low saturation or value
00516     cv::imshow("Low current_s_channel", current_s_channel < 90);
00517     //cv::imshow("Low current_v_channel", current_v_channel < 10);
00518     cv::imshow("Low previous_s_channel", previous_s_channel < 90);
00519     //cv::imshow("Low previous_v_channel", previous_v_channel < 10);
00520 #endif
00521 
00522     // Threshold the circular hue difference to find points that change enough
00523     cv::threshold(difference_h_channel, thresholded_difference_h_channel, 20, 255, cv::THRESH_BINARY);
00524 
00525 #ifdef DISPLAY_IMAGES_DILWCC
00526     cv::imshow("Thresholded circular difference of HUE", thresholded_difference_h_channel);
00527 #endif
00528 
00529     // Threshold the circular hue difference without black pixels to find points that change enough
00530     cv::threshold(difference_h_channel_wo_black, thresholded_difference_h_channel_wo_black, 15, 255, cv::THRESH_BINARY);
00531 
00532 #ifdef DISPLAY_IMAGES_DILWCC
00533     cv::imshow("Thresholded circular difference of HUE without black pixels", thresholded_difference_h_channel_wo_black);
00534 #endif
00535 
00536     cv::Mat thresholded_difference_h_channel_wo_black_wo_noisy_pixels;
00537     int openning_size = 1;
00538     // Element: 0: Rect - 1: Cross - 2: Ellipse
00539     int openning_element_type = 2;
00540     cv::Mat openning_element = cv::getStructuringElement( openning_element_type, cv::Size( 2*openning_size + 1, 2*openning_size+1 ), cv::Point( openning_size, openning_size ) );
00541     cv::morphologyEx(thresholded_difference_h_channel_wo_black, thresholded_difference_h_channel_wo_black_wo_noisy_pixels, cv::MORPH_OPEN, openning_element);
00542 
00543 #ifdef DISPLAY_IMAGES_DILWCC
00544     cv::imshow("Thresholded circular difference of HUE without black pixels nor NOISY pixels", thresholded_difference_h_channel_wo_black_wo_noisy_pixels);
00545 #endif
00546 
00547     // We use color to detect things that move and do not change depth. Therefore, we set to zero
00548     // points that changed their color AND their depth
00549     cv::Mat changing_depth_points;
00550     if(this->_depth_filling)
00551     {
00552         cv::absdiff(this->_current_ffs._dm_filled, this->_previous_ffs._dm_filled, changing_depth_points);
00553     }else{
00554         cv::absdiff(this->_current_ffs._dm->image, this->_previous_ffs._dm->image, changing_depth_points);
00555     }
00556 
00557     // The mask of moving points in current frame are the points that changed color and that are valid
00558     cv::Mat motion_mask_current= thresholded_difference_h_channel_wo_black_wo_noisy_pixels.clone();
00559     motion_mask_current.setTo(cv::Scalar(0), this->_current_depth_mask);
00560 
00561     // The mask of moving points in previous frame are the points that changed color and that are valid
00562     cv::Mat motion_mask_previous = thresholded_difference_h_channel_wo_black_wo_noisy_pixels.clone();
00563     motion_mask_previous.setTo(cv::Scalar(0), this->_previous_depth_mask);
00564 
00565 #ifdef DISPLAY_IMAGES_DILWCC
00566     cv::imshow("Current color candidates", motion_mask_current);
00567     cv::imshow("Previous color candidates", motion_mask_previous);
00568 #endif
00569 
00570 #ifdef DISPLAY_IMAGES_DILWCC
00571     cv::waitKey(-1);
00572 #endif
00573 
00574     Image8u2Indices(motion_mask_current, this->_moving_pts_of_current_idx_color);
00575     Image8u2Indices(motion_mask_previous, this->_moving_pts_of_previous_idx_color);
00576 
00577     if(this->_record_videos)
00578     {
00579         _videos.write("original_color",ros::Time::now(), current_rgb_cv->toImageMsg());
00580         sensor_msgs::ImagePtr dm_msg;
00581         DepthImage2CvImage(thresholded_difference_h_channel, dm_msg);
00582         this->_videos.write("changes_in_color", this->_current_ffs._time, dm_msg);
00583     }
00584 }
00585 
00586 void ShapeReconstruction::_TestMotionCoherencyOfPointsInImageLocationsWhereColorChanged()
00587 {
00588     ROS_INFO_STREAM_NAMED("ShapeReconstruction._TestMotionCoherencyOfPointsInImageLocationsWhereColorChanged",
00589                           "[RB" << this->_rb_id << "]: Estimating which of the points that changed COLOR moved coherently with the RB motion.");
00590 
00591     // Filter the previous point cloud, transform only the filtered points and find the nearest neighbors in the current point cloud
00592     this->_FindCandidatesInPreviousPC(this->_previous_ffs,
00593                                       this->_current_ffs,
00594                                       this->_moving_pts_of_previous_idx_color,
00595                                       this->_current_to_previous_HTransform,
00596                                       this->_current_k_indices,
00597                                       this->_sqrt_dist);
00598 
00599     // Filter the current point cloud, transform only the filtered points and find the nearest neighbors in the previous point cloud
00600     this->_FindCandidatesInCurrentPC(this->_previous_ffs,
00601                                      this->_current_ffs,
00602                                      this->_moving_pts_of_current_idx_color,
00603                                      this->_previous_to_current_HTransform,
00604                                      this->_previous_k_indices,
00605                                      this->_sqrt_dist);
00606 }
00607 
00608 void ShapeReconstruction::_FindCandidatesInPreviousPC(FrameForSegmentation& _previous_ffs,
00609                                                       const FrameForSegmentation& _current_ffs,
00610                                                       pcl::PointIndices::Ptr& _moving_pts_of_previous_idx,
00611                                                       Eigen::Matrix4d& _current_to_previous_HTransform,
00612                                                       std::vector<std::vector<int > >& _previous_k_indices,
00613                                                       std::vector<std::vector<float > >& _previous_sqrt_dist)
00614 {
00615     _previous_ffs._pc_moving_pts =  omip::SRPointCloud::Ptr(new omip::SRPointCloud);
00616     this->_extractor.setInputCloud(_previous_ffs._pc);
00617     this->_extractor.setIndices(_moving_pts_of_previous_idx);
00618     this->_extractor.setNegative(false);
00619     this->_extractor.filter(*_previous_ffs._pc_moving_pts);
00620 
00621     std::vector<std::vector<int > > previous_k_indices;
00622     if(_previous_ffs._pc_moving_pts->points.size())
00623     {
00624         pcl::transformPointCloud<SRPoint>(*_previous_ffs._pc_moving_pts, *_previous_ffs._pc_moving_pts_transf, _current_to_previous_HTransform.cast<float>());
00625 
00626         this->_knn->setInputCloud(_current_ffs._pc_without_nans);
00627         std::vector<int> empty;
00628 
00629         this->_knn->radiusSearch(*_previous_ffs._pc_moving_pts_transf, empty, this->_knn_min_radius, previous_k_indices, _previous_sqrt_dist);
00630 
00631         ROS_INFO_STREAM_NAMED("ShapeReconstruction._FindCandidatesInPreviousPC", "[RB" << this->_rb_id << "]: After radius search for finding candidates in previous PC. Queries: "
00632                               << _previous_ffs._pc_moving_pts_transf->points.size() );
00633 
00634     }
00635 
00636     _previous_k_indices.insert(_previous_k_indices.end(), previous_k_indices.begin(), previous_k_indices.end());
00637 }
00638 
00639 void ShapeReconstruction::_FindCandidatesInCurrentPC(const FrameForSegmentation& _previous_ffs,
00640                                                      FrameForSegmentation& _current_ffs,
00641                                                      pcl::PointIndices::Ptr& _moving_pts_of_current_idx,
00642                                                      Eigen::Matrix4d& _previous_to_current_HTransform,
00643                                                      std::vector<std::vector<int > >& _current_k_indices,
00644                                                      std::vector<std::vector<float > >& _current_sqrt_dist)
00645 {
00646     _current_ffs._pc_moving_pts =  omip::SRPointCloud::Ptr(new omip::SRPointCloud);
00647     this->_extractor.setInputCloud(_current_ffs._pc);
00648     this->_extractor.setIndices(_moving_pts_of_current_idx);
00649     this->_extractor.setNegative(false);
00650     this->_extractor.filter(*_current_ffs._pc_moving_pts);
00651 
00652     std::vector<std::vector<int > > current_k_indices;
00653     if(_current_ffs._pc_moving_pts->points.size())
00654     {
00655         pcl::transformPointCloud<SRPoint>(*_current_ffs._pc_moving_pts, *_current_ffs._pc_moving_pts_transf, _previous_to_current_HTransform.cast<float>());
00656 
00657         this->_knn->setInputCloud(_previous_ffs._pc_without_nans);
00658 
00659         std::vector<int> empty;
00660         this->_knn->radiusSearch(*_current_ffs._pc_moving_pts_transf, empty, this->_knn_min_radius, current_k_indices, _current_sqrt_dist);
00661 
00662         ROS_INFO_STREAM_NAMED("ShapeReconstruction._FindCandidatesInPreviousPC", "[RB" << this->_rb_id << "]: After radius search for finding candidates in current PC. Queries: "
00663                               << _current_ffs._pc_moving_pts_transf->points.size() );
00664     }
00665 
00666     _current_k_indices.insert(_current_k_indices.end(), current_k_indices.begin(), current_k_indices.end());
00667 }
00668 
00669 
00670 void ShapeReconstruction::_MergeValidPointsIntoModels()
00671 {
00672     // We create these transformed point clouds in the general function to avoid repeating it in the subfunctions
00673 
00674     // Transform the points of the current point cloud that belong to the model to the object frame (corresponds to the frame of the first point cloud)
00675     omip::SRPointCloud::Ptr points_of_current_in_origin(new omip::SRPointCloud);
00676     pcl::transformPointCloud<SRPoint>(*this->_current_ffs._pc_without_nans, *points_of_current_in_origin, this->_current_HTransform_inv.cast<float>());
00677 
00678     // Transform the points of the previous point cloud that belong to the model into the object frame (corresponds to the frame of the first point cloud)
00679     omip::SRPointCloud::Ptr points_of_previous_in_origin(new omip::SRPointCloud);
00680     pcl::transformPointCloud<SRPoint>(*this->_previous_ffs._pc_without_nans, *points_of_previous_in_origin, this->_previous_HTransform_inv.cast<float>());
00681 
00682     //Now we have all the indices together
00683     _MergeValidPointsIntoModels("DEPTHANDCOLOR",
00684                                     points_of_current_in_origin,
00685                                     points_of_previous_in_origin,
00686                                     this->_rb_shape,
00687                                     this->_current_k_indices,
00688                                     this->_previous_k_indices);
00689 
00690     this->_candidates->points.clear();
00691     this->_candidates->width = 0;
00692 }
00693 
00694 void ShapeReconstruction::_MergeValidPointsIntoModels(const std::string& logger_name,
00695                                                       omip::SRPointCloud::Ptr& points_of_current_in_origin,
00696                                                       omip::SRPointCloud::Ptr& points_of_previous_in_origin,
00697                                                       omip::SRPointCloud::Ptr& rb_model,
00698                                                       const std::vector<std::vector<int > >& current_k_indices,
00699                                                       const std::vector<std::vector<int > >& previous_k_indices)
00700 {
00701     int model_size_before_adding = rb_model->width;
00702 
00703     // Add the valid points of the current point cloud to the model
00704     for (unsigned int i = 0; i < current_k_indices.size(); i++) {
00705         if (current_k_indices[i].size() > 0){
00706             rb_model->points.push_back(points_of_current_in_origin->points[current_k_indices[i].at(0)]);
00707             rb_model->width++;
00708         }
00709     }
00710 
00711     // Add the valid points of the previous point cloud to the model
00712     for (unsigned int i = 0; i < previous_k_indices.size(); i++) {
00713         if (previous_k_indices[i].size() > 0){
00714             rb_model->points.push_back(points_of_previous_in_origin->points[previous_k_indices[i].at(0)]);
00715             rb_model->width++;
00716         }
00717     }
00718 
00719     int number_of_points_added = rb_model->width - model_size_before_adding;
00720     ROS_INFO_STREAM_NAMED("ShapeReconstruction._MergeValidPointsIntoModels", "[RB" << this->_rb_id << "] "
00721                           << ": Added " << number_of_points_added << " points to the model based on changes in " << logger_name);
00722 }
00723 
00724 void ShapeReconstruction::_RemoveInconsistentPointsFromModelsAndExtendToRegions()
00725 {
00726     // clean up models by removing points inconsistent with the current view
00727     this->_RemoveInconsistentPointsFromRBModel("BeforeExtendingToRegions", this->_current_HTransform,
00728                                                this->_rb_shape,this->_current_ffs._dm->image,this->_current_ffs._pc,this->_rb_segment);
00729 
00730     if(_estimate_supervoxels)
00731     {
00732         // After cleaning up the models we extend to regions
00733         this->_ExtendPointsToRegions();
00734 
00735         this->_RemoveInconsistentPointsFromRBModel("AfterExtendingToRegions", this->_current_HTransform,
00736                                                    this->_rb_shape,this->_current_ffs._dm->image,this->_current_ffs._pc,this->_rb_segment);
00737     }
00738 }
00739 
00740 void ShapeReconstruction::_FilterModel()
00741 {
00742     ROS_INFO_STREAM_NAMED("ShapeReconstruction._FilterModel", "[RB" << this->_rb_id << "]: Before approximate voxel grid filter. Num of points: "
00743                            << this->_rb_shape->points.size() );
00744     this->_approximate_voxel_grid_filter.setInputCloud (this->_rb_shape);
00745     this->_approximate_voxel_grid_filter.filter (*this->_rb_shape);
00746     ROS_INFO_STREAM_NAMED("ShapeReconstruction._FilterModel", "[RB" << this->_rb_id << "]: After approximate voxel grid filter. Num of points: "
00747                            << this->_rb_shape->points.size() );
00748 
00749     // Apply the outlier removal filter to keep the model clean of small noisy clusters
00750     ROS_INFO_STREAM_NAMED("ShapeReconstruction._FilterModel", "[RB" << this->_rb_id << "] " << ": Before outlier removal. Num of points: "
00751                           << this->_rb_shape->points.size() );
00752     this->_radius_outlier_removal.setInputCloud(this->_rb_shape);
00753     this->_radius_outlier_removal.filter (*this->_rb_shape);
00754     ROS_INFO_STREAM_NAMED("ShapeReconstruction._FilterModel", "[RB" << this->_rb_id << "] " << ": After outlier removal. Num of points: "
00755                           << this->_rb_shape->points.size() );
00756 
00757 }
00758 
00759 void ShapeReconstruction::_RemoveInconsistentPointsFromRBModel(const std::string logger_name,
00760                                                                const Eigen::Matrix4d& HTransform,
00761                                                                SRPointCloud::Ptr& rb_shape,
00762                                                                cv::Mat& current_dm,
00763                                                                SRPointCloud::Ptr current_pc,
00764                                                                SRPointCloud::Ptr& rb_segment)
00765 {
00766     // are self-occlusions handled?
00767     // yes they should be accounted for implicitly because if the "front" & the "back" are in the model, only
00768     // the front (which should be visible in current) will be projected to rb_rip.
00769 
00770     ROS_INFO_STREAM_NAMED("ShapeReconstruction._RemoveInconsistentPointsFromRBModel", "[RB" << this->_rb_id << "] " << logger_name <<
00771                           ": Removing inconsistent points");
00772 
00773     // move the rb_shape into the current frame
00774     omip::SRPointCloud::Ptr rb_shape_current(new omip::SRPointCloud);
00775     pcl::transformPointCloud<SRPoint>(*rb_shape, *rb_shape_current, HTransform.cast<float>());
00776 
00777     // move the rb_shape into the current frame (new variant)
00778     omip::SRPointCloud::Ptr rb_shape_current_new(new omip::SRPointCloud);
00779     *rb_shape_current_new = *rb_shape_current;
00780 
00781     std::stringstream rb_id_str;
00782     rb_id_str << _rb_id;
00783 
00784     pcl::PointIndicesPtr indices_to_remove(new pcl::PointIndices);
00785 
00786     // these are the points in the current image that are consistent
00787     // with the model -> the actual segment in the image
00788     pcl::PointIndicesPtr indices_matching_in_model(new pcl::PointIndices);
00789     pcl::PointIndicesPtr indices_matching_in_current(new pcl::PointIndices);
00790 
00791     // find points that are inconsistent between moved rb_shape
00792     // and current depth image
00793     _FindInconsistentPoints(rb_shape_current_new, current_dm, // input
00794                                indices_to_remove, // output
00795                                indices_matching_in_model, // output
00796                                indices_matching_in_current // output
00797                                );
00798 
00799     // remove indices
00800     SRPointCloud::Ptr rb_new_clean(new SRPointCloud);
00801     if (_remove_inconsistent_points)
00802     {
00803         // move the rb_shape into the current frame
00804         this->_extractor.setNegative(true);
00805         this->_extractor.setInputCloud(rb_shape);
00806         this->_extractor.setIndices(indices_to_remove);
00807         this->_extractor.setKeepOrganized(false);
00808         this->_extractor.filter(*rb_new_clean);
00809     } else {
00810         indices_to_remove->indices.clear();
00811     }
00812 
00813     // create segment (always in current frame)
00814     SRPointCloud::Ptr rb_segment_new(new SRPointCloud);
00815     this->_extractor.setNegative(false);
00816     this->_extractor.setInputCloud(current_pc);
00817     this->_extractor.setIndices(indices_matching_in_current);
00818     this->_extractor.setKeepOrganized(true);
00819     this->_extractor.filter(*rb_segment_new);
00820     this->_extractor.setKeepOrganized(false);
00821 
00822     cv::Mat rb_segment_new_dm(IMG_HEIGHT, IMG_WIDTH, CV_32FC1);
00823     OrganizedPC2DepthMap(rb_segment_new, rb_segment_new_dm);
00824 
00825     ROS_INFO_STREAM_NAMED("ShapeReconstruction._RemoveInconsistentPointsAndComputeSegment", "[RB" << this->_rb_id << "] " << logger_name << ":" << std::endl
00826                           << "\tPoints of the original model: " << rb_shape->points.size() << "," << std::endl
00827                           << "\tPoints matching in model: " << indices_matching_in_model->indices.size() << "," << std::endl
00828                           << "\tPoints matching in model: " << rb_new_clean->points.size()  << "," << std::endl
00829                           << "\tPoints matching in current point cloud: " << indices_matching_in_current->indices.size() << std::endl
00830                           << "\tPoints removed: " << indices_to_remove->indices.size() << (indices_to_remove->indices.empty() ? " (NO POINTS REMOVED) " : "")
00831                           );
00832 
00833     if (_remove_inconsistent_points)
00834     {
00835         rb_shape = rb_new_clean;
00836     }
00837     rb_segment = rb_segment_new;
00838 }
00839 
00840 void ShapeReconstruction::_FindInconsistentPoints(const omip::SRPointCloud::Ptr& pc_source,
00841                                                      const cv::Mat & dm_true,
00842                                                      pcl::PointIndicesPtr& indices_to_remove,
00843                                                      pcl::PointIndicesPtr& indices_matching_in_true,
00844                                                      pcl::PointIndicesPtr& indices_matching_in_dm,
00845                                                      const double min_depth_error) {
00846 
00847     indices_to_remove->indices.clear();
00848 
00849     using ::shape_reconstruction::RangeImagePlanar;
00850     RangeImagePlanar::Ptr dm_source_rip(new RangeImagePlanar);
00851 
00852     Eigen::Affine3f sensor_pose;
00853     sensor_pose.matrix() = Eigen::Matrix4f::Identity(); // this->_current_HTransform_inv.cast<float>();
00854     pcl::RangeImagePlanar::CoordinateFrame coordinate_frame = pcl::RangeImagePlanar::CAMERA_FRAME;
00855 
00856     int width = dm_true.cols, height = dm_true.rows;
00857     dm_source_rip->matchPointCloudAndImage (
00858                 *pc_source,
00859                 width,
00860                 height,
00861                 this->_ci.P[2], //width/2 -0.5, //319.5, // 329.245223575443,
00862             this->_ci.P[6], //height/2 - 0.5, // 239.5, //  239.458
00863             this->_ci.P[0], // fx
00864             this->_ci.P[5], // fy
00865             sensor_pose,
00866             coordinate_frame,
00867             dm_true,
00868             min_depth_error,
00869             indices_matching_in_true,
00870             indices_matching_in_dm,
00871             indices_to_remove
00872             );
00873 
00874 }
00875 
00876 void ShapeReconstruction::growSVRecursively(uint32_t label_sv_seed,
00877                                             std::vector<uint32_t>& labels_extension,
00878                                             std::multimap<uint32_t, uint32_t>& supervoxel_adjacency,
00879                                             std::vector<uint32_t>& sv_containing_supporting_features,
00880                                             int& distance_to_feature_sv,
00881                                             std::map<uint32_t, std::unordered_set<int> >& labels_of_segments_to_extend)
00882 {
00883     std::pair< std::multimap<uint32_t, uint32_t>::iterator, std::multimap<uint32_t, uint32_t>::iterator > neighbors;
00884 
00885     // Get the adjacency map of the sv
00886     neighbors =supervoxel_adjacency.equal_range(label_sv_seed);
00887 
00888     // Iterate over the pairs of sv-neighbor
00889     for(std::multimap<uint32_t, uint32_t>::iterator neighbor_it = neighbors.first; neighbor_it!= neighbors.second; ++neighbor_it)
00890     {
00891         bool add_neighbor = false;
00892         // Check if the neighbor sv was already added
00893         if(std::find(labels_extension.begin(), labels_extension.end(), neighbor_it->second)  != labels_extension.end())
00894         {
00895             add_neighbor = false;
00896         }else{
00897             // Check if the neighbor sv should be added
00898             // if it contains a feature OR if (it contains enough points that changed (color OR depth) AND the distance to a feature-containing SV is small)
00899             if(std::find(sv_containing_supporting_features.begin(), sv_containing_supporting_features.end(), neighbor_it->second)  != sv_containing_supporting_features.end())
00900             {
00901                 add_neighbor = true;
00902                 distance_to_feature_sv = 0;
00903             }else{
00904                 std::map<uint32_t, std::unordered_set<int> >::iterator set_it = labels_of_segments_to_extend.find(neighbor_it->second);
00905                 if(set_it != labels_of_segments_to_extend.end())
00906                 {
00907                     if(set_it->second.size() > this->_min_number_model_pixels_in_sv)
00908                     {
00909                         if(distance_to_feature_sv <= 2)
00910                         {
00911                             add_neighbor = true;
00912                             distance_to_feature_sv++;
00913                         }
00914                     }
00915                 }
00916 
00917             }
00918         }
00919 
00920         if(add_neighbor)
00921         {
00922             labels_extension.push_back(neighbor_it->second);
00923             growSVRecursively(neighbor_it->second,
00924                               labels_extension,
00925                               supervoxel_adjacency,
00926                               sv_containing_supporting_features,
00927                               distance_to_feature_sv,
00928                               labels_of_segments_to_extend);
00929         }
00930     }
00931 }
00932 
00933 void ShapeReconstruction::_ExtendPointsToRegions()
00934 {
00935     std::vector<uint32_t> labels_to_extend;
00936 
00937     //Retrieve the labeled point cloud. Labels == Supervoxel ids
00938     pcl::PointCloud<pcl::PointXYZL>::Ptr labeled_cloud = (*_supervoxelizer_ptr_ptr)->getLabeledCloud();
00939 
00940     //Remove NaNs from the labeled point cloud
00941     pcl::PointCloud<pcl::PointXYZL>::Ptr labeled_cloud_not_nans(new pcl::PointCloud<pcl::PointXYZL>());
00942     std::vector<int> unused;
00943     pcl::removeNaNFromPointCloud< pcl::PointXYZL >(*labeled_cloud, *labeled_cloud_not_nans, unused);
00944 
00945     //Copy labeled point cloud without NaNs to a point cloud of XYZRGB points
00946     omip::SRPointCloud::Ptr labeled_cloud_not_nans_xyzrgb(new omip::SRPointCloud);
00947     pcl::copyPointCloud(*labeled_cloud_not_nans, *labeled_cloud_not_nans_xyzrgb);
00948 
00949     //Pass the new point cloud as input to a KNN. We will use it to find neighbors of the features and the points of the model
00950     this->_knn->setInputCloud(labeled_cloud_not_nans_xyzrgb);
00951 
00952     //Copy the supporting features of this RB into a point cloud without labels (only xyz)
00953     pcl::PointCloud<pcl::PointXYZ>::Ptr supporting_feats_no_labels(new pcl::PointCloud<pcl::PointXYZ>);
00954     pcl::copyPointCloud(*this->_supporting_features, *supporting_feats_no_labels);
00955 
00956     //Find the neighbor of each supporting feature in the point cloud of supervoxels -> find the supervoxel that contains each supporting feature
00957     //and add the supervoxels to the candidates
00958     for(int idx_sf =0; idx_sf<supporting_feats_no_labels->points.size(); idx_sf++)
00959     {
00960         std::vector<int> neighbors;
00961         std::vector<float> distances;
00962         this->_knn->nearestKSearchT<pcl::PointXYZ>(supporting_feats_no_labels->points[idx_sf], 1, neighbors, distances);
00963         if(std::find(labels_to_extend.begin(), labels_to_extend.end(), labeled_cloud_not_nans->points[neighbors[0]].label) == labels_to_extend.end())
00964         {
00965             if(labeled_cloud_not_nans->points[neighbors[0]].label != 0 )
00966             {
00967                 labels_to_extend.push_back(labeled_cloud_not_nans->points[neighbors[0]].label);
00968             }
00969         }
00970     }
00971 
00972     // THIS PART COULD BE IMPROVED TO ACCELERATE THE PROCESS!
00973     // Move the model to the current location
00974     // and find neighbors in the labeled point cloud (supervoxels)
00975     omip::SRPointCloud::Ptr model_in_current_frame(new omip::SRPointCloud);
00976     std::vector<std::vector<int > > indices_model;
00977     std::vector<std::vector<float > > unused2;
00978     unused.clear();
00979     if(this->_rb_shape->points.size())
00980     {
00981         pcl::transformPointCloud<SRPoint>(*this->_rb_shape, *model_in_current_frame, this->_current_HTransform.cast<float>());
00982         this->_knn->radiusSearch(*model_in_current_frame, unused, this->_knn_min_radius, indices_model, unused2);
00983     }
00984 
00985     // Build a set that contains all the labels of the supervoxels containing points of the depth-based
00986     // and the indices of the points of the supervoxels that are neighbors of the models (this avoids counting the same point several times)
00987     std::map<uint32_t, std::unordered_set<int> > labels_of_segments_to_extend;
00988     for (unsigned int i = 0; i < indices_model.size(); i++)
00989     {
00990         for(unsigned int k=0; k < indices_model[i].size(); k++)
00991         {
00992             if(indices_model[i].at(k))
00993             {
00994                 // For the first point we need to create the entry of the map
00995                 if(labels_of_segments_to_extend.find(labeled_cloud_not_nans->points.at(indices_model[i].at(k)).label) ==  labels_of_segments_to_extend.end())
00996                 {
00997                     std::unordered_set<int> initial_set_of_indices = {indices_model[i].at(k)};
00998                     labels_of_segments_to_extend[labeled_cloud_not_nans->points.at(indices_model[i].at(k)).label] = initial_set_of_indices;
00999                 }
01000                 else{
01001                     // If this point was not counted before as neighbor of another point we add its label
01002                     if(labels_of_segments_to_extend.at(labeled_cloud_not_nans->points.at(indices_model[i].at(k)).label).find(indices_model[i].at(k))
01003                             == labels_of_segments_to_extend.at(labeled_cloud_not_nans->points.at(indices_model[i].at(k)).label).end())
01004                     {
01005                         labels_of_segments_to_extend.at(labeled_cloud_not_nans->points.at(indices_model[i].at(k)).label).insert(indices_model[i].at(k));
01006                     }
01007                 }
01008             }
01009         }
01010     }
01011 
01012     std::map<uint32_t, std::unordered_set<int> >::iterator it = labels_of_segments_to_extend.begin();
01013     std::map<uint32_t, std::unordered_set<int> >::iterator itend = labels_of_segments_to_extend.end();
01014     // Iterate over the map-counter and add the supervoxels that contain enough points
01015     for(; it != itend; it++)
01016     {
01017         if(it->second.size() > _min_number_model_pixels_in_sv && std::find(labels_to_extend.begin(), labels_to_extend.end(), it->first) == labels_to_extend.end())
01018         {
01019             if(it->first != 0)
01020             {
01021                 labels_to_extend.push_back(it->first);
01022             }
01023         }
01024     }
01025     // END OF (THIS PART COULD BE IMPROVED TO ACCELERATE THE PROCESS!)
01026 
01027     std::multimap<uint32_t, uint32_t> supervoxel_adjacency;
01028     (*_supervoxelizer_ptr_ptr)->getSupervoxelAdjacency (supervoxel_adjacency);
01029 
01030     ROS_INFO_STREAM_NAMED("ShapeReconstruction._ExtendPointsToRegions",
01031                           "[RB" << this->_rb_id << "]: Superpixels to extend the results: " << labels_to_extend.size());
01032 
01033     if(this->_extend_to_neighbor_sv)
01034     {
01035         this->_ExtendToNeighborSV(supervoxel_adjacency, labels_to_extend);
01036 
01037         ROS_INFO_STREAM_NAMED("ShapeReconstruction._ExtendPointsToRegions",
01038                               "[RB" << this->_rb_id << "]: Superpixels to extend the results (after extending to SV of similar properties): " << labels_to_extend.size());
01039     }
01040 
01041     // Add the points of the supervoxels to extend
01042     shape_reconstruction::PassThrough<pcl::PointXYZL>::Ptr pt_filter(new shape_reconstruction::PassThrough<pcl::PointXYZL>());
01043     pt_filter->setInputCloud(boost::shared_ptr<pcl::PointCloud<pcl::PointXYZL> >(labeled_cloud_not_nans));
01044     pt_filter->setFilterFieldName("label");
01045     omip::SRPointCloud::Ptr points_of_current_in_origin(new omip::SRPointCloud);
01046     pcl::transformPointCloud<SRPoint>(*this->_current_ffs._pc_without_nans, *points_of_current_in_origin, this->_current_HTransform_inv.cast<float>());
01047 
01048     pt_filter->setLabels(labels_to_extend);
01049 
01050     boost::shared_ptr<std::vector<int> > passing_indices_ptr = boost::shared_ptr<std::vector<int> >(new std::vector<int>());
01051     pt_filter->filter(*passing_indices_ptr);
01052 
01053     ROS_INFO_STREAM_NAMED("ShapeReconstruction._ExtendPointsToRegions",
01054                            "[RB" << this->_rb_id << "]: Adding " << (*passing_indices_ptr).size() << " points to the extended model (currently " <<this->_rb_shape->width << " points)");
01055     for (unsigned int i = 0; i < (*passing_indices_ptr).size(); i++)
01056     {
01057         this->_rb_shape->points.push_back(points_of_current_in_origin->points[(*passing_indices_ptr)[i]]);
01058         this->_rb_shape->width++;
01059     }
01060 }
01061 
01062 void ShapeReconstruction::_ExtendToNeighborSV(std::multimap<uint32_t, uint32_t>& supervoxel_adjacency,
01063                                               std::vector<uint32_t>& labels_extension)
01064 {
01065     //EXPERIMENTAL: Naive extension of the SV to neighboring SV with similar properties
01066 
01067     int number_of_sv = _supervoxel_clusters_ptr->size();
01068     std::vector<uint32_t> labels_extension_safety_copy = labels_extension;
01069 
01070     //We retrieve the range of elements with each of the added SV
01071     std::pair< std::multimap<uint32_t, uint32_t>::iterator, std::multimap<uint32_t, uint32_t>::iterator > neighbors;
01072 
01073     int num_extended_labels = labels_extension.size();
01074     int num_extended_labels_with_neigbors = 0;
01075 
01076     // This repeats until we do not add any other label
01077     while(num_extended_labels != num_extended_labels_with_neigbors)
01078     {
01079         num_extended_labels = labels_extension.size();
01080         std::vector<uint32_t> labels_extension_copy = labels_extension;
01081         for(std::vector<uint32_t>::iterator labels_it = labels_extension.begin(); labels_it!=labels_extension.end(); labels_it++)
01082         {
01083             // Find the neighbors of the sv
01084             neighbors =supervoxel_adjacency.equal_range(*labels_it);
01085 
01086             // Query the supervoxel to be extended
01087             if(_supervoxel_clusters_ptr->find(*labels_it) != _supervoxel_clusters_ptr->end())
01088             {
01089                 pcl::Supervoxel<pcl::PointXYZRGB>::Ptr sv = _supervoxel_clusters_ptr->at(*labels_it);
01090 
01091                 // Normal of the supervoxel to be extended
01092                 Eigen::Vector3d sv_normal(sv->normal_.data_n[0], sv->normal_.data_n[1], sv->normal_.data_n[2]);
01093 
01094                 // Position and color (RGB) of the supervoxel to be extended
01095                 pcl::PointXYZRGB sv_centroid_xyzrgb( sv->centroid_.r, sv->centroid_.g, sv->centroid_.b);
01096                 sv_centroid_xyzrgb.x = sv->centroid_.x;
01097                 sv_centroid_xyzrgb.y = sv->centroid_.y;
01098                 sv_centroid_xyzrgb.z = sv->centroid_.z;
01099 
01100                 // Position and color (HSV) of the supervoxel to be extended
01101                 pcl::PointXYZHSV sv_centroid_xyzhsv;
01102                 pcl::PointXYZRGBtoXYZHSV(sv_centroid_xyzrgb, sv_centroid_xyzhsv);
01103 
01104                 // Iterate over the pairs of sv-neighbor
01105                 for(std::multimap<uint32_t, uint32_t>::iterator neighbor_it = neighbors.first; neighbor_it!= neighbors.second; ++neighbor_it)
01106                 {
01107                     // Check if the neighbor was already added
01108                     if(std::find(labels_extension_copy.begin(), labels_extension_copy.end(), neighbor_it->second)  == labels_extension_copy.end() )
01109                     {
01110                         // Query the neighbor supervoxel to extend to
01111                         pcl::Supervoxel<pcl::PointXYZRGB>::Ptr nsv = _supervoxel_clusters_ptr->at(neighbor_it->second);
01112 
01113                         // Normal of the neighbor supervoxel to extend to
01114                         Eigen::Vector3d nsv_normal(nsv->normal_.data_n[0], nsv->normal_.data_n[1], nsv->normal_.data_n[2]);
01115 
01116                         // Position and color (RGB) of the neighbor supervoxel to extend to
01117                         pcl::PointXYZRGB nsv_centroid_xyzrgb( nsv->centroid_.r, nsv->centroid_.g, nsv->centroid_.b);
01118                         nsv_centroid_xyzrgb.x = nsv->centroid_.x;
01119                         nsv_centroid_xyzrgb.y = nsv->centroid_.y;
01120                         nsv_centroid_xyzrgb.z = nsv->centroid_.z;
01121 
01122                         // Position and color (HSV) of the neighbor supervoxel to extend to
01123                         pcl::PointXYZHSV nsv_centroid_xyzhsv;
01124                         pcl::PointXYZRGBtoXYZHSV(nsv_centroid_xyzrgb, nsv_centroid_xyzhsv);
01125 
01126                         // We compare the mean normal and the color of the centroid point
01127                         // CAREFUL: h values in pcl are between 0 and 360
01128                         double h_diff = std::fabs(sv_centroid_xyzhsv.h - nsv_centroid_xyzhsv.h);
01129                         h_diff = std::fmod(h_diff, 360.0);
01130                         h_diff = h_diff > 180.0 ? 360.0 - h_diff : h_diff;
01131                         // TODO: Handle black and white regions (use s and v)
01132 
01133                         if(std::fabs(sv_normal.dot(nsv_normal) - 1.0) < _similarity_in_normal*(M_PI/180.0) && h_diff < _similarity_in_h)
01134                         {
01135                             labels_extension_copy.push_back(neighbor_it->second);
01136                         }
01137                     }
01138                 }
01139             }
01140         }
01141         num_extended_labels_with_neigbors = labels_extension_copy.size();
01142         labels_extension = labels_extension_copy;
01143     }
01144 
01145     // Emergency brake! -> We are adding too many svs
01146     int added_supervoxels = labels_extension.size() - labels_extension_safety_copy.size();
01147     // Options: max number of sv to add (absolute) or max fraction of the total amount of sv to add (relative to the total amount of sv)
01148     if((double)added_supervoxels/(double)number_of_sv > 0.2 )
01149     {
01150         ROS_INFO_STREAM_NAMED("ShapeReconstruction._ExtendPointsToRegions","We are adding too many supervoxels! Undo!");
01151         labels_extension = labels_extension_safety_copy;
01152     }
01153 }
01154 
01155 void ShapeReconstruction::_EstimateTransformations()
01156 {
01157     ROSTwist2EigenTwist(this->_previous_ffs._transformation.twist, this->_previous_twist);
01158     Twist2TransformMatrix(this->_previous_twist, this->_previous_HTransform);
01159     this->_previous_HTransform_inv = this->_previous_HTransform.inverse();
01160 
01161     ROSTwist2EigenTwist(this->_current_ffs._transformation.twist, this->_current_twist);
01162     Twist2TransformMatrix(this->_current_twist, this->_current_HTransform);
01163     this->_current_HTransform_inv = this->_current_HTransform.inverse();
01164 
01165     ROS_DEBUG_STREAM_NAMED("ShapeReconstruction._EstimateTransformations",  "[RB" << this->_rb_id << "]: _current_HTransform " << std::endl <<
01166                            _current_HTransform);
01167 
01168     this->_current_to_previous_HTransform = this->_current_HTransform*this->_previous_HTransform_inv;
01169 
01170     this->_previous_to_current_HTransform = this->_current_to_previous_HTransform.inverse();
01171 
01172     ROS_DEBUG_STREAM_NAMED("ShapeReconstruction._EstimateTransformations",  "[RB" << this->_rb_id << "]: current to previous " << std::endl <<
01173                            _current_to_previous_HTransform);
01174 }
01175 
01176 void ShapeReconstruction::_GenerateMesh(const omip::SRPointCloud::Ptr& pc_source,
01177                                         std::string shape_file_prefix)
01178 {
01179     if(pc_source->points.size())
01180     {
01181         // Normal estimation*
01182         this->_rb_tree_for_normal_estimation_ptr->setInputCloud(pc_source);
01183         this->_rb_normal_estimator_ptr->setInputCloud(pc_source);
01184         this->_rb_normal_estimator_ptr->setSearchMethod (this->_rb_tree_for_normal_estimation_ptr);
01185         this->_rb_normal_estimator_ptr->compute(*this->_rb_estimated_normals_pc_ptr);
01186 
01187         // Concatenate the XYZ and normal fields*
01188         pcl::concatenateFields (*pc_source, *this->_rb_estimated_normals_pc_ptr, *this->_rb_position_color_and_normals_pc_ptr);
01189         this->_rb_tree_for_triangulation_ptr->setInputCloud (this->_rb_position_color_and_normals_pc_ptr);
01190 
01191         // Get result
01192         this->_rb_greedy_projection_triangulator_ptr->setInputCloud(this->_rb_position_color_and_normals_pc_ptr);
01193         this->_rb_greedy_projection_triangulator_ptr->setSearchMethod(this->_rb_tree_for_triangulation_ptr);
01194         this->_rb_greedy_projection_triangulator_ptr->reconstruct(*this->_rb_triangulated_mesh_ptr);
01195 
01196         // Additional vertex information
01197         //std::vector<int> parts = gp3.getPartIDs();
01198         //std::vector<int> states = gp3.getPointStates();
01199 
01200         std::string mesh_path = ros::package::getPath("shape_reconstruction") + std::string("/meshes/");
01201         std::stringstream rb_name_ss;
01202         rb_name_ss << shape_file_prefix << this->_rb_id << ".stl";
01203 
01204         std::string rb_mesh_full_file_name = mesh_path + rb_name_ss.str();
01205 
01206         pcl::io::mesh2vtk(*this->_rb_triangulated_mesh_ptr, this->_rb_polygon_data_ptr);
01207 
01208         this->_rb_polygon_writer_ptr->SetInput (this->_rb_polygon_data_ptr);
01209         this->_rb_polygon_writer_ptr->SetFileName (rb_mesh_full_file_name.c_str ());
01210         this->_rb_polygon_writer_ptr->Write ();
01211 
01212         ROS_INFO_STREAM_NAMED("ShapeReconstruction.generateMesh",  "[RB" << this->_rb_id << "]: Resulting triangular mesh written to " << rb_mesh_full_file_name);
01213     }else{
01214         ROS_WARN_STREAM_NAMED("ShapeReconstruction.generateMesh",  "[RB" << this->_rb_id << "]: Impossible to generate a triangular mesh for this model, it doesn't contain any points!");
01215     }
01216 }
01217 
01218 void ShapeReconstruction::generateMesh()
01219 {
01220     ROS_INFO_STREAM_NAMED("ShapeReconstruction.generateMesh",  "[RB" << this->_rb_id << "]: Called the generation of 3D triangular mesh from the model of RB" << this->_rb_id <<
01221                           " based on extending other models with supervoxels.");
01222     std::string shape_ext_d_and_c_file_prefix("shape_ext_d_and_c_rb");
01223     this->_GenerateMesh(this->_rb_shape, shape_ext_d_and_c_file_prefix);
01224 
01225 
01226     ROS_INFO_STREAM_NAMED("ShapeReconstruction.generateMesh",  "[RB" << this->_rb_id << "]: Finished the generation of 3D triangular mesh from the point cloud of RB " << this->_rb_id);
01227 }
01228 
01229 void ShapeReconstruction::getShapeModel(omip_msgs::ShapeModelsPtr shapes)
01230 {
01231     omip_msgs::ShapeModel shape;
01232     shape.rb_id = this->_rb_id;
01233     sensor_msgs::PointCloud2 rb_shape_ros;
01234     rb_shape_ros.header.frame_id = "/camera_rgb_optical_frame";
01235 
01236     this->_rb_shape->header = this->_current_ffs._pc->header;
01237     pcl::toROSMsg(*this->_rb_shape, rb_shape_ros);
01238 
01239     shape.rb_shape_model = rb_shape_ros;
01240     shapes->rb_shape_models.push_back(shape);
01241 }
01242 
01243 void ShapeReconstruction::PublishMovedModelAndSegment(const ros::Time current_time,
01244                                                       const geometry_msgs::TwistWithCovariance &rb_transformation,
01245                                                       rosbag::Bag& bag,
01246                                                       bool bagOpen)
01247 {
01248     Eigen::Twistd current_twist;
01249     ROSTwist2EigenTwist(rb_transformation.twist, current_twist);
01250 
01251     Eigen::Matrix4d current_HTransform;
01252     Twist2TransformMatrix(current_twist, current_HTransform);
01253 
01254     ROS_INFO_STREAM_NAMED("ShapeReconstruction._PublishMovedModel",  "[RB" << this->_rb_id << "]: Publishing " << this->_rb_shape->points.size() << " points reconstructed from RB" << this->_rb_id
01255                           << " based on extending both depth and color model with supervoxels.");
01256     this->_rb_shape->header = this->_current_ffs._pc->header;
01257     this->_rb_shape->header.frame_id = "/camera_rgb_optical_frame";
01258     this->_rb_shape_in_current_frame.reset(new omip::SRPointCloud);
01259     pcl::transformPointCloud<SRPoint>(*this->_rb_shape, *this->_rb_shape_in_current_frame, current_HTransform.cast<float>());
01260     sensor_msgs::PointCloud2 rb_shape_ext_d_and_c_in_current_frame_ros;
01261     pcl::toROSMsg(*this->_rb_shape_in_current_frame, rb_shape_ext_d_and_c_in_current_frame_ros);
01262     rb_shape_ext_d_and_c_in_current_frame_ros.header.frame_id = "/camera_rgb_optical_frame";
01263     this->_rb_shape_pub.publish(rb_shape_ext_d_and_c_in_current_frame_ros);
01264 
01265     if (bagOpen){
01266         bag.write(_rb_shape_pub.getTopic(), current_time, rb_shape_ext_d_and_c_in_current_frame_ros);
01267     }
01268 
01269     // segment
01270     {
01271         this->_rb_segment->header = this->_current_ffs._pc->header;
01272         this->_rb_segment->header.frame_id = "/camera_rgb_optical_frame";
01273         sensor_msgs::PointCloud2 rb_segment_in_current_frame_ros;
01274         pcl::toROSMsg(*this->_rb_segment, rb_segment_in_current_frame_ros);
01275         rb_segment_in_current_frame_ros.header.frame_id = "/camera_rgb_optical_frame";
01276         this->_rb_segment_pub.publish(rb_segment_in_current_frame_ros);
01277         if (bagOpen){
01278             bag.write(_rb_segment_pub.getTopic(), current_time, rb_segment_in_current_frame_ros);
01279         }
01280     }
01281 }
01282 
01283 void ShapeReconstruction::RemoveInconsistentPoints(const SRPointCloud::Ptr &pc_msg,
01284                                                    const geometry_msgs::TwistWithCovariance &rb_transformation)
01285 {
01286     Eigen::Twistd current_twist;
01287     ROSTwist2EigenTwist(rb_transformation.twist, current_twist);
01288     Eigen::Matrix4d current_HTransform;
01289     Twist2TransformMatrix(current_twist, current_HTransform);
01290 
01291     SRPointCloud::Ptr current_pc(new SRPointCloud());
01292     pcl::copyPointCloud(*pc_msg, *current_pc);
01293 
01294     // Create a depth map from the current organized depth map
01295     cv::Mat current_dm;
01296     this->_current_ffs._dm->image.copyTo(current_dm);   // Just to initialize current_dm correctly
01297     OrganizedPC2DepthMap(current_pc, current_dm);
01298 
01299     this->_RemoveInconsistentPointsFromRBModel("", current_HTransform, this->_rb_shape, current_dm,current_pc, this->_rb_segment);
01300 
01301 }


shape_reconstruction
Author(s): Roberto Martín-Martín
autogenerated on Sat Jun 8 2019 18:35:22