curvature_estimation_taubin.h
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2014, Andreas ten Pas
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *
00018  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00019  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00020  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00021  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00022  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00023  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00024  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00026  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00027  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00028  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00029  *  POSSIBILITY OF SUCH DAMAGE.
00030  */
00031 
00032 #ifndef PCL_FEATURES_CURVATURE_ESTIMATION_TAUBIN_H_
00033 #define PCL_FEATURES_CURVATURE_ESTIMATION_TAUBIN_H_
00034 
00035 #include <pcl/features/feature.h>
00036 #include <pcl/filters/extract_indices.h>
00037 #include <pcl/point_types.h>
00038 #include <Eigen/Dense>
00039 #include <vector>
00040 
00041 // Lapack function to solve the generalized eigenvalue problem
00042 extern "C" void dggev_(const char* JOBVL, const char* JOBVR, const int* N, const double* A, const int* LDA,
00043                        const double* B, const int* LDB, double* ALPHAR, double* ALPHAI, double* BETA, double* VL,
00044                        const int* LDVL, double* VR, const int* LDVR, double* WORK, const int* LWORK, int* INFO);
00045 
00046 namespace pcl
00047 {
00051 struct PointCurvatureTaubin
00052 {
00053   union
00054   {
00055     float normal[4];
00056     struct
00057     {
00058       float normal_x;
00059       float normal_y;
00060       float normal_z;
00061     };
00062   };
00063   union
00064   {
00065     float curvature_axis[4];
00066     struct
00067     {
00068       float curvature_axis_x;
00069       float curvature_axis_y;
00070       float curvature_axis_z;
00071     };
00072   };
00073   union
00074   {
00075     float curvature_centroid[4];
00076     struct
00077     {
00078       float curvature_centroid_x;
00079       float curvature_centroid_y;
00080       float curvature_centroid_z;
00081     };
00082   };
00083   union
00084   {
00085     float median_curvature;
00086   };EIGEN_MAKE_ALIGNED_OPERATOR_NEW // make sure the allocators are aligned (SSE optimization)
00087 }EIGEN_ALIGN16;
00088 
00089 // size of matrices in Taubin Quadric Fitting
00090 const int TAUBIN_MATRICES_SIZE = 10;
00091 
00098 template<typename PointInT, typename PointOutT>
00099 class CurvatureEstimationTaubin : public Feature<PointInT, PointOutT>
00100 {
00101 public:
00102   using Feature<PointInT, PointOutT>::feature_name_;
00103   using Feature<PointInT, PointOutT>::indices_;
00104   using Feature<PointInT, PointOutT>::input_;
00105   using Feature<PointInT, PointOutT>::surface_;
00106   using Feature<PointInT, PointOutT>::k_;
00107   using Feature<PointInT, PointOutT>::search_radius_;
00108   using Feature<PointInT, PointOutT>::search_parameter_;
00109 
00110   typedef typename Feature<PointInT, PointOutT>::PointCloudOut PointCloudOut;
00111 
00115   CurvatureEstimationTaubin(unsigned int num_threads = 0)
00116   {
00117     num_threads_ = num_threads;
00118     feature_name_ = "CurvatureEstimationTaubin";
00119   }
00120 
00130   inline void fitQuadric(const std::vector<int> &indices, Eigen::VectorXd &quadric_parameters,
00131                          Eigen::Vector3d &quadric_centroid, Eigen::Matrix3d &quadric_covariance_matrix)
00132   {
00133     int n = indices.size();
00134 
00135     // calculate matrices M and N
00136     Eigen::Matrix<double, TAUBIN_MATRICES_SIZE, TAUBIN_MATRICES_SIZE> M;
00137     Eigen::Matrix<double, TAUBIN_MATRICES_SIZE, TAUBIN_MATRICES_SIZE> N;
00138     M.setZero(10, 10);
00139     N.setZero(10, 10);
00140 
00141     for (int i = 0; i < n; i++)
00142     {
00143       if (isnan(this->input_->points[indices[i]].x))
00144         continue;
00145 
00146       double x = this->input_->points[indices[i]].x;
00147       double y = this->input_->points[indices[i]].y;
00148       double z = this->input_->points[indices[i]].z;
00149       double x2 = x * x;
00150       double y2 = y * y;
00151       double z2 = z * z;
00152       double xy = x * y;
00153       double yz = y * z;
00154       double xz = x * z;
00155 
00156       // required calculations for M
00157       M(0, 0) += x2 * x2;
00158       M(0, 1) += x2 * y2;
00159       M(0, 2) += x2 * z2;
00160       M(0, 3) += x2 * xy;
00161       M(0, 4) += x2 * yz;
00162       M(0, 5) += x2 * xz;
00163       M(0, 6) += x2 * x;
00164       M(0, 7) += x2 * y;
00165       M(0, 8) += x2 * z;
00166       M(0, 9) += x2;
00167       M(1, 1) += y2 * y2;
00168       M(1, 2) += y2 * z2;
00169       M(1, 3) += y2 * xy;
00170       M(1, 4) += y2 * yz;
00171       M(1, 5) += y2 * xz;
00172       M(1, 6) += y2 * x;
00173       M(1, 7) += y2 * y;
00174       M(1, 8) += y2 * z;
00175       M(1, 9) += y2;
00176       M(2, 2) += z2 * z2;
00177       M(2, 3) += z2 * xy;
00178       M(2, 4) += z2 * yz;
00179       M(2, 5) += z2 * xz;
00180       M(2, 6) += z2 * x;
00181       M(2, 7) += z2 * y;
00182       M(2, 8) += z2 * z;
00183       M(2, 9) += z2;
00184       M(3, 8) += x * yz;
00185       M(3, 9) += xy;
00186       M(4, 9) += yz;
00187       M(5, 9) += xz;
00188       M(6, 9) += x;
00189       M(7, 9) += y;
00190       M(8, 9) += z;
00191 
00192       // repeating elements in M
00193       M(3, 3) = M(0, 1);
00194       M(5, 5) = M(0, 2);
00195       M(3, 5) = M(0, 4);
00196       M(3, 6) = M(0, 7);
00197       M(5, 6) = M(0, 8);
00198       M(6, 6) = M(0, 9);
00199 
00200       M(4, 4) = M(1, 2);
00201       M(3, 4) = M(1, 5);
00202       M(3, 7) = M(1, 6);
00203       M(4, 7) = M(1, 8);
00204       M(7, 7) = M(1, 9);
00205 
00206       M(4, 5) = M(2, 3);
00207       M(5, 8) = M(2, 6);
00208       M(4, 8) = M(2, 7);
00209       M(8, 8) = M(2, 9);
00210 
00211       M(4, 6) = M(3, 8);
00212       M(5, 7) = M(3, 8);
00213       M(6, 7) = M(3, 9);
00214 
00215       M(7, 8) = M(4, 9);
00216 
00217       M(6, 8) = M(5, 9);
00218 
00219       // required calculations for N
00220       N(0, 0) += 4 * x2;
00221       N(0, 3) += 2 * xy;
00222       N(0, 5) += 2 * xz;
00223       N(0, 6) += 2 * x;
00224 
00225       N(1, 1) += 4 * y2;
00226       N(1, 3) += 2 * xy;
00227       N(1, 4) += 2 * yz;
00228       N(1, 7) += 2 * y;
00229 
00230       N(2, 2) += 4 * z2;
00231       N(2, 4) += 2 * yz;
00232       N(2, 5) += 2 * xz;
00233       N(2, 8) += 2 * z;
00234 
00235       N(3, 3) += x2 + y2;
00236       N(3, 4) += xz;
00237       N(3, 5) += yz;
00238       N(3, 6) += y;
00239       N(3, 7) += x;
00240 
00241       N(4, 4) += y2 + z2;
00242       N(4, 5) += xy;
00243       N(4, 7) += z;
00244       N(4, 8) += y;
00245 
00246       N(5, 5) += x2 + z2;
00247       N(5, 6) += z;
00248       N(5, 8) += x;
00249     }
00250 
00251     M(9, 9) = n;
00252     // reflect upper triangular part in lower triangular part
00253     M.triangularView<Eigen::StrictlyLower>() = M.triangularView<Eigen::StrictlyUpper>().transpose();
00254     N(6, 6) = n;
00255     N(7, 7) = n;
00256     N(8, 8) = n;
00257     // reflect upper triangular part in lower triangular part
00258     N.triangularView<Eigen::StrictlyLower>() = N.triangularView<Eigen::StrictlyUpper>().transpose();
00259 
00260     // solve generalized Eigen problem to find quadric parameters
00261     Eigen::MatrixXd eigen_vectors;
00262     Eigen::MatrixXd lambda;
00263     this->solveGeneralizedEigenProblem(M, N, eigen_vectors, lambda);
00264     Eigen::VectorXd eigen_values = lambda.col(0).cwiseQuotient(lambda.col(2));
00265     int min_index;
00266     eigen_values.segment(0, 9).minCoeff(&min_index);
00267     quadric_parameters = eigen_vectors.col(min_index);
00268     quadric_parameters.segment(3, 3) *= 0.5;
00269 
00270     // compute centroid and covariance matrix of quadric
00271     this->unpackQuadric(quadric_parameters, quadric_centroid, quadric_covariance_matrix);
00272   }
00273 
00278   static inline bool isSecondElementSmaller(const std::vector<double>& p1, const std::vector<double>& p2)
00279   {
00280     return p1[1] < p2[1];
00281   }
00282 
00294   inline void estimateMedianCurvature(const std::vector<int> &indices, const Eigen::VectorXd &quadric_parameters,
00295                                       double &median_curvature, Eigen::Vector3d &normal,
00296                                       Eigen::Vector3d &curvature_axis, Eigen::Vector3d &curvature_centroid,
00297                                       bool is_deterministic = false)
00298   {
00299     // quadric parameters in implicit form
00300     double a = quadric_parameters(0);
00301     double b = quadric_parameters(1);
00302     double c = quadric_parameters(2);
00303     double d = 2 * quadric_parameters(3);
00304     double e = 2 * quadric_parameters(4);
00305     double f = 2 * quadric_parameters(5);
00306     double g = quadric_parameters(6);
00307     double h = quadric_parameters(7);
00308     double i = quadric_parameters(8);
00309 
00310     // create matrix to store <sample_num> samples that are close to the quadric
00311     Eigen::Matrix<double, 3, Eigen::Dynamic> samples_near_surf;
00312     int sample_num;
00313 
00314     // stochastic algorithm: look at a sample of 50 neighborhood points
00315     if (!is_deterministic)
00316     {
00317       sample_num = 50;
00318       Eigen::Matrix<double, 3, Eigen::Dynamic> samples_matrix(3, sample_num);
00319 
00320       for (int t = 0; t < sample_num; t++)
00321       {
00322         int r = rand() % indices.size();
00323 
00324         if (isnan(this->input_->points[indices[r]].x))
00325           continue;
00326 
00327         samples_matrix.col(t) << this->input_->points[indices[r]].x, this->input_->points[indices[r]].y, this->input_->points[indices[r]].z;
00328       }
00329 
00330       samples_near_surf = samples_matrix;
00331     }
00332     // deterministic algorithm: look at all points in the neighborhood
00333     else
00334     {
00335       Eigen::Matrix<double, 3, Eigen::Dynamic> samples_matrix(3, indices.size());
00336       sample_num = 0;
00337 
00338       for (std::size_t t = 0; t < indices.size(); t++)
00339       {
00340         if (isnan(this->input_->points[indices[t]].x))
00341           continue;
00342 
00343         samples_matrix(0, t) = this->input_->points[indices[t]].x;
00344         samples_matrix(1, t) = this->input_->points[indices[t]].y;
00345         samples_matrix(2, t) = this->input_->points[indices[t]].z;
00346         sample_num++;
00347       }
00348 
00349       samples_matrix.conservativeResize(3, sample_num);
00350       samples_near_surf = samples_matrix;
00351       sample_num = samples_near_surf.cols();
00352     }
00353 
00354     // calculate normals and gradient magnitude at each of these pts
00355     Eigen::MatrixXd fx =
00356         (2 * a * samples_near_surf.row(0) + d * samples_near_surf.row(1) + f * samples_near_surf.row(2)).array() + g;
00357     Eigen::MatrixXd fy =
00358         (2 * b * samples_near_surf.row(1) + d * samples_near_surf.row(0) + e * samples_near_surf.row(2)).array() + h;
00359     Eigen::MatrixXd fz =
00360         (2 * c * samples_near_surf.row(2) + e * samples_near_surf.row(1) + f * samples_near_surf.row(0)).array() + i;
00361     Eigen::MatrixXd normals(3, sample_num);
00362     normals << fx, fy, fz;
00363     Eigen::MatrixXd gradient_magnitude = ((normals.cwiseProduct(normals)).colwise().sum()).cwiseSqrt();
00364     normals = normals.cwiseQuotient(gradient_magnitude.replicate(3, 1));
00365 
00366     // calculate 2nd derivative of implicit quadric form
00367     Eigen::Matrix3d second_derivative_f;
00368     second_derivative_f << 2 * a, d, f, d, 2 * b, e, f, e, 2 * c;
00369     std::vector < std::vector<double> > list(sample_num, std::vector<double>(2));
00370 
00371     // iterate over the samples
00372     for (int t = 0; t < sample_num; t++)
00373     {
00374       // compute curvature by solving Eigen problem
00375       Eigen::Matrix3d curvature_matrix = -1
00376           * (Eigen::MatrixXd::Identity(3, 3) - normals.col(t) * normals.col(t).transpose()) * second_derivative_f
00377           / gradient_magnitude(t);
00378       Eigen::EigenSolver<Eigen::Matrix3d> eigen_solver(curvature_matrix);
00379       Eigen::Vector3d curvatures = eigen_solver.eigenvalues().real();
00380       int index;
00381       double max_coeff = curvatures.cwiseAbs().maxCoeff(&index);
00382       list[t][0] = t;
00383       list[t][1] = curvatures(index);
00384       normals.col(t) *= this->sign(list[t][1]);
00385       list[t][1] = fabs(list[t][1]);
00386     }
00387 
00388     // sort list of curvatures in increasing order
00389     std::sort(list.begin(), list.end(), isSecondElementSmaller);
00390 
00391     // compute median curvature
00392     int median_curvature_index = list[sample_num / 2 - 1][0];
00393     Eigen::Vector3d median_curvature_point = samples_near_surf.col(median_curvature_index);
00394     median_curvature = list[sample_num / 2 - 1][1];
00395     normal = normals.col(median_curvature_index);
00396     curvature_centroid = median_curvature_point + (normal / median_curvature);
00397 
00398     // find curvature axis by solving Eigen problem
00399     Eigen::Matrix3d curvature_matrix = (-1) * (Eigen::MatrixXd::Identity(3, 3) - normal * normal.transpose())
00400         * second_derivative_f / gradient_magnitude(median_curvature_index);
00401     Eigen::EigenSolver<Eigen::Matrix3d> eigen_solver(curvature_matrix);
00402     Eigen::Matrix3d curvature_vectors = eigen_solver.eigenvectors().real();
00403     int max_index;
00404     eigen_solver.eigenvalues().real().cwiseAbs().maxCoeff(&max_index);
00405     curvature_axis = curvature_vectors.col(max_index).cross(normal);
00406   }
00407 
00411   inline void setNumSamples(int num_samples)
00412   {
00413     num_samples_ = num_samples;
00414   }
00415 
00419   inline void setNumThreads(int num_threads)
00420   {
00421     num_threads_ = num_threads;
00422   }
00423 
00426   inline std::vector<std::vector<int> > const &getNeighborhoods() const
00427   {
00428     return neighborhoods_;
00429   }
00430   ;
00431 
00434   inline std::vector<int> const &getNeighborhoodCentroids() const
00435   {
00436     return neighborhood_centroids_;
00437   }
00438   ;
00439 
00445   void
00446   computeFeature(const Eigen::MatrixXd &samples, PointCloudOut &output);
00447 
00448 protected:
00449 
00457   void
00458   computeFeature(PointCloudOut &output);
00459 
00460 private:
00461 
00467   void
00468   computeFeature(const std::vector<int> &nn_indices, int index, PointCloudOut &output);
00469 
00477   inline void unpackQuadric(const Eigen::VectorXd &quadric_parameters, Eigen::Vector3d &quadric_centroid,
00478                             Eigen::Matrix3d &quadric_covariance_matrix)
00479   {
00480     double a = quadric_parameters(0);
00481     double b = quadric_parameters(1);
00482     double c = quadric_parameters(2);
00483     double d = quadric_parameters(3);
00484     double e = quadric_parameters(4);
00485     double f = quadric_parameters(5);
00486     double g = quadric_parameters(6);
00487     double h = quadric_parameters(7);
00488     double i = quadric_parameters(8);
00489     double j = quadric_parameters(9);
00490 
00491     Eigen::Matrix3d parameter_matrix;
00492     parameter_matrix << a, d, f, d, b, e, f, e, c;
00493     Eigen::Vector3d ghi;
00494     ghi << g, h, i;
00495     Eigen::Matrix3d inverse_parameter_matrix = parameter_matrix.inverse();
00496     quadric_centroid = -0.5 * inverse_parameter_matrix * ghi;
00497     double k = j - 0.25 * ghi.transpose() * inverse_parameter_matrix * ghi;
00498     quadric_covariance_matrix = -1 * parameter_matrix / k;
00499   }
00500 
00504   inline int sign(double x)
00505   {
00506     if (x > 0)
00507       return 1;
00508     if (x < 0)
00509       return -1;
00510     return 0;
00511   }
00512 
00522   inline bool solveGeneralizedEigenProblem(const Eigen::MatrixXd& A, const Eigen::MatrixXd& B, Eigen::MatrixXd& v,
00523                                            Eigen::MatrixXd& lambda)
00524   {
00525     int N = A.cols(); // Number of columns of A and B. Number of rows of v.
00526     if (B.cols() != N || A.rows() != N || B.rows() != N)
00527       return false;
00528 
00529     v.resize(N, N);
00530     lambda.resize(N, 3);
00531 
00532     int LDA = A.outerStride();
00533     int LDB = B.outerStride();
00534     int LDV = v.outerStride();
00535 
00536     double WORKDUMMY;
00537     int LWORK = -1; // Request optimum work size.
00538     int INFO = 0;
00539 
00540     double* alphar = const_cast<double*>(lambda.col(0).data());
00541     double* alphai = const_cast<double*>(lambda.col(1).data());
00542     double* beta = const_cast<double*>(lambda.col(2).data());
00543 
00544     // Get the optimum work size.
00545     dggev_("N", "V", &N, A.data(), &LDA, B.data(), &LDB, alphar, alphai, beta, 0, &LDV, v.data(), &LDV, &WORKDUMMY,
00546            &LWORK, &INFO);
00547 
00548     LWORK = int(WORKDUMMY) + 32;
00549     Eigen::VectorXd WORK(LWORK);
00550 
00551     dggev_("N", "V", &N, A.data(), &LDA, B.data(), &LDB, alphar, alphai, beta, 0, &LDV, v.data(), &LDV, WORK.data(),
00552            &LWORK, &INFO);
00553 
00554     return INFO == 0;
00555   }
00556   ;
00557 
00558   unsigned int num_samples_; // number of samples (neighborhoods)
00559   unsigned int num_threads_; // number of threads for parallelization
00560   std::vector<std::vector<int> > neighborhoods_; // list of lists of point cloud indices for each neighborhood
00561   std::vector<int> neighborhood_centroids_; // list of point cloud indices corresponding to neighborhood centroids
00562   double time_taubin;
00563   double time_curvature;
00564 };
00565 }
00566 
00567 #endif // PCL_FEATURES_CURVATURE_ESTIMATION_TAUBIN_H_


handle_detector
Author(s):
autogenerated on Thu Jun 6 2019 17:36:23