00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00033 #include <door_handle_detector/geometry/angles.h>
00034 #include <door_handle_detector/geometry/nearest.h>
00035 #include <door_handle_detector/geometry/point.h>
00036 #include <door_handle_detector/geometry/statistics.h>
00037
00038 #include <door_handle_detector/kdtree/kdtree.h>
00039 #include <door_handle_detector/kdtree/kdtree_ann.h>
00040
00041 #include <Eigen/Eigenvalues>
00042
00043 namespace cloud_geometry
00044 {
00045
00046 namespace nearest
00047 {
00048
00050
00054 void
00055 computeCentroid (const sensor_msgs::PointCloud &points, sensor_msgs::PointCloud ¢roid)
00056 {
00057
00058 centroid.points.resize (1);
00059 centroid.points[0].x = centroid.points[0].y = centroid.points[0].z = 0;
00060 centroid.channels.resize (points.channels.size ());
00061 for (unsigned int d = 0; d < points.channels.size (); d++)
00062 {
00063 centroid.channels[d].name = points.channels[d].name;
00064 centroid.channels[d].values.resize (1);
00065 }
00066
00067
00068 for (unsigned int i = 0; i < points.points.size (); i++)
00069 {
00070 centroid.points[0].x += points.points[i].x;
00071 centroid.points[0].y += points.points[i].y;
00072 centroid.points[0].z += points.points[i].z;
00073
00074 for (unsigned int d = 0; d < points.channels.size (); d++)
00075 centroid.channels[d].values[0] += points.channels[d].values[i];
00076 }
00077
00078 centroid.points[0].x /= points.points.size ();
00079 centroid.points[0].y /= points.points.size ();
00080 centroid.points[0].z /= points.points.size ();
00081 for (unsigned int d = 0; d < points.channels.size (); d++)
00082 centroid.channels[d].values[0] /= points.points.size ();
00083 }
00084
00086
00091 void
00092 computeCentroid (const sensor_msgs::PointCloud &points, const std::vector<int> &indices, sensor_msgs::PointCloud ¢roid)
00093 {
00094
00095 centroid.points.resize (1);
00096 centroid.points[0].x = centroid.points[0].y = centroid.points[0].z = 0;
00097 centroid.channels.resize (points.channels.size ());
00098 for (unsigned int d = 0; d < points.channels.size (); d++)
00099 {
00100 centroid.channels[d].name = points.channels[d].name;
00101 centroid.channels[d].values.resize (1);
00102 }
00103
00104
00105 for (unsigned int i = 0; i < indices.size (); i++)
00106 {
00107 centroid.points[0].x += points.points.at (indices.at (i)).x;
00108 centroid.points[0].y += points.points.at (indices.at (i)).y;
00109 centroid.points[0].z += points.points.at (indices.at (i)).z;
00110
00111 for (unsigned int d = 0; d < points.channels.size (); d++)
00112 centroid.channels[d].values[0] += points.channels[d].values.at (indices.at (i));
00113 }
00114
00115 centroid.points[0].x /= indices.size ();
00116 centroid.points[0].y /= indices.size ();
00117 centroid.points[0].z /= indices.size ();
00118 for (unsigned int d = 0; d < points.channels.size (); d++)
00119 centroid.channels[d].values[0] /= indices.size ();
00120 }
00121
00123
00128 void
00129 computePatchEigen (const sensor_msgs::PointCloud &points, Eigen::Matrix3d &eigen_vectors, Eigen::Vector3d &eigen_values)
00130 {
00131 geometry_msgs::Point32 centroid;
00132
00133 Eigen::Matrix3d covariance_matrix;
00134 computeCovarianceMatrix (points, covariance_matrix, centroid);
00135
00136
00137 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> ei_symm (covariance_matrix);
00138 eigen_values = ei_symm.eigenvalues ();
00139 eigen_vectors = ei_symm.eigenvectors ();
00140 }
00141
00143
00149 void
00150 computePatchEigen (const sensor_msgs::PointCloud &points, const std::vector<int> &indices, Eigen::Matrix3d &eigen_vectors, Eigen::Vector3d &eigen_values)
00151 {
00152 geometry_msgs::Point32 centroid;
00153
00154 Eigen::Matrix3d covariance_matrix;
00155 computeCovarianceMatrix (points, indices, covariance_matrix, centroid);
00156
00157
00158 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> ei_symm (covariance_matrix);
00159 eigen_values = ei_symm.eigenvalues ();
00160 eigen_vectors = ei_symm.eigenvectors ();
00161 }
00162
00164
00170 void
00171 computePatchEigenNormalized (const sensor_msgs::PointCloud &points, Eigen::Matrix3d &eigen_vectors, Eigen::Vector3d &eigen_values, geometry_msgs::Point32& centroid)
00172 {
00173
00174
00175 Eigen::Matrix3d covariance_matrix;
00176 computeCovarianceMatrix (points, covariance_matrix, centroid);
00177
00178
00179 for (unsigned int i = 0 ; i < 3 ; i++)
00180 {
00181 for (unsigned int j = 0 ; j < 3 ; j++)
00182 {
00183 covariance_matrix(i, j) /= static_cast<double> (points.points.size ());
00184 }
00185 }
00186
00187
00188 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> ei_symm (covariance_matrix);
00189 eigen_values = ei_symm.eigenvalues ();
00190 eigen_vectors = ei_symm.eigenvectors ();
00191 }
00192
00194
00201 void
00202 computePatchEigenNormalized (const sensor_msgs::PointCloud &points, const std::vector<int> &indices, Eigen::Matrix3d &eigen_vectors, Eigen::Vector3d &eigen_values, geometry_msgs::Point32& centroid)
00203 {
00204
00205
00206 Eigen::Matrix3d covariance_matrix;
00207 computeCovarianceMatrix (points, indices, covariance_matrix, centroid);
00208
00209
00210 for (unsigned int i = 0 ; i < 3 ; i++)
00211 {
00212 for (unsigned int j = 0 ; j < 3 ; j++)
00213 {
00214 covariance_matrix(i, j) /= static_cast<double> (indices.size ());
00215 }
00216 }
00217
00218
00219
00220 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> ei_symm (covariance_matrix);
00221 eigen_values = ei_symm.eigenvalues ();
00222 eigen_vectors = ei_symm.eigenvectors ();
00223 }
00224
00226
00235 void
00236 computePointNormal (const sensor_msgs::PointCloud &points, Eigen::Vector4d &plane_parameters, double &curvature)
00237 {
00238 geometry_msgs::Point32 centroid;
00239
00240 Eigen::Matrix3d covariance_matrix;
00241 computeCovarianceMatrix (points, covariance_matrix, centroid);
00242
00243
00244 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> ei_symm (covariance_matrix);
00245 Eigen::Vector3d eigen_values = ei_symm.eigenvalues ();
00246 Eigen::Matrix3d eigen_vectors = ei_symm.eigenvectors ();
00247
00248
00249 double norm = sqrt ( eigen_vectors (0, 0) * eigen_vectors (0, 0) +
00250 eigen_vectors (0, 1) * eigen_vectors (0, 1) +
00251 eigen_vectors (0, 2) * eigen_vectors (0, 2));
00252 plane_parameters (0) = eigen_vectors (0, 0) / norm;
00253 plane_parameters (1) = eigen_vectors (0, 1) / norm;
00254 plane_parameters (2) = eigen_vectors (0, 2) / norm;
00255
00256
00257 plane_parameters (3) = -1 * (plane_parameters (0) * centroid.x + plane_parameters (1) * centroid.y + plane_parameters (2) * centroid.z);
00258
00259
00260 curvature = fabs ( eigen_values (0) / (eigen_values (0) + eigen_values (1) + eigen_values (2)) );
00261 }
00262
00264
00274 void
00275 computePointNormal (const sensor_msgs::PointCloud &points, const std::vector<int> &indices, Eigen::Vector4d &plane_parameters, double &curvature)
00276 {
00277 geometry_msgs::Point32 centroid;
00278
00279 Eigen::Matrix3d covariance_matrix;
00280 computeCovarianceMatrix (points, indices, covariance_matrix, centroid);
00281
00282
00283 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> ei_symm (covariance_matrix);
00284 Eigen::Vector3d eigen_values = ei_symm.eigenvalues ();
00285 Eigen::Matrix3d eigen_vectors = ei_symm.eigenvectors ();
00286
00287
00288
00289 double norm = sqrt ( eigen_vectors (0, 0) * eigen_vectors (0, 0) +
00290 eigen_vectors (1, 0) * eigen_vectors (1, 0) +
00291 eigen_vectors (2, 0) * eigen_vectors (2, 0));
00292 plane_parameters (0) = eigen_vectors (0, 0) / norm;
00293 plane_parameters (1) = eigen_vectors (1, 0) / norm;
00294 plane_parameters (2) = eigen_vectors (2, 0) / norm;
00295
00296
00297 plane_parameters (3) = -1 * (plane_parameters (0) * centroid.x + plane_parameters (1) * centroid.y + plane_parameters (2) * centroid.z);
00298
00299
00300 curvature = fabs ( eigen_values (0) / (eigen_values (0) + eigen_values (1) + eigen_values (2)) );
00301 }
00302
00304
00314 void
00315 computePointNormal (const sensor_msgs::PointCloudConstPtr &points, const std::vector<int> &indices, Eigen::Vector4d &plane_parameters, double &curvature)
00316 {
00317 geometry_msgs::Point32 centroid;
00318
00319 Eigen::Matrix3d covariance_matrix;
00320 computeCovarianceMatrix (points, indices, covariance_matrix, centroid);
00321
00322
00323 Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> ei_symm (covariance_matrix);
00324 Eigen::Vector3d eigen_values = ei_symm.eigenvalues ();
00325 Eigen::Matrix3d eigen_vectors = ei_symm.eigenvectors ();
00326
00327
00328
00329 double norm = sqrt ( eigen_vectors (0, 0) * eigen_vectors (0, 0) +
00330 eigen_vectors (1, 0) * eigen_vectors (1, 0) +
00331 eigen_vectors (2, 0) * eigen_vectors (2, 0));
00332 plane_parameters (0) = eigen_vectors (0, 0) / norm;
00333 plane_parameters (1) = eigen_vectors (1, 0) / norm;
00334 plane_parameters (2) = eigen_vectors (2, 0) / norm;
00335
00336
00337 plane_parameters (3) = -1 * (plane_parameters (0) * centroid.x + plane_parameters (1) * centroid.y + plane_parameters (2) * centroid.z);
00338
00339
00340 curvature = fabs ( eigen_values (0) / (eigen_values (0) + eigen_values (1) + eigen_values (2)) );
00341 }
00342
00343
00345
00351 void
00352 computeMomentInvariants (const sensor_msgs::PointCloud &points, double &j1, double &j2, double &j3)
00353 {
00354
00355 geometry_msgs::Point32 centroid;
00356 computeCentroid (points, centroid);
00357
00358
00359 sensor_msgs::PointCloud points_c;
00360 points_c.points.resize (points.points.size ());
00361 for (unsigned int i = 0; i < points.points.size (); i++)
00362 {
00363 points_c.points[i].x = points.points[i].x - centroid.x;
00364 points_c.points[i].y = points.points[i].y - centroid.y;
00365 points_c.points[i].z = points.points[i].z - centroid.z;
00366 }
00367
00368 double mu200 = cloud_geometry::statistics::computeCentralizedMoment (points_c, 2.0, 0.0, 0.0);
00369 double mu020 = cloud_geometry::statistics::computeCentralizedMoment (points_c, 0.0, 2.0, 0.0);
00370 double mu002 = cloud_geometry::statistics::computeCentralizedMoment (points_c, 0.0, 0.0, 2.0);
00371 double mu110 = cloud_geometry::statistics::computeCentralizedMoment (points_c, 1.0, 1.0, 0.0);
00372 double mu101 = cloud_geometry::statistics::computeCentralizedMoment (points_c, 1.0, 0.0, 1.0);
00373 double mu011 = cloud_geometry::statistics::computeCentralizedMoment (points_c, 0.0, 1.0, 1.0);
00374
00375 j1 = mu200 + mu020 + mu002;
00376 j2 = mu200*mu020 + mu200*mu002 + mu020*mu002 - mu110*mu110 - mu101*mu101 - mu011*mu011;
00377 j3 = mu200*mu020*mu002 + 2*mu110*mu101*mu011 - mu002*mu110*mu110 - mu020*mu101*mu101 - mu200*mu011*mu011;
00378 }
00379
00381
00388 void
00389 computeMomentInvariants (const sensor_msgs::PointCloud &points, const std::vector<int> &indices, double &j1, double &j2, double &j3)
00390 {
00391
00392 geometry_msgs::Point32 centroid;
00393 computeCentroid (points, indices, centroid);
00394
00395
00396 sensor_msgs::PointCloud points_c;
00397 points_c.points.resize (indices.size ());
00398 for (unsigned int i = 0; i < indices.size (); i++)
00399 {
00400 points_c.points[i].x = points.points.at (indices.at (i)).x - centroid.x;
00401 points_c.points[i].y = points.points.at (indices.at (i)).y - centroid.y;
00402 points_c.points[i].z = points.points.at (indices.at (i)).z - centroid.z;
00403 }
00404
00405 double mu200 = cloud_geometry::statistics::computeCentralizedMoment (points_c, 2.0, 0.0, 0.0);
00406 double mu020 = cloud_geometry::statistics::computeCentralizedMoment (points_c, 0.0, 2.0, 0.0);
00407 double mu002 = cloud_geometry::statistics::computeCentralizedMoment (points_c, 0.0, 0.0, 2.0);
00408 double mu110 = cloud_geometry::statistics::computeCentralizedMoment (points_c, 1.0, 1.0, 0.0);
00409 double mu101 = cloud_geometry::statistics::computeCentralizedMoment (points_c, 1.0, 0.0, 1.0);
00410 double mu011 = cloud_geometry::statistics::computeCentralizedMoment (points_c, 0.0, 1.0, 1.0);
00411
00412 j1 = mu200 + mu020 + mu002;
00413 j2 = mu200*mu020 + mu200*mu002 + mu020*mu002 - mu110*mu110 - mu101*mu101 - mu011*mu011;
00414 j3 = mu200*mu020*mu002 + 2*mu110*mu101*mu011 - mu002*mu110*mu110 - mu020*mu101*mu101 - mu200*mu011*mu011;
00415 }
00416
00418
00427 bool
00428 isBoundaryPoint (const sensor_msgs::PointCloud &points, int q_idx, const std::vector<int> &neighbors,
00429 const Eigen::Vector3d& u, const Eigen::Vector3d& v, double angle_threshold)
00430 {
00431 if (neighbors.size () < 3)
00432 return (false);
00433 double uvn_nn[2];
00434
00435 std::vector<double> angles;
00436 angles.reserve (neighbors.size ());
00437 for (unsigned int i = 0; i < neighbors.size (); i++)
00438 {
00439 uvn_nn[0] = (points.points.at (neighbors.at (i)).x - points.points.at (q_idx).x) * u (0) +
00440 (points.points.at (neighbors.at (i)).y - points.points.at (q_idx).y) * u (1) +
00441 (points.points.at (neighbors.at (i)).z - points.points.at (q_idx).z) * u (2);
00442 uvn_nn[1] = (points.points.at (neighbors.at (i)).x - points.points.at (q_idx).x) * v (0) +
00443 (points.points.at (neighbors.at (i)).y - points.points.at (q_idx).y) * v (1) +
00444 (points.points.at (neighbors.at (i)).z - points.points.at (q_idx).z) * v (2);
00445 if (uvn_nn[0] == 0 && uvn_nn[1] == 0)
00446 continue;
00447 angles.push_back (cloud_geometry::angles::getAngle2D (uvn_nn));
00448 }
00449 sort (angles.begin (), angles.end ());
00450
00451
00452 double max_dif = DBL_MIN, dif;
00453 for (unsigned int i = 0; i < angles.size () - 1; i++)
00454 {
00455 dif = angles[i + 1] - angles[i];
00456 if (max_dif < dif)
00457 max_dif = dif;
00458 }
00459
00460 dif = 2 * M_PI - angles[angles.size () - 1] + angles[0];
00461 if (max_dif < dif)
00462 max_dif = dif;
00463
00464
00465 if (max_dif > angle_threshold)
00466 return (true);
00467 else
00468 return (false);
00469 }
00470
00472
00479 void
00480 computePointCloudNormals (sensor_msgs::PointCloud &points, const sensor_msgs::PointCloud &surface, int k,
00481 const geometry_msgs::PointStamped &viewpoint)
00482 {
00483 int nr_points = points.points.size ();
00484 int orig_dims = points.channels.size ();
00485 points.channels.resize (orig_dims + 4);
00486 points.channels[orig_dims + 0].name = "nx";
00487 points.channels[orig_dims + 1].name = "ny";
00488 points.channels[orig_dims + 2].name = "nz";
00489 points.channels[orig_dims + 3].name = "curvatures";
00490 for (unsigned int d = orig_dims; d < points.channels.size (); d++)
00491 points.channels[d].values.resize (nr_points);
00492
00493 cloud_kdtree::KdTree *kdtree = new cloud_kdtree::KdTreeANN (surface);
00494
00495 #pragma omp parallel for schedule(dynamic)
00496 for (int i = 0; i < nr_points; i++)
00497 {
00498 std::vector<int> nn_indices;
00499 std::vector<float> nn_distances;
00500 kdtree->nearestKSearch (points.points[i], k, nn_indices, nn_distances);
00501
00502 Eigen::Vector4d plane_parameters;
00503 double curvature;
00504 computePointNormal (surface, nn_indices, plane_parameters, curvature);
00505
00506 cloud_geometry::angles::flipNormalTowardsViewpoint (plane_parameters, points.points[i], viewpoint);
00507
00508 points.channels[orig_dims + 0].values[i] = plane_parameters (0);
00509 points.channels[orig_dims + 1].values[i] = plane_parameters (1);
00510 points.channels[orig_dims + 2].values[i] = plane_parameters (2);
00511 points.channels[orig_dims + 3].values[i] = curvature;
00512 }
00513 delete kdtree;
00514 }
00515
00517
00524 void
00525 computePointCloudNormals (sensor_msgs::PointCloud &points, const sensor_msgs::PointCloud &surface, double radius,
00526 const geometry_msgs::PointStamped &viewpoint)
00527 {
00528 int nr_points = points.points.size ();
00529 int orig_dims = points.channels.size ();
00530 points.channels.resize (orig_dims + 4);
00531 points.channels[orig_dims + 0].name = "nx";
00532 points.channels[orig_dims + 1].name = "ny";
00533 points.channels[orig_dims + 2].name = "nz";
00534 points.channels[orig_dims + 3].name = "curvatures";
00535 for (unsigned int d = orig_dims; d < points.channels.size (); d++)
00536 points.channels[d].values.resize (nr_points);
00537
00538 cloud_kdtree::KdTree *kdtree = new cloud_kdtree::KdTreeANN (surface);
00539
00540 #pragma omp parallel for schedule(dynamic)
00541 for (int i = 0; i < nr_points; i++)
00542 {
00543 std::vector<int> nn_indices;
00544 std::vector<float> nn_distances;
00545 kdtree->radiusSearch (points.points[i], radius, nn_indices, nn_distances);
00546
00547 Eigen::Vector4d plane_parameters;
00548 double curvature;
00549 computePointNormal (surface, nn_indices, plane_parameters, curvature);
00550
00551 cloud_geometry::angles::flipNormalTowardsViewpoint (plane_parameters, points.points[i], viewpoint);
00552
00553 points.channels[orig_dims + 0].values[i] = plane_parameters (0);
00554 points.channels[orig_dims + 1].values[i] = plane_parameters (1);
00555 points.channels[orig_dims + 2].values[i] = plane_parameters (2);
00556 points.channels[orig_dims + 3].values[i] = curvature;
00557 }
00558 delete kdtree;
00559 }
00560
00562
00567 void
00568 computePointCloudNormals (sensor_msgs::PointCloud &points, int k, const geometry_msgs::PointStamped &viewpoint)
00569 {
00570 int nr_points = points.points.size ();
00571 int orig_dims = points.channels.size ();
00572 points.channels.resize (orig_dims + 4);
00573 points.channels[orig_dims + 0].name = "nx";
00574 points.channels[orig_dims + 1].name = "ny";
00575 points.channels[orig_dims + 2].name = "nz";
00576 points.channels[orig_dims + 3].name = "curvatures";
00577 for (unsigned int d = orig_dims; d < points.channels.size (); d++)
00578 points.channels[d].values.resize (nr_points);
00579
00580
00581 ros::Time ts = ros::Time::now();
00582 cloud_kdtree::KdTree *kdtree = new cloud_kdtree::KdTreeANN (points);
00583 ROS_INFO("KdTree created in %f seconds", (ros::Time::now () - ts).toSec ());
00584
00585 double total_search_time = 0.0, total_normal_time = 0.0;
00586
00587 #pragma omp parallel for schedule(dynamic)
00588 for (int i = 0; i < nr_points; i++)
00589 {
00590 std::vector<int> nn_indices;
00591 std::vector<float> nn_distances;
00592
00593 ros::Time ts_search = ros::Time::now();
00594 kdtree->nearestKSearch (points.points[i], k, nn_indices, nn_distances);
00595 total_search_time += (ros::Time::now () - ts_search).toSec ();
00596
00597 Eigen::Vector4d plane_parameters;
00598 double curvature;
00599
00600 ros::Time ts_normal = ros::Time::now();
00601 computePointNormal (points, nn_indices, plane_parameters, curvature);
00602 total_normal_time += (ros::Time::now () - ts_normal).toSec ();
00603
00604 cloud_geometry::angles::flipNormalTowardsViewpoint (plane_parameters, points.points[i], viewpoint);
00605
00606 points.channels[orig_dims + 0].values[i] = plane_parameters (0);
00607 points.channels[orig_dims + 1].values[i] = plane_parameters (1);
00608 points.channels[orig_dims + 2].values[i] = plane_parameters (2);
00609 points.channels[orig_dims + 3].values[i] = curvature;
00610 }
00611 ROS_INFO("Breakdown: %f seconds to search, %f seconds to compute normals", total_search_time, total_normal_time);
00612
00613 delete kdtree;
00614 }
00615
00617
00622 void
00623 computePointCloudNormals (sensor_msgs::PointCloud &points, double radius, const geometry_msgs::PointStamped &viewpoint)
00624 {
00625 int nr_points = points.points.size ();
00626 int orig_dims = points.channels.size ();
00627 points.channels.resize (orig_dims + 4);
00628 points.channels[orig_dims + 0].name = "nx";
00629 points.channels[orig_dims + 1].name = "ny";
00630 points.channels[orig_dims + 2].name = "nz";
00631 points.channels[orig_dims + 3].name = "curvatures";
00632 for (unsigned int d = orig_dims; d < points.channels.size (); d++)
00633 points.channels[d].values.resize (nr_points);
00634
00635 cloud_kdtree::KdTree *kdtree = new cloud_kdtree::KdTreeANN (points);
00636
00637 #pragma omp parallel for schedule(dynamic)
00638 for (int i = 0; i < nr_points; i++)
00639 {
00640 std::vector<int> nn_indices;
00641 std::vector<float> nn_distances;
00642 kdtree->radiusSearch (points.points[i], radius, nn_indices, nn_distances);
00643
00644 Eigen::Vector4d plane_parameters;
00645 double curvature;
00646 computePointNormal (points, nn_indices, plane_parameters, curvature);
00647
00648 cloud_geometry::angles::flipNormalTowardsViewpoint (plane_parameters, points.points[i], viewpoint);
00649
00650 points.channels[orig_dims + 0].values[i] = plane_parameters (0);
00651 points.channels[orig_dims + 1].values[i] = plane_parameters (1);
00652 points.channels[orig_dims + 2].values[i] = plane_parameters (2);
00653 points.channels[orig_dims + 3].values[i] = curvature;
00654 }
00655 delete kdtree;
00656 }
00657
00659
00675 void
00676 computeOrganizedPointCloudNormals (sensor_msgs::PointCloud &points, const sensor_msgs::PointCloud &surface,
00677 int k, int downsample_factor, int width, int height, double max_z,
00678 const geometry_msgs::Point32 &viewpoint)
00679 {
00680
00681 int nr_points = lrint (ceil (width / (double)downsample_factor)) *
00682 lrint (ceil (height / (double)downsample_factor));
00683 int orig_dims = points.channels.size ();
00684 points.channels.resize (orig_dims + 4);
00685 points.channels[orig_dims + 0].name = "nx";
00686 points.channels[orig_dims + 1].name = "ny";
00687 points.channels[orig_dims + 2].name = "nz";
00688 points.channels[orig_dims + 3].name = "curvatures";
00689 points.points.resize (nr_points);
00690
00691 for (unsigned int d = orig_dims; d < points.channels.size (); d++)
00692 points.channels[d].values.resize (nr_points);
00693
00694
00695 std::vector<int> nn_indices ((k + k + 1) * (k + k + 1));
00696 Eigen::Vector4d plane_parameters;
00697 double curvature;
00698
00699 int j = 0;
00700 #pragma omp parallel for schedule(dynamic)
00701 for (int i = 0; i < (int)surface.points.size (); i++)
00702 {
00703
00704 int u = i / width;
00705 int v = i % width;
00706
00707
00708 if ((u % downsample_factor != 0) || (v % downsample_factor != 0))
00709 continue;
00710
00711
00712 points.points[j] = surface.points[i];
00713
00714
00715 int l = 0;
00716 for (int x = -k; x < k+1; x++)
00717 {
00718 for (int y = -k; y < k+1; y++)
00719 {
00720 int idx = (u+x) * width + (v+y);
00721 if (idx == i)
00722 continue;
00723
00724 if (idx < 0 || idx >= (int)surface.points.size ())
00725 continue;
00726
00727 if (max_z != -1)
00728 {
00729 if ( fabs (points.points[j].z - surface.points[idx].z) < max_z )
00730 nn_indices[l++] = idx;
00731 }
00732 else
00733 nn_indices[l++] = idx;
00734 }
00735 }
00736 nn_indices.resize (l);
00737 if (nn_indices.size () < 4)
00738 {
00739
00740 continue;
00741 }
00742
00743
00744 cloud_geometry::nearest::computePointNormal (surface, nn_indices, plane_parameters, curvature);
00745 cloud_geometry::angles::flipNormalTowardsViewpoint (plane_parameters, surface.points[i], viewpoint);
00746
00747 points.channels[orig_dims + 0].values[j] = plane_parameters (0);
00748 points.channels[orig_dims + 1].values[j] = plane_parameters (1);
00749 points.channels[orig_dims + 2].values[j] = plane_parameters (2);
00750 points.channels[orig_dims + 3].values[j] = curvature;
00751 j++;
00752 }
00753 }
00754
00756
00772 void
00773 computeOrganizedPointCloudNormals (sensor_msgs::PointCloud &points, const sensor_msgs::PointCloudConstPtr &surface,
00774 int k, int downsample_factor, int width, int height, double max_z,
00775 const geometry_msgs::Point32 &viewpoint)
00776 {
00777
00778 int nr_points = lrint (ceil (width / (double)downsample_factor)) *
00779 lrint (ceil (height / (double)downsample_factor));
00780 int orig_dims = points.channels.size ();
00781 points.channels.resize (orig_dims + 4);
00782 points.channels[orig_dims + 0].name = "nx";
00783 points.channels[orig_dims + 1].name = "ny";
00784 points.channels[orig_dims + 2].name = "nz";
00785 points.channels[orig_dims + 3].name = "curvatures";
00786 points.points.resize (nr_points);
00787
00788 for (unsigned int d = orig_dims; d < points.channels.size (); d++)
00789 points.channels[d].values.resize (nr_points);
00790
00791
00792 std::vector<int> nn_indices ((k + k + 1) * (k + k + 1));
00793 Eigen::Vector4d plane_parameters;
00794 double curvature;
00795
00796 int j = 0;
00797 #pragma omp parallel for schedule(dynamic)
00798 for (int i = 0; i < (int)surface->points.size (); i++)
00799 {
00800
00801 int u = i / width;
00802 int v = i % width;
00803
00804
00805 if ((u % downsample_factor != 0) || (v % downsample_factor != 0))
00806 continue;
00807
00808
00809 points.points[j] = surface->points[i];
00810
00811
00812 int l = 0;
00813 for (int x = -k; x < k+1; x++)
00814 {
00815 for (int y = -k; y < k+1; y++)
00816 {
00817 int idx = (u+x) * width + (v+y);
00818 if (idx == i)
00819 continue;
00820
00821 if (idx < 0 || idx >= (int)surface->points.size ())
00822 continue;
00823
00824 if (max_z != -1)
00825 {
00826 if ( fabs (points.points[j].z - surface->points[idx].z) < max_z )
00827 nn_indices[l++] = idx;
00828 }
00829 else
00830 nn_indices[l++] = idx;
00831 }
00832 }
00833 nn_indices.resize (l);
00834 if (nn_indices.size () < 4)
00835 {
00836
00837 continue;
00838 }
00839
00840
00841 cloud_geometry::nearest::computePointNormal (surface, nn_indices, plane_parameters, curvature);
00842 cloud_geometry::angles::flipNormalTowardsViewpoint (plane_parameters, surface->points[i], viewpoint);
00843
00844 points.channels[orig_dims + 0].values[j] = plane_parameters (0);
00845 points.channels[orig_dims + 1].values[j] = plane_parameters (1);
00846 points.channels[orig_dims + 2].values[j] = plane_parameters (2);
00847 points.channels[orig_dims + 3].values[j] = curvature;
00848 j++;
00849 }
00850 }
00851
00853
00871 void
00872 computeOrganizedPointCloudNormalsWithFiltering (sensor_msgs::PointCloud &points, const sensor_msgs::PointCloud &surface,
00873 int k, int downsample_factor, int width, int height, double max_z,
00874 double min_angle, double max_angle,
00875 const geometry_msgs::Point32 &viewpoint)
00876 {
00877
00878 int nr_points = lrint (ceil (width / (double)downsample_factor)) *
00879 lrint (ceil (height / (double)downsample_factor));
00880 int orig_dims = points.channels.size ();
00881 points.channels.resize (orig_dims + 4);
00882 points.channels[orig_dims + 0].name = "nx";
00883 points.channels[orig_dims + 1].name = "ny";
00884 points.channels[orig_dims + 2].name = "nz";
00885 points.channels[orig_dims + 3].name = "curvatures";
00886 points.points.resize (nr_points);
00887
00888 for (unsigned int d = orig_dims; d < points.channels.size (); d++)
00889 points.channels[d].values.resize (nr_points);
00890
00891
00892 std::vector<int> nn_indices ((k + k + 1) * (k + k + 1));
00893 Eigen::Vector4d plane_parameters;
00894 double curvature;
00895
00896 int j = 0;
00897 #pragma omp parallel for schedule(dynamic)
00898 for (int i = 0; i < (int)surface.points.size (); i++)
00899 {
00900
00901 int u = i / width;
00902 int v = i % width;
00903
00904
00905 if ((u % downsample_factor != 0) || (v % downsample_factor != 0))
00906 continue;
00907
00908
00909 points.points[j] = surface.points[i];
00910
00911
00912 int l = 0;
00913 for (int x = -k; x < k+1; x++)
00914 {
00915 for (int y = -k; y < k+1; y++)
00916 {
00917 int idx = (u+x) * width + (v+y);
00918 if (idx == i)
00919 continue;
00920
00921
00922 if (idx < 0 || idx >= (int)surface.points.size ())
00923 continue;
00924
00925
00926 double angle = getAngleWithViewpoint (points.points[j].x, points.points[j].y, points.points[j].z,
00927 surface.points[idx].x, surface.points[idx].y, surface.points[idx].z,
00928 viewpoint.x, viewpoint.y, viewpoint.z);
00929 if (angle < min_angle || angle > max_angle)
00930 continue;
00931
00932
00933 if (max_z != -1)
00934 {
00935 if ( fabs (points.points[j].z - surface.points[idx].z) < max_z )
00936 nn_indices[l++] = idx;
00937 }
00938 else
00939 nn_indices[l++] = idx;
00940 }
00941 }
00942 nn_indices.resize (l);
00943 if (nn_indices.size () < 4)
00944 {
00945
00946 continue;
00947 }
00948
00949
00950 cloud_geometry::nearest::computePointNormal (surface, nn_indices, plane_parameters, curvature);
00951 cloud_geometry::angles::flipNormalTowardsViewpoint (plane_parameters, surface.points[i], viewpoint);
00952
00953 points.channels[orig_dims + 0].values[j] = plane_parameters (0);
00954 points.channels[orig_dims + 1].values[j] = plane_parameters (1);
00955 points.channels[orig_dims + 2].values[j] = plane_parameters (2);
00956 points.channels[orig_dims + 3].values[j] = curvature;
00957 j++;
00958 }
00959 }
00960
00962
00974 void
00975 filterJumpEdges (const sensor_msgs::PointCloud &points, sensor_msgs::PointCloud &points_filtered, int k, int width, int height,
00976 double min_angle, double max_angle, const geometry_msgs::Point32 &viewpoint)
00977 {
00978
00979 points_filtered.header = points.header;
00980 points_filtered.points.resize (points.points.size ());
00981 points_filtered.channels.resize (points.channels.size ());
00982 for (unsigned int d = 0; d < points.channels.size (); d++)
00983 {
00984 points_filtered.channels[d].name = points.channels[d].name;
00985 points_filtered.channels[d].values.resize (points.channels[d].values.size ());
00986 }
00987
00988 int j = 0;
00989 #pragma omp parallel for schedule(dynamic)
00990 for (int i = 0; i < (int)points.points.size (); i++)
00991 {
00992
00993 int u = i / width;
00994 int v = i % width;
00995
00996
00997 bool valid_point = true;
00998 for (int x = -k; x < k+1; x++)
00999 {
01000 if (!valid_point)
01001 break;
01002 for (int y = -k; y < k+1; y++)
01003 {
01004 int idx = (u+x) * width + (v+y);
01005 if (idx == i)
01006 continue;
01007
01008
01009 if (idx < 0 || idx >= (int)points.points.size ())
01010 continue;
01011
01012
01013 double angle = getAngleWithViewpoint (points.points[i].x, points.points[i].y, points.points[i].z,
01014 points.points[idx].x, points.points[idx].y, points.points[idx].z,
01015 viewpoint.x, viewpoint.y, viewpoint.z);
01016 if (angle < min_angle || angle > max_angle)
01017 {
01018 valid_point = false;
01019 break;
01020 }
01021
01022 }
01023 }
01024
01025 if (valid_point)
01026 {
01027
01028 points_filtered.points[j] = points.points[i];
01029 for (unsigned int d = 0; d < points.channels.size (); d++)
01030 points_filtered.channels[d].values[j] = points.channels[d].values[j];
01031 j++;
01032 }
01033 }
01034 points_filtered.points.resize (j);
01035 for (unsigned int d = 0; d < points.channels.size (); d++)
01036 points_filtered.channels[d].values.resize (j);
01037 }
01038
01040
01054 void
01055 extractEuclideanClusters (const sensor_msgs::PointCloud &points, const std::vector<int> &indices, double tolerance,
01056 std::vector<std::vector<int> > &clusters,
01057 int nx_idx, int ny_idx, int nz_idx, double eps_angle, unsigned int min_pts_per_cluster)
01058 {
01059
01060 cloud_kdtree::KdTree* tree = new cloud_kdtree::KdTreeANN (points, indices);
01061
01062 int nr_points = indices.size ();
01063
01064 std::vector<bool> processed;
01065 processed.resize (nr_points, false);
01066
01067 std::vector<int> nn_indices;
01068 std::vector<float> nn_distances;
01069
01070 for (int i = 0; i < nr_points; i++)
01071 {
01072 if (processed[i])
01073 continue;
01074
01075 std::vector<int> seed_queue;
01076
01077 int sq_idx = 0;
01078 seed_queue.push_back (i);
01079
01080 processed[i] = true;
01081
01082 while (sq_idx < (int)seed_queue.size ())
01083 {
01084
01085 tree->radiusSearch (seed_queue.at (sq_idx), tolerance, nn_indices, nn_distances);
01086
01087 for (unsigned int j = 1; j < nn_indices.size (); j++)
01088 {
01089 if (processed.at (nn_indices[j]))
01090 continue;
01091
01092 processed[nn_indices[j]] = true;
01093 if (nx_idx != -1)
01094 {
01095
01096 double dot_p = points.channels[nx_idx].values[indices.at (i)] * points.channels[nx_idx].values[indices.at (nn_indices[j])] +
01097 points.channels[ny_idx].values[indices.at (i)] * points.channels[ny_idx].values[indices.at (nn_indices[j])] +
01098 points.channels[nz_idx].values[indices.at (i)] * points.channels[nz_idx].values[indices.at (nn_indices[j])];
01099 if ( fabs (acos (dot_p)) < eps_angle )
01100 {
01101 processed[nn_indices[j]] = true;
01102 seed_queue.push_back (nn_indices[j]);
01103 }
01104 }
01105
01106 else
01107 {
01108 processed[nn_indices[j]] = true;
01109 seed_queue.push_back (nn_indices[j]);
01110 }
01111 }
01112
01113 sq_idx++;
01114 }
01115
01116
01117 if (seed_queue.size () >= min_pts_per_cluster)
01118 {
01119 std::vector<int> r (seed_queue.size ());
01120 for (unsigned int j = 0; j < seed_queue.size (); j++)
01121 r[j] = indices.at (seed_queue[j]);
01122
01123 sort (r.begin (), r.end ());
01124 r.erase (unique (r.begin (), r.end ()), r.end ());
01125
01126 clusters.push_back (r);
01127 }
01128 }
01129
01130
01131 delete tree;
01132 }
01133
01135
01148 void
01149 extractEuclideanClusters (const sensor_msgs::PointCloud &points, double tolerance,
01150 std::vector<std::vector<int> > &clusters,
01151 int nx_idx, int ny_idx, int nz_idx, double eps_angle, unsigned int min_pts_per_cluster)
01152 {
01153
01154 cloud_kdtree::KdTree* tree = new cloud_kdtree::KdTreeANN (points);
01155
01156 int nr_points = points.points.size ();
01157
01158 std::vector<bool> processed;
01159 processed.resize (nr_points, false);
01160
01161 std::vector<int> nn_indices;
01162 std::vector<float> nn_distances;
01163
01164 for (int i = 0; i < nr_points; i++)
01165 {
01166 if (processed[i])
01167 continue;
01168
01169 std::vector<int> seed_queue;
01170 int sq_idx = 0;
01171 seed_queue.push_back (i);
01172
01173 processed[i] = true;
01174
01175 while (sq_idx < (int)seed_queue.size ())
01176 {
01177
01178 tree->radiusSearch (seed_queue.at (sq_idx), tolerance, nn_indices, nn_distances);
01179
01180 for (unsigned int j = 1; j < nn_indices.size (); j++)
01181 {
01182 if (processed.at (nn_indices[j]))
01183 continue;
01184
01185 processed[nn_indices[j]] = true;
01186 if (nx_idx != -1)
01187 {
01188
01189 double dot_p = points.channels[nx_idx].values[i] * points.channels[nx_idx].values[nn_indices[j]] +
01190 points.channels[ny_idx].values[i] * points.channels[ny_idx].values[nn_indices[j]] +
01191 points.channels[nz_idx].values[i] * points.channels[nz_idx].values[nn_indices[j]];
01192 if ( fabs (acos (dot_p)) < eps_angle )
01193 {
01194 processed[nn_indices[j]] = true;
01195 seed_queue.push_back (nn_indices[j]);
01196 }
01197 }
01198
01199 else
01200 {
01201 processed[nn_indices[j]] = true;
01202 seed_queue.push_back (nn_indices[j]);
01203 }
01204 }
01205
01206 sq_idx++;
01207 }
01208
01209
01210 if (seed_queue.size () >= min_pts_per_cluster)
01211 {
01212 std::vector<int> r (seed_queue.size ());
01213 for (unsigned int j = 0; j < seed_queue.size (); j++)
01214 r[j] = seed_queue[j];
01215
01216
01217 sort (r.begin (), r.end ());
01218 r.erase (unique (r.begin (), r.end ()), r.end ());
01219
01220 clusters.push_back (r);
01221 }
01222 }
01223
01224
01225 delete tree;
01226 }
01227
01228
01229 }
01230 }