nearest.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008 Radu Bogdan Rusu <rusu -=- cs.tum.edu>
00003  *
00004  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions are met:
00008  *
00009  *     * Redistributions of source code must retain the above copyright
00010  *       notice, this list of conditions and the following disclaimer.
00011  *     * Redistributions in binary form must reproduce the above copyright
00012  *       notice, this list of conditions and the following disclaimer in the
00013  *       documentation and/or other materials provided with the distribution.
00014  *
00015  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00016  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00017  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00018  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00019  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00020  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00021  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00022  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00023  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00024  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00025  * POSSIBILITY OF SUCH DAMAGE.
00026  *
00027  * $Id$
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 &centroid)
00056     {
00057       // Prepare the data output
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       // For each point in the cloud
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 &centroid)
00093     {
00094       // Prepare the data output
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       // For each point in the cloud
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       // Compute the 3x3 covariance matrix
00133       Eigen::Matrix3d covariance_matrix;
00134       computeCovarianceMatrix (points, covariance_matrix, centroid);
00135 
00136       // Extract the eigenvalues and eigenvectors
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       // Compute the 3x3 covariance matrix
00154       Eigen::Matrix3d covariance_matrix;
00155       computeCovarianceMatrix (points, indices, covariance_matrix, centroid);
00156 
00157       // Extract the eigenvalues and eigenvectors
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       //geometry_msgs::Point32 centroid;
00174       // Compute the 3x3 covariance matrix
00175       Eigen::Matrix3d covariance_matrix;
00176       computeCovarianceMatrix (points, covariance_matrix, centroid);
00177 
00178       // Normalize the matrix by 1/N
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       // Extract the eigenvalues and eigenvectors
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       //geometry_msgs::Point32 centroid;
00205       // Compute the 3x3 covariance matrix
00206       Eigen::Matrix3d covariance_matrix;
00207       computeCovarianceMatrix (points, indices, covariance_matrix, centroid);
00208 
00209       // Normalize the matrix by 1/N
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       // Extract the eigenvalues and eigenvectors
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       // Compute the 3x3 covariance matrix
00240       Eigen::Matrix3d covariance_matrix;
00241       computeCovarianceMatrix (points, covariance_matrix, centroid);
00242 
00243       // Extract the eigenvalues and eigenvectors
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       // Normalize the surface normal (eigenvector corresponding to the smallest eigenvalue)
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       // Hessian form (D = nc . p_plane (centroid here) + p)
00257       plane_parameters (3) = -1 * (plane_parameters (0) * centroid.x + plane_parameters (1) * centroid.y + plane_parameters (2) * centroid.z);
00258 
00259       // Compute the curvature surface change
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       // Compute the 3x3 covariance matrix
00279       Eigen::Matrix3d covariance_matrix;
00280       computeCovarianceMatrix (points, indices, covariance_matrix, centroid);
00281 
00282       // Extract the eigenvalues and eigenvectors
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       // Normalize the surface normal (eigenvector corresponding to the smallest eigenvalue)
00288       // Note: Remember to take care of the eigen_vectors ordering
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       // Hessian form (D = nc . p_plane (centroid here) + p)
00297       plane_parameters (3) = -1 * (plane_parameters (0) * centroid.x + plane_parameters (1) * centroid.y + plane_parameters (2) * centroid.z);
00298 
00299       // Compute the curvature surface change
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       // Compute the 3x3 covariance matrix
00319       Eigen::Matrix3d covariance_matrix;
00320       computeCovarianceMatrix (points, indices, covariance_matrix, centroid);
00321 
00322       // Extract the eigenvalues and eigenvectors
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       // Normalize the surface normal (eigenvector corresponding to the smallest eigenvalue)
00328       // Note: Remember to take care of the eigen_vectors ordering
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       // Hessian form (D = nc . p_plane (centroid here) + p)
00337       plane_parameters (3) = -1 * (plane_parameters (0) * centroid.x + plane_parameters (1) * centroid.y + plane_parameters (2) * centroid.z);
00338 
00339       // Compute the curvature surface change
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       // Compute the centroid
00355       geometry_msgs::Point32 centroid;
00356       computeCentroid (points, centroid);
00357 
00358       // Demean the pointset
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       // Compute the centroid
00392       geometry_msgs::Point32 centroid;
00393       computeCentroid (points, indices, centroid);
00394 
00395       // Demean the pointset
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       // Compute the angles between each neighbouring point and the query point itself
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       // Compute the maximal angle difference between two consecutive angles
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       // Get the angle difference between the last and the first
00460       dif = 2 * M_PI - angles[angles.size () - 1] + angles[0];
00461       if (max_dif < dif)
00462         max_dif = dif;
00463 
00464       // Check results
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);                     // Reserve space for 4 channels: nx, ny, nz, curvature
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++)                     // Get the nearest neighbors for all the point indices in the bounds
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;                     // Compute the point normals (nx, ny, nz), surface curvature estimates (c)
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;                                          // Delete the kd-tree
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);                     // Reserve space for 4 channels: nx, ny, nz, curvature
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++)                     // Get the nearest neighbors for all the point indices in the bounds
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;                     // Compute the point normals (nx, ny, nz), surface curvature estimates (c)
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;                                          // Delete the kd-tree
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);                     // Reserve space for 4 channels: nx, ny, nz, curvature
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       // Peter: timing the different pieces of this
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++)                     // Get the nearest neighbors for all the point indices in the bounds
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;                     // Compute the point normals (nx, ny, nz), surface curvature estimates (c)
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;                                          // Delete the kd-tree
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);                     // Reserve space for 4 channels: nx, ny, nz, curvature
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++)                     // Get the nearest neighbors for all the point indices in the bounds
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;                     // Compute the point normals (nx, ny, nz), surface curvature estimates (c)
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;                                          // Delete the kd-tree
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       // Reduce by a factor of N
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);                     // Reserve space for 4 channels: nx, ny, nz, curvature
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       // Reserve space for 4 channels: nx, ny, nz, curvature
00691       for (unsigned int d = orig_dims; d < points.channels.size (); d++)
00692         points.channels[d].values.resize (nr_points);
00693 
00694       // Reserve enough space for the neighborhood
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         // Obtain the <u,v> pixel values
00704         int u = i / width;
00705         int v = i % width;
00706 
00707         // Get every Nth pixel in both rows, cols
00708         if ((u % downsample_factor != 0) || (v % downsample_factor != 0))
00709           continue;
00710 
00711         // Copy the data
00712         points.points[j] = surface.points[i];
00713 
00714         // Get all point neighbors in a k x k window
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             // If the index is not in the point cloud, continue
00724             if (idx < 0 || idx >= (int)surface.points.size ())
00725               continue;
00726             // If the difference in Z (depth) between the query point and the current neighbor is smaller than max_z
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           //ROS_ERROR ("Not enough neighboring indices found for point %d (%f, %f, %f).", i, surface.points[i].x, surface.points[i].y, surface.points[i].z);
00740           continue;
00741         }
00742 
00743         // Compute the point normals (nx, ny, nz), surface curvature estimates (c)
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       // Reduce by a factor of N
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);                     // Reserve space for 4 channels: nx, ny, nz, curvature
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       // Reserve space for 4 channels: nx, ny, nz, curvature
00788       for (unsigned int d = orig_dims; d < points.channels.size (); d++)
00789         points.channels[d].values.resize (nr_points);
00790 
00791       // Reserve enough space for the neighborhood
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         // Obtain the <u,v> pixel values
00801         int u = i / width;
00802         int v = i % width;
00803 
00804         // Get every Nth pixel in both rows, cols
00805         if ((u % downsample_factor != 0) || (v % downsample_factor != 0))
00806           continue;
00807 
00808         // Copy the data
00809         points.points[j] = surface->points[i];
00810 
00811         // Get all point neighbors in a k x k window
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             // If the index is not in the point cloud, continue
00821             if (idx < 0 || idx >= (int)surface->points.size ())
00822               continue;
00823             // If the difference in Z (depth) between the query point and the current neighbor is smaller than max_z
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           //ROS_ERROR ("Not enough neighboring indices found for point %d (%f, %f, %f).", i, surface->points[i].x, surface->points[i].y, surface->points[i].z);
00837           continue;
00838         }
00839 
00840         // Compute the point normals (nx, ny, nz), surface curvature estimates (c)
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       // Reduce by a factor of N
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);                     // Reserve space for 4 channels: nx, ny, nz, curvature
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       // Reserve space for 4 channels: nx, ny, nz, curvature
00888       for (unsigned int d = orig_dims; d < points.channels.size (); d++)
00889         points.channels[d].values.resize (nr_points);
00890 
00891       // Reserve enough space for the neighborhood
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         // Obtain the <u,v> pixel values
00901         int u = i / width;
00902         int v = i % width;
00903 
00904         // Get every Nth pixel in both rows, cols
00905         if ((u % downsample_factor != 0) || (v % downsample_factor != 0))
00906           continue;
00907 
00908         // Copy the data
00909         points.points[j] = surface.points[i];
00910 
00911         // Get all point neighbors in a k x k window
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             // If the index is not in the point cloud, continue
00922             if (idx < 0 || idx >= (int)surface.points.size ())
00923               continue;
00924 
00925             // Get the angle between the lines formed with the viewpoint
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             // If the difference in Z (depth) between the query point and the current neighbor is smaller than max_z
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           //ROS_ERROR ("Not enough neighboring indices found for point %d (%f, %f, %f).", i, surface.points[i].x, surface.points[i].y, surface.points[i].z);
00946           continue;
00947         }
00948 
00949         // Compute the point normals (nx, ny, nz), surface curvature estimates (c)
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       // Copy the header, and reserve the correct number of channels and points, etc
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         // Obtain the <u,v> pixel values
00993         int u = i / width;
00994         int v = i % width;
00995 
00996         // Get all point neighbors in a k x k window
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             // If the index is not in the point cloud, continue
01009             if (idx < 0 || idx >= (int)points.points.size ())
01010               continue;
01011 
01012             // Get the angle between the lines formed with the viewpoint
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           // Copy the data
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       // Create a tree for these points
01060       cloud_kdtree::KdTree* tree = new cloud_kdtree::KdTreeANN (points, indices);
01061 
01062       int nr_points = indices.size ();
01063       // Create a bool vector of processed point indices, and initialize it to false
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       // Process all points in the indices vector
01070       for (int i = 0; i < nr_points; i++)
01071       {
01072         if (processed[i])
01073           continue;
01074 
01075         std::vector<int> seed_queue;
01076 //        seed_queue.reserve (indices.size ());
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           // Search for sq_idx
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++)       // nn_indices[0] should be sq_idx
01088           {
01089             if (processed.at (nn_indices[j]))                         // Has this point been processed before ?
01090               continue;
01091 
01092             processed[nn_indices[j]] = true;
01093             if (nx_idx != -1)                                         // Are point normals present ?
01094             {
01095               // [-1;1]
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             // If normal information is not present, perform a simple Euclidean clustering
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         // If this queue is satisfactory, add to the clusters
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       // Destroy the tree
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       // Create a tree for these points
01154       cloud_kdtree::KdTree* tree = new cloud_kdtree::KdTreeANN (points);
01155 
01156       int nr_points = points.points.size ();
01157       // Create a bool vector of processed point indices, and initialize it to false
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       // Process all points in the indices vector
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           // Search for sq_idx
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++)       // nn_indices[0] should be sq_idx
01181           {
01182             if (processed.at (nn_indices[j]))                         // Has this point been processed before ?
01183               continue;
01184 
01185             processed[nn_indices[j]] = true;
01186             if (nx_idx != -1)                                         // Are point normals present ?
01187             {
01188               // [-1;1]
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             // If normal information is not present, perform a simple Euclidean clustering
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         // If this queue is satisfactory, add to the clusters
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           // Remove duplicates
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       // Destroy the tree
01225       delete tree;
01226     }
01227         
01228 
01229   }
01230 }


door_handle_detector
Author(s): Radu Bogdan Rusu, Marius
autogenerated on Wed Dec 11 2013 14:17:01