Point Cloud Library (PCL)  1.9.0-dev
tsdf_volume.hpp
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2011, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage, Inc. nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * $Id$
35  */
36 
37 #ifndef TSDF_VOLUME_HPP_
38 #define TSDF_VOLUME_HPP_
39 
40 #include "tsdf_volume.h"
41 
42 #include <fstream>
43 
44 
45 template <typename VoxelT, typename WeightT> bool
46 pcl::TSDFVolume<VoxelT, WeightT>::load (const std::string &filename, bool binary)
47 {
48  pcl::console::print_info ("Loading TSDF volume from "); pcl::console::print_value ("%s ... ", filename.c_str());
49  std::cout << std::flush;
50 
51  std::ifstream file (filename.c_str());
52 
53  if (file.is_open())
54  {
55  if (binary)
56  {
57  // read HEADER
58  file.read ((char*) &header_, sizeof (Header));
59  /* file.read (&header_.resolution, sizeof(Eigen::Array3i));
60  file.read (&header_.volume_size, sizeof(Eigen::Vector3f));
61  file.read (&header_.volume_element_size, sizeof(int));
62  file.read (&header_.weights_element_size, sizeof(int)); */
63 
64  // check if element size fits to data
65  if (header_.volume_element_size != sizeof(VoxelT))
66  {
67  pcl::console::print_error ("[TSDFVolume::load] Error: Given volume element size (%d) doesn't fit data (%d)", sizeof(VoxelT), header_.volume_element_size);
68  return false;
69  }
70  if ( header_.weights_element_size != sizeof(WeightT))
71  {
72  pcl::console::print_error ("[TSDFVolume::load] Error: Given weights element size (%d) doesn't fit data (%d)", sizeof(WeightT), header_.weights_element_size);
73  return false;
74  }
75 
76  // read DATA
77  int num_elements = header_.getVolumeSize();
78  volume_->resize (num_elements);
79  weights_->resize (num_elements);
80  file.read ((char*) &(*volume_)[0], num_elements * sizeof(VoxelT));
81  file.read ((char*) &(*weights_)[0], num_elements * sizeof(WeightT));
82  }
83  else
84  {
85  pcl::console::print_error ("[TSDFVolume::load] Error: ASCII loading not implemented.\n");
86  }
87 
88  file.close ();
89  }
90  else
91  {
92  pcl::console::print_error ("[TSDFVolume::load] Error: Cloudn't read file %s.\n", filename.c_str());
93  return false;
94  }
95 
96  const Eigen::Vector3i &res = this->gridResolution();
97  pcl::console::print_info ("done [%d voxels, res %dx%dx%d]\n", this->size(), res[0], res[1], res[2]);
98 
99  return true;
100 }
101 
102 
103 template <typename VoxelT, typename WeightT> bool
104 pcl::TSDFVolume<VoxelT, WeightT>::save (const std::string &filename, bool binary) const
105 {
106  pcl::console::print_info ("Saving TSDF volume to "); pcl::console::print_value ("%s ... ", filename.c_str());
107  std::cout << std::flush;
108 
109  std::ofstream file (filename.c_str(), binary ? std::ios_base::binary : std::ios_base::out);
110 
111  if (file.is_open())
112  {
113  if (binary)
114  {
115  // HEADER
116  // write resolution and size of volume
117  file.write ((char*) &header_, sizeof (Header));
118  /* file.write ((char*) &header_.resolution, sizeof(Eigen::Vector3i));
119  file.write ((char*) &header_.volume_size, sizeof(Eigen::Vector3f));
120  // write element size
121  int volume_element_size = sizeof(VolumeT);
122  file.write ((char*) &volume_element_size, sizeof(int));
123  int weights_element_size = sizeof(WeightT);
124  file.write ((char*) &weights_element_size, sizeof(int)); */
125 
126  // DATA
127  // write data
128  file.write ((char*) &(volume_->at(0)), volume_->size()*sizeof(VoxelT));
129  file.write ((char*) &(weights_->at(0)), weights_->size()*sizeof(WeightT));
130  }
131  else
132  {
133  // write resolution and size of volume and element size
134  file << header_.resolution(0) << " " << header_.resolution(1) << " " << header_.resolution(2) << std::endl;
135  file << header_.volume_size(0) << " " << header_.volume_size(1) << " " << header_.volume_size(2) << std::endl;
136  file << sizeof (VoxelT) << " " << sizeof(WeightT) << std::endl;
137 
138  // write data
139  for (typename std::vector<VoxelT>::const_iterator iter = volume_->begin(); iter != volume_->end(); ++iter)
140  file << *iter << std::endl;
141  }
142 
143  file.close();
144  }
145  else
146  {
147  pcl::console::print_error ("[saveTsdfVolume] Error: Couldn't open file %s.\n", filename.c_str());
148  return false;
149  }
150 
151  pcl::console::print_info ("done [%d voxels]\n", this->size());
152 
153  return true;
154 }
155 
156 
157 template <typename VoxelT, typename WeightT> void
159  const unsigned step) const
160 {
161  int sx = header_.resolution(0);
162  int sy = header_.resolution(1);
163  int sz = header_.resolution(2);
164 
165  const int cloud_size = header_.getVolumeSize() / (step*step*step);
166 
167  cloud->clear();
168  cloud->reserve (std::min (cloud_size/10, 500000));
169 
170  int volume_idx = 0, cloud_idx = 0;
171 //#pragma omp parallel for // if used, increment over idx not possible! use index calculation
172  for (int z = 0; z < sz; z+=step)
173  for (int y = 0; y < sy; y+=step)
174  for (int x = 0; x < sx; x+=step, ++cloud_idx)
175  {
176  volume_idx = sx*sy*z + sx*y + x;
177  // pcl::PointXYZI &point = cloud->points[cloud_idx];
178 
179  if (weights_->at(volume_idx) == 0 || volume_->at(volume_idx) > 0.98 )
180  continue;
181 
182  pcl::PointXYZI point;
183  point.x = x; point.y = y; point.z = z;//*64;
184  point.intensity = volume_->at(volume_idx);
185  cloud->push_back (point);
186  }
187 
188  // cloud->width = cloud_size;
189  // cloud->height = 1;
190 }
191 
192 
193 template <typename VoxelT, typename WeightT> template <typename PointT> void
194 pcl::TSDFVolume<VoxelT, WeightT>::getVoxelCoord (const PointT &point, Eigen::Vector3i &coord) const
195 {
196  static Eigen::Array3f voxel_size = voxelSize().array();
197 
198  // point coordinates in world coordinate frame and voxel coordinates
199  Eigen::Array3f point_coord (point.x, point.y, point.z);
200  Eigen::Array3f voxel_coord = (point_coord / voxel_size) - 0.5f; // 0.5f offset due to voxel center vs grid
201  coord(0) = round(voxel_coord(0));
202  coord(1) = round(voxel_coord(1));
203  coord(2) = round(voxel_coord(2));
204 }
205 
206 
207 /** \brief Returns the 3D voxel coordinate and point offset wrt. to the voxel center (in mm) */
208 template <typename VoxelT, typename WeightT> template <typename PointT> void
210  Eigen::Vector3i &coord, Eigen::Vector3f &offset) const
211 {
212  static Eigen::Array3f voxel_size = voxelSize().array();
213 
214  // point coordinates in world coordinate frame and voxel coordinates
215  Eigen::Array3f point_coord (point.x, point.y, point.z);
216  Eigen::Array3f voxel_coord = (point_coord / voxel_size) - 0.5f; // 0.5f offset due to voxel center vs grid
217  coord(0) = round(voxel_coord(0));
218  coord(1) = round(voxel_coord(1));
219  coord(2) = round(voxel_coord(2));
220 
221  // offset of point wrt. to voxel center
222  offset = (voxel_coord - coord.cast<float>().array() * voxel_size).matrix();
223 }
224 
225 
226 template <typename VoxelT, typename WeightT> bool
227 pcl::TSDFVolume<VoxelT, WeightT>::extractNeighborhood (const Eigen::Vector3i &voxel_coord, int neighborhood_size,
228  VoxelTVec &neighborhood) const
229 {
230  // point_index is at the center of a cube of scale_ x scale_ x scale_ voxels
231  int shift = (neighborhood_size - 1) / 2;
232  Eigen::Vector3i min_index = voxel_coord.array() - shift;
233  Eigen::Vector3i max_index = voxel_coord.array() + shift;
234 
235  // check that index is within range
236  if (getLinearVoxelIndex(min_index) < 0 || getLinearVoxelIndex(max_index) >= (int)size())
237  {
238  pcl::console::print_info ("[extractNeighborhood] Skipping voxel with coord (%d, %d, %d).\n", voxel_coord(0), voxel_coord(1), voxel_coord(2));
239  return false;
240  }
241 
242  static const int descriptor_size = neighborhood_size*neighborhood_size*neighborhood_size;
243  neighborhood.resize (descriptor_size);
244 
245  const Eigen::RowVector3i offset_vector (1, neighborhood_size, neighborhood_size*neighborhood_size);
246 
247  // loop over all voxels in 3D neighborhood
248  #pragma omp parallel for
249  for (int z = min_index(2); z <= max_index(2); ++z)
250  {
251  for (int y = min_index(1); y <= max_index(1); ++y)
252  {
253  for (int x = min_index(0); x <= max_index(0); ++x)
254  {
255  // linear voxel index in volume and index in descriptor vector
256  Eigen::Vector3i point (x,y,z);
257  int volume_idx = getLinearVoxelIndex (point);
258  int descr_idx = offset_vector * (point - min_index);
259 
260 /* std::cout << "linear index " << volume_idx << std::endl;
261  std::cout << "weight " << weights_->at (volume_idx) << std::endl;
262  std::cout << "volume " << volume_->at (volume_idx) << std::endl;
263  std::cout << "descr " << neighborhood.rows() << "x" << neighborhood.cols() << ", val = " << neighborhood << std::endl;
264  std::cout << "descr index = " << descr_idx << std::endl;
265 */
266  // get the TSDF value and store as descriptor entry
267  if (weights_->at (volume_idx) != 0)
268  neighborhood (descr_idx) = volume_->at (volume_idx);
269  else
270  neighborhood (descr_idx) = -1.0; // if never visited we assume inside of object (outside captured and thus filled with positive values)
271  }
272  }
273  }
274 
275  return true;
276 }
277 
278 
279 template <typename VoxelT, typename WeightT> bool
280 pcl::TSDFVolume<VoxelT, WeightT>::addNeighborhood (const Eigen::Vector3i &voxel_coord, int neighborhood_size,
281  const VoxelTVec &neighborhood, WeightT voxel_weight)
282 {
283  // point_index is at the center of a cube of scale_ x scale_ x scale_ voxels
284  int shift = (neighborhood_size - 1) / 2;
285  Eigen::Vector3i min_index = voxel_coord.array() - shift;
286  Eigen::Vector3i max_index = voxel_coord.array() + shift;
287 
288  // check that index is within range
289  if (getLinearVoxelIndex(min_index) < 0 || getLinearVoxelIndex(max_index) >= (int)size())
290  {
291  pcl::console::print_info ("[addNeighborhood] Skipping voxel with coord (%d, %d, %d).\n", voxel_coord(0), voxel_coord(1), voxel_coord(2));
292  return false;
293  }
294 
295  // static const int descriptor_size = neighborhood_size*neighborhood_size*neighborhood_size;
296  const Eigen::RowVector3i offset_vector (1, neighborhood_size, neighborhood_size*neighborhood_size);
297 
298  Eigen::Vector3i index = min_index;
299  // loop over all voxels in 3D neighborhood
300  #pragma omp parallel for
301  for (int z = min_index(2); z <= max_index(2); ++z)
302  {
303  for (int y = min_index(1); y <= max_index(1); ++y)
304  {
305  for (int x = min_index(0); x <= max_index(0); ++x)
306  {
307  // linear voxel index in volume and index in descriptor vector
308  Eigen::Vector3i point (x,y,z);
309  int volume_idx = getLinearVoxelIndex (point);
310  int descr_idx = offset_vector * (point - min_index);
311 
312  // add the descriptor entry to the volume
313  VoxelT &voxel = volume_->at (volume_idx);
314  WeightT &weight = weights_->at (volume_idx);
315 
316  // TODO check that this simple lock works correctly!!
317  #pragma omp atomic
318  voxel += neighborhood (descr_idx);
319 
320  #pragma omp atomic
321  weight += voxel_weight;
322  }
323  }
324  }
325 
326  return true;
327 }
328 
329 
330 template <typename VoxelT, typename WeightT> void
332 {
333  #pragma omp parallel for
334  for (size_t i = 0; i < volume_->size(); ++i)
335  {
336  WeightT &w = weights_->at(i);
337  if (w > 0.0)
338  {
339  volume_->at(i) /= w;
340  w = 1.0;
341  }
342  }
343 }
344 
345 
346 /*template <typename VoxelT, typename WeightT> template <typename PointT> void
347 pcl::TSDFVolume<VoxelT, WeightT>::createFromCloud (const typename pcl::PointCloud<PointT>::ConstPtr &cloud, const Intr &intr)
348 {
349  // get depth map from cloud
350  float bad_point = std::numeric_limits<float>::quiet_NaN ();
351  // Eigen::MatrixXf depth = Eigen::MatrixXf::Constant(cloud_->height, cloud_->width, bad_point);
352  Eigen::MatrixXf depth (cloud->height, cloud->width);
353 
354  // determine max and min value
355  float min = 3000.0, max = 0.0;
356  for (int x = 0; x < cloud->width; ++x)
357  for (int y = 0; y < cloud->height; ++y)
358  {
359  depth(y,x) = cloud->at(x,y).z;
360  if (!isnan(depth(y,x)))
361  {
362  if (depth(y,x) > max) max = depth(y,x);
363  if (depth(y,x) < min) min = depth(y,x);
364  }
365  }
366 
367  std::cout << " depth size = " << depth.rows() << "x" << depth.cols() << ", min/max = " << min << "/" << max << std::endl;
368 
369 
370  // BOOST_FOREACH (const PointT &p, cloud->points)
371  typename pcl::PointCloud<PointT>::const_iterator iter = cloud->begin();
372  for (; iter != cloud_>end(); ++iter)
373  {
374  const PointT &p = *iter;
375 
376  std::cout << "orig point = " << p << std::endl;
377 
378  Eigen::Array2f point (intr.fx * p.x + intr.cx * p.z,
379  intr.fx * p.y + intr.cy * p.z);
380  Eigen::Array2i pixel (round(point(0))/p.z, round(point(1))/p.z);
381 
382  std::cout << "point = " << point.transpose() << std::endl;
383  std::cout << "pixel = " << pixel.transpose() << std::endl;
384 
385  depth (pixel(1), pixel(0)) = p.z;
386  }
387 
388  std::cout << " scaling depth map" << std::endl;
389  // scale depth map
390  Eigen::MatrixXf depth_scaled;
391  // scaleDepth (depth, depth_scaled, intr);
392  // TODO find out what this should do! projection on unit sphere?!
393  depth_scaled = depth;
394 
395  // generate volume
396  // std::cout << " generating volume" << std::endl;
397  // resizeDefaultSize();
398  Eigen::Vector3f volume_size = volumeSize();
399  Eigen::Vector3f voxel_size = voxelSize();
400 
401  float tranc_dist = std::max (DEFAULT_TRANCATION_DISTANCE, 2.1f * voxel_size.maxCoeff());
402 
403  Eigen::Matrix3f R_inv_init = Eigen::Matrix3f::Identity();
404  Eigen::Vector3f t_init = volume_size * 0.5f - Eigen::Vector3f (0, 0, volume_size(2)/2.0f * 1.2f);
405  // std::cout << "initial pose: R_inv = " << R_inv_init << ", t_init = " << t_init.transpose() << std::endl;
406 
407  std::cout << " integrating values" << std::endl;
408  integrateVolume (depth_scaled, tranc_dist, R_inv_init, t_init, intr);
409 }*/
410 
411 
412 /*template <typename VoxelT, typename WeightT> void
413 pcl::TSDFVolume<VoxelT, WeightT>::scaleDepth (const Eigen::MatrixXf &depth, Eigen::MatrixXf &depth_scaled, const Intr &intr) const
414 {
415  // function ported from KinFu GPU code
416  depth_scaled.resizeLike (depth);
417 
418  float min = 3000.0, max = 0.0;
419  // loop over depth image
420  for (int x = 0; x < depth.cols(); ++x)
421  for (int y = 0; y < depth.rows(); ++y)
422  {
423  int Dp = depth(y,x);
424 
425  float xl = (x - intr.cx) / intr.fx;
426  float yl = (y - intr.cy) / intr.fy;
427  float lambda = sqrtf (xl * xl + yl * yl + 1);
428 
429  depth_scaled(y,x) = Dp * lambda;
430 
431  if (!isnan(depth_scaled(y,x)))
432  {
433  if (depth_scaled(y,x) > max) max = depth_scaled(y,x);
434  if (depth_scaled(y,x) < min) min = depth_scaled(y,x);
435  }
436  }
437 
438  std::cout << "depth_scaled size = " << depth_scaled.rows() << "x" << depth_scaled.cols() << ", min/max = " << min << "/" << max << std::endl;
439 }*/
440 
441 
442 /*template <typename VoxelT, typename WeightT> void
443 pcl::TSDFVolume<VoxelT, WeightT>::integrateVolume (const Eigen::MatrixXf &depth_scaled,
444  float tranc_dist,
445  const Eigen::Matrix3f &R_inv,
446  const Eigen::Vector3f &t,
447  const Intr &intr)
448 {
449  Eigen::Array3f voxel_size = voxelSize();
450  Eigen::Array3i volume_res = gridResolution();
451  Eigen::Array3f intr_arr (intr.fx, intr.fy, 1.0f);
452  Eigen::Array3i voxel_coord (0,0,0);
453 
454  // loop over grid in X and Y dimension
455  #pragma omp parallel for
456  // for (voxel_coord(0) = 0; voxel_coord(0) < volume_res(0); ++voxel_coord(0))
457  for (int i = 0; i < volume_res(0); ++i)
458  {
459  voxel_coord(0) = i;
460 
461  // std::stringstream ss;
462  // ss << voxel_coord(0) << "/" << volume_res(0) << " ";
463  // std::cout << ss.str();
464  std::cout << ". " << std::flush;
465 
466  for (voxel_coord(1) = 0; voxel_coord(1) < volume_res(1); ++voxel_coord(1))
467  {
468  voxel_coord(2) = 0;
469  // points at depth 0, shifted by t
470  Eigen::Vector3f v_g = (voxel_coord.cast<float>() + 0.5f) * voxel_size - t.array();
471  float v_g_part_norm = v_g(0)*v_g(0) + v_g(1)*v_g(1);
472 
473  // rays in 3d
474  Eigen::Vector3f v = (R_inv * v_g).array() * intr_arr;
475 
476  float z_scaled = 0;
477 
478  Eigen::Array3f R_inv_z_scaled = R_inv.col(2).array() * voxel_size(2) * intr_arr;
479 
480  float tranc_dist_inv = 1.0f / tranc_dist;
481 
482  // loop over depth values
483  for (voxel_coord(2) = 0; voxel_coord(2) < volume_res(2); ++voxel_coord(2),
484  v_g(2) += voxel_size(2),
485  z_scaled += voxel_size(2),
486  v(0) += R_inv_z_scaled(0),
487  v(1) += R_inv_z_scaled(1))
488  {
489  float inv_z = 1.0f / (v(2) + R_inv(2,2) * z_scaled);
490 
491  // std::cout << "z = " << voxel_coord(2) << ", inv_z = " << inv_z << std::endl;
492 
493  if (inv_z < 0)
494  continue;
495 
496  // project to camera
497  Eigen::Array2i img_coord (round(v(0) * inv_z + intr.cx),
498  round(v(1) * inv_z + intr.cy));
499 
500  // std::cout << "img_coord = " << img_coord.transpose() << std::endl;
501 
502  if (img_coord(0) >= 0 && img_coord(1) >= 0 && img_coord(0) < depth_scaled.cols() && img_coord(1) < depth_scaled.rows()) //6
503  {
504  float Dp_scaled = depth_scaled(img_coord(1), img_coord(0));
505 
506  // signed distance function
507  float sdf = Dp_scaled - sqrtf (v_g(2) * v_g(2) + v_g_part_norm);
508 
509  if (Dp_scaled != 0 && sdf >= -tranc_dist)
510  {
511  // get truncated distance function value
512  float tsdf = fmin (1.0f, sdf * tranc_dist_inv);
513 
514  // add values to volume
515  int idx = getLinearVoxelIndex (voxel_coord);
516  VoxelT &tsdf_val = volume_->at(idx);
517  short &weight = weights_->at(idx);
518  tsdf_val = tsdf_val * weight + tsdf;
519  weight += 1;
520  }
521  }
522  } // loop over depths
523  }
524  }
525  std::cout << std::endl;
526 }*/
527 
528 #define PCL_INSTANTIATE_TSDFVolume(VT,WT) template class PCL_EXPORTS pcl::reconstruction::TSDFVolume<VT,WT>;
529 
530 #endif /* TSDF_VOLUME_HPP_ */
void getVoxelCoord(const PointT &point, Eigen::Vector3i &voxel_coord) const
Converts the volume to a surface representation via a point cloud.
void reserve(size_t n)
Definition: point_cloud.h:449
PCL_EXPORTS void print_value(const char *format,...)
Print a value message on stream with colors.
bool addNeighborhood(const Eigen::Vector3i &voxel_coord, int neighborhood_size, const VoxelTVec &neighborhood, WeightT voxel_weight)
adds voxel values in local neighborhood
bool load(const std::string &filename, bool binary=true)
Loads volume from file.
Definition: tsdf_volume.hpp:46
PCL_EXPORTS void print_info(const char *format,...)
Print an info message on stream with colors.
Structure storing voxel grid resolution, volume size (in mm) and element_size of stored data...
Definition: tsdf_volume.h:69
void push_back(const PointT &pt)
Insert a new point in the cloud, at the end of the container.
Definition: point_cloud.h:480
bool extractNeighborhood(const Eigen::Vector3i &voxel_coord, int neighborhood_size, VoxelTVec &neighborhood) const
extracts voxels in neighborhood of given voxel
void averageValues()
averages voxel values by the weight value
boost::shared_ptr< PointCloud< PointT > > Ptr
Definition: point_cloud.h:428
void convertToTsdfCloud(pcl::PointCloud< pcl::PointXYZI >::Ptr &cloud, const unsigned step=2) const
Converts volume to cloud of TSDF values.
void getVoxelCoordAndOffset(const PointT &point, Eigen::Vector3i &voxel_coord, Eigen::Vector3f &offset) const
Returns the 3D voxel coordinate and point offset wrt.
void clear()
Removes all points in a cloud and sets the width and height to 0.
Definition: point_cloud.h:576
Eigen::VectorXf VoxelTVec
Definition: tsdf_volume.h:66
A point structure representing Euclidean xyz coordinates, and the RGB color.
PCL_EXPORTS void print_error(const char *format,...)
Print an error message on stream with colors.
bool save(const std::string &filename="tsdf_volume.dat", bool binary=true) const
Saves volume to file.