Point Cloud Library (PCL)  1.9.1-dev
transforms.hpp
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Point Cloud Library (PCL) - www.pointclouds.org
5  * Copyright (c) 2010, Willow Garage, Inc.
6  * Copyright (c) 2012-, Open Perception, Inc.
7  *
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * * Redistributions of source code must retain the above copyright
15  * notice, this list of conditions and the following disclaimer.
16  * * Redistributions in binary form must reproduce the above
17  * copyright notice, this list of conditions and the following
18  * disclaimer in the documentation and/or other materials provided
19  * with the distribution.
20  * * Neither the name of the copyright holder(s) nor the names of its
21  * contributors may be used to endorse or promote products derived
22  * from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  *
37  *
38  */
39 
40 #if defined(__SSE2__)
41 #include <xmmintrin.h>
42 #endif
43 
44 #if defined(__AVX__)
45 #include <immintrin.h>
46 #endif
47 
48 namespace pcl
49 {
50 
51  namespace detail
52  {
53 
54  /** A helper struct to apply an SO3 or SE3 transform to a 3D point.
55  * Supports single and double precision transform matrices. */
56  template<typename Scalar>
57  struct Transformer
58  {
59  const Eigen::Matrix<Scalar, 4, 4>& tf;
60 
61  /** Construct a transformer object.
62  * The transform matrix is captured by const reference. Make sure that it does not go out of scope before this
63  * object does. */
64  Transformer (const Eigen::Matrix<Scalar, 4, 4>& transform) : tf (transform) { };
65 
66  /** Apply SO3 transform (top-left corner of the transform matrix).
67  * \param[in] src input 3D point (pointer to 3 floats)
68  * \param[out] tgt output 3D point (pointer to 4 floats), can be the same as input. The fourth element is set to 0. */
69  void so3 (const float* src, float* tgt) const
70  {
71  const Scalar p[3] = { src[0], src[1], src[2] }; // need this when src == tgt
72  tgt[0] = static_cast<float> (tf (0, 0) * p[0] + tf (0, 1) * p[1] + tf (0, 2) * p[2]);
73  tgt[1] = static_cast<float> (tf (1, 0) * p[0] + tf (1, 1) * p[1] + tf (1, 2) * p[2]);
74  tgt[2] = static_cast<float> (tf (2, 0) * p[0] + tf (2, 1) * p[1] + tf (2, 2) * p[2]);
75  tgt[3] = 0;
76  }
77 
78  /** Apply SE3 transform.
79  * \param[in] src input 3D point (pointer to 3 floats)
80  * \param[out] tgt output 3D point (pointer to 4 floats), can be the same as input. The fourth element is set to 1. */
81  void se3 (const float* src, float* tgt) const
82  {
83  const Scalar p[3] = { src[0], src[1], src[2] }; // need this when src == tgt
84  tgt[0] = static_cast<float> (tf (0, 0) * p[0] + tf (0, 1) * p[1] + tf (0, 2) * p[2] + tf (0, 3));
85  tgt[1] = static_cast<float> (tf (1, 0) * p[0] + tf (1, 1) * p[1] + tf (1, 2) * p[2] + tf (1, 3));
86  tgt[2] = static_cast<float> (tf (2, 0) * p[0] + tf (2, 1) * p[1] + tf (2, 2) * p[2] + tf (2, 3));
87  tgt[3] = 1;
88  }
89  };
90 
91 #if defined(__SSE2__)
92 
93  /** Optimized version for single-precision transforms using SSE2 intrinsics. */
94  template<>
95  struct Transformer<float>
96  {
97  /// Columns of the transform matrix stored in XMM registers.
98  __m128 c[4];
99 
100  Transformer(const Eigen::Matrix4f& tf)
101  {
102  for (size_t i = 0; i < 4; ++i)
103  c[i] = _mm_load_ps (tf.col (i).data ());
104  }
105 
106  void so3 (const float* src, float* tgt) const
107  {
108  __m128 p0 = _mm_mul_ps (_mm_load_ps1 (&src[0]), c[0]);
109  __m128 p1 = _mm_mul_ps (_mm_load_ps1 (&src[1]), c[1]);
110  __m128 p2 = _mm_mul_ps (_mm_load_ps1 (&src[2]), c[2]);
111  _mm_store_ps (tgt, _mm_add_ps(p0, _mm_add_ps(p1, p2)));
112  }
113 
114  void se3 (const float* src, float* tgt) const
115  {
116  __m128 p0 = _mm_mul_ps (_mm_load_ps1 (&src[0]), c[0]);
117  __m128 p1 = _mm_mul_ps (_mm_load_ps1 (&src[1]), c[1]);
118  __m128 p2 = _mm_mul_ps (_mm_load_ps1 (&src[2]), c[2]);
119  _mm_store_ps (tgt, _mm_add_ps(p0, _mm_add_ps(p1, _mm_add_ps(p2, c[3]))));
120  }
121  };
122 
123 #if !defined(__AVX__)
124 
125  /** Optimized version for double-precision transform using SSE2 intrinsics. */
126  template<>
127  struct Transformer<double>
128  {
129  /// Columns of the transform matrix stored in XMM registers.
130  __m128d c[4][2];
131 
132  Transformer(const Eigen::Matrix4d& tf)
133  {
134  for (size_t i = 0; i < 4; ++i)
135  {
136  c[i][0] = _mm_load_pd (tf.col (i).data () + 0);
137  c[i][1] = _mm_load_pd (tf.col (i).data () + 2);
138  }
139  }
140 
141  void so3 (const float* src, float* tgt) const
142  {
143  __m128d xx = _mm_cvtps_pd (_mm_load_ps1 (&src[0]));
144  __m128d p0 = _mm_mul_pd (xx, c[0][0]);
145  __m128d p1 = _mm_mul_pd (xx, c[0][1]);
146 
147  for (size_t i = 1; i < 3; ++i)
148  {
149  __m128d vv = _mm_cvtps_pd (_mm_load_ps1 (&src[i]));
150  p0 = _mm_add_pd (_mm_mul_pd (vv, c[i][0]), p0);
151  p1 = _mm_add_pd (_mm_mul_pd (vv, c[i][1]), p1);
152  }
153 
154  _mm_store_ps (tgt, _mm_movelh_ps (_mm_cvtpd_ps (p0), _mm_cvtpd_ps (p1)));
155  }
156 
157  void se3 (const float* src, float* tgt) const
158  {
159  __m128d p0 = c[3][0];
160  __m128d p1 = c[3][1];
161 
162  for (size_t i = 0; i < 3; ++i)
163  {
164  __m128d vv = _mm_cvtps_pd (_mm_load_ps1 (&src[i]));
165  p0 = _mm_add_pd (_mm_mul_pd (vv, c[i][0]), p0);
166  p1 = _mm_add_pd (_mm_mul_pd (vv, c[i][1]), p1);
167  }
168 
169  _mm_store_ps (tgt, _mm_movelh_ps (_mm_cvtpd_ps (p0), _mm_cvtpd_ps (p1)));
170  }
171 
172  };
173 
174 #else
175 
176  /** Optimized version for double-precision transform using AVX intrinsics. */
177  template<>
178  struct Transformer<double>
179  {
180  __m256d c[4];
181 
182  Transformer(const Eigen::Matrix4d& tf)
183  {
184  for (size_t i = 0; i < 4; ++i)
185  c[i] = _mm256_load_pd (tf.col (i).data ());
186  }
187 
188  void so3 (const float* src, float* tgt) const
189  {
190  __m256d p0 = _mm256_mul_pd (_mm256_cvtps_pd (_mm_load_ps1 (&src[0])), c[0]);
191  __m256d p1 = _mm256_mul_pd (_mm256_cvtps_pd (_mm_load_ps1 (&src[1])), c[1]);
192  __m256d p2 = _mm256_mul_pd (_mm256_cvtps_pd (_mm_load_ps1 (&src[2])), c[2]);
193  _mm_store_ps (tgt, _mm256_cvtpd_ps (_mm256_add_pd(p0, _mm256_add_pd(p1, p2))));
194  }
195 
196  void se3 (const float* src, float* tgt) const
197  {
198  __m256d p0 = _mm256_mul_pd (_mm256_cvtps_pd (_mm_load_ps1 (&src[0])), c[0]);
199  __m256d p1 = _mm256_mul_pd (_mm256_cvtps_pd (_mm_load_ps1 (&src[1])), c[1]);
200  __m256d p2 = _mm256_mul_pd (_mm256_cvtps_pd (_mm_load_ps1 (&src[2])), c[2]);
201  _mm_store_ps (tgt, _mm256_cvtpd_ps (_mm256_add_pd(p0, _mm256_add_pd(p1, _mm256_add_pd(p2, c[3])))));
202  }
203 
204  };
205 
206 #endif
207 #endif
208 
209  }
210 
211 }
212 
213 ///////////////////////////////////////////////////////////////////////////////////////////
214 template <typename PointT, typename Scalar> void
216  pcl::PointCloud<PointT> &cloud_out,
217  const Eigen::Transform<Scalar, 3, Eigen::Affine> &transform,
218  bool copy_all_fields)
219 {
220  if (&cloud_in != &cloud_out)
221  {
222  cloud_out.header = cloud_in.header;
223  cloud_out.is_dense = cloud_in.is_dense;
224  cloud_out.width = cloud_in.width;
225  cloud_out.height = cloud_in.height;
226  cloud_out.points.reserve (cloud_in.points.size ());
227  if (copy_all_fields)
228  cloud_out.points.assign (cloud_in.points.begin (), cloud_in.points.end ());
229  else
230  cloud_out.points.resize (cloud_in.points.size ());
231  cloud_out.sensor_orientation_ = cloud_in.sensor_orientation_;
232  cloud_out.sensor_origin_ = cloud_in.sensor_origin_;
233  }
234 
235  pcl::detail::Transformer<Scalar> tf (transform.matrix ());
236  if (cloud_in.is_dense)
237  {
238  // If the dataset is dense, simply transform it!
239  for (size_t i = 0; i < cloud_out.points.size (); ++i)
240  tf.se3 (cloud_in[i].data, cloud_out[i].data);
241  }
242  else
243  {
244  // Dataset might contain NaNs and Infs, so check for them first,
245  // otherwise we get errors during the multiplication (?)
246  for (size_t i = 0; i < cloud_out.points.size (); ++i)
247  {
248  if (!std::isfinite (cloud_in.points[i].x) ||
249  !std::isfinite (cloud_in.points[i].y) ||
250  !std::isfinite (cloud_in.points[i].z))
251  continue;
252  tf.se3 (cloud_in[i].data, cloud_out[i].data);
253  }
254  }
255 }
256 
257 ///////////////////////////////////////////////////////////////////////////////////////////
258 template <typename PointT, typename Scalar> void
260  const std::vector<int> &indices,
261  pcl::PointCloud<PointT> &cloud_out,
262  const Eigen::Transform<Scalar, 3, Eigen::Affine> &transform,
263  bool copy_all_fields)
264 {
265  size_t npts = indices.size ();
266  // In order to transform the data, we need to remove NaNs
267  cloud_out.is_dense = cloud_in.is_dense;
268  cloud_out.header = cloud_in.header;
269  cloud_out.width = static_cast<int> (npts);
270  cloud_out.height = 1;
271  cloud_out.points.resize (npts);
272  cloud_out.sensor_orientation_ = cloud_in.sensor_orientation_;
273  cloud_out.sensor_origin_ = cloud_in.sensor_origin_;
274 
275  pcl::detail::Transformer<Scalar> tf (transform.matrix ());
276  if (cloud_in.is_dense)
277  {
278  // If the dataset is dense, simply transform it!
279  for (size_t i = 0; i < npts; ++i)
280  {
281  // Copy fields first, then transform xyz data
282  if (copy_all_fields)
283  cloud_out.points[i] = cloud_in.points[indices[i]];
284  tf.se3 (cloud_in[indices[i]].data, cloud_out[i].data);
285  }
286  }
287  else
288  {
289  // Dataset might contain NaNs and Infs, so check for them first,
290  // otherwise we get errors during the multiplication (?)
291  for (size_t i = 0; i < npts; ++i)
292  {
293  if (copy_all_fields)
294  cloud_out.points[i] = cloud_in.points[indices[i]];
295  if (!std::isfinite (cloud_in.points[indices[i]].x) ||
296  !std::isfinite (cloud_in.points[indices[i]].y) ||
297  !std::isfinite (cloud_in.points[indices[i]].z))
298  continue;
299  tf.se3 (cloud_in[indices[i]].data, cloud_out[i].data);
300  }
301  }
302 }
303 
304 ///////////////////////////////////////////////////////////////////////////////////////////
305 template <typename PointT, typename Scalar> void
307  pcl::PointCloud<PointT> &cloud_out,
308  const Eigen::Transform<Scalar, 3, Eigen::Affine> &transform,
309  bool copy_all_fields)
310 {
311  if (&cloud_in != &cloud_out)
312  {
313  // Note: could be replaced by cloud_out = cloud_in
314  cloud_out.header = cloud_in.header;
315  cloud_out.width = cloud_in.width;
316  cloud_out.height = cloud_in.height;
317  cloud_out.is_dense = cloud_in.is_dense;
318  cloud_out.points.reserve (cloud_out.points.size ());
319  if (copy_all_fields)
320  cloud_out.points.assign (cloud_in.points.begin (), cloud_in.points.end ());
321  else
322  cloud_out.points.resize (cloud_in.points.size ());
323  cloud_out.sensor_orientation_ = cloud_in.sensor_orientation_;
324  cloud_out.sensor_origin_ = cloud_in.sensor_origin_;
325  }
326 
327  pcl::detail::Transformer<Scalar> tf (transform.matrix ());
328  // If the data is dense, we don't need to check for NaN
329  if (cloud_in.is_dense)
330  {
331  for (size_t i = 0; i < cloud_out.points.size (); ++i)
332  {
333  tf.se3 (cloud_in[i].data, cloud_out[i].data);
334  tf.so3 (cloud_in[i].data_n, cloud_out[i].data_n);
335  }
336  }
337  // Dataset might contain NaNs and Infs, so check for them first.
338  else
339  {
340  for (size_t i = 0; i < cloud_out.points.size (); ++i)
341  {
342  if (!std::isfinite (cloud_in.points[i].x) ||
343  !std::isfinite (cloud_in.points[i].y) ||
344  !std::isfinite (cloud_in.points[i].z))
345  continue;
346  tf.se3 (cloud_in[i].data, cloud_out[i].data);
347  tf.so3 (cloud_in[i].data_n, cloud_out[i].data_n);
348  }
349  }
350 }
351 
352 ///////////////////////////////////////////////////////////////////////////////////////////
353 template <typename PointT, typename Scalar> void
355  const std::vector<int> &indices,
356  pcl::PointCloud<PointT> &cloud_out,
357  const Eigen::Transform<Scalar, 3, Eigen::Affine> &transform,
358  bool copy_all_fields)
359 {
360  size_t npts = indices.size ();
361  // In order to transform the data, we need to remove NaNs
362  cloud_out.is_dense = cloud_in.is_dense;
363  cloud_out.header = cloud_in.header;
364  cloud_out.width = static_cast<int> (npts);
365  cloud_out.height = 1;
366  cloud_out.points.resize (npts);
367  cloud_out.sensor_orientation_ = cloud_in.sensor_orientation_;
368  cloud_out.sensor_origin_ = cloud_in.sensor_origin_;
369 
370  pcl::detail::Transformer<Scalar> tf (transform.matrix ());
371  // If the data is dense, we don't need to check for NaN
372  if (cloud_in.is_dense)
373  {
374  for (size_t i = 0; i < cloud_out.points.size (); ++i)
375  {
376  // Copy fields first, then transform
377  if (copy_all_fields)
378  cloud_out.points[i] = cloud_in.points[indices[i]];
379  tf.se3 (cloud_in[indices[i]].data, cloud_out[i].data);
380  tf.so3 (cloud_in[indices[i]].data_n, cloud_out[i].data_n);
381  }
382  }
383  // Dataset might contain NaNs and Infs, so check for them first.
384  else
385  {
386  for (size_t i = 0; i < cloud_out.points.size (); ++i)
387  {
388  // Copy fields first, then transform
389  if (copy_all_fields)
390  cloud_out.points[i] = cloud_in.points[indices[i]];
391 
392  if (!std::isfinite (cloud_in.points[indices[i]].x) ||
393  !std::isfinite (cloud_in.points[indices[i]].y) ||
394  !std::isfinite (cloud_in.points[indices[i]].z))
395  continue;
396 
397  tf.se3 (cloud_in[indices[i]].data, cloud_out[i].data);
398  tf.so3 (cloud_in[indices[i]].data_n, cloud_out[i].data_n);
399  }
400  }
401 }
402 
403 ///////////////////////////////////////////////////////////////////////////////////////////
404 template <typename PointT, typename Scalar> inline void
406  pcl::PointCloud<PointT> &cloud_out,
407  const Eigen::Matrix<Scalar, 3, 1> &offset,
408  const Eigen::Quaternion<Scalar> &rotation,
409  bool copy_all_fields)
410 {
411  Eigen::Translation<Scalar, 3> translation (offset);
412  // Assemble an Eigen Transform
413  Eigen::Transform<Scalar, 3, Eigen::Affine> t (translation * rotation);
414  transformPointCloud (cloud_in, cloud_out, t, copy_all_fields);
415 }
416 
417 ///////////////////////////////////////////////////////////////////////////////////////////
418 template <typename PointT, typename Scalar> inline void
420  pcl::PointCloud<PointT> &cloud_out,
421  const Eigen::Matrix<Scalar, 3, 1> &offset,
422  const Eigen::Quaternion<Scalar> &rotation,
423  bool copy_all_fields)
424 {
425  Eigen::Translation<Scalar, 3> translation (offset);
426  // Assemble an Eigen Transform
427  Eigen::Transform<Scalar, 3, Eigen::Affine> t (translation * rotation);
428  transformPointCloudWithNormals (cloud_in, cloud_out, t, copy_all_fields);
429 }
430 
431 ///////////////////////////////////////////////////////////////////////////////////////////
432 template <typename PointT, typename Scalar> inline PointT
434  const Eigen::Transform<Scalar, 3, Eigen::Affine> &transform)
435 {
436  PointT ret = point;
437  pcl::detail::Transformer<Scalar> tf (transform.matrix ());
438  tf.se3 (point.data, ret.data);
439  return (ret);
440 }
441 
442 ///////////////////////////////////////////////////////////////////////////////////////////
443 template <typename PointT, typename Scalar> inline PointT
445  const Eigen::Transform<Scalar, 3, Eigen::Affine> &transform)
446 {
447  PointT ret = point;
448  pcl::detail::Transformer<Scalar> tf (transform.matrix ());
449  tf.se3 (point.data, ret.data);
450  tf.so3 (point.data_n, ret.data_n);
451  return (ret);
452 }
453 
454 ///////////////////////////////////////////////////////////////////////////////////////////
455 template <typename PointT, typename Scalar> double
457  Eigen::Transform<Scalar, 3, Eigen::Affine> &transform)
458 {
459  EIGEN_ALIGN16 Eigen::Matrix<Scalar, 3, 3> covariance_matrix;
460  Eigen::Matrix<Scalar, 4, 1> centroid;
461 
462  pcl::computeMeanAndCovarianceMatrix (cloud, covariance_matrix, centroid);
463 
464  EIGEN_ALIGN16 Eigen::Matrix<Scalar, 3, 3> eigen_vects;
465  Eigen::Matrix<Scalar, 3, 1> eigen_vals;
466  pcl::eigen33 (covariance_matrix, eigen_vects, eigen_vals);
467 
468  double rel1 = eigen_vals.coeff (0) / eigen_vals.coeff (1);
469  double rel2 = eigen_vals.coeff (1) / eigen_vals.coeff (2);
470 
471  transform.translation () = centroid.head (3);
472  transform.linear () = eigen_vects;
473 
474  return (std::min (rel1, rel2));
475 }
476 
void transformPoint(const Eigen::Matrix< Scalar, 3, 1 > &point_in, Eigen::Matrix< Scalar, 3, 1 > &point_out, const Eigen::Transform< Scalar, 3, Eigen::Affine > &transformation)
Transform a point using an affine matrix.
Definition: eigen.h:423
double getPrincipalTransformation(const pcl::PointCloud< PointT > &cloud, Eigen::Transform< Scalar, 3, Eigen::Affine > &transform)
Calculates the principal (PCA-based) alignment of the point cloud.
Definition: transforms.hpp:456
std::vector< PointT, Eigen::aligned_allocator< PointT > > points
The point data.
Definition: point_cloud.h:409
struct pcl::PointXYZIEdge EIGEN_ALIGN16
unsigned int computeMeanAndCovarianceMatrix(const pcl::PointCloud< PointT > &cloud, Eigen::Matrix< Scalar, 3, 3 > &covariance_matrix, Eigen::Matrix< Scalar, 4, 1 > &centroid)
Compute the normalized 3x3 covariance matrix and the centroid of a given set of points in a single lo...
Definition: centroid.hpp:489
void so3(const float *src, float *tgt) const
Apply SO3 transform (top-left corner of the transform matrix).
Definition: transforms.hpp:69
This file defines compatibility wrappers for low level I/O functions.
Definition: convolution.h:44
void transformPointCloudWithNormals(const pcl::PointCloud< PointT > &cloud_in, pcl::PointCloud< PointT > &cloud_out, const Eigen::Transform< Scalar, 3, Eigen::Affine > &transform, bool copy_all_fields=true)
Transform a point cloud and rotate its normals using an Eigen transform.
Definition: transforms.hpp:306
uint32_t height
The point cloud height (if organized as an image-structure).
Definition: point_cloud.h:414
Eigen::Vector4f sensor_origin_
Sensor acquisition pose (origin/translation).
Definition: point_cloud.h:420
uint32_t width
The point cloud width (if organized as an image-structure).
Definition: point_cloud.h:412
Transformer(const Eigen::Matrix< Scalar, 4, 4 > &transform)
Construct a transformer object.
Definition: transforms.hpp:64
const Eigen::Matrix< Scalar, 4, 4 > & tf
Definition: transforms.hpp:59
void transformPointCloud(const pcl::PointCloud< PointT > &cloud_in, pcl::PointCloud< PointT > &cloud_out, const Eigen::Transform< Scalar, 3, Eigen::Affine > &transform, bool copy_all_fields=true)
Apply an affine transform defined by an Eigen Transform.
Definition: transforms.hpp:215
Eigen::Quaternionf sensor_orientation_
Sensor acquisition pose (rotation).
Definition: point_cloud.h:422
PointCloud represents the base class in PCL for storing collections of 3D points. ...
pcl::PCLHeader header
The point cloud header.
Definition: point_cloud.h:406
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...
Definition: eigen.hpp:251
PointT transformPointWithNormal(const PointT &point, const Eigen::Transform< Scalar, 3, Eigen::Affine > &transform)
Transform a point with members x,y,z,normal_x,normal_y,normal_z.
Definition: transforms.hpp:444
void se3(const float *src, float *tgt) const
Apply SE3 transform.
Definition: transforms.hpp:81
bool is_dense
True if no points are invalid (e.g., have NaN or Inf values in any of their floating point fields)...
Definition: point_cloud.h:417
A point structure representing Euclidean xyz coordinates, and the RGB color.
A helper struct to apply an SO3 or SE3 transform to a 3D point.
Definition: transforms.hpp:57