40 #ifndef PCL_SURFACE_IMPL_MLS_H_
41 #define PCL_SURFACE_IMPL_MLS_H_
45 #include <pcl/common/copy_point.h>
46 #include <pcl/common/eigen.h>
47 #include <pcl/search/auto.h>
48 #include <pcl/surface/mls.h>
49 #include <pcl/type_traits.h>
51 #include <Eigen/Geometry>
61 template <
typename Po
intInT,
typename Po
intOutT>
void
72 normals_->header = input_->header;
74 normals_->width = normals_->height = 0;
75 normals_->points.clear ();
79 output.
header = input_->header;
83 if (search_radius_ <= 0 || sqr_gauss_param_ <= 0)
85 PCL_ERROR (
"[pcl::%s::process] Invalid search radius (%f) or Gaussian parameter (%f)!\n", getClassName ().c_str (), search_radius_, sqr_gauss_param_);
90 if (upsample_method_ == DISTINCT_CLOUD && !distinct_cloud_)
92 PCL_ERROR (
"[pcl::%s::process] Upsample method was set to DISTINCT_CLOUD, but no distinct cloud was specified.\n", getClassName ().c_str ());
102 KdTreePtr tree (pcl::search::autoSelectMethod<PointInT> (input_,
false));
103 setSearchMethod (tree);
108 tree_->setInputCloud (input_);
111 switch (upsample_method_)
114 case (RANDOM_UNIFORM_DENSITY):
116 std::random_device rd;
118 const double tmp = search_radius_ / 2.0;
119 rng_uniform_distribution_ = std::make_unique<std::uniform_real_distribution<>> (-tmp, tmp);
123 case (VOXEL_GRID_DILATION):
124 case (DISTINCT_CLOUD):
126 if (!cache_mls_results_)
127 PCL_WARN (
"The cache mls results is forced when using upsampling method VOXEL_GRID_DILATION or DISTINCT_CLOUD.\n");
129 cache_mls_results_ =
true;
136 if (cache_mls_results_)
138 mls_results_.resize (input_->size ());
142 mls_results_.resize (1);
146 performProcessing (output);
148 if (compute_normals_)
150 normals_->height = 1;
151 normals_->width = normals_->size ();
153 for (std::size_t i = 0; i < output.
size (); ++i)
155 using FieldList =
typename pcl::traits::fieldList<PointOutT>::type;
172 template <
typename Po
intInT,
typename Po
intOutT>
void
183 mls_result.
computeMLSSurface<PointInT> (*input_, index, nn_indices, search_radius_, order_);
185 switch (upsample_method_)
190 addProjectedPointNormal (index, proj.
point, proj.
normal, mls_result.
curvature, projected_points, projected_points_normals, corresponding_input_indices);
194 case (SAMPLE_LOCAL_PLANE):
197 for (
float u_disp = -
static_cast<float> (upsampling_radius_); u_disp <= upsampling_radius_; u_disp += static_cast<float> (upsampling_step_))
198 for (
float v_disp = -
static_cast<float> (upsampling_radius_); v_disp <= upsampling_radius_; v_disp += static_cast<float> (upsampling_step_))
199 if (u_disp * u_disp + v_disp * v_disp < upsampling_radius_ * upsampling_radius_)
202 addProjectedPointNormal (index, proj.
point, proj.
normal, mls_result.
curvature, projected_points, projected_points_normals, corresponding_input_indices);
207 case (RANDOM_UNIFORM_DENSITY):
210 const int num_points_to_add =
static_cast<int> (std::floor (desired_num_points_in_radius_ / 2.0 /
static_cast<double> (nn_indices.size ())));
213 if (num_points_to_add <= 0)
217 addProjectedPointNormal (index, proj.
point, proj.
normal, mls_result.
curvature, projected_points, projected_points_normals, corresponding_input_indices);
222 for (
int num_added = 0; num_added < num_points_to_add;)
224 const double u = (*rng_uniform_distribution_) (rng_);
225 const double v = (*rng_uniform_distribution_) (rng_);
228 if (u * u + v * v > search_radius_ * search_radius_ / 4)
237 addProjectedPointNormal (index, proj.
point, proj.
normal, mls_result.
curvature, projected_points, projected_points_normals, corresponding_input_indices);
250 template <
typename Po
intInT,
typename Po
intOutT>
void
252 const Eigen::Vector3d &point,
253 const Eigen::Vector3d &normal,
260 aux.x =
static_cast<float> (point[0]);
261 aux.y =
static_cast<float> (point[1]);
262 aux.z =
static_cast<float> (point[2]);
265 copyMissingFields ((*input_)[index], aux);
268 corresponding_input_indices.
indices.push_back (index);
270 if (compute_normals_)
273 aux_normal.normal_x =
static_cast<float> (normal[0]);
274 aux_normal.normal_y =
static_cast<float> (normal[1]);
275 aux_normal.normal_z =
static_cast<float> (normal[2]);
277 projected_points_normals.
push_back (aux_normal);
282 template <
typename Po
intInT,
typename Po
intOutT>
void
286 nr_coeff_ = (order_ + 1) * (order_ + 2) / 2;
290 const unsigned int threads = threads_ == 0 ? 1 : threads_;
294 std::vector<PointIndices> corresponding_input_indices (threads);
298 #pragma omp parallel for \
300 shared(corresponding_input_indices, projected_points, projected_points_normals) \
301 schedule(dynamic,1000) \
303 for (
int cp = 0; cp < static_cast<int> (indices_->size ()); ++cp)
308 std::vector<float> nn_sqr_dists;
311 if (searchForNeighbors ((*indices_)[cp], nn_indices, nn_sqr_dists))
314 if (nn_indices.size () >= 3)
318 const int tn = omp_get_thread_num ();
320 std::size_t pp_size = projected_points[tn].size ();
327 const int index = (*indices_)[cp];
329 std::size_t mls_result_index = 0;
330 if (cache_mls_results_)
331 mls_result_index = index;
334 computeMLSPointNormal (index, nn_indices, projected_points[tn], projected_points_normals[tn], corresponding_input_indices[tn], mls_results_[mls_result_index]);
337 for (std::size_t pp = pp_size; pp < projected_points[tn].
size (); ++pp)
338 copyMissingFields ((*input_)[(*indices_)[cp]], projected_points[tn][pp]);
340 computeMLSPointNormal (index, nn_indices, projected_points, projected_points_normals, *corresponding_input_indices_, mls_results_[mls_result_index]);
343 output.
insert (output.
end (), projected_points.
begin (), projected_points.
end ());
344 if (compute_normals_)
345 normals_->insert (normals_->end (), projected_points_normals.
begin (), projected_points_normals.
end ());
353 for (
unsigned int tn = 0; tn < threads; ++tn)
355 output.
insert (output.
end (), projected_points[tn].begin (), projected_points[tn].end ());
356 corresponding_input_indices_->indices.insert (corresponding_input_indices_->indices.end (),
357 corresponding_input_indices[tn].indices.begin (), corresponding_input_indices[tn].indices.end ());
358 if (compute_normals_)
359 normals_->insert (normals_->end (), projected_points_normals[tn].begin (), projected_points_normals[tn].end ());
364 performUpsampling (output);
368 template <
typename Po
intInT,
typename Po
intOutT>
void
372 if (upsample_method_ == DISTINCT_CLOUD)
375 for (std::size_t dp_i = 0; dp_i < distinct_cloud_->size (); ++dp_i)
378 if (!std::isfinite ((*distinct_cloud_)[dp_i].x))
384 std::vector<float> nn_dists;
385 tree_->nearestKSearch ((*distinct_cloud_)[dp_i], 1, nn_indices, nn_dists);
386 const auto input_index = nn_indices.front ();
390 if (!mls_results_[input_index].valid)
393 Eigen::Vector3d add_point = (*distinct_cloud_)[dp_i].getVector3fMap ().template cast<double> ();
395 addProjectedPointNormal (input_index, proj.
point, proj.
normal, mls_results_[input_index].curvature, output, *normals_, *corresponding_input_indices_);
401 if (upsample_method_ == VOXEL_GRID_DILATION)
405 MLSVoxelGrid voxel_grid (input_, indices_, voxel_size_, dilation_iteration_num_);
406 for (
int iteration = 0; iteration < dilation_iteration_num_; ++iteration)
421 std::vector<float> nn_dists (1);
422 tree_->nearestKSearch (p, 1, nn_indices, nn_dists);
423 const auto input_index = nn_indices.front ();
427 if (!mls_results_[input_index].valid)
430 Eigen::Vector3d add_point = p.getVector3fMap ().template cast<double> ();
432 addProjectedPointNormal (input_index, proj.
point, proj.
normal, mls_results_[input_index].curvature, output, *normals_, *corresponding_input_indices_);
439 const Eigen::Vector3d &a_mean,
440 const Eigen::Vector3d &a_plane_normal,
441 const Eigen::Vector3d &a_u,
442 const Eigen::Vector3d &a_v,
443 const Eigen::VectorXd &a_c_vec,
444 const int a_num_neighbors,
445 const float a_curvature,
447 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),
448 curvature (a_curvature), order (a_order), valid (true)
454 Eigen::Vector3d delta = pt - mean;
455 u = delta.dot (u_axis);
456 v = delta.dot (v_axis);
457 w = delta.dot (plane_normal);
463 Eigen::Vector3d delta = pt - mean;
464 u = delta.dot (u_axis);
465 v = delta.dot (v_axis);
476 for (
int ui = 0; ui <= order; ++ui)
479 for (
int vi = 0; vi <= order - ui; ++vi)
481 result += c_vec[j++] * u_pow * v_pow;
496 Eigen::VectorXd u_pow (order + 2), v_pow (order + 2);
499 d.z = d.z_u = d.z_v = d.z_uu = d.z_vv = d.z_uv = 0;
500 u_pow (0) = v_pow (0) = 1;
501 for (
int ui = 0; ui <= order; ++ui)
503 for (
int vi = 0; vi <= order - ui; ++vi)
506 d.z += u_pow (ui) * v_pow (vi) * c_vec[j];
510 d.z_u += c_vec[j] * ui * u_pow (ui - 1) * v_pow (vi);
513 d.z_v += c_vec[j] * vi * u_pow (ui) * v_pow (vi - 1);
515 if (ui >= 1 && vi >= 1)
516 d.z_uv += c_vec[j] * ui * u_pow (ui - 1) * vi * v_pow (vi - 1);
519 d.z_uu += c_vec[j] * ui * (ui - 1) * u_pow (ui - 2) * v_pow (vi);
522 d.z_vv += c_vec[j] * vi * (vi - 1) * u_pow (ui) * v_pow (vi - 2);
525 v_pow (vi + 1) = v_pow (vi) * v;
529 u_pow (ui + 1) = u_pow (ui) * u;
543 result.
normal = plane_normal;
544 if (order > 1 && c_vec.size () >= (order + 1) * (order + 2) / 2 && std::isfinite (c_vec[0]))
549 const double dist1 = std::abs (gw - w);
553 double e1 = (gu - u) + d.
z_u * gw - d.
z_u * w;
554 double e2 = (gv - v) + d.
z_v * gw - d.
z_v * w;
562 Eigen::MatrixXd J (2, 2);
568 Eigen::Vector2d err (e1, e2);
569 Eigen::Vector2d update = J.inverse () * err;
573 d = getPolynomialPartialDerivative (gu, gv);
575 dist2 = std::sqrt ((gu - u) * (gu - u) + (gv - v) * (gv - v) + (gw - w) * (gw - w));
577 err_total = std::sqrt (e1 * e1 + e2 * e2);
579 }
while (err_total > 1e-8 && dist2 < dist1);
585 d = getPolynomialPartialDerivative (u, v);
592 result.
normal.normalize ();
595 result.
point = mean + gu * u_axis + gv * v_axis + gw * plane_normal;
606 result.
normal = plane_normal;
607 result.
point = mean + u * u_axis + v * v_axis;
620 result.
normal = plane_normal;
622 if (order > 1 && c_vec.size () >= (order + 1) * (order + 2) / 2 && std::isfinite (c_vec[0]))
627 result.
normal.normalize ();
630 result.
point = mean + u * u_axis + v * v_axis + w * plane_normal;
639 getMLSCoordinates (pt, u, v, w);
642 if (order > 1 && num_neighbors >= required_neighbors && std::isfinite (c_vec[0]) && method != NONE)
644 if (method == ORTHOGONAL)
645 proj = projectPointOrthogonalToPolynomialSurface (u, v, w);
647 proj = projectPointSimpleToPolynomialSurface (u, v);
651 proj = projectPointToMLSPlane (u, v);
661 if (order > 1 && num_neighbors >= required_neighbors && std::isfinite (c_vec[0]) && method != NONE)
663 if (method == ORTHOGONAL)
666 getMLSCoordinates (query_point, u, v, w);
667 proj = projectPointOrthogonalToPolynomialSurface (u, v, w);
672 proj.
point = mean + (c_vec[0] * plane_normal);
675 proj.
normal = plane_normal - c_vec[order + 1] * u_axis - c_vec[1] * v_axis;
681 proj.
normal = plane_normal;
688 template <
typename Po
intT>
void
692 double search_radius,
693 int polynomial_order,
694 std::function<
double(
const double)> weight_func)
697 EIGEN_ALIGN16 Eigen::Matrix3d covariance_matrix;
698 Eigen::Vector4d xyz_centroid;
705 EIGEN_ALIGN16 Eigen::Vector3d::Scalar eigen_value;
706 EIGEN_ALIGN16 Eigen::Vector3d eigen_vector;
707 Eigen::Vector4d model_coefficients (0, 0, 0, 0);
708 pcl::eigen33 (covariance_matrix, eigen_value, eigen_vector);
709 model_coefficients.head<3> ().matrix () = eigen_vector;
710 model_coefficients[3] = -1 * model_coefficients.dot (xyz_centroid);
712 query_point = cloud[index].getVector3fMap ().template cast<double> ();
714 if (!std::isfinite(eigen_vector[0]) || !std::isfinite(eigen_vector[1]) || !std::isfinite(eigen_vector[2]))
725 const double distance = query_point.dot (model_coefficients.head<3> ()) + model_coefficients[3];
726 mean = query_point -
distance * model_coefficients.head<3> ();
728 curvature = covariance_matrix.trace ();
731 curvature = std::abs (eigen_value / curvature);
734 plane_normal = model_coefficients.head<3> ();
737 v_axis = plane_normal.unitOrthogonal ();
738 u_axis = plane_normal.cross (v_axis);
742 num_neighbors =
static_cast<int> (nn_indices.size ());
743 order = polynomial_order;
746 const int nr_coeff = (order + 1) * (order + 2) / 2;
748 if (num_neighbors >= nr_coeff)
751 weight_func = [
this, search_radius] (
const double sq_dist) {
return this->computeMLSWeight (sq_dist, search_radius * search_radius); };
754 Eigen::VectorXd weight_vec (num_neighbors);
755 Eigen::MatrixXd P (nr_coeff, num_neighbors);
756 Eigen::VectorXd f_vec (num_neighbors);
757 Eigen::MatrixXd P_weight_Pt (nr_coeff, nr_coeff);
761 std::vector<Eigen::Vector3d, Eigen::aligned_allocator<Eigen::Vector3d> > de_meaned (num_neighbors);
762 for (std::size_t ni = 0; ni < static_cast<std::size_t>(num_neighbors); ++ni)
764 de_meaned[ni][0] = cloud[nn_indices[ni]].x - mean[0];
765 de_meaned[ni][1] = cloud[nn_indices[ni]].y - mean[1];
766 de_meaned[ni][2] = cloud[nn_indices[ni]].z - mean[2];
767 weight_vec (ni) = weight_func (de_meaned[ni].dot (de_meaned[ni]));
772 for (std::size_t ni = 0; ni < static_cast<std::size_t>(num_neighbors); ++ni)
775 const double u_coord = de_meaned[ni].dot(u_axis);
776 const double v_coord = de_meaned[ni].dot(v_axis);
777 f_vec (ni) = de_meaned[ni].dot (plane_normal);
782 for (
int ui = 0; ui <= order; ++ui)
785 for (
int vi = 0; vi <= order - ui; ++vi)
787 P (j++, ni) = u_pow * v_pow;
795 const Eigen::MatrixXd P_weight = P * weight_vec.asDiagonal();
796 P_weight_Pt.noalias() = P_weight * P.transpose ();
797 c_vec.noalias() = P_weight * f_vec;
798 P_weight_Pt.llt ().solveInPlace (c_vec);
804 template <
typename Po
intInT,
typename Po
intOutT>
808 int dilation_iteration_num) :
809 voxel_grid_ (), voxel_size_ (voxel_size)
816 const double max_size = (std::max) ((std::max)(bounding_box_size.x (), bounding_box_size.y ()), bounding_box_size.z ());
819 for (std::size_t i = 0; i < indices->size (); ++i)
820 if (std::isfinite ((*cloud)[(*indices)[i]].x))
823 getCellIndex ((*cloud)[(*indices)[i]].getVector3fMap (), pos);
825 std::uint64_t index_1d;
833 template <
typename Po
intInT,
typename Po
intOutT>
void
836 HashMap new_voxel_grid = voxel_grid_;
837 for (
auto m_it = voxel_grid_.begin (); m_it != voxel_grid_.end (); ++m_it)
839 Eigen::Vector3i index;
840 getIndexIn3D (m_it->first, index);
843 for (
int x = -1; x <= 1; ++x)
844 for (
int y = -1; y <= 1; ++y)
845 for (
int z = -1; z <= 1; ++z)
846 if (x != 0 || y != 0 || z != 0)
848 Eigen::Vector3i new_index;
849 new_index = index + Eigen::Vector3i (x, y, z);
851 std::uint64_t index_1d;
852 getIndexIn1D (new_index, index_1d);
854 new_voxel_grid[index_1d] = leaf;
857 voxel_grid_ = new_voxel_grid;
862 template <
typename Po
intInT,
typename Po
intOutT>
void
864 PointOutT &point_out)
const
866 PointOutT temp = point_out;
868 point_out.x = temp.x;
869 point_out.y = temp.y;
870 point_out.z = temp.z;
873 #define PCL_INSTANTIATE_MovingLeastSquares(T,OutT) template class PCL_EXPORTS pcl::MovingLeastSquares<T,OutT>;
874 #define PCL_INSTANTIATE_MovingLeastSquaresOMP(T,OutT) template class PCL_EXPORTS pcl::MovingLeastSquaresOMP<T,OutT>;
Define methods for centroid estimation and covariance matrix calculus.
A minimalistic implementation of a voxel grid, necessary for the point cloud upsampling.
void getPosition(const std::uint64_t &index_1d, Eigen::Vector3f &point) const
Eigen::Vector4f bounding_min_
void getIndexIn1D(const Eigen::Vector3i &index, std::uint64_t &index_1d) const
Eigen::Vector4f bounding_max_
void getCellIndex(const Eigen::Vector3f &p, Eigen::Vector3i &index) const
MLSVoxelGrid(PointCloudInConstPtr &cloud, IndicesPtr &indices, float voxel_size, int dilation_iteration_num)
std::map< std::uint64_t, Leaf > HashMap
void performUpsampling(PointCloudOut &output)
Perform upsampling for the distinct-cloud and voxel-grid methods.
typename KdTree::Ptr KdTreePtr
typename PointCloudIn::ConstPtr PointCloudInConstPtr
void copyMissingFields(const PointInT &point_in, PointOutT &point_out) const
void performProcessing(PointCloudOut &output) override
Abstract surface reconstruction method.
void 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
Smooth a given point and its neighborghood using Moving Least Squares.
void process(PointCloudOut &output) override
Base method for surface reconstruction for all points given in <setInputCloud (), setIndices ()>
void 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
This is a helper function for adding projected points.
void push_back(const PointT &pt)
Insert a new point in the cloud, at the end of the container.
std::uint32_t width
The point cloud width (if organized as an image-structure).
iterator insert(iterator position, const PointT &pt)
Insert a new point in the cloud, given an iterator.
pcl::PCLHeader header
The point cloud header.
std::vector< PointCloud< PointOutT >, Eigen::aligned_allocator< PointCloud< PointOutT > > > CloudVectorType
std::uint32_t height
The point cloud height (if organized as an image-structure).
void clear()
Removes all points in a cloud and sets the width and height to 0.
iterator begin() noexcept
Define standard C methods and C++ classes that are common to all methods.
void getMinMax3D(const pcl::PointCloud< PointT > &cloud, PointT &min_pt, PointT &max_pt)
Get the minimum and maximum values on each of the 3 (x-y-z) dimensions in a given pointcloud.
void copyPoint(const PointInT &point_in, PointOutT &point_out)
Copy the fields of a source point into a target point.
unsigned int computeCovarianceMatrix(const pcl::PointCloud< PointT > &cloud, const Eigen::Matrix< Scalar, 4, 1 > ¢roid, Eigen::Matrix< Scalar, 3, 3 > &covariance_matrix)
Compute the 3x3 covariance matrix of a given set of points.
void eigen33(const Matrix &mat, typename Matrix::Scalar &eigenvalue, Vector &eigenvector)
determines the eigenvector and eigenvalue of the smallest eigenvalue of the symmetric positive semi d...
unsigned int compute3DCentroid(ConstCloudIterator< PointT > &cloud_iterator, Eigen::Matrix< Scalar, 4, 1 > ¢roid)
Compute the 3D (X-Y-Z) centroid of a set of points and return it as a 3D vector.
float distance(const PointT &p1, const PointT &p2)
detail::int_type_t< detail::index_type_size, detail::index_type_signed > index_t
Type used for an index in PCL.
IndicesAllocator<> Indices
Type used for indices in PCL.
shared_ptr< Indices > IndicesPtr
Data structure used to store the MLS projection results.
Eigen::Vector3d point
The projected point.
double v
The v-coordinate of the projected point in local MLS frame.
Eigen::Vector3d normal
The projected point's normal.
double u
The u-coordinate of the projected point in local MLS frame.
Data structure used to store the MLS polynomial partial derivatives.
double z_uv
The partial derivative d^2z/dudv.
double z_u
The partial derivative dz/du.
double z_uu
The partial derivative d^2z/du^2.
double z
The z component of the polynomial evaluated at z(u, v).
double z_vv
The partial derivative d^2z/dv^2.
double z_v
The partial derivative dz/dv.
Data structure used to store the results of the MLS fitting.
MLSProjectionResults projectPoint(const Eigen::Vector3d &pt, ProjectionMethod method, int required_neighbors=0) const
Project a point using the specified method.
MLSProjectionResults projectPointOrthogonalToPolynomialSurface(const double u, const double v, const double w) const
Project a point orthogonal to the polynomial surface.
int num_neighbors
The number of neighbors used to create the mls surface.
void computeMLSSurface(const pcl::PointCloud< PointT > &cloud, pcl::index_t index, const pcl::Indices &nn_indices, double search_radius, int polynomial_order=2, std::function< double(const double)> weight_func={})
Smooth a given point and its neighborhood using Moving Least Squares.
void getMLSCoordinates(const Eigen::Vector3d &pt, double &u, double &v, double &w) const
Given a point calculate its 3D location in the MLS frame.
float curvature
The curvature at the query point.
PolynomialPartialDerivative getPolynomialPartialDerivative(const double u, const double v) const
Calculate the polynomial's first and second partial derivatives.
MLSProjectionResults projectPointSimpleToPolynomialSurface(const double u, const double v) const
Project a point along the MLS plane normal to the polynomial surface.
MLSProjectionResults projectPointToMLSPlane(const double u, const double v) const
Project a point onto the MLS plane.
double getPolynomialValue(const double u, const double v) const
Calculate the polynomial.
MLSProjectionResults projectQueryPoint(ProjectionMethod method, int required_neighbors=0) const
Project the query point used to generate the mls surface about using the specified method.
A point structure representing normal coordinates and the surface curvature estimate.
A helper functor that can set a specific value in a field if the field exists.