ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
VoxelGrid.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include "VoxelGrid.h"
9 
10 #include <Helper.h>
11 #include <Logging.h>
12 
13 #include <numeric>
14 #include <unordered_map>
15 
16 #include "Image.h"
17 #include "Octree.h"
19 #include "ecvBBox.h"
20 #include "ecvOrientedBBox.h"
21 
22 namespace cloudViewer {
23 namespace geometry {
24 
25 using namespace cloudViewer;
26 
27 VoxelGrid::VoxelGrid(const VoxelGrid &src_voxel_grid,
28  const char *name /* = "VoxelGrid"*/)
29  : ccHObject(name),
30  voxel_size_(src_voxel_grid.voxel_size_),
31  origin_(src_voxel_grid.origin_),
32  rotation_(src_voxel_grid.rotation_),
33  voxels_(src_voxel_grid.voxels_) {}
34 
35 ccBBox VoxelGrid::getOwnBB(bool withGLFeatures) {
37 }
38 
40  voxel_size_ = 0.0;
41  origin_ = Eigen::Vector3d::Zero();
42  voxels_.clear();
43  return *this;
44 }
45 
46 Eigen::Vector3d VoxelGrid::GetMinBound() const {
47  if (!HasVoxels()) {
48  return origin_;
49  }
50 
51  const auto corners = GetAllVoxelCorners();
52 
53  Eigen::Vector3d min_bound = corners.front();
54  for (const auto &p : corners) {
55  min_bound = min_bound.cwiseMin(p);
56  }
57 
58  return min_bound;
59 }
60 
61 Eigen::Vector3d VoxelGrid::GetMaxBound() const {
62  if (!HasVoxels()) {
63  return origin_;
64  }
65 
66  const auto corners = GetAllVoxelCorners();
67 
68  Eigen::Vector3d max_bound = corners.front();
69  for (const auto &p : corners) {
70  max_bound = max_bound.cwiseMax(p);
71  }
72 
73  return max_bound;
74 }
75 
76 std::vector<Eigen::Vector3d> VoxelGrid::GetAllVoxelCorners() const {
77  std::vector<Eigen::Vector3d> voxel_corners;
78  voxel_corners.reserve(voxels_.size() * 8);
79 
80  // Precompute voxel corner offsets in local grid space
81  static const Eigen::Vector3d offsets[8] = {{0, 0, 0}, {1, 0, 0}, {0, 1, 0},
82  {1, 1, 0}, {0, 0, 1}, {1, 0, 1},
83  {0, 1, 1}, {1, 1, 1}};
84 
85  for (const auto &it : voxels_) {
86  const Eigen::Vector3i &grid_index = it.second.grid_index_;
87  Eigen::Vector3d base =
88  origin_ + rotation_ * (grid_index.cast<double>() * voxel_size_);
89 
90  // Add all 8 corners directly without constructing temporary vectors
91  for (const auto &off : offsets) {
92  voxel_corners.push_back(base + rotation_ * (off * voxel_size_));
93  }
94  }
95 
96  return voxel_corners;
97 }
98 
99 Eigen::Vector3d VoxelGrid::GetCenter() const {
100  Eigen::Vector3d center(0, 0, 0);
101  if (!HasVoxels()) {
102  return center;
103  }
104  const Eigen::Vector3d half_voxel_size(0.5 * voxel_size_, 0.5 * voxel_size_,
105  0.5 * voxel_size_);
106  for (const auto &it : voxels_) {
107  const geometry::Voxel &voxel = it.second;
108  center += voxel.grid_index_.cast<double>() * voxel_size_ +
109  half_voxel_size;
110  }
111  center /= double(voxels_.size());
112  return origin_ + rotation_ * center;
113 }
114 
116  ccBBox box;
117  box.minCorner() = GetMinBound();
118  box.maxCorner() = GetMaxBound();
119  box.setValidity(!box.IsEmpty());
120  return box;
121 }
122 
125 }
126 
127 VoxelGrid &VoxelGrid::Transform(const Eigen::Matrix4d &transformation) {
128  // Only update origin_ and rotation_ (lazy transform)
129  origin_ = (transformation.block<3, 3>(0, 0) * origin_) +
130  transformation.block<3, 1>(0, 3);
131  rotation_ = transformation.block<3, 3>(0, 0) * rotation_;
132  return *this;
133 }
134 
135 VoxelGrid &VoxelGrid::Translate(const Eigen::Vector3d &translation,
136  bool relative) {
137  origin_ += (relative ? translation : translation - GetCenter());
138  return *this;
139 }
140 
141 VoxelGrid &VoxelGrid::Scale(const double scale, const Eigen::Vector3d &center) {
142  voxel_size_ *= scale;
143  origin_ = (origin_ - center) * scale + center;
144  return *this;
145 }
146 
147 VoxelGrid &VoxelGrid::Rotate(const Eigen::Matrix3d &R,
148  const Eigen::Vector3d &center) {
149  // Rotate the origin and the orientation
150  origin_ = R * (origin_ - center) + center;
151  rotation_ = R * rotation_;
152  return *this;
153 }
154 
156  if (voxel_size_ != voxelgrid.voxel_size_) {
158  "[VoxelGrid] Could not combine VoxelGrid because voxel_size "
159  "differs (this=%f, other=%f)",
160  voxel_size_, voxelgrid.voxel_size_);
161  }
162  if (origin_ != voxelgrid.origin_) {
164  "[VoxelGrid] Could not combine VoxelGrid because origin "
165  "differs (this=%f,%f,%f, other=%f,%f,%f)",
166  origin_(0), origin_(1), origin_(2), voxelgrid.origin_(0),
167  voxelgrid.origin_(1), voxelgrid.origin_(2));
168  }
169  if (this->HasColors() != voxelgrid.HasColors()) {
171  "[VoxelGrid] Could not combine VoxelGrid one has colors and "
172  "the other not.");
173  }
174  std::unordered_map<Eigen::Vector3i, AvgColorVoxel,
176  voxelindex_to_accpoint;
177  Eigen::Vector3d ref_coord;
179  bool has_colors = voxelgrid.HasColors();
180  for (const auto &it : voxelgrid.voxels_) {
181  const geometry::Voxel &voxel = it.second;
182  if (has_colors) {
183  voxelindex_to_accpoint[voxel.grid_index_].Add(voxel.grid_index_,
184  voxel.color_);
185  } else {
186  voxelindex_to_accpoint[voxel.grid_index_].Add(voxel.grid_index_);
187  }
188  }
189  for (const auto &it : voxels_) {
190  const geometry::Voxel &voxel = it.second;
191  if (has_colors) {
192  voxelindex_to_accpoint[voxel.grid_index_].Add(voxel.grid_index_,
193  voxel.color_);
194  } else {
195  voxelindex_to_accpoint[voxel.grid_index_].Add(voxel.grid_index_);
196  }
197  }
198  this->voxels_.clear();
199  for (const auto &accpoint : voxelindex_to_accpoint) {
200  this->AddVoxel(Voxel(accpoint.second.GetVoxelIndex(),
201  accpoint.second.GetAverageColor()));
202  }
203  return *this;
204 }
205 
206 VoxelGrid VoxelGrid::operator+(const VoxelGrid &voxelgrid) const {
207  return (VoxelGrid(*this) += voxelgrid);
208 }
209 
210 Eigen::Vector3i VoxelGrid::GetVoxel(const Eigen::Vector3d &point) const {
211  // Convert world point to local grid frame
212  Eigen::Vector3d local = rotation_.transpose() * (point - origin_);
213  Eigen::Vector3d voxel_f = local / voxel_size_;
214  return (Eigen::floor(voxel_f.array())).cast<int>();
215 }
216 
217 std::vector<Eigen::Vector3d> VoxelGrid::GetVoxelBoundingPoints(
218  const Eigen::Vector3i &index) const {
219  double r = voxel_size_ / 2.0;
220  auto x = GetVoxelCenterCoordinate(index);
221  std::vector<Eigen::Vector3d> points(8);
222  points[0] = x + rotation_ * Eigen::Vector3d(-r, -r, -r);
223  points[1] = x + rotation_ * Eigen::Vector3d(-r, -r, r);
224  points[2] = x + rotation_ * Eigen::Vector3d(r, -r, -r);
225  points[3] = x + rotation_ * Eigen::Vector3d(r, -r, r);
226  points[4] = x + rotation_ * Eigen::Vector3d(-r, r, -r);
227  points[5] = x + rotation_ * Eigen::Vector3d(-r, r, r);
228  points[6] = x + rotation_ * Eigen::Vector3d(r, r, -r);
229  points[7] = x + rotation_ * Eigen::Vector3d(r, r, r);
230  return points;
231 }
232 
233 void VoxelGrid::AddVoxel(const Voxel &voxel) {
234  voxels_[voxel.grid_index_] = voxel;
235 }
236 
237 void VoxelGrid::RemoveVoxel(const Eigen::Vector3i &idx) { voxels_.erase(idx); }
238 
239 std::vector<bool> VoxelGrid::CheckIfIncluded(
240  const std::vector<Eigen::Vector3d> &queries) {
241  std::vector<bool> output;
242  output.resize(queries.size());
243  size_t i = 0;
244  for (auto &query_double : queries) {
245  auto query = GetVoxel(query_double);
246  output[i] = voxels_.count(query) > 0;
247  i++;
248  }
249  return output;
250 }
251 
253  // TODO: currently only handles color leaf nodes
254  // Get leaf nodes and their node_info
255  std::unordered_map<std::shared_ptr<OctreeColorLeafNode>,
256  std::shared_ptr<OctreeNodeInfo>>
257  map_node_to_node_info;
258  auto f_collect_nodes =
259  [&map_node_to_node_info](
260  const std::shared_ptr<OctreeNode> &node,
261  const std::shared_ptr<OctreeNodeInfo> &node_info) -> bool {
262  if (auto color_leaf_node =
263  std::dynamic_pointer_cast<OctreeColorLeafNode>(node)) {
264  map_node_to_node_info[color_leaf_node] = node_info;
265  }
266  return false;
267  };
268  octree.Traverse(f_collect_nodes);
269 
270  // Prepare dimensions for voxel
271  origin_ = octree.origin_;
272  voxels_.clear();
273  for (const auto &it : map_node_to_node_info) {
274  voxel_size_ = std::min(voxel_size_, it.second->size_);
275  }
276 
277  // Convert nodes to voxels
278  for (const auto &it : map_node_to_node_info) {
279  const std::shared_ptr<OctreeColorLeafNode> &node = it.first;
280  const std::shared_ptr<OctreeNodeInfo> &node_info = it.second;
281  Eigen::Array3d node_center =
282  Eigen::Array3d(node_info->origin_) + node_info->size_ / 2.0;
283  Eigen::Vector3i grid_index =
284  Eigen::floor((node_center - Eigen::Array3d(origin_)) /
285  voxel_size_)
286  .cast<int>();
287  AddVoxel(Voxel(grid_index, node->color_));
288  }
289 }
290 
291 std::shared_ptr<geometry::Octree> VoxelGrid::ToOctree(
292  const size_t &max_depth) const {
293  auto octree = std::make_shared<geometry::Octree>(max_depth);
294  octree->CreateFromVoxelGrid(*this);
295  return octree;
296 }
297 
299  const Image &depth_map,
300  const camera::PinholeCameraParameters &camera_parameter,
301  bool keep_voxels_outside_image) {
302  if (depth_map.height_ != camera_parameter.intrinsic_.height_ ||
303  depth_map.width_ != camera_parameter.intrinsic_.width_) {
305  "[VoxelGrid] provided depth_map dimensions are not compatible "
306  "with the provided camera_parameters");
307  }
308 
309  auto rot = camera_parameter.extrinsic_.block<3, 3>(0, 0);
310  auto trans = camera_parameter.extrinsic_.block<3, 1>(0, 3);
311  auto intrinsic = camera_parameter.intrinsic_.intrinsic_matrix_;
312 
313  // get for each voxel if it projects to a valid pixel and check if the voxel
314  // depth is behind the depth of the depth map at the projected pixel.
315  for (auto it = voxels_.begin(); it != voxels_.end();) {
316  bool carve = true;
317  const geometry::Voxel &voxel = it->second;
318  auto pts = GetVoxelBoundingPoints(voxel.grid_index_);
319  for (auto &x : pts) {
320  auto x_trans = rot * x + trans;
321  auto uvz = intrinsic * x_trans;
322  double z = uvz(2);
323  double u = uvz(0) / z;
324  double v = uvz(1) / z;
325  double d;
326  bool within_boundary;
327  std::tie(within_boundary, d) = depth_map.FloatValueAt(u, v);
328  if ((!within_boundary && keep_voxels_outside_image) ||
329  (within_boundary && d > 0 && z >= d)) {
330  carve = false;
331  break;
332  }
333  }
334  if (carve)
335  it = voxels_.erase(it);
336  else
337  it++;
338  }
339  return *this;
340 }
341 
343  const Image &silhouette_mask,
344  const camera::PinholeCameraParameters &camera_parameter,
345  bool keep_voxels_outside_image) {
346  if (silhouette_mask.height_ != camera_parameter.intrinsic_.height_ ||
347  silhouette_mask.width_ != camera_parameter.intrinsic_.width_) {
349  "[VoxelGrid] provided silhouette_mask dimensions are not "
350  "compatible with the provided camera_parameters");
351  }
352 
353  auto rot = camera_parameter.extrinsic_.block<3, 3>(0, 0);
354  auto trans = camera_parameter.extrinsic_.block<3, 1>(0, 3);
355  auto intrinsic = camera_parameter.intrinsic_.intrinsic_matrix_;
356 
357  // get for each voxel if it projects to a valid pixel and check if the pixel
358  // is set (>0).
359  for (auto it = voxels_.begin(); it != voxels_.end();) {
360  bool carve = true;
361  const geometry::Voxel &voxel = it->second;
362  auto pts = GetVoxelBoundingPoints(voxel.grid_index_);
363  for (auto &x : pts) {
364  auto x_trans = rot * x + trans;
365  auto uvz = intrinsic * x_trans;
366  double z = uvz(2);
367  double u = uvz(0) / z;
368  double v = uvz(1) / z;
369  double d;
370  bool within_boundary;
371  std::tie(within_boundary, d) = silhouette_mask.FloatValueAt(u, v);
372  if ((!within_boundary && keep_voxels_outside_image) ||
373  (within_boundary && d > 0)) {
374  carve = false;
375  break;
376  }
377  }
378  if (carve)
379  it = voxels_.erase(it);
380  else
381  it++;
382  }
383  return *this;
384 }
385 
386 std::vector<Voxel> VoxelGrid::GetVoxels() const {
387  std::vector<Voxel> result;
388  result.reserve(voxels_.size());
389  for (const auto &keyval : voxels_) {
390  result.push_back(keyval.second);
391  }
392  return result;
393 }
394 
395 } // namespace geometry
396 } // namespace cloudViewer
bool has_colors
std::string name
int points
long voxel_index
Definition: VoxelGridIO.cpp:29
core::Tensor result
Definition: VtkUtils.cpp:76
Bounding box structure.
Definition: ecvBBox.h:25
virtual bool IsEmpty() const override
Definition: ecvBBox.h:72
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
const Vector3Tpl< T > & maxCorner() const
Returns max corner (const)
Definition: BoundingBox.h:156
void setValidity(bool state)
Sets bonding box validity.
Definition: BoundingBox.h:200
const Vector3Tpl< T > & minCorner() const
Returns min corner (const)
Definition: BoundingBox.h:154
Contains both intrinsic and extrinsic pinhole camera parameters.
PinholeCameraIntrinsic intrinsic_
PinholeCameraIntrinsic object.
Eigen::Matrix4d_u extrinsic_
Camera extrinsic parameters.
Class to aggregate color values from different votes in one voxel Computes the average color value in...
Definition: VoxelGrid.h:281
The Image class stores image with customizable width, height, num of channels and bytes per channel.
Definition: Image.h:33
std::pair< bool, double > FloatValueAt(double u, double v) const
Definition: Image.cpp:58
int height_
Height of the image.
Definition: Image.h:221
int width_
Width of the image.
Definition: Image.h:219
Octree datastructure.
Definition: Octree.h:250
VoxelGrid is a collection of voxels which are aligned in grid.
Definition: VoxelGrid.h:64
virtual Eigen::Vector3d GetMinBound() const override
Returns min bounds for geometry coordinates.
Definition: VoxelGrid.cpp:46
std::vector< Voxel > GetVoxels() const
Definition: VoxelGrid.cpp:386
void CreateFromOctree(const Octree &octree)
Definition: VoxelGrid.cpp:252
VoxelGrid & CarveSilhouette(const Image &silhouette_mask, const camera::PinholeCameraParameters &camera_parameter, bool keep_voxels_outside_image)
Definition: VoxelGrid.cpp:342
virtual VoxelGrid & Rotate(const Eigen::Matrix3d &R, const Eigen::Vector3d &center) override
Apply rotation to the geometry coordinates and normals. Given a rotation matrix , and center ,...
Definition: VoxelGrid.cpp:147
std::vector< bool > CheckIfIncluded(const std::vector< Eigen::Vector3d > &queries)
Definition: VoxelGrid.cpp:239
virtual ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
Definition: VoxelGrid.cpp:35
std::shared_ptr< geometry::Octree > ToOctree(const size_t &max_depth) const
Definition: VoxelGrid.cpp:291
std::vector< Eigen::Vector3d > GetAllVoxelCorners() const
Returns the 3D coordinates of all corners of every voxel in the grid.
Definition: VoxelGrid.cpp:76
virtual ccBBox GetAxisAlignedBoundingBox() const override
Returns an axis-aligned bounding box of the geometry.
Definition: VoxelGrid.cpp:115
std::unordered_map< Eigen::Vector3i, Voxel, cloudViewer::utility::hash_eigen< Eigen::Vector3i > > voxels_
Voxels contained in voxel grid.
Definition: VoxelGrid.h:274
double voxel_size_
Size of the voxel.
Definition: VoxelGrid.h:264
virtual VoxelGrid & Transform(const Eigen::Matrix4d &transformation) override
Apply transformation (4x4 matrix) to the geometry coordinates.
Definition: VoxelGrid.cpp:127
Eigen::Vector3d GetVoxelCenterCoordinate(const Eigen::Vector3i &idx) const
Function that returns the 3d coordinates of the queried voxel center.
Definition: VoxelGrid.h:115
virtual ecvOrientedBBox GetOrientedBoundingBox() const override
Definition: VoxelGrid.cpp:123
bool HasColors() const
Returns true if the voxel grid contains voxel colors.
Definition: VoxelGrid.h:108
VoxelGrid & CarveDepthMap(const Image &depth_map, const camera::PinholeCameraParameters &camera_parameter, bool keep_voxels_outside_image)
Definition: VoxelGrid.cpp:298
bool HasVoxels() const
Returns true if the voxel grid contains voxels.
Definition: VoxelGrid.h:106
virtual Eigen::Vector3d GetCenter() const override
Returns the center of the geometry coordinates.
Definition: VoxelGrid.cpp:99
VoxelGrid operator+(const VoxelGrid &voxelgrid) const
Definition: VoxelGrid.cpp:206
Eigen::Vector3d origin_
Coorindate of the origin point.
Definition: VoxelGrid.h:266
VoxelGrid(const char *name="VoxelGrid")
Default Constructor.
Definition: VoxelGrid.h:67
VoxelGrid & operator+=(const VoxelGrid &voxelgrid)
Definition: VoxelGrid.cpp:155
virtual VoxelGrid & Translate(const Eigen::Vector3d &translation, bool relative=true) override
Apply translation to the geometry coordinates.
Definition: VoxelGrid.cpp:135
Eigen::Vector3i GetVoxel(const Eigen::Vector3d &point) const
Returns voxel index given query point.
Definition: VoxelGrid.cpp:210
void RemoveVoxel(const Eigen::Vector3i &idx)
Remove a voxel with specified grid index.
Definition: VoxelGrid.cpp:237
virtual Eigen::Vector3d GetMaxBound() const override
Returns max bounds for geometry coordinates.
Definition: VoxelGrid.cpp:61
void AddVoxel(const Voxel &voxel)
Add a voxel with specified grid index and color.
Definition: VoxelGrid.cpp:233
std::vector< Eigen::Vector3d > GetVoxelBoundingPoints(const Eigen::Vector3i &index) const
Return a vector of 3D coordinates that define the indexed voxel cube.
Definition: VoxelGrid.cpp:217
virtual VoxelGrid & Scale(const double s, const Eigen::Vector3d &center) override
Apply scaling to the geometry coordinates. Given a scaling factor , and center , a given point is tr...
Definition: VoxelGrid.cpp:141
Base Voxel class, containing grid id and color.
Definition: VoxelGrid.h:38
Eigen::Vector3i grid_index_
Grid coordinate index of the voxel.
Definition: VoxelGrid.h:56
Eigen::Vector3d color_
Color of the voxel.
Definition: VoxelGrid.h:58
static ecvOrientedBBox CreateFromPoints(const std::vector< Eigen::Vector3d > &points)
#define LogError(...)
Definition: Logging.h:60
normal_z x
normal_z z
MiniVec< float, N > floor(const MiniVec< float, N > &a)
Definition: MiniVec.h:75
Generic file read and write utility for python interface.
Eigen::Matrix< Index, 3, 1 > Vector3i
Definition: knncpp.h:30
cloudViewer::DgmOctree * octree
#define local
Definition: unzip.c:93