surface_finder.cpp
Go to the documentation of this file.
00001 #include "surface_perception/surface_finder.h"
00002 #include "surface_perception/surface_history_recorder.h"
00003 
00004 #include <algorithm>
00005 #include <cmath>
00006 #include <cstdlib>
00007 #include <ctime>
00008 #include <functional>
00009 #include <limits>
00010 #include <map>
00011 #include <utility>
00012 #include <vector>
00013 
00014 #include "pcl/ModelCoefficients.h"
00015 #include "pcl/PointIndices.h"
00016 #include "pcl/common/angles.h"
00017 #include "pcl/filters/extract_indices.h"
00018 #include "pcl/point_cloud.h"
00019 #include "pcl/point_types.h"
00020 #include "ros/ros.h"
00021 
00022 typedef pcl::PointXYZRGB PointC;
00023 typedef pcl::PointCloud<pcl::PointXYZRGB> PointCloudC;
00024 
00025 namespace {
00030 double calculateAngle(const double& a, const double& b, const double& c) {
00031   double denominator = sqrt(pow(a, 2) + pow(b, 2) + pow(c, 2));
00032   double nominator = fabs(c);
00033   double tmpRes = nominator / denominator;
00034   return acos(tmpRes);
00035 }
00036 
00042 void planeEquation(const std::vector<PointC>& pts, double* a, double* b,
00043                    double* c, double* d) {
00044   *a = (pts[1].y - pts[0].y) * (pts[2].z - pts[0].z) -
00045        (pts[2].y - pts[0].y) * (pts[1].z - pts[0].z);
00046   *b = (pts[1].z - pts[0].z) * (pts[2].x - pts[0].x) -
00047        (pts[2].z - pts[0].z) * (pts[1].x - pts[0].x);
00048   *c = (pts[1].x - pts[0].x) * (pts[2].y - pts[0].y) -
00049        (pts[2].x - pts[0].x) * (pts[1].y - pts[0].y);
00050 
00051   if (*c < 0.0) {
00052     *a *= -1.0;
00053     *b *= -1.0;
00054     *c *= -1.0;
00055   }
00056 
00057   // Force normal vector to be a unit vector
00058   double denominator = sqrt(pow(*a, 2) + pow(*b, 2) + pow(*c, 2));
00059   *a /= denominator;
00060   *b /= denominator;
00061   *c /= denominator;
00062 
00063   *d = -1 * (*a) * pts[0].x - (*b) * pts[0].y - (*c) * pts[0].z;
00064 }
00065 
00070 void filterIndices(const double& dist_limit,
00071                    const std::map<double, std::vector<int> >& sortedIndices,
00072                    const double& z_val, std::vector<int>* output_pts) {
00073   std::map<double, std::vector<int> >::const_iterator iter =
00074       sortedIndices.find(z_val);
00075   if (iter == sortedIndices.end()) {
00076     ROS_INFO("Error: sampled point with height %f not on the plane", z_val);
00077     return;
00078   }
00079 
00080   std::map<double, std::vector<int> >::const_iterator curr_iter = iter;
00081   iter++;
00082 
00083   // Find the indices below z_val
00084   while (curr_iter != sortedIndices.begin() &&
00085          (z_val - curr_iter->first) <= dist_limit) {
00086     for (size_t i = 0; i < curr_iter->second.size(); i++) {
00087       output_pts->push_back(curr_iter->second[i]);
00088     }
00089     curr_iter--;
00090   }
00091   if (curr_iter == sortedIndices.begin() &&
00092       (z_val - curr_iter->first) <= dist_limit) {
00093     for (size_t i = 0; i < curr_iter->second.size(); i++) {
00094       output_pts->push_back(curr_iter->second[i]);
00095     }
00096   }
00097 
00098   // Find the indices above z_val
00099   curr_iter = iter;
00100   while (curr_iter != sortedIndices.end() &&
00101          (curr_iter->first - z_val) <= dist_limit) {
00102     for (size_t i = 0; i < curr_iter->second.size(); i++) {
00103       output_pts->push_back(curr_iter->second[i]);
00104     }
00105     curr_iter++;
00106   }
00107 }
00108 
00117 bool isSimilar(const double& dist, const pcl::ModelCoefficients& plane1,
00118                const pcl::ModelCoefficients& plane2) {
00119   double z1 = -1.0 * plane1.values[3] / plane1.values[2];
00120   double z2 = -1.0 * plane2.values[3] / plane2.values[2];
00121   return fabs(z1 - z2) < 2.0 * dist;
00122 }
00123 
00127 void sampleThreeNums(size_t rand_range, size_t nums_size, size_t rand_nums[]) {
00128   for (size_t i = 0; i < nums_size; i++) {
00129     rand_nums[i] = std::rand() % rand_range;
00130     for (size_t j = 0; j < i; j++) {
00131       while (rand_nums[j] == rand_nums[i]) {
00132         rand_nums[i] = std::rand() % rand_range;
00133       }
00134     }
00135   }
00136 }
00137 }  // Anonymous namespace
00138 
00139 namespace surface_perception {
00140 SurfaceFinder::SurfaceFinder()
00141     : cloud_(new PointCloudC),
00142       cloud_indices_(new pcl::PointIndices),
00143       angle_tolerance_degree_(5.0),
00144       max_point_distance_(0.01),
00145       min_iteration_(100),
00146       surface_point_threshold_(1000),
00147       min_surface_amount_(0),
00148       max_surface_amount_(10),
00149       sorted_indices_() {}
00150 
00151 void SurfaceFinder::set_cloud(const PointCloudC::Ptr& cloud) { cloud_ = cloud; }
00152 
00153 void SurfaceFinder::set_cloud_indices(
00154     const pcl::PointIndices::Ptr cloud_indices) {
00155   cloud_indices_->header.frame_id = cloud_indices->header.frame_id;
00156   cloud_indices_->indices = cloud_indices->indices;
00157 }
00158 
00159 void SurfaceFinder::set_angle_tolerance_degree(double angle_tolerance_degree) {
00160   angle_tolerance_degree_ = angle_tolerance_degree;
00161 }
00162 
00163 void SurfaceFinder::set_max_point_distance(double max_point_distance) {
00164   max_point_distance_ = max_point_distance;
00165 }
00166 
00167 void SurfaceFinder::set_min_iteration(int min_iteration) {
00168   if (min_iteration < 0) {
00169     ROS_ERROR(
00170         "set_min_iteration can not have a negative value as the parameter.");
00171     return;
00172   }
00173   min_iteration_ = min_iteration;
00174 }
00175 
00176 void SurfaceFinder::set_surface_point_threshold(int surface_point_threshold) {
00177   if (surface_point_threshold < 0) {
00178     ROS_ERROR(
00179         "set_surface_point_threshold can not have a negative value as the "
00180         "parameter.");
00181     return;
00182   }
00183   surface_point_threshold_ = surface_point_threshold;
00184 }
00185 
00186 void SurfaceFinder::set_min_surface_amount(int min_surface_amount) {
00187   if (min_surface_amount < 0) {
00188     ROS_ERROR(
00189         "set_min_surface_amount can not have a negative value as the "
00190         "parameter.");
00191     return;
00192   }
00193   min_surface_amount_ = min_surface_amount;
00194 }
00195 
00196 void SurfaceFinder::set_max_surface_amount(int max_surface_amount) {
00197   if (max_surface_amount < 0) {
00198     ROS_ERROR(
00199         "set_max_surface_amount can not have a negative value as the "
00200         "parameter.");
00201     return;
00202   }
00203   max_surface_amount_ = max_surface_amount;
00204 }
00205 
00206 void SurfaceFinder::ExploreSurfaces(
00207     std::vector<pcl::PointIndices::Ptr>* indices_vec,
00208     std::vector<pcl::ModelCoefficients>* coeffs_vec) {
00209   bool debug = false;
00210 
00211   // Check if input cloud is set
00212   if (cloud_->points.size() == 0) {
00213     ROS_ERROR("The input point cloud is not set.");
00214     return;
00215   }
00216 
00217   // Prepare indices and sort points by height
00218   if (cloud_indices_->indices.size() == 0) {
00219     for (size_t i = 0; i < cloud_->points.size(); i++) {
00220       cloud_indices_->indices.push_back(i);
00221     }
00222     cloud_indices_->header.frame_id = cloud_->header.frame_id;
00223   }
00224   SortIndices();
00225 
00226   // Algorithm overview:
00227   // 1. Get a point randomly from cloud_->points, which is a vector<PointCloudC>
00228   // 2. Calculate the horizontal plane
00229   // 3. Store the plane and rank it by number of points the plane covers
00230 
00231   if (debug) {
00232     ROS_INFO("Start exploring surfaces in %ld indices of %s",
00233              cloud_indices_->indices.size(),
00234              cloud_indices_->header.frame_id.c_str());
00235   }
00236   size_t num_surface = 0;
00237   size_t max_inlier_count = std::numeric_limits<size_t>::min();
00238   std::srand(unsigned(std::time(0)));
00239 
00240   std::map<size_t,
00241            std::pair<pcl::ModelCoefficients::Ptr, pcl::PointIndices::Ptr>,
00242            std::greater<size_t> >
00243       ranking;
00244 
00245   SurfaceHistoryRecorder recorder;
00246 
00247   // Timer start point
00248   clock_t start = std::clock();
00249 
00250   // Sample min_iteration_ of horizontal surfaces
00251   while (num_surface < min_iteration_ || ranking.size() < min_surface_amount_) {
00252     pcl::ModelCoefficients::Ptr coeff(new pcl::ModelCoefficients);
00253     coeff->values.resize(4);
00254     pcl::PointIndices::Ptr indices(new pcl::PointIndices);
00255 
00256     // Sample 1 point randomly to establish a plane, by assuming it's horizontal
00257     size_t rand_index = std::rand() % cloud_indices_->indices.size();
00258     const PointC& pt = cloud_->points[cloud_indices_->indices[rand_index]];
00259 
00260     // Count points within given distance to the plane
00261     std::vector<int> inlier_indices;
00262     filterIndices(max_point_distance_, sorted_indices_, pt.z, &inlier_indices);
00263 
00264     // Establish coefficients
00265     coeff->values[0] = 0;
00266     coeff->values[1] = 0;
00267     coeff->values[2] = 1;
00268     coeff->values[3] = -1 * pt.z;
00269     indices->indices = inlier_indices;
00270 
00271     // Check surface point threshold
00272     if (indices->indices.size() > surface_point_threshold_) {
00273       // Update plane choices
00274       bool qualify = true;
00275       bool pre_exist = false;
00276       size_t old_indices_size;
00277       for (std::map<
00278                size_t,
00279                std::pair<pcl::ModelCoefficients::Ptr, pcl::PointIndices::Ptr>,
00280                std::greater<size_t> >::iterator it = ranking.begin();
00281            it != ranking.end(); it++) {
00282         pcl::ModelCoefficients& old_coeff = *(it->second.first);
00283 
00284         old_indices_size = it->first;
00285 
00286         // If newly found surface is better, replace
00287         if (isSimilar(max_point_distance_, old_coeff, *coeff)) {
00288           if (old_indices_size < indices->indices.size()) {
00289             ranking.erase(it);
00290             pre_exist = true;
00291             break;
00292           } else {
00293             qualify = false;
00294           }
00295         }
00296       }
00297       if (qualify) {
00298         std::pair<pcl::ModelCoefficients::Ptr, pcl::PointIndices::Ptr> pr(
00299             coeff, indices);
00300         ranking[indices->indices.size()] = pr;
00301         if (debug) {
00302           if (pre_exist) {
00303             recorder.Update(old_indices_size, indices->indices.size(), cloud_,
00304                             indices, num_surface);
00305           } else {
00306             recorder.Record(indices->indices.size(), cloud_, indices,
00307                             num_surface);
00308           }
00309         }
00310       }
00311     }
00312 
00313     num_surface++;
00314   }
00315 
00316   //  Report surfaces
00317   if (ranking.size() > 0) {
00318     size_t amount = max_surface_amount_;
00319     for (std::map<
00320              size_t,
00321              std::pair<pcl::ModelCoefficients::Ptr, pcl::PointIndices::Ptr>,
00322              std::greater<size_t> >::iterator it = ranking.begin();
00323          it != ranking.end(); it++) {
00324       if (amount == 0) {
00325         break;
00326       }
00327       pcl::ModelCoefficients::Ptr old_coeff_ptr = it->second.first;
00328       pcl::PointIndices::Ptr old_indices_ptr = it->second.second;
00329 
00330       if (debug) {
00331         clock_t elapsed_clock;
00332         size_t iter_amount;
00333         PointCloudC::Ptr past_cloud(new PointCloudC);
00334         recorder.GetCloudHistory(old_indices_ptr->indices.size(), past_cloud);
00335         recorder.GetClock(old_indices_ptr->indices.size(), &elapsed_clock);
00336         recorder.GetIteration(old_indices_ptr->indices.size(), &iter_amount);
00337 
00338         ROS_INFO(
00339             "%f seconds spent at %ldth iteration for  %ldth surface with size "
00340             "%ld",
00341             ((float)elapsed_clock - start) / CLOCKS_PER_SEC, iter_amount,
00342             max_surface_amount_ - amount + 1, old_indices_ptr->indices.size());
00343       }
00344 
00345       // Only perform refinement when the angle tolerance is greater than 0.0
00346       if (angle_tolerance_degree_ > 0.0) {
00347         pcl::ModelCoefficients::Ptr new_coeff_ptr(new pcl::ModelCoefficients);
00348         pcl::PointIndices::Ptr new_indices_ptr(new pcl::PointIndices);
00349         FitSurface(old_indices_ptr, old_coeff_ptr, new_indices_ptr,
00350                    new_coeff_ptr);
00351 
00352         indices_vec->push_back(new_indices_ptr);
00353         coeffs_vec->push_back(*new_coeff_ptr);
00354       } else {
00355         indices_vec->push_back(old_indices_ptr);
00356         coeffs_vec->push_back(*old_coeff_ptr);
00357       }
00358 
00359       amount--;
00360     }
00361   } else {
00362     ROS_INFO("Warning: no surface found.");
00363   }
00364 }
00365 
00366 void SurfaceFinder::SortIndices() {
00367   sorted_indices_.clear();
00368 
00369   // Record the indices and sort by height
00370   for (size_t i = 0; i < cloud_indices_->indices.size(); i++) {
00371     const PointC& pt = cloud_->points[cloud_indices_->indices[i]];
00372     std::map<double, std::vector<int> >::iterator iter =
00373         sorted_indices_.find(pt.z);
00374     if (iter != sorted_indices_.end()) {
00375       sorted_indices_[pt.z].push_back(cloud_indices_->indices[i]);
00376     } else {
00377       std::vector<int> indices_vec;
00378       indices_vec.push_back(cloud_indices_->indices[i]);
00379       sorted_indices_[pt.z] = indices_vec;
00380     }
00381   }
00382 }
00383 
00384 void SurfaceFinder::FitSurface(const pcl::PointIndices::Ptr old_indices_ptr,
00385                                const pcl::ModelCoefficients::Ptr old_coeff_ptr,
00386                                pcl::PointIndices::Ptr new_indices_ptr,
00387                                pcl::ModelCoefficients::Ptr new_coeff_ptr) {
00388   size_t iteration_each = std::max(min_iteration_ / 10, (size_t)10);  // Use 10% of minimum iterations
00389   size_t iteration = 0;
00390 
00391   size_t max_num_points = old_indices_ptr->indices.size();
00392   new_coeff_ptr->values.resize(4);
00393   new_coeff_ptr->values = old_coeff_ptr->values;
00394   new_indices_ptr->indices.clear();
00395   new_indices_ptr->indices = old_indices_ptr->indices;
00396 
00397   // Refine the surface given the indices
00398   while (iteration < iteration_each && ros::ok()) {
00399     // Only sample the result from the given set of points
00400     size_t rand_indices[3];
00401     sampleThreeNums(old_indices_ptr->indices.size(), 3, rand_indices);
00402     std::vector<PointC> points;
00403     points.resize(3);
00404     for (size_t i = 0; i < points.size(); i++) {
00405       points[i] = cloud_->points[old_indices_ptr->indices[rand_indices[i]]];
00406     }
00407 
00408     double a, b, c, d;
00409     planeEquation(points, &a, &b, &c, &d);
00410     if (calculateAngle(a, b, c) > pcl::deg2rad(angle_tolerance_degree_)) {
00411       continue;
00412     }
00413 
00414     // Evaluate the quality of new tilted surface
00415     std::vector<int> covered_indices;
00416     for (size_t i = 0; i < cloud_indices_->indices.size(); i++) {
00417       const PointC& pt = cloud_->points[cloud_indices_->indices[i]];
00418       double dist = fabs(a * pt.x + b * pt.y + c * pt.z + d) /
00419                     sqrt(pow(a, 2.0) + pow(b, 2.0) + pow(c, 2.0));
00420       if (dist < max_point_distance_) {
00421         covered_indices.push_back(cloud_indices_->indices[i]);
00422       }
00423     }
00424 
00425     // Update the best model
00426     if (covered_indices.size() >= new_indices_ptr->indices.size()) {
00427       new_indices_ptr->indices.clear();
00428       new_indices_ptr->indices = covered_indices;
00429       new_coeff_ptr->values.clear();
00430       new_coeff_ptr->values.resize(4);
00431       new_coeff_ptr->values[0] = a;
00432       new_coeff_ptr->values[1] = b;
00433       new_coeff_ptr->values[2] = c;
00434       new_coeff_ptr->values[3] = d;
00435     }
00436 
00437     iteration++;
00438   }
00439   return;
00440 }
00441 }  // namespace surface_perception


surface_perception
Author(s): Yu-Tang Peng
autogenerated on Thu Jun 6 2019 17:36:21