/* * Software License Agreement (BSD License) * * Point Cloud Library (PCL) - www.pointclouds.org * Copyright (c) 2009-2011, Willow Garage, Inc. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of Willow Garage, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $Id$ * */ #ifndef PCL_SURFACE_IMPL_MLS_H_ #define PCL_SURFACE_IMPL_MLS_H_ #include #include #include // for getMinMax3D #include #include #include #include // for KdTree #include // for OrganizedNeighbor #include // for cross #include // for inverse #ifdef _OPENMP #include #endif ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::MovingLeastSquares::process (PointCloudOut &output) { // Reset or initialize the collection of indices corresponding_input_indices_.reset (new PointIndices); // Check if normals have to be computed/saved if (compute_normals_) { normals_.reset (new NormalCloud); // Copy the header normals_->header = input_->header; // Clear the fields in case the method exits before computation normals_->width = normals_->height = 0; normals_->points.clear (); } // Copy the header output.header = input_->header; output.width = output.height = 0; output.clear (); if (search_radius_ <= 0 || sqr_gauss_param_ <= 0) { PCL_ERROR ("[pcl::%s::process] Invalid search radius (%f) or Gaussian parameter (%f)!\n", getClassName ().c_str (), search_radius_, sqr_gauss_param_); return; } // Check if distinct_cloud_ was set if (upsample_method_ == DISTINCT_CLOUD && !distinct_cloud_) { PCL_ERROR ("[pcl::%s::process] Upsample method was set to DISTINCT_CLOUD, but no distinct cloud was specified.\n", getClassName ().c_str ()); return; } if (!initCompute ()) return; // Initialize the spatial locator if (!tree_) { KdTreePtr tree; if (input_->isOrganized ()) tree.reset (new pcl::search::OrganizedNeighbor ()); else tree.reset (new pcl::search::KdTree (false)); setSearchMethod (tree); } // Send the surface dataset to the spatial locator tree_->setInputCloud (input_); switch (upsample_method_) { // Initialize random number generator if necessary case (RANDOM_UNIFORM_DENSITY): { std::random_device rd; rng_.seed (rd()); const double tmp = search_radius_ / 2.0; rng_uniform_distribution_.reset (new std::uniform_real_distribution<> (-tmp, tmp)); break; } case (VOXEL_GRID_DILATION): case (DISTINCT_CLOUD): { if (!cache_mls_results_) PCL_WARN ("The cache mls results is forced when using upsampling method VOXEL_GRID_DILATION or DISTINCT_CLOUD.\n"); cache_mls_results_ = true; break; } default: break; } if (cache_mls_results_) { mls_results_.resize (input_->size ()); } else { mls_results_.resize (1); // Need to have a reference to a single dummy result. } // Perform the actual surface reconstruction performProcessing (output); if (compute_normals_) { normals_->height = 1; normals_->width = normals_->size (); for (std::size_t i = 0; i < output.size (); ++i) { using FieldList = typename pcl::traits::fieldList::type; pcl::for_each_type (SetIfFieldExists (output[i], "normal_x", (*normals_)[i].normal_x)); pcl::for_each_type (SetIfFieldExists (output[i], "normal_y", (*normals_)[i].normal_y)); pcl::for_each_type (SetIfFieldExists (output[i], "normal_z", (*normals_)[i].normal_z)); pcl::for_each_type (SetIfFieldExists (output[i], "curvature", (*normals_)[i].curvature)); } } // Set proper widths and heights for the clouds output.height = 1; output.width = output.size (); deinitCompute (); } ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::MovingLeastSquares::computeMLSPointNormal (pcl::index_t index, const pcl::Indices &nn_indices, PointCloudOut &projected_points, NormalCloud &projected_points_normals, PointIndices &corresponding_input_indices, MLSResult &mls_result) const { // Note: this method is const because it needs to be thread-safe // (MovingLeastSquaresOMP calls it from multiple threads) mls_result.computeMLSSurface (*input_, index, nn_indices, search_radius_, order_); switch (upsample_method_) { case (NONE): { const MLSResult::MLSProjectionResults proj = mls_result.projectQueryPoint (projection_method_, nr_coeff_); addProjectedPointNormal (index, proj.point, proj.normal, mls_result.curvature, projected_points, projected_points_normals, corresponding_input_indices); break; } case (SAMPLE_LOCAL_PLANE): { // Uniformly sample a circle around the query point using the radius and step parameters for (float u_disp = -static_cast (upsampling_radius_); u_disp <= upsampling_radius_; u_disp += static_cast (upsampling_step_)) for (float v_disp = -static_cast (upsampling_radius_); v_disp <= upsampling_radius_; v_disp += static_cast (upsampling_step_)) if (u_disp * u_disp + v_disp * v_disp < upsampling_radius_ * upsampling_radius_) { MLSResult::MLSProjectionResults proj = mls_result.projectPointSimpleToPolynomialSurface (u_disp, v_disp); addProjectedPointNormal (index, proj.point, proj.normal, mls_result.curvature, projected_points, projected_points_normals, corresponding_input_indices); } break; } case (RANDOM_UNIFORM_DENSITY): { // Compute the local point density and add more samples if necessary const int num_points_to_add = static_cast (std::floor (desired_num_points_in_radius_ / 2.0 / static_cast (nn_indices.size ()))); // Just add the query point, because the density is good if (num_points_to_add <= 0) { // Just add the current point const MLSResult::MLSProjectionResults proj = mls_result.projectQueryPoint (projection_method_, nr_coeff_); addProjectedPointNormal (index, proj.point, proj.normal, mls_result.curvature, projected_points, projected_points_normals, corresponding_input_indices); } else { // Sample the local plane for (int num_added = 0; num_added < num_points_to_add;) { const double u = (*rng_uniform_distribution_) (rng_); const double v = (*rng_uniform_distribution_) (rng_); // Check if inside circle; if not, try another coin flip if (u * u + v * v > search_radius_ * search_radius_ / 4) continue; MLSResult::MLSProjectionResults proj; if (order_ > 1 && mls_result.num_neighbors >= 5 * nr_coeff_) proj = mls_result.projectPointSimpleToPolynomialSurface (u, v); else proj = mls_result.projectPointToMLSPlane (u, v); addProjectedPointNormal (index, proj.point, proj.normal, mls_result.curvature, projected_points, projected_points_normals, corresponding_input_indices); num_added++; } } break; } default: break; } } template void pcl::MovingLeastSquares::addProjectedPointNormal (pcl::index_t index, const Eigen::Vector3d &point, const Eigen::Vector3d &normal, double curvature, PointCloudOut &projected_points, NormalCloud &projected_points_normals, PointIndices &corresponding_input_indices) const { PointOutT aux; aux.x = static_cast (point[0]); aux.y = static_cast (point[1]); aux.z = static_cast (point[2]); // Copy additional point information if available copyMissingFields ((*input_)[index], aux); projected_points.push_back (aux); corresponding_input_indices.indices.push_back (index); if (compute_normals_) { pcl::Normal aux_normal; aux_normal.normal_x = static_cast (normal[0]); aux_normal.normal_y = static_cast (normal[1]); aux_normal.normal_z = static_cast (normal[2]); aux_normal.curvature = curvature; projected_points_normals.push_back (aux_normal); } } ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::MovingLeastSquares::performProcessing (PointCloudOut &output) { // Compute the number of coefficients nr_coeff_ = (order_ + 1) * (order_ + 2) / 2; #ifdef _OPENMP // (Maximum) number of threads const unsigned int threads = threads_ == 0 ? 1 : threads_; // Create temporaries for each thread in order to avoid synchronization typename PointCloudOut::CloudVectorType projected_points (threads); typename NormalCloud::CloudVectorType projected_points_normals (threads); std::vector corresponding_input_indices (threads); #endif // For all points #pragma omp parallel for \ default(none) \ shared(corresponding_input_indices, projected_points, projected_points_normals) \ schedule(dynamic,1000) \ num_threads(threads) for (int cp = 0; cp < static_cast (indices_->size ()); ++cp) { // Allocate enough space to hold the results of nearest neighbor searches // \note resize is irrelevant for a radiusSearch (). pcl::Indices nn_indices; std::vector nn_sqr_dists; // Get the initial estimates of point positions and their neighborhoods if (searchForNeighbors ((*indices_)[cp], nn_indices, nn_sqr_dists)) { // Check the number of nearest neighbors for normal estimation (and later for polynomial fit as well) if (nn_indices.size () >= 3) { // This thread's ID (range 0 to threads-1) #ifdef _OPENMP const int tn = omp_get_thread_num (); // Size of projected points before computeMLSPointNormal () adds points std::size_t pp_size = projected_points[tn].size (); #else PointCloudOut projected_points; NormalCloud projected_points_normals; #endif // Get a plane approximating the local surface's tangent and project point onto it const int index = (*indices_)[cp]; std::size_t mls_result_index = 0; if (cache_mls_results_) mls_result_index = index; // otherwise we give it a dummy location. #ifdef _OPENMP computeMLSPointNormal (index, nn_indices, projected_points[tn], projected_points_normals[tn], corresponding_input_indices[tn], mls_results_[mls_result_index]); // Copy all information from the input cloud to the output points (not doing any interpolation) for (std::size_t pp = pp_size; pp < projected_points[tn].size (); ++pp) copyMissingFields ((*input_)[(*indices_)[cp]], projected_points[tn][pp]); #else computeMLSPointNormal (index, nn_indices, projected_points, projected_points_normals, *corresponding_input_indices_, mls_results_[mls_result_index]); // Append projected points to output output.insert (output.end (), projected_points.begin (), projected_points.end ()); if (compute_normals_) normals_->insert (normals_->end (), projected_points_normals.begin (), projected_points_normals.end ()); #endif } } } #ifdef _OPENMP // Combine all threads' results into the output vectors for (unsigned int tn = 0; tn < threads; ++tn) { output.insert (output.end (), projected_points[tn].begin (), projected_points[tn].end ()); corresponding_input_indices_->indices.insert (corresponding_input_indices_->indices.end (), corresponding_input_indices[tn].indices.begin (), corresponding_input_indices[tn].indices.end ()); if (compute_normals_) normals_->insert (normals_->end (), projected_points_normals[tn].begin (), projected_points_normals[tn].end ()); } #endif // Perform the distinct-cloud or voxel-grid upsampling performUpsampling (output); } ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::MovingLeastSquares::performUpsampling (PointCloudOut &output) { if (upsample_method_ == DISTINCT_CLOUD) { corresponding_input_indices_.reset (new PointIndices); for (std::size_t dp_i = 0; dp_i < distinct_cloud_->size (); ++dp_i) // dp_i = distinct_point_i { // Distinct cloud may have nan points, skip them if (!std::isfinite ((*distinct_cloud_)[dp_i].x)) continue; // Get 3D position of point //Eigen::Vector3f pos = (*distinct_cloud_)[dp_i].getVector3fMap (); pcl::Indices nn_indices; std::vector nn_dists; tree_->nearestKSearch ((*distinct_cloud_)[dp_i], 1, nn_indices, nn_dists); const auto input_index = nn_indices.front (); // If the closest point did not have a valid MLS fitting result // OR if it is too far away from the sampled point if (mls_results_[input_index].valid == false) continue; Eigen::Vector3d add_point = (*distinct_cloud_)[dp_i].getVector3fMap ().template cast (); MLSResult::MLSProjectionResults proj = mls_results_[input_index].projectPoint (add_point, projection_method_, 5 * nr_coeff_); addProjectedPointNormal (input_index, proj.point, proj.normal, mls_results_[input_index].curvature, output, *normals_, *corresponding_input_indices_); } } // For the voxel grid upsampling method, generate the voxel grid and dilate it // Then, project the newly obtained points to the MLS surface if (upsample_method_ == VOXEL_GRID_DILATION) { corresponding_input_indices_.reset (new PointIndices); MLSVoxelGrid voxel_grid (input_, indices_, voxel_size_); for (int iteration = 0; iteration < dilation_iteration_num_; ++iteration) voxel_grid.dilate (); for (typename MLSVoxelGrid::HashMap::iterator m_it = voxel_grid.voxel_grid_.begin (); m_it != voxel_grid.voxel_grid_.end (); ++m_it) { // Get 3D position of point Eigen::Vector3f pos; voxel_grid.getPosition (m_it->first, pos); PointInT p; p.x = pos[0]; p.y = pos[1]; p.z = pos[2]; pcl::Indices nn_indices; std::vector nn_dists; tree_->nearestKSearch (p, 1, nn_indices, nn_dists); const auto input_index = nn_indices.front (); // If the closest point did not have a valid MLS fitting result // OR if it is too far away from the sampled point if (mls_results_[input_index].valid == false) continue; Eigen::Vector3d add_point = p.getVector3fMap ().template cast (); MLSResult::MLSProjectionResults proj = mls_results_[input_index].projectPoint (add_point, projection_method_, 5 * nr_coeff_); addProjectedPointNormal (input_index, proj.point, proj.normal, mls_results_[input_index].curvature, output, *normals_, *corresponding_input_indices_); } } } ////////////////////////////////////////////////////////////////////////////////////////////// pcl::MLSResult::MLSResult (const Eigen::Vector3d &a_query_point, const Eigen::Vector3d &a_mean, const Eigen::Vector3d &a_plane_normal, const Eigen::Vector3d &a_u, const Eigen::Vector3d &a_v, const Eigen::VectorXd &a_c_vec, const int a_num_neighbors, const float a_curvature, const int a_order) : query_point (a_query_point), mean (a_mean), plane_normal (a_plane_normal), u_axis (a_u), v_axis (a_v), c_vec (a_c_vec), num_neighbors (a_num_neighbors), curvature (a_curvature), order (a_order), valid (true) {} void pcl::MLSResult::getMLSCoordinates (const Eigen::Vector3d &pt, double &u, double &v, double &w) const { Eigen::Vector3d delta = pt - mean; u = delta.dot (u_axis); v = delta.dot (v_axis); w = delta.dot (plane_normal); } void pcl::MLSResult::getMLSCoordinates (const Eigen::Vector3d &pt, double &u, double &v) const { Eigen::Vector3d delta = pt - mean; u = delta.dot (u_axis); v = delta.dot (v_axis); } double pcl::MLSResult::getPolynomialValue (const double u, const double v) const { // Compute the polynomial's terms at the current point // Example for second order: z = a + b*y + c*y^2 + d*x + e*x*y + f*x^2 int j = 0; double u_pow = 1; double result = 0; for (int ui = 0; ui <= order; ++ui) { double v_pow = 1; for (int vi = 0; vi <= order - ui; ++vi) { result += c_vec[j++] * u_pow * v_pow; v_pow *= v; } u_pow *= u; } return (result); } pcl::MLSResult::PolynomialPartialDerivative pcl::MLSResult::getPolynomialPartialDerivative (const double u, const double v) const { // Compute the displacement along the normal using the fitted polynomial // and compute the partial derivatives needed for estimating the normal PolynomialPartialDerivative d{}; Eigen::VectorXd u_pow (order + 2), v_pow (order + 2); int j = 0; d.z = d.z_u = d.z_v = d.z_uu = d.z_vv = d.z_uv = 0; u_pow (0) = v_pow (0) = 1; for (int ui = 0; ui <= order; ++ui) { for (int vi = 0; vi <= order - ui; ++vi) { // Compute displacement along normal d.z += u_pow (ui) * v_pow (vi) * c_vec[j]; // Compute partial derivatives if (ui >= 1) d.z_u += c_vec[j] * ui * u_pow (ui - 1) * v_pow (vi); if (vi >= 1) d.z_v += c_vec[j] * vi * u_pow (ui) * v_pow (vi - 1); if (ui >= 1 && vi >= 1) d.z_uv += c_vec[j] * ui * u_pow (ui - 1) * vi * v_pow (vi - 1); if (ui >= 2) d.z_uu += c_vec[j] * ui * (ui - 1) * u_pow (ui - 2) * v_pow (vi); if (vi >= 2) d.z_vv += c_vec[j] * vi * (vi - 1) * u_pow (ui) * v_pow (vi - 2); if (ui == 0) v_pow (vi + 1) = v_pow (vi) * v; ++j; } u_pow (ui + 1) = u_pow (ui) * u; } return (d); } Eigen::Vector2f pcl::MLSResult::calculatePrincipalCurvatures (const double u, const double v) const { Eigen::Vector2f k (1e-5, 1e-5); // Note: this use the Monge Patch to derive the Gaussian curvature and Mean Curvature found here http://mathworld.wolfram.com/MongePatch.html // Then: // k1 = H + sqrt(H^2 - K) // k2 = H - sqrt(H^2 - K) if (order > 1 && c_vec.size () >= (order + 1) * (order + 2) / 2 && std::isfinite (c_vec[0])) { const PolynomialPartialDerivative d = getPolynomialPartialDerivative (u, v); const double Z = 1 + d.z_u * d.z_u + d.z_v * d.z_v; const double Zlen = std::sqrt (Z); const double K = (d.z_uu * d.z_vv - d.z_uv * d.z_uv) / (Z * Z); const double H = ((1.0 + d.z_v * d.z_v) * d.z_uu - 2.0 * d.z_u * d.z_v * d.z_uv + (1.0 + d.z_u * d.z_u) * d.z_vv) / (2.0 * Zlen * Zlen * Zlen); const double disc2 = H * H - K; assert (disc2 >= 0.0); const double disc = std::sqrt (disc2); k[0] = H + disc; k[1] = H - disc; if (std::abs (k[0]) > std::abs (k[1])) std::swap (k[0], k[1]); } else { PCL_ERROR ("No Polynomial fit data, unable to calculate the principal curvatures!\n"); } return (k); } pcl::MLSResult::MLSProjectionResults pcl::MLSResult::projectPointOrthogonalToPolynomialSurface (const double u, const double v, const double w) const { double gu = u; double gv = v; double gw = 0; MLSProjectionResults result; result.normal = plane_normal; if (order > 1 && c_vec.size () >= (order + 1) * (order + 2) / 2 && std::isfinite (c_vec[0])) { PolynomialPartialDerivative d = getPolynomialPartialDerivative (gu, gv); gw = d.z; double err_total; const double dist1 = std::abs (gw - w); double dist2; do { double e1 = (gu - u) + d.z_u * gw - d.z_u * w; double e2 = (gv - v) + d.z_v * gw - d.z_v * w; const double F1u = 1 + d.z_uu * gw + d.z_u * d.z_u - d.z_uu * w; const double F1v = d.z_uv * gw + d.z_u * d.z_v - d.z_uv * w; const double F2u = d.z_uv * gw + d.z_v * d.z_u - d.z_uv * w; const double F2v = 1 + d.z_vv * gw + d.z_v * d.z_v - d.z_vv * w; Eigen::MatrixXd J (2, 2); J (0, 0) = F1u; J (0, 1) = F1v; J (1, 0) = F2u; J (1, 1) = F2v; Eigen::Vector2d err (e1, e2); Eigen::Vector2d update = J.inverse () * err; gu -= update (0); gv -= update (1); d = getPolynomialPartialDerivative (gu, gv); gw = d.z; dist2 = std::sqrt ((gu - u) * (gu - u) + (gv - v) * (gv - v) + (gw - w) * (gw - w)); err_total = std::sqrt (e1 * e1 + e2 * e2); } while (err_total > 1e-8 && dist2 < dist1); if (dist2 > dist1) // the optimization was diverging reset the coordinates for simple projection { gu = u; gv = v; d = getPolynomialPartialDerivative (u, v); gw = d.z; } result.u = gu; result.v = gv; result.normal -= (d.z_u * u_axis + d.z_v * v_axis); result.normal.normalize (); } result.point = mean + gu * u_axis + gv * v_axis + gw * plane_normal; return (result); } pcl::MLSResult::MLSProjectionResults pcl::MLSResult::projectPointToMLSPlane (const double u, const double v) const { MLSProjectionResults result; result.u = u; result.v = v; result.normal = plane_normal; result.point = mean + u * u_axis + v * v_axis; return (result); } pcl::MLSResult::MLSProjectionResults pcl::MLSResult::projectPointSimpleToPolynomialSurface (const double u, const double v) const { MLSProjectionResults result; double w = 0; result.u = u; result.v = v; result.normal = plane_normal; if (order > 1 && c_vec.size () >= (order + 1) * (order + 2) / 2 && std::isfinite (c_vec[0])) { const PolynomialPartialDerivative d = getPolynomialPartialDerivative (u, v); w = d.z; result.normal -= (d.z_u * u_axis + d.z_v * v_axis); result.normal.normalize (); } result.point = mean + u * u_axis + v * v_axis + w * plane_normal; return (result); } pcl::MLSResult::MLSProjectionResults pcl::MLSResult::projectPoint (const Eigen::Vector3d &pt, ProjectionMethod method, int required_neighbors) const { double u, v, w; getMLSCoordinates (pt, u, v, w); MLSResult::MLSProjectionResults proj; if (order > 1 && num_neighbors >= required_neighbors && std::isfinite (c_vec[0]) && method != NONE) { if (method == ORTHOGONAL) proj = projectPointOrthogonalToPolynomialSurface (u, v, w); else // SIMPLE proj = projectPointSimpleToPolynomialSurface (u, v); } else { proj = projectPointToMLSPlane (u, v); } return (proj); } pcl::MLSResult::MLSProjectionResults pcl::MLSResult::projectQueryPoint (ProjectionMethod method, int required_neighbors) const { MLSResult::MLSProjectionResults proj; if (order > 1 && num_neighbors >= required_neighbors && std::isfinite (c_vec[0]) && method != NONE) { if (method == ORTHOGONAL) { double u, v, w; getMLSCoordinates (query_point, u, v, w); proj = projectPointOrthogonalToPolynomialSurface (u, v, w); } else // SIMPLE { // Projection onto MLS surface along Darboux normal to the height at (0,0) proj.point = mean + (c_vec[0] * plane_normal); // Compute tangent vectors using the partial derivates evaluated at (0,0) which is c_vec[order_+1] and c_vec[1] proj.normal = plane_normal - c_vec[order + 1] * u_axis - c_vec[1] * v_axis; proj.normal.normalize (); } } else { proj.normal = plane_normal; proj.point = mean; } return (proj); } template void pcl::MLSResult::computeMLSSurface (const pcl::PointCloud &cloud, pcl::index_t index, const pcl::Indices &nn_indices, double search_radius, int polynomial_order, std::function weight_func) { // Compute the plane coefficients EIGEN_ALIGN16 Eigen::Matrix3d covariance_matrix; Eigen::Vector4d xyz_centroid; // Estimate the XYZ centroid pcl::compute3DCentroid (cloud, nn_indices, xyz_centroid); // Compute the 3x3 covariance matrix pcl::computeCovarianceMatrix (cloud, nn_indices, xyz_centroid, covariance_matrix); EIGEN_ALIGN16 Eigen::Vector3d::Scalar eigen_value; EIGEN_ALIGN16 Eigen::Vector3d eigen_vector; Eigen::Vector4d model_coefficients (0, 0, 0, 0); pcl::eigen33 (covariance_matrix, eigen_value, eigen_vector); model_coefficients.head<3> ().matrix () = eigen_vector; model_coefficients[3] = -1 * model_coefficients.dot (xyz_centroid); query_point = cloud[index].getVector3fMap ().template cast (); if (!std::isfinite(eigen_vector[0]) || !std::isfinite(eigen_vector[1]) || !std::isfinite(eigen_vector[2])) { // Invalid plane coefficients, this may happen if the input cloud is non-dense (it contains invalid points). // Keep the input point and stop here. valid = false; mean = query_point; return; } // Projected query point valid = true; const double distance = query_point.dot (model_coefficients.head<3> ()) + model_coefficients[3]; mean = query_point - distance * model_coefficients.head<3> (); curvature = covariance_matrix.trace (); // Compute the curvature surface change if (curvature != 0) curvature = std::abs (eigen_value / curvature); // Get a copy of the plane normal easy access plane_normal = model_coefficients.head<3> (); // Local coordinate system (Darboux frame) v_axis = plane_normal.unitOrthogonal (); u_axis = plane_normal.cross (v_axis); // Perform polynomial fit to update point and normal //////////////////////////////////////////////////// num_neighbors = static_cast (nn_indices.size ()); order = polynomial_order; if (order > 1) { const int nr_coeff = (order + 1) * (order + 2) / 2; if (num_neighbors >= nr_coeff) { if (!weight_func) weight_func = [=] (const double sq_dist) { return this->computeMLSWeight (sq_dist, search_radius * search_radius); }; // Allocate matrices and vectors to hold the data used for the polynomial fit Eigen::VectorXd weight_vec (num_neighbors); Eigen::MatrixXd P (nr_coeff, num_neighbors); Eigen::VectorXd f_vec (num_neighbors); Eigen::MatrixXd P_weight_Pt (nr_coeff, nr_coeff); // Update neighborhood, since point was projected, and computing relative // positions. Note updating only distances for the weights for speed std::vector > de_meaned (num_neighbors); for (std::size_t ni = 0; ni < static_cast(num_neighbors); ++ni) { de_meaned[ni][0] = cloud[nn_indices[ni]].x - mean[0]; de_meaned[ni][1] = cloud[nn_indices[ni]].y - mean[1]; de_meaned[ni][2] = cloud[nn_indices[ni]].z - mean[2]; weight_vec (ni) = weight_func (de_meaned[ni].dot (de_meaned[ni])); } // Go through neighbors, transform them in the local coordinate system, // save height and the evaluation of the polynomial's terms for (std::size_t ni = 0; ni < static_cast(num_neighbors); ++ni) { // Transforming coordinates const double u_coord = de_meaned[ni].dot(u_axis); const double v_coord = de_meaned[ni].dot(v_axis); f_vec (ni) = de_meaned[ni].dot (plane_normal); // Compute the polynomial's terms at the current point int j = 0; double u_pow = 1; for (int ui = 0; ui <= order; ++ui) { double v_pow = 1; for (int vi = 0; vi <= order - ui; ++vi) { P (j++, ni) = u_pow * v_pow; v_pow *= v_coord; } u_pow *= u_coord; } } // Computing coefficients const Eigen::MatrixXd P_weight = P * weight_vec.asDiagonal(); // size will be (nr_coeff_, nn_indices.size ()); P_weight_Pt = P_weight * P.transpose (); c_vec = P_weight * f_vec; P_weight_Pt.llt ().solveInPlace (c_vec); } } } ////////////////////////////////////////////////////////////////////////////////////////////// template pcl::MovingLeastSquares::MLSVoxelGrid::MLSVoxelGrid (PointCloudInConstPtr& cloud, IndicesPtr &indices, float voxel_size) : voxel_grid_ (), data_size_ (), voxel_size_ (voxel_size) { pcl::getMinMax3D (*cloud, *indices, bounding_min_, bounding_max_); Eigen::Vector4f bounding_box_size = bounding_max_ - bounding_min_; const double max_size = (std::max) ((std::max)(bounding_box_size.x (), bounding_box_size.y ()), bounding_box_size.z ()); // Put initial cloud in voxel grid data_size_ = static_cast (1.5 * max_size / voxel_size_); for (std::size_t i = 0; i < indices->size (); ++i) if (std::isfinite ((*cloud)[(*indices)[i]].x)) { Eigen::Vector3i pos; getCellIndex ((*cloud)[(*indices)[i]].getVector3fMap (), pos); std::uint64_t index_1d; getIndexIn1D (pos, index_1d); Leaf leaf; voxel_grid_[index_1d] = leaf; } } ////////////////////////////////////////////////////////////////////////////////////////////// template void pcl::MovingLeastSquares::MLSVoxelGrid::dilate () { HashMap new_voxel_grid = voxel_grid_; for (typename MLSVoxelGrid::HashMap::iterator m_it = voxel_grid_.begin (); m_it != voxel_grid_.end (); ++m_it) { Eigen::Vector3i index; getIndexIn3D (m_it->first, index); // Now dilate all of its voxels for (int x = -1; x <= 1; ++x) for (int y = -1; y <= 1; ++y) for (int z = -1; z <= 1; ++z) if (x != 0 || y != 0 || z != 0) { Eigen::Vector3i new_index; new_index = index + Eigen::Vector3i (x, y, z); std::uint64_t index_1d; getIndexIn1D (new_index, index_1d); Leaf leaf; new_voxel_grid[index_1d] = leaf; } } voxel_grid_ = new_voxel_grid; } ///////////////////////////////////////////////////////////////////////////////////////////// template void pcl::MovingLeastSquares::copyMissingFields (const PointInT &point_in, PointOutT &point_out) const { PointOutT temp = point_out; copyPoint (point_in, point_out); point_out.x = temp.x; point_out.y = temp.y; point_out.z = temp.z; } #define PCL_INSTANTIATE_MovingLeastSquares(T,OutT) template class PCL_EXPORTS pcl::MovingLeastSquares; #define PCL_INSTANTIATE_MovingLeastSquaresOMP(T,OutT) template class PCL_EXPORTS pcl::MovingLeastSquaresOMP; #endif // PCL_SURFACE_IMPL_MLS_H_