Point Cloud Library (PCL)  1.6.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
mls.hpp
Go to the documentation of this file.
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Point Cloud Library (PCL) - www.pointclouds.org
5  * Copyright (c) 2009-2011, Willow Garage, Inc.
6  *
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * * Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * * Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following
17  * disclaimer in the documentation and/or other materials provided
18  * with the distribution.
19  * * Neither the name of Willow Garage, Inc. nor the names of its
20  * contributors may be used to endorse or promote products derived
21  * from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  *
36  * $Id: mls.hpp 6439 2012-07-17 07:13:58Z aichim $
37  *
38  */
39 
40 #ifndef PCL_SURFACE_IMPL_MLS_H_
41 #define PCL_SURFACE_IMPL_MLS_H_
42 
43 #include <pcl/point_traits.h>
44 #include <pcl/surface/mls.h>
45 #include <pcl/common/io.h>
46 #include <pcl/common/centroid.h>
47 #include <pcl/common/eigen.h>
48 #include <pcl/common/geometry.h>
49 
51 template <typename PointInT, typename PointOutT> void
53 {
54  // Check if normals have to be computed/saved
55  if (compute_normals_)
56  {
57  normals_.reset (new NormalCloud);
58  // Copy the header
59  normals_->header = input_->header;
60  // Clear the fields in case the method exits before computation
61  normals_->width = normals_->height = 0;
62  normals_->points.clear ();
63  }
64 
65 
66  // Copy the header
67  output.header = input_->header;
68  output.width = output.height = 0;
69  output.points.clear ();
70 
71  if (search_radius_ <= 0 || sqr_gauss_param_ <= 0)
72  {
73  PCL_ERROR ("[pcl::%s::reconstruct] Invalid search radius (%f) or Gaussian parameter (%f)!\n", getClassName ().c_str (), search_radius_, sqr_gauss_param_);
74  return;
75  }
76 
77  if (!initCompute ())
78  return;
79 
80 
81  // Initialize the spatial locator
82  if (!tree_)
83  {
84  KdTreePtr tree;
85  if (input_->isOrganized ())
86  tree.reset (new pcl::search::OrganizedNeighbor<PointInT> ());
87  else
88  tree.reset (new pcl::search::KdTree<PointInT> (false));
89  setSearchMethod (tree);
90  }
91 
92  // Send the surface dataset to the spatial locator
93  tree_->setInputCloud (input_, indices_);
94 
95  // Initialize random number generator if necessary
96  switch (upsample_method_)
97  {
98  case (RANDOM_UNIFORM_DENSITY):
99  {
100  boost::mt19937 *rng = new boost::mt19937 (static_cast<unsigned int>(std::time(0)));
101  float tmp = static_cast<float> (search_radius_ / 2.0f);
102  boost::uniform_real<float> *uniform_distrib = new boost::uniform_real<float> (-tmp, tmp);
103  rng_uniform_distribution_ = new boost::variate_generator<boost::mt19937, boost::uniform_real<float> > (*rng, *uniform_distrib);
104  break;
105  }
106  case (VOXEL_GRID_DILATION):
107  {
108  mls_results_.resize (input_->size ());
109  break;
110  }
111  default:
112  break;
113  }
114 
115  // Perform the actual surface reconstruction
116  performProcessing (output);
117 
118  if (compute_normals_)
119  {
120  normals_->height = 1;
121  normals_->width = static_cast<uint32_t> (normals_->size ());
122 
123  // TODO!!! MODIFY TO PER-CLOUD COPYING - much faster than per-point
124  for (unsigned int i = 0; i < output.size (); ++i)
125  {
126  typedef typename pcl::traits::fieldList<PointOutT>::type FieldList;
127  pcl::for_each_type<FieldList> (SetIfFieldExists<PointOutT, float> (output.points[i], "normal_x", normals_->points[i].normal_x));
128  pcl::for_each_type<FieldList> (SetIfFieldExists<PointOutT, float> (output.points[i], "normal_y", normals_->points[i].normal_y));
129  pcl::for_each_type<FieldList> (SetIfFieldExists<PointOutT, float> (output.points[i], "normal_z", normals_->points[i].normal_z));
130  pcl::for_each_type<FieldList> (SetIfFieldExists<PointOutT, float> (output.points[i], "curvature", normals_->points[i].curvature));
131  }
132 
133  }
134 
135  // Set proper widths and heights for the clouds
136  output.height = 1;
137  output.width = static_cast<uint32_t> (output.size ());
138 
139  deinitCompute ();
140 }
141 
143 template <typename PointInT, typename PointOutT> void
145  const PointCloudIn &input,
146  const std::vector<int> &nn_indices,
147  std::vector<float> &nn_sqr_dists,
148  PointCloudOut &projected_points,
149  NormalCloud &projected_points_normals)
150 {
151  // Compute the plane coefficients
152  //pcl::computePointNormal<PointInT> (*input_, nn_indices, model_coefficients, curvature);
153  EIGEN_ALIGN16 Eigen::Matrix3f covariance_matrix;
154  Eigen::Vector4f xyz_centroid;
155 
156  // Estimate the XYZ centroid
157  pcl::compute3DCentroid (input, nn_indices, xyz_centroid);
158  //pcl::compute3DCentroid (input, nn_indices, xyz_centroid);
159 
160  pcl::computeCovarianceMatrix (input, nn_indices, xyz_centroid, covariance_matrix);
161  // Compute the 3x3 covariance matrix
162 
163  EIGEN_ALIGN16 Eigen::Vector3f::Scalar eigen_value;
164  EIGEN_ALIGN16 Eigen::Vector3f eigen_vector;
165  Eigen::Vector4f model_coefficients;
166  pcl::eigen33 (covariance_matrix, eigen_value, eigen_vector);
167  model_coefficients.head<3> () = eigen_vector;
168  model_coefficients[3] = 0;
169  model_coefficients[3] = -1 * model_coefficients.dot (xyz_centroid);
170 
171  // Projected query point
172  Eigen::Vector3f point = input[(*indices_)[index]].getVector3fMap ();
173  float distance = point.dot (model_coefficients.head<3> ()) + model_coefficients[3];
174  point -= distance * model_coefficients.head<3> ();
175 
176  float curvature = covariance_matrix.trace ();
177  // Compute the curvature surface change
178  if (curvature != 0)
179  curvature = fabsf (eigen_value / curvature);
180 
181 
182  // Get a copy of the plane normal easy access
183  Eigen::Vector3d plane_normal = model_coefficients.head<3> ().cast<double> ();
184  // Vector in which the polynomial coefficients will be put
185  Eigen::VectorXd c_vec;
186  // Local coordinate system (Darboux frame)
187  Eigen::Vector3d v (0.0f, 0.0f, 0.0f), u (0.0f, 0.0f, 0.0f);
188 
189 
190 
191  // Perform polynomial fit to update point and normal
193  if (polynomial_fit_ && static_cast<int> (nn_indices.size ()) >= nr_coeff_)
194  {
195  // Update neighborhood, since point was projected, and computing relative
196  // positions. Note updating only distances for the weights for speed
197  std::vector<Eigen::Vector3d> de_meaned (nn_indices.size ());
198  for (size_t ni = 0; ni < nn_indices.size (); ++ni)
199  {
200  de_meaned[ni][0] = input_->points[nn_indices[ni]].x - point[0];
201  de_meaned[ni][1] = input_->points[nn_indices[ni]].y - point[1];
202  de_meaned[ni][2] = input_->points[nn_indices[ni]].z - point[2];
203  nn_sqr_dists[ni] = static_cast<float> (de_meaned[ni].dot (de_meaned[ni]));
204  }
205 
206  // Allocate matrices and vectors to hold the data used for the polynomial fit
207  Eigen::VectorXd weight_vec (nn_indices.size ());
208  Eigen::MatrixXd P (nr_coeff_, nn_indices.size ());
209  Eigen::VectorXd f_vec (nn_indices.size ());
210  Eigen::MatrixXd P_weight; // size will be (nr_coeff_, nn_indices.size ());
211  Eigen::MatrixXd P_weight_Pt (nr_coeff_, nr_coeff_);
212 
213  // Get local coordinate system (Darboux frame)
214  v = plane_normal.unitOrthogonal ();
215  u = plane_normal.cross (v);
216 
217  // Go through neighbors, transform them in the local coordinate system,
218  // save height and the evaluation of the polynome's terms
219  double u_coord, v_coord, u_pow, v_pow;
220  for (size_t ni = 0; ni < nn_indices.size (); ++ni)
221  {
222  // (re-)compute weights
223  weight_vec (ni) = exp (-nn_sqr_dists[ni] / sqr_gauss_param_);
224 
225  // transforming coordinates
226  u_coord = de_meaned[ni].dot (u);
227  v_coord = de_meaned[ni].dot (v);
228  f_vec (ni) = de_meaned[ni].dot (plane_normal);
229 
230  // compute the polynomial's terms at the current point
231  int j = 0;
232  u_pow = 1;
233  for (int ui = 0; ui <= order_; ++ui)
234  {
235  v_pow = 1;
236  for (int vi = 0; vi <= order_ - ui; ++vi)
237  {
238  P (j++, ni) = u_pow * v_pow;
239  v_pow *= v_coord;
240  }
241  u_pow *= u_coord;
242  }
243  }
244 
245  // Computing coefficients
246  P_weight = P * weight_vec.asDiagonal ();
247  P_weight_Pt = P_weight * P.transpose ();
248  c_vec = P_weight * f_vec;
249  P_weight_Pt.llt ().solveInPlace (c_vec);
250  }
251 
252  switch (upsample_method_)
253  {
254  case (NONE):
255  {
256  Eigen::Vector3d normal = plane_normal;
257 
258  if (polynomial_fit_ && static_cast<int> (nn_indices.size ()) >= nr_coeff_ && pcl_isfinite (c_vec[0]))
259  {
260  point += (c_vec[0] * plane_normal).cast<float> ();
261 
262  // Compute tangent vectors using the partial derivates evaluated at (0,0) which is c_vec[order_+1] and c_vec[1]
263  if (compute_normals_)
264  normal = plane_normal - c_vec[order_ + 1] * u - c_vec[1] * v;
265  }
266 
267  PointOutT aux;
268  aux.x = point[0];
269  aux.y = point[1];
270  aux.z = point[2];
271  projected_points.push_back (aux);
272 
273  if (compute_normals_)
274  {
275  pcl::Normal aux_normal;
276  aux_normal.normal_x = static_cast<float> (normal[0]);
277  aux_normal.normal_y = static_cast<float> (normal[1]);
278  aux_normal.normal_z = static_cast<float> (normal[2]);
279  aux_normal.curvature = curvature;
280  projected_points_normals.push_back (aux_normal);
281  }
282 
283  break;
284  }
285 
286  case (SAMPLE_LOCAL_PLANE):
287  {
288  // Uniformly sample a circle around the query point using the radius and step parameters
289  for (float u_disp = -static_cast<float> (upsampling_radius_); u_disp <= upsampling_radius_; u_disp += static_cast<float> (upsampling_step_))
290  for (float v_disp = -static_cast<float> (upsampling_radius_); v_disp <= upsampling_radius_; v_disp += static_cast<float> (upsampling_step_))
291  if (u_disp*u_disp + v_disp*v_disp < upsampling_radius_*upsampling_radius_)
292  {
293  PointOutT projected_point;
294  pcl::Normal projected_normal;
295  projectPointToMLSSurface (u_disp, v_disp, u, v, plane_normal, curvature, point, c_vec,
296  static_cast<int> (nn_indices.size ()),
297  projected_point, projected_normal);
298 
299  projected_points.push_back (projected_point);
300  if (compute_normals_)
301  projected_points_normals.push_back (projected_normal);
302  }
303  break;
304  }
305 
306  case (RANDOM_UNIFORM_DENSITY):
307  {
308  // Compute the local point density and add more samples if necessary
309  int num_points_to_add = static_cast<int> (floor (desired_num_points_in_radius_ / 2.0 / static_cast<double> (nn_indices.size ())));
310 
311  // Just add the query point, because the density is good
312  if (num_points_to_add <= 0)
313  {
314  // Just add the current point
315  Eigen::Vector3d normal = plane_normal;
316  if (polynomial_fit_ && static_cast<int> (nn_indices.size ()) >= nr_coeff_ && pcl_isfinite (c_vec[0]))
317  {
318  // Projection onto MLS surface along Darboux normal to the height at (0,0)
319  point += (c_vec[0] * plane_normal).cast<float> ();
320  // Compute tangent vectors using the partial derivates evaluated at (0,0) which is c_vec[order_+1] and c_vec[1]
321  if (compute_normals_)
322  normal = plane_normal - c_vec[order_ + 1] * u - c_vec[1] * v;
323  }
324  PointOutT aux;
325  aux.x = point[0];
326  aux.y = point[1];
327  aux.z = point[2];
328  projected_points.push_back (aux);
329 
330  if (compute_normals_)
331  {
332  pcl::Normal aux_normal;
333  aux_normal.normal_x = static_cast<float> (normal[0]);
334  aux_normal.normal_y = static_cast<float> (normal[1]);
335  aux_normal.normal_z = static_cast<float> (normal[2]);
336  aux_normal.curvature = curvature;
337  projected_points_normals.push_back (aux_normal);
338  }
339  }
340  else
341  {
342  // Sample the local plane
343  for (int num_added = 0; num_added < num_points_to_add;)
344  {
345  float u_disp = (*rng_uniform_distribution_) (),
346  v_disp = (*rng_uniform_distribution_) ();
347  // Check if inside circle; if not, try another coin flip
348  if (u_disp * u_disp + v_disp * v_disp > search_radius_ * search_radius_/4)
349  continue;
350 
351 
352  PointOutT projected_point;
353  pcl::Normal projected_normal;
354  projectPointToMLSSurface (u_disp, v_disp, u, v, plane_normal, curvature, point, c_vec,
355  static_cast<int> (nn_indices.size ()),
356  projected_point, projected_normal);
357 
358  projected_points.push_back (projected_point);
359  if (compute_normals_)
360  projected_points_normals.push_back (projected_normal);
361 
362  num_added ++;
363  }
364  }
365  break;
366  }
367 
368  case (VOXEL_GRID_DILATION):
369  {
370  // Take all point pairs and sample space between them in a grid-fashion
371  // \note consider only point pairs with increasing indices
372  MLSResult result (plane_normal, u, v, c_vec, static_cast<int> (nn_indices.size ()), curvature);
373  mls_results_[index] = result;
374  break;
375  }
376  }
377 }
378 
379 
380 
381 template <typename PointInT, typename PointOutT> void
383  Eigen::Vector3d &u, Eigen::Vector3d &v,
384  Eigen::Vector3d &plane_normal,
385  float &curvature,
386  Eigen::Vector3f &query_point,
387  Eigen::VectorXd &c_vec,
388  int num_neighbors,
389  PointOutT &result_point,
390  pcl::Normal &result_normal)
391 {
392  double n_disp = 0.0f;
393  double d_u = 0.0f, d_v = 0.0f;
394 
395  // HARDCODED 5*nr_coeff_ to guarantee that the computed polynomial had a proper point set basis
396  if (polynomial_fit_ && num_neighbors >= 5*nr_coeff_ && pcl_isfinite (c_vec[0]))
397  {
398  // Compute the displacement along the normal using the fitted polynomial
399  // and compute the partial derivatives needed for estimating the normal
400  int j = 0;
401  float u_pow = 1.0f, v_pow = 1.0f, u_pow_prev = 1.0f, v_pow_prev = 1.0f;
402  for (int ui = 0; ui <= order_; ++ui)
403  {
404  v_pow = 1;
405  for (int vi = 0; vi <= order_ - ui; ++vi)
406  {
407  // Compute displacement along normal
408  n_disp += u_pow * v_pow * c_vec[j++];
409 
410  // Compute partial derivatives
411  if (ui >= 1)
412  d_u += c_vec[j-1] * ui * u_pow_prev * v_pow;
413  if (vi >= 1)
414  d_v += c_vec[j-1] * vi * u_pow * v_pow_prev;
415 
416  v_pow_prev = v_pow;
417  v_pow *= v_disp;
418  }
419  u_pow_prev = u_pow;
420  u_pow *= u_disp;
421  }
422  }
423 
424  Eigen::Vector3d normal = plane_normal - d_u * u - d_v * v;
425  normal.normalize ();
426  result_point.x = static_cast<float> (query_point[0] + u[0] * u_disp + v[0] * v_disp + normal[0] * n_disp);
427  result_point.y = static_cast<float> (query_point[1] + u[1] * u_disp + v[1] * v_disp + normal[1] * n_disp);
428  result_point.z = static_cast<float> (query_point[2] + u[2] * u_disp + v[2] * v_disp + normal[2] * n_disp);
429 
430  result_normal.normal_x = static_cast<float> (normal[0]);
431  result_normal.normal_y = static_cast<float> (normal[1]);
432  result_normal.normal_z = static_cast<float> (normal[2]);
433  result_normal.curvature = curvature;
434 }
435 
436 
438 template <typename PointInT, typename PointOutT> void
440 {
441  // Compute the number of coefficients
442  nr_coeff_ = (order_ + 1) * (order_ + 2) / 2;
443 
444  // Allocate enough space to hold the results of nearest neighbor searches
445  // \note resize is irrelevant for a radiusSearch ().
446  std::vector<int> nn_indices;
447  std::vector<float> nn_sqr_dists;
448 
449  // For all points
450  for (size_t cp = 0; cp < indices_->size (); ++cp)
451  {
452  // Get the initial estimates of point positions and their neighborhoods
453  if (!searchForNeighbors (int (cp), nn_indices, nn_sqr_dists))
454  continue;
455 
456  // Check the number of nearest neighbors for normal estimation (and later
457  // for polynomial fit as well)
458  if (nn_indices.size () < 3)
459  continue;
460 
461 
462  PointCloudOut projected_points;
463  NormalCloud projected_points_normals;
464  // Get a plane approximating the local surface's tangent and project point onto it
465  computeMLSPointNormal (int (cp), *input_, nn_indices, nn_sqr_dists, projected_points, projected_points_normals);
466 
467  // Append projected points to output
468  output.insert (output.end (), projected_points.begin (), projected_points.end ());
469  if (compute_normals_)
470  normals_->insert (normals_->end (), projected_points_normals.begin (), projected_points_normals.end ());
471  }
472 
473 
474 
475  // For the voxel grid upsampling method, generate the voxel grid and dilate it
476  // Then, project the newly obtained points to the MLS surface
477  if (upsample_method_ == VOXEL_GRID_DILATION)
478  {
479  MLSVoxelGrid voxel_grid (input_, indices_, voxel_size_);
480 
481  for (int iteration = 0; iteration < dilation_iteration_num_; ++iteration)
482  voxel_grid.dilate ();
483 
484 
485  for (typename MLSVoxelGrid::HashMap::iterator m_it = voxel_grid.voxel_grid_.begin (); m_it != voxel_grid.voxel_grid_.end (); ++m_it)
486  {
487  // Get 3D position of point
488  Eigen::Vector3f pos;
489  voxel_grid.getPosition (m_it->first, pos);
490 
491  PointInT p;
492  p.x = pos[0];
493  p.y = pos[1];
494  p.z = pos[2];
495 
496  std::vector<int> nn_indices;
497  std::vector<float> nn_dists;
498  tree_->nearestKSearch (p, 1, nn_indices, nn_dists);
499  int input_index = nn_indices.front ();
500 
501  // If the closest point did not have a valid MLS fitting result
502  // OR if it is too far away from the sampled point
503  if (mls_results_[input_index].valid == false)
504  continue;
505 
506  Eigen::Vector3f add_point = p.getVector3fMap (),
507  input_point = input_->points[input_index].getVector3fMap ();
508 
509  Eigen::Vector3d aux = mls_results_[input_index].u;
510  Eigen::Vector3f u = aux.cast<float> ();
511  aux = mls_results_[input_index].v;
512  Eigen::Vector3f v = aux.cast<float> ();
513 
514  float u_disp = (add_point - input_point).dot (u),
515  v_disp = (add_point - input_point).dot (v);
516 
517  PointOutT result_point;
518  pcl::Normal result_normal;
519  projectPointToMLSSurface (u_disp, v_disp,
520  mls_results_[input_index].u, mls_results_[input_index].v,
521  mls_results_[input_index].plane_normal,
522  mls_results_[input_index].curvature,
523  input_point,
524  mls_results_[input_index].c_vec,
525  mls_results_[input_index].num_neighbors,
526  result_point, result_normal);
527 
528  float d_before = (pos - input_point).norm (),
529  d_after = (result_point.getVector3fMap () - input_point). norm();
530  if (d_after > d_before)
531  continue;
532 
533  output.push_back (result_point);
534  if (compute_normals_)
535  normals_->push_back (result_normal);
536  }
537  }
538 }
539 
540 
541 
542 
543 
544 
546 template <typename PointInT, typename PointOutT>
548  Eigen::Vector3d &a_u,
549  Eigen::Vector3d &a_v,
550  Eigen::VectorXd a_c_vec,
551  int a_num_neighbors,
552  float &a_curvature) :
553  plane_normal (a_plane_normal), u (a_u), v (a_v), c_vec (a_c_vec), num_neighbors (a_num_neighbors),
554  curvature (a_curvature), valid (true)
555 {
556 }
557 
559 template <typename PointInT, typename PointOutT>
561  IndicesPtr &indices,
562  float voxel_size) :
563  voxel_grid_ (), bounding_min_ (), bounding_max_ (), data_size_ (), voxel_size_ (voxel_size)
564 {
565  pcl::getMinMax3D (*cloud, *indices, bounding_min_, bounding_max_);
566 
567  Eigen::Vector4f bounding_box_size = bounding_max_ - bounding_min_;
568  double max_size = (std::max) ((std::max)(bounding_box_size.x (), bounding_box_size.y ()), bounding_box_size.z ());
569  // Put initial cloud in voxel grid
570  data_size_ = static_cast<uint64_t> (1.5 * max_size / voxel_size_);
571  for (unsigned int i = 0; i < indices->size (); ++i)
572  if (pcl_isfinite (cloud->points[(*indices)[i]].x))
573  {
574  Eigen::Vector3i pos;
575  getCellIndex (cloud->points[(*indices)[i]].getVector3fMap (), pos);
576 
577  uint64_t index_1d;
578  getIndexIn1D (pos, index_1d);
579  Leaf leaf;
580  voxel_grid_[index_1d] = leaf;
581  }
582 }
583 
585 template <typename PointInT, typename PointOutT> void
587 {
588  HashMap new_voxel_grid = voxel_grid_;
589  for (typename MLSVoxelGrid::HashMap::iterator m_it = voxel_grid_.begin (); m_it != voxel_grid_.end (); ++m_it)
590  {
591  Eigen::Vector3i index;
592  getIndexIn3D (m_it->first, index);
593 
595  for (int x = -1; x <= 1; ++x)
596  for (int y = -1; y <= 1; ++y)
597  for (int z = -1; z <= 1; ++z)
598  if (x != 0 || y != 0 || z != 0)
599  {
600  Eigen::Vector3i new_index;
601  new_index = index + Eigen::Vector3i (x, y, z);
602 
603  uint64_t index_1d;
604  getIndexIn1D (new_index, index_1d);
605  Leaf leaf;
606  new_voxel_grid[index_1d] = leaf;
607  }
608  }
609  voxel_grid_ = new_voxel_grid;
610 }
611 
612 
613 #define PCL_INSTANTIATE_MovingLeastSquares(T,OutT) template class PCL_EXPORTS pcl::MovingLeastSquares<T,OutT>;
614 
615 #endif // PCL_SURFACE_IMPL_MLS_H_