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
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
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
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 }
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
00212 if (cloud_->points.size() == 0) {
00213 ROS_ERROR("The input point cloud is not set.");
00214 return;
00215 }
00216
00217
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
00227
00228
00229
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
00248 clock_t start = std::clock();
00249
00250
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
00257 size_t rand_index = std::rand() % cloud_indices_->indices.size();
00258 const PointC& pt = cloud_->points[cloud_indices_->indices[rand_index]];
00259
00260
00261 std::vector<int> inlier_indices;
00262 filterIndices(max_point_distance_, sorted_indices_, pt.z, &inlier_indices);
00263
00264
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
00272 if (indices->indices.size() > surface_point_threshold_) {
00273
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
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
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
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
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);
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
00398 while (iteration < iteration_each && ros::ok()) {
00399
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
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
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 }