Point Cloud Library (PCL)  1.5.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
mls.hpp
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (BSD License)
00003  *
00004  * Point Cloud Library (PCL) - www.pointclouds.org
00005  * Copyright (c) 2009-2011, Willow Garage, Inc.
00006  *
00007  * All rights reserved.
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted provided that the following conditions
00011  * are met:
00012  *
00013  * * Redistributions of source code must retain the above copyright
00014  *   notice, this list of conditions and the following disclaimer.
00015  * * Redistributions in binary form must reproduce the above
00016  *   copyright notice, this list of conditions and the following
00017  *   disclaimer in the documentation and/or other materials provided
00018  *   with the distribution.
00019  * * Neither the name of Willow Garage, Inc. nor the names of its
00020  *   contributors may be used to endorse or promote products derived
00021  *   from this software without specific prior written permission.
00022  *
00023  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00024  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00025  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00026  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00027  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00028  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00029  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00030  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00031  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00033  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00034  * POSSIBILITY OF SUCH DAMAGE.
00035  *
00036  * $Id: mls.hpp 4702 2012-02-23 09:39:33Z gedikli $
00037  *
00038  */
00039 
00040 #ifndef PCL_SURFACE_IMPL_MLS_H_
00041 #define PCL_SURFACE_IMPL_MLS_H_
00042 
00043 #include "pcl/surface/mls.h"
00044 #include <pcl/common/io.h>
00045 #include <pcl/common/centroid.h>
00046 #include <pcl/common/eigen.h>
00047 
00049 template <typename PointInT, typename NormalOutT> void
00050 pcl::MovingLeastSquares<PointInT, NormalOutT>::reconstruct (PointCloudIn &output)
00051 {
00052   // check if normals have to be computed/saved
00053   if (normals_)
00054   {
00055     // Copy the header
00056     normals_->header = input_->header;
00057     // Clear the fields in case the method exits before computation
00058     normals_->width = normals_->height = 0;
00059     normals_->points.clear ();
00060   }
00061 
00062   // Copy the header
00063   output.header = input_->header;
00064 
00065   if (search_radius_ <= 0 || sqr_gauss_param_ <= 0)
00066   {
00067     PCL_ERROR ("[pcl::%s::reconstruct] Invalid search radius (%f) or Gaussian parameter (%f)!\n", getClassName ().c_str (), search_radius_, sqr_gauss_param_);
00068     output.width = output.height = 0;
00069     output.points.clear ();
00070     return;
00071   }
00072 
00073   if (!initCompute ())
00074   {
00075     output.width = output.height = 0;
00076     output.points.clear ();
00077     return;
00078   }
00079 
00080   // Initialize the spatial locator
00081   if (!tree_)
00082   {
00083     KdTreePtr tree;
00084     if (input_->isOrganized ())
00085       tree.reset (new pcl::search::OrganizedNeighbor<PointInT> ());
00086     else
00087       tree.reset (new pcl::search::KdTree<PointInT> (false));
00088     setSearchMethod (tree);
00089   }
00090 
00091   // Send the surface dataset to the spatial locator
00092   tree_->setInputCloud (input_, indices_);
00093 
00094   // Use original point positions for fitting
00095   // \note no up/down/adapting-sampling or hole filling possible like this
00096   output.points.resize (indices_->size ());
00097   // Check if fake indices were used, otherwise the output loses its organized structure
00098   if (!fake_indices_)
00099     pcl::copyPointCloud (*input_, *indices_, output);
00100   else
00101     output = *input_;
00102 
00103   // Resize the output normal dataset
00104   if (normals_)
00105   {
00106     normals_->points.resize (output.points.size ());
00107     normals_->width    = output.width;
00108     normals_->height   = output.height;
00109     normals_->is_dense = output.is_dense;
00110   }
00111 
00112   // Perform the actual surface reconstruction
00113   performReconstruction (output);
00114 
00115   deinitCompute ();
00116 }
00117 
00119 template <typename PointInT, typename NormalOutT> void
00120 pcl::MovingLeastSquares<PointInT, NormalOutT>::computeMLSPointNormal (PointInT &pt,
00121                                                                       const PointCloudIn &input,
00122                                                                       const std::vector<int> &nn_indices,
00123                                                                       std::vector<float> &nn_sqr_dists,
00124                                                                       Eigen::Vector4f &model_coefficients)
00125 {
00126   // Compute the plane coefficients
00127   //pcl::computePointNormal<PointInT> (*input_, nn_indices, model_coefficients, curvature);
00128   EIGEN_ALIGN16 Eigen::Matrix3f covariance_matrix;
00129   Eigen::Vector4f xyz_centroid;
00130 
00131   // Estimate the XYZ centroid
00132   pcl::compute3DCentroid (input, nn_indices, xyz_centroid);
00133 
00134   // Compute the 3x3 covariance matrix
00135   pcl::computeCovarianceMatrix (input, nn_indices, xyz_centroid, covariance_matrix);
00136 
00137   // Get the plane normal
00138   EIGEN_ALIGN16 Eigen::Vector3f::Scalar eigen_value = -1;
00139   EIGEN_ALIGN16 Eigen::Vector3f eigen_vector;
00140   pcl::eigen33 (covariance_matrix, eigen_value, eigen_vector);
00141   model_coefficients.head<3> () = eigen_vector;
00142   model_coefficients[3] = -1 * model_coefficients.dot (xyz_centroid);
00143 
00144   // Projected point
00145   Eigen::Vector3f point = pt.getVector3fMap ();
00146   float distance = point.dot (model_coefficients.head<3> ()) + model_coefficients[3];
00147   point -= distance * model_coefficients.head<3> ();
00148 
00149   float curvature = covariance_matrix.trace ();
00150   // Compute the curvature surface change
00151   if (curvature != 0)
00152     curvature = fabs (eigen_value / curvature);
00153 
00154   // Perform polynomial fit to update point and normal
00156   if (polynomial_fit_ && (int)nn_indices.size () >= nr_coeff_)
00157   {
00158     // Get a copy of the plane normal easy access
00159     Eigen::Vector3d plane_normal = model_coefficients.head<3> ().cast<double> ();
00160 
00161     // Update neighborhood, since point was projected, and computing relative
00162     // positions. Note updating only distances for the weights for speed
00163     std::vector<Eigen::Vector3d> de_meaned (nn_indices.size ());
00164     for (size_t ni = 0; ni < nn_indices.size (); ++ni)
00165     {
00166       de_meaned[ni][0] = input_->points[nn_indices[ni]].x - point[0];
00167       de_meaned[ni][1] = input_->points[nn_indices[ni]].y - point[1];
00168       de_meaned[ni][2] = input_->points[nn_indices[ni]].z - point[2];
00169       nn_sqr_dists[ni] = de_meaned[ni].dot (de_meaned[ni]);
00170     }
00171 
00172     // Allocate matrices and vectors to hold the data used for the polynomial fit
00173     Eigen::VectorXd weight_vec (nn_indices.size ());
00174     Eigen::MatrixXd P (nr_coeff_, nn_indices.size ());
00175     Eigen::VectorXd f_vec (nn_indices.size ());
00176     Eigen::VectorXd c_vec;
00177     Eigen::MatrixXd P_weight; // size will be (nr_coeff_, nn_indices.size ());
00178     Eigen::MatrixXd P_weight_Pt (nr_coeff_, nr_coeff_);
00179 
00180     // Get local coordinate system (Darboux frame)
00181     Eigen::Vector3d v = plane_normal.unitOrthogonal ();
00182     Eigen::Vector3d u = plane_normal.cross (v);
00183 
00184     // Go through neighbors, transform them in the local coordinate system,
00185     // save height and the evaluation of the polynome's terms
00186     double u_coord, v_coord, u_pow, v_pow;
00187     for (size_t ni = 0; ni < nn_indices.size (); ++ni)
00188     {
00189       // (re-)compute weights
00190       weight_vec (ni) = exp (-nn_sqr_dists[ni] / sqr_gauss_param_);
00191 
00192       // transforming coordinates
00193       u_coord = de_meaned[ni].dot (u);
00194       v_coord = de_meaned[ni].dot (v);
00195       f_vec (ni) = de_meaned[ni].dot (plane_normal);
00196 
00197       // compute the polynomial's terms at the current point
00198       int j = 0;
00199       u_pow = 1;
00200       for (int ui = 0; ui <= order_; ++ui)
00201       {
00202         v_pow = 1;
00203         for (int vi = 0; vi <= order_ - ui; ++vi)
00204         {
00205           P (j++, ni) = u_pow * v_pow;
00206           v_pow *= v_coord;
00207         }
00208         u_pow *= u_coord;
00209       }
00210     }
00211 
00212     // Computing coefficients
00213     P_weight = P * weight_vec.asDiagonal ();
00214     P_weight_Pt = P_weight * P.transpose ();
00215     c_vec = P_weight * f_vec;
00216     P_weight_Pt.llt ().solveInPlace (c_vec);
00217 
00218     // Projection onto MLS surface along Darboux normal to the height at (0,0)
00219     if (pcl_isfinite (c_vec[0]))
00220     {
00221       point += (c_vec[0] * plane_normal).cast<float> ();
00222 
00223       // Compute tangent vectors using the partial derivates evaluated at (0,0) which is c_vec[order_+1] and c_vec[1]
00224       if (normals_)
00225       {
00226         Eigen::Vector3d normal = c_vec[order_ + 1] * u + c_vec[1] * v - plane_normal;
00227         model_coefficients.head<3> () = normal.cast<float> ();
00228         model_coefficients.head<3> ().normalize ();
00229       }
00230     }
00231   }
00232 
00233   // Save the smoothed results
00234   pt.x = point[0];
00235   pt.y = point[1];
00236   pt.z = point[2];
00237 
00238   model_coefficients[3] = curvature;
00239 }
00240 
00242 template <typename PointInT, typename NormalOutT> void
00243 pcl::MovingLeastSquares<PointInT, NormalOutT>::performReconstruction (PointCloudIn &output)
00244 {
00245   // Compute the number of coefficients
00246   nr_coeff_ = (order_ + 1) * (order_ + 2) / 2;
00247 
00248   // Allocate enough space to hold the results of nearest neighbor searches
00249   // \note resize is irrelevant for a radiusSearch ().
00250   std::vector<int> nn_indices;
00251   std::vector<float> nn_sqr_dists;
00252   
00253   // For all points
00254   for (size_t cp = 0; cp < indices_->size (); ++cp)
00255   {
00256     // Get the initial estimates of point positions and their neighborhoods
00257     if (!searchForNeighbors ((*indices_)[cp], nn_indices, nn_sqr_dists))
00258     {
00259       if (normals_)
00260         normals_->points[cp].normal[0] = normals_->points[cp].normal[1] = normals_->points[cp].normal[2] = normals_->points[cp].curvature = std::numeric_limits<float>::quiet_NaN ();
00261       continue;
00262     }
00263 
00264     // Check the number of nearest neighbors for normal estimation (and later
00265     // for polynomial fit as well)
00266     if (nn_indices.size () < 3)
00267       continue;
00268 
00269     Eigen::Vector4f model_coefficients;
00270     // Get a plane approximating the local surface's tangent and project point onto it
00271     computeMLSPointNormal (output.points[cp], *input_, nn_indices, nn_sqr_dists,
00272                            model_coefficients); 
00273 
00274     // Save results to output cloud
00275     if (normals_)
00276     {
00277       normals_->points[cp].normal[0] = model_coefficients[0];
00278       normals_->points[cp].normal[1] = model_coefficients[1];
00279       normals_->points[cp].normal[2] = model_coefficients[2];
00280       normals_->points[cp].curvature = model_coefficients[3];
00281     }
00282   }
00283 }
00284 
00285 #define PCL_INSTANTIATE_MovingLeastSquares(T,OutT) template class PCL_EXPORTS pcl::MovingLeastSquares<T,OutT>;
00286 
00287 #endif    // PCL_SURFACE_IMPL_MLS_H_
00288