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
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
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
00087 }EIGEN_ALIGN16;
00088
00089
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
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
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
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
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
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
00258 N.triangularView<Eigen::StrictlyLower>() = N.triangularView<Eigen::StrictlyUpper>().transpose();
00259
00260
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
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
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
00311 Eigen::Matrix<double, 3, Eigen::Dynamic> samples_near_surf;
00312 int sample_num;
00313
00314
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
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
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
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
00372 for (int t = 0; t < sample_num; t++)
00373 {
00374
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
00389 std::sort(list.begin(), list.end(), isSecondElementSmaller);
00390
00391
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
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();
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;
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
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_;
00559 unsigned int num_threads_;
00560 std::vector<std::vector<int> > neighborhoods_;
00561 std::vector<int> neighborhood_centroids_;
00562 double time_taubin;
00563 double time_curvature;
00564 };
00565 }
00566
00567 #endif // PCL_FEATURES_CURVATURE_ESTIMATION_TAUBIN_H_