ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
Octree.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 "Octree.h"
9 
10 #include <Logging.h>
11 #include <json/json.h>
12 
13 #include <Eigen/Dense>
14 #include <algorithm>
15 #include <unordered_map>
16 
17 #include "VoxelGrid.h"
18 #include "ecvBBox.h"
19 #include "ecvOrientedBBox.h"
20 #include "ecvPointCloud.h"
21 
22 namespace cloudViewer {
23 namespace geometry {
24 
25 using namespace cloudViewer;
26 
27 std::shared_ptr<OctreeNode> OctreeNode::ConstructFromJsonValue(
28  const Json::Value& value) {
29  // Construct node from class name
30  std::string class_name = value.get("class_name", "").asString();
31  std::shared_ptr<OctreeNode> node = nullptr;
32  if (value != Json::nullValue && class_name != "") {
33  if (class_name == "OctreeInternalNode") {
34  node = std::make_shared<OctreeInternalNode>();
35  } else if (class_name == "OctreeInternalPointNode") {
36  node = std::make_shared<OctreeInternalPointNode>();
37  } else if (class_name == "OctreeColorLeafNode") {
38  node = std::make_shared<OctreeColorLeafNode>();
39  } else if (class_name == "OctreePointColorLeafNode") {
40  node = std::make_shared<OctreePointColorLeafNode>();
41  } else {
42  utility::LogError("Unhandled class name {}", class_name);
43  }
44  }
45  // Convert from json
46  if (node != nullptr) {
47  bool convert_success = node->ConvertFromJsonValue(value);
48  if (!convert_success) {
49  node = nullptr;
50  }
51  }
52  return node;
53 }
54 
55 std::shared_ptr<OctreeNodeInfo> OctreeInternalNode::GetInsertionNodeInfo(
56  const std::shared_ptr<OctreeNodeInfo>& node_info,
57  const Eigen::Vector3d& point) {
58  if (!Octree::IsPointInBound(point, node_info->origin_, node_info->size_)) {
60  "Internal error: cannot insert to child since point not in "
61  "parent node bound.");
62  }
63 
64  double child_size = node_info->size_ / 2.0;
65  size_t x_index = point(0) < node_info->origin_(0) + child_size ? 0 : 1;
66  size_t y_index = point(1) < node_info->origin_(1) + child_size ? 0 : 1;
67  size_t z_index = point(2) < node_info->origin_(2) + child_size ? 0 : 1;
68  size_t child_index = x_index + y_index * 2 + z_index * 4;
69  Eigen::Vector3d child_origin =
70  node_info->origin_ + Eigen::Vector3d(x_index * child_size,
71  y_index * child_size,
72  z_index * child_size);
73  auto child_node_info = std::make_shared<OctreeNodeInfo>(
74  child_origin, child_size, node_info->depth_ + 1, child_index);
75  return child_node_info;
76 }
77 
78 std::function<std::shared_ptr<OctreeInternalNode>()>
79 OctreeInternalNode::GetInitFunction() {
80  return []() -> std::shared_ptr<geometry::OctreeInternalNode> {
81  return std::make_shared<geometry::OctreeInternalNode>();
82  };
83 }
84 
85 std::function<void(std::shared_ptr<OctreeInternalNode>)>
86 OctreeInternalNode::GetUpdateFunction() {
87  return [](std::shared_ptr<geometry::OctreeInternalNode> node) -> void {};
88 }
89 
90 bool OctreeInternalNode::ConvertToJsonValue(Json::Value& value) const {
91  bool rc = true;
92  value["class_name"] = "OctreeInternalNode";
93  value["children"] = Json::arrayValue;
94  value["children"].resize(8);
95  for (int cid = 0; cid < 8; ++cid) {
96  if (children_[cid] == nullptr) {
97  value["children"][Json::ArrayIndex(cid)] = Json::objectValue;
98  } else {
99  rc = rc && children_[cid]->ConvertToJsonValue(
100  value["children"][Json::ArrayIndex(cid)]);
101  }
102  }
103  return rc;
104 }
105 
106 bool OctreeInternalNode::ConvertFromJsonValue(const Json::Value& value) {
107  if (!value.isObject()) {
109  "ConvertFromJsonValue read JSON failed: unsupported json "
110  "format.");
111  return false;
112  }
113  std::string class_name = value.get("class_name", "").asString();
114  if (class_name != "OctreeInternalNode") {
115  utility::LogWarning("class_name {} != OctreeInternalNode", class_name);
116  return false;
117  }
118  bool rc = true;
119  for (int cid = 0; cid < 8; ++cid) {
120  const auto& child_value = value["children"][Json::ArrayIndex(cid)];
121  children_[cid] = OctreeNode::ConstructFromJsonValue(child_value);
122  }
123  return rc;
124 }
125 
126 std::function<std::shared_ptr<OctreeInternalNode>()>
127 OctreeInternalPointNode::GetInitFunction() {
128  return []() -> std::shared_ptr<geometry::OctreeInternalNode> {
129  return std::make_shared<geometry::OctreeInternalPointNode>();
130  };
131 }
132 
133 std::function<void(std::shared_ptr<OctreeInternalNode>)>
134 OctreeInternalPointNode::GetUpdateFunction(size_t idx) {
135  // Here the captured "idx" cannot be a reference, must be a copy,
136  // otherwise pybind does not have the correct value
137  return [idx](std::shared_ptr<geometry::OctreeInternalNode> node) -> void {
138  if (auto internal_point_node = std::dynamic_pointer_cast<
139  geometry::OctreeInternalPointNode>(node)) {
140  internal_point_node->indices_.push_back(idx);
141  } else {
143  "Internal error: internal node must be "
144  "OctreeInternalPointNode");
145  }
146  };
147 }
148 
149 bool OctreeInternalPointNode::ConvertToJsonValue(Json::Value& value) const {
150  // TODO: use inheritance here (copy value, change class_name to base class)
151  bool rc = true;
152  value["class_name"] = "OctreeInternalPointNode";
153  value["children"] = Json::arrayValue;
154  value["children"].resize(8);
155  for (int cid = 0; cid < 8; ++cid) {
156  if (children_[cid] == nullptr) {
157  value["children"][Json::ArrayIndex(cid)] = Json::objectValue;
158  } else {
159  rc = rc && children_[cid]->ConvertToJsonValue(
160  value["children"][Json::ArrayIndex(cid)]);
161  }
162  }
163  value["indices"].clear();
164  for (size_t idx : indices_) {
165  value["indices"].append(static_cast<Json::UInt>(idx));
166  }
167  return rc;
168 }
169 
170 bool OctreeInternalPointNode::ConvertFromJsonValue(const Json::Value& value) {
171  // TODO: use inheritance here (copy value, change class_name to base class)
172  if (!value.isObject()) {
174  "ConvertFromJsonValue read JSON failed: unsupported json "
175  "format.");
176  return false;
177  }
178  std::string class_name = value.get("class_name", "").asString();
179  if (class_name != "OctreeInternalPointNode") {
180  utility::LogWarning("class_name {} != OctreeInternalPointNode",
181  class_name);
182  return false;
183  }
184  bool rc = true;
185  for (int cid = 0; cid < 8; ++cid) {
186  const auto& child_value = value["children"][Json::ArrayIndex(cid)];
187  children_[cid] = OctreeNode::ConstructFromJsonValue(child_value);
188  }
189  indices_.reserve(value["indices"].size());
190  for (const auto& v : value["indices"]) {
191  indices_.push_back(v.asUInt());
192  }
193  return rc;
194 }
195 
196 std::function<std::shared_ptr<OctreeLeafNode>()>
198  return []() -> std::shared_ptr<geometry::OctreeLeafNode> {
199  return std::make_shared<geometry::OctreeColorLeafNode>();
200  };
201 }
202 
203 std::function<void(std::shared_ptr<OctreeLeafNode>)>
205  // Here the captured "color" cannot be a reference, must be a copy,
206  // otherwise pybind does not have the correct value
207  return [color](std::shared_ptr<geometry::OctreeLeafNode> node) -> void {
208  if (auto color_leaf_node =
209  std::dynamic_pointer_cast<geometry::OctreeColorLeafNode>(
210  node)) {
211  color_leaf_node->color_ = color;
212  } else {
214  "Internal error: leaf node must be OctreeColorLeafNode");
215  }
216  };
217 }
218 
219 std::shared_ptr<OctreeLeafNode> OctreeColorLeafNode::Clone() const {
220  auto cloned_node = std::make_shared<OctreeColorLeafNode>();
221  cloned_node->color_ = color_;
222  return cloned_node;
223 }
224 
226  try {
227  const OctreeColorLeafNode& that_color_node =
228  dynamic_cast<const OctreeColorLeafNode&>(that);
229  return this->color_.isApprox(that_color_node.color_);
230  } catch (const std::exception&) {
231  return false;
232  }
233 }
234 
236  value["class_name"] = "OctreeColorLeafNode";
237  return EigenVector3dToJsonArray(color_, value["color"]);
238 }
239 
241  if (value.isObject() == false) {
243  "OctreeColorLeafNode read JSON failed: unsupported json "
244  "format.");
245  return false;
246  }
247  if (value.get("class_name", "") != "OctreeColorLeafNode") {
248  return false;
249  }
250  return EigenVector3dFromJsonArray(color_, value["color"]);
251 }
252 
253 std::function<std::shared_ptr<OctreeLeafNode>()>
255  return []() -> std::shared_ptr<geometry::OctreeLeafNode> {
256  return std::make_shared<geometry::OctreePointColorLeafNode>();
257  };
258 }
259 
260 std::function<void(std::shared_ptr<OctreeLeafNode>)>
262  const Eigen::Vector3d& color) {
263  // Here the captured "idx" cannot be a reference, must be a copy,
264  // otherwise pybind does not have the correct value
265  return [idx,
266  color](std::shared_ptr<geometry::OctreeLeafNode> node) -> void {
267  if (auto point_color_leaf_node = std::dynamic_pointer_cast<
270  point_color_leaf_node);
271  point_color_leaf_node->indices_.push_back(idx);
272  } else {
274  "Internal error: leaf node must be "
275  "OctreePointColorLeafNode");
276  }
277  };
278 }
279 
280 std::shared_ptr<OctreeLeafNode> OctreePointColorLeafNode::Clone() const {
281  auto cloned_node = std::make_shared<OctreePointColorLeafNode>();
282  cloned_node->color_ = color_;
283  cloned_node->indices_ = indices_;
284  return cloned_node;
285 }
286 
288  try {
289  const OctreePointColorLeafNode& that_point_color_node =
290  dynamic_cast<const OctreePointColorLeafNode&>(that);
291 
292  return this->color_.isApprox(that_point_color_node.color_) &&
293  this->indices_.size() == that_point_color_node.indices_.size() &&
294  this->indices_ == that_point_color_node.indices_;
295  } catch (const std::exception&) {
296  return false;
297  }
298 }
299 
301  value["class_name"] = "OctreePointColorLeafNode";
302  EigenVector3dToJsonArray(color_, value["color"]);
303  value["indices"].clear();
304  for (size_t idx : indices_) {
305  value["indices"].append(static_cast<Json::UInt>(idx));
306  }
307  return true;
308 }
309 
311  if (!value.isObject()) {
313  "OctreePointColorLeafNode read JSON failed: unsupported json "
314  "format.");
315  return false;
316  }
317  if (value.get("class_name", "") != "OctreePointColorLeafNode") {
318  return false;
319  }
320  EigenVector3dFromJsonArray(color_, value["color"]);
321  indices_.reserve(value["indices"].size());
322  for (const auto& v : value["indices"]) {
323  indices_.push_back(v.asInt());
324  }
325  return true;
326 }
327 
328 Octree::Octree(const Octree& src_octree, const char* name /* = "Octree2"*/)
329  : ccHObject(name),
330  origin_(src_octree.origin_),
331  size_(src_octree.size_),
332  max_depth_(src_octree.max_depth_) {
333  // First traversal: clone nodes without edges
334  std::unordered_map<std::shared_ptr<OctreeNode>, std::shared_ptr<OctreeNode>>
335  map_src_to_dst_node;
336  auto f_build_map =
337  [&map_src_to_dst_node](
338  const std::shared_ptr<OctreeNode>& src_node,
339  const std::shared_ptr<OctreeNodeInfo>& src_node_info)
340  -> bool {
341  if (auto src_internal_node =
342  std::dynamic_pointer_cast<OctreeInternalNode>(src_node)) {
343  auto dst_internal_node = std::make_shared<OctreeInternalNode>();
344  map_src_to_dst_node[src_internal_node] = dst_internal_node;
345  } else if (auto src_leaf_node =
346  std::dynamic_pointer_cast<OctreeLeafNode>(
347  src_node)) {
348  map_src_to_dst_node[src_leaf_node] = src_leaf_node->Clone();
349  } else {
350  utility::LogError("Internal error: unknown node type");
351  }
352  return false;
353  };
354  src_octree.Traverse(f_build_map);
355 
356  // Second traversal: add edges
357  auto f_clone_edges =
358  [&map_src_to_dst_node](
359  const std::shared_ptr<OctreeNode>& src_node,
360  const std::shared_ptr<OctreeNodeInfo>& src_node_info)
361  -> bool {
362  if (auto src_internal_node =
363  std::dynamic_pointer_cast<OctreeInternalNode>(src_node)) {
364  auto dst_internal_node =
365  std::dynamic_pointer_cast<OctreeInternalNode>(
366  map_src_to_dst_node.at(src_internal_node));
367  for (size_t child_index = 0; child_index < 8; child_index++) {
368  auto src_child_node = src_internal_node->children_[child_index];
369  if (src_child_node != nullptr) {
370  auto dst_child_node =
371  map_src_to_dst_node.at(src_child_node);
372  dst_internal_node->children_[child_index] = dst_child_node;
373  }
374  }
375  }
376  return false;
377  };
378  src_octree.Traverse(f_clone_edges);
379 
380  // Save root node to dst_octree (this octree)
381  root_node_ = map_src_to_dst_node.at(src_octree.root_node_);
382 }
383 
384 bool Octree::operator==(const Octree& that) const {
385  // Check basic properties
386  bool rc = true;
387  rc = rc && origin_.isApprox(that.origin_);
388  rc = rc && size_ == that.size_;
389  rc = rc && max_depth_ == that.max_depth_;
390  if (!rc) {
391  return rc;
392  }
393 
394  // Assign and check node ids
395  std::unordered_map<std::shared_ptr<OctreeNode>, size_t> map_node_to_id;
396  std::unordered_map<size_t, std::shared_ptr<OctreeNode>> map_id_to_node;
397  size_t next_id = 0;
398  auto f_assign_node_id =
399  [&map_node_to_id, &map_id_to_node, &next_id](
400  const std::shared_ptr<OctreeNode>& node,
401  const std::shared_ptr<OctreeNodeInfo>& node_info) -> bool {
402  map_node_to_id[node] = next_id;
403  map_id_to_node[next_id] = node;
404  next_id++;
405  return false;
406  };
407 
408  map_node_to_id.clear();
409  map_id_to_node.clear();
410  next_id = 0;
411  Traverse(f_assign_node_id);
412  std::unordered_map<std::shared_ptr<OctreeNode>, size_t>
413  this_map_node_to_id = map_node_to_id;
414  std::unordered_map<size_t, std::shared_ptr<OctreeNode>>
415  this_map_id_to_node = map_id_to_node;
416  size_t num_nodes = next_id;
417 
418  map_node_to_id.clear();
419  map_id_to_node.clear();
420  next_id = 0;
421  that.Traverse(f_assign_node_id);
422  std::unordered_map<std::shared_ptr<OctreeNode>, size_t>
423  that_map_node_to_id = map_node_to_id;
424  std::unordered_map<size_t, std::shared_ptr<OctreeNode>>
425  that_map_id_to_node = map_id_to_node;
426 
427  rc = rc && this_map_node_to_id.size() == num_nodes &&
428  that_map_node_to_id.size() == num_nodes &&
429  this_map_id_to_node.size() == num_nodes &&
430  that_map_id_to_node.size() == num_nodes;
431  if (!rc) {
432  return rc;
433  }
434 
435  // Check nodes
436  for (size_t id = 0; id < num_nodes; ++id) {
437  std::shared_ptr<OctreeNode> this_node = this_map_id_to_node.at(id);
438  std::shared_ptr<OctreeNode> that_node = that_map_id_to_node.at(id);
439  // Check if internal node
440  auto is_same_node_type = false;
441  auto this_internal_node =
442  std::dynamic_pointer_cast<OctreeInternalNode>(this_node);
443  auto that_internal_node =
444  std::dynamic_pointer_cast<OctreeInternalNode>(that_node);
445  if (this_internal_node != nullptr && that_internal_node != nullptr) {
446  is_same_node_type = true;
447  for (size_t child_index = 0; child_index < 8; child_index++) {
448  const std::shared_ptr<OctreeNode>& this_child =
449  this_internal_node->children_[child_index];
450  int this_child_id = -1;
451  if (this_child != nullptr) {
452  this_child_id = int(this_map_node_to_id.at(this_child));
453  }
454  const std::shared_ptr<OctreeNode>& that_child =
455  that_internal_node->children_[child_index];
456  int that_child_id = -1;
457  if (that_child != nullptr) {
458  that_child_id = int(that_map_node_to_id.at(that_child));
459  }
460  rc = rc && this_child_id == that_child_id;
461  }
462  }
463  // Check if leaf node
464  auto this_leaf_node =
465  std::dynamic_pointer_cast<OctreeLeafNode>(this_node);
466  auto that_leaf_node =
467  std::dynamic_pointer_cast<OctreeLeafNode>(that_node);
468  if (this_leaf_node != nullptr && that_leaf_node != nullptr) {
469  is_same_node_type = true;
470  rc = rc && (*this_leaf_node) == (*that_leaf_node);
471  }
472  // Handle case where node types are different
473  rc = rc && is_same_node_type;
474  }
475 
476  return rc;
477 }
478 
479 ccBBox Octree::getOwnBB(bool withGLFeatures) {
480  return GetAxisAlignedBoundingBox();
481 }
482 
484  // Inherited Clear function
485  root_node_ = nullptr;
486  origin_.setZero();
487  size_ = 0;
488  return *this;
489 }
490 
491 Eigen::Vector3d Octree::GetMinBound() const {
492  if (IsEmpty()) {
493  return Eigen::Vector3d::Zero();
494  } else {
495  return origin_;
496  }
497 }
498 
499 Eigen::Vector3d Octree::GetMaxBound() const {
500  if (IsEmpty()) {
501  return Eigen::Vector3d::Zero();
502  } else {
503  return origin_ + Eigen::Vector3d(size_, size_, size_);
504  }
505 }
506 
507 Eigen::Vector3d Octree::GetCenter() const {
508  return origin_ + Eigen::Vector3d(size_, size_, size_) / 2;
509 }
510 
512  ccBBox box;
513  box.minCorner() = GetMinBound();
514  box.maxCorner() = GetMaxBound();
515  box.setValidity(!box.IsEmpty());
516  return box;
517 }
518 
522 }
523 
524 Octree& Octree::Transform(const Eigen::Matrix4d& transformation) {
525  utility::LogError("Not implemented");
526  return *this;
527 }
528 
529 Octree& Octree::Translate(const Eigen::Vector3d& translation, bool relative) {
530  utility::LogError("Not implemented");
531  return *this;
532 }
533 
534 Octree& Octree::Scale(const double s, const Eigen::Vector3d& center) {
535  utility::LogError("Not implemented");
536  return *this;
537 }
538 
539 Octree& Octree::Rotate(const Eigen::Matrix3d& R,
540  const Eigen::Vector3d& center) {
541  utility::LogError("Not implemented");
542  return *this;
543 }
544 
546  double size_expand) {
547  if (size_expand > 1 || size_expand < 0) {
548  utility::LogError("size_expand shall be between 0 and 1");
549  }
550 
551  // Set bounds
552  Clear();
553  Eigen::Array3d min_bound = point_cloud.GetMinBound();
554  Eigen::Array3d max_bound = point_cloud.GetMaxBound();
555  Eigen::Array3d center = (min_bound + max_bound) / 2;
556  Eigen::Array3d half_sizes = center - min_bound;
557  double max_half_size = half_sizes.maxCoeff();
558  origin_ = min_bound.min(center - max_half_size);
559  if (max_half_size == 0) {
560  size_ = size_expand;
561  } else {
562  size_ = max_half_size * 2 * (1 + size_expand);
563  }
564 
565  // Insert points
566  const bool has_colors = point_cloud.hasColors();
567  for (size_t idx = 0; idx < point_cloud.size(); idx++) {
568  const Eigen::Vector3d& color = has_colors
569  ? point_cloud.getEigenColor(idx)
570  : Eigen::Vector3d::Zero();
571  InsertPoint(point_cloud.getEigenPoint(idx),
574  OctreeInternalPointNode::GetInitFunction(),
575  OctreeInternalPointNode::GetUpdateFunction(idx));
576  }
577 }
578 
580  const Eigen::Vector3d& point,
581  const std::function<std::shared_ptr<OctreeLeafNode>()>& fl_init,
582  const std::function<void(std::shared_ptr<OctreeLeafNode>)>& fl_update,
583  const std::function<std::shared_ptr<OctreeInternalNode>()>& fi_init,
584  const std::function<void(std::shared_ptr<OctreeInternalNode>)>&
585  fi_update) {
586  // if missing, create basic internal node init and update functions
587  auto _fi_init = fi_init;
588  if (_fi_init == nullptr) {
589  _fi_init = OctreeInternalNode::GetInitFunction();
590  }
591  auto _fi_update = fi_update;
592  if (_fi_update == nullptr) {
593  _fi_update = OctreeInternalNode::GetUpdateFunction();
594  }
595 
596  if (root_node_ == nullptr) {
597  if (max_depth_ == 0) {
598  root_node_ = fl_init();
599  } else {
600  root_node_ = _fi_init();
601  }
602  }
603  auto root_node_info =
604  std::make_shared<OctreeNodeInfo>(origin_, size_, 0, 0);
605 
606  InsertPointRecurse(root_node_, root_node_info, point, fl_init, fl_update,
607  _fi_init, _fi_update);
608 }
609 
610 void Octree::InsertPointRecurse(
611  const std::shared_ptr<OctreeNode>& node,
612  const std::shared_ptr<OctreeNodeInfo>& node_info,
613  const Eigen::Vector3d& point,
614  const std::function<std::shared_ptr<OctreeLeafNode>()>& fl_init,
615  const std::function<void(std::shared_ptr<OctreeLeafNode>)>& fl_update,
616  const std::function<std::shared_ptr<OctreeInternalNode>()>& fi_init,
617  const std::function<void(std::shared_ptr<OctreeInternalNode>)>&
618  fi_update) {
619  if (!IsPointInBound(point, node_info->origin_, node_info->size_)) {
620  return;
621  }
622  if (node_info->depth_ > max_depth_) {
623  return;
624  } else if (node_info->depth_ == max_depth_) {
625  if (auto leaf_node = std::dynamic_pointer_cast<OctreeLeafNode>(node)) {
626  fl_update(leaf_node);
627  } else {
629  "Internal error: leaf node must be OctreeLeafNode");
630  }
631  } else {
632  if (auto internal_node =
633  std::dynamic_pointer_cast<OctreeInternalNode>(node)) {
634  // Update internal node with information about the current point
635  fi_update(internal_node);
636 
637  // Get child node info
638  std::shared_ptr<OctreeNodeInfo> child_node_info =
639  internal_node->GetInsertionNodeInfo(node_info, point);
640 
641  // Create child node with factory function
642  size_t child_index = child_node_info->child_index_;
643  if (internal_node->children_[child_index] == nullptr) {
644  if (node_info->depth_ == max_depth_ - 1) {
645  internal_node->children_[child_index] = fl_init();
646  } else {
647  internal_node->children_[child_index] = fi_init();
648  }
649  }
650 
651  // Insert to the child
652  InsertPointRecurse(internal_node->children_[child_index],
653  child_node_info, point, fl_init, fl_update,
654  fi_init, fi_update);
655  } else {
657  "Internal error: internal node must be "
658  "OctreeInternalNode");
659  }
660  }
661 }
662 
663 bool Octree::IsPointInBound(const Eigen::Vector3d& point,
664  const Eigen::Vector3d& origin,
665  const double& size) {
666  bool rc = (Eigen::Array3d(origin) <= Eigen::Array3d(point)).all() &&
667  (Eigen::Array3d(point) < Eigen::Array3d(origin) + size).all();
668  return rc;
669 }
670 
672  const std::function<bool(const std::shared_ptr<OctreeNode>&,
673  const std::shared_ptr<OctreeNodeInfo>&)>& f) {
674  // root_node_'s child index is 0, though it isn't a child node
675  TraverseRecurse(root_node_,
676  std::make_shared<OctreeNodeInfo>(origin_, size_, 0, 0), f);
677 }
678 
680  const std::function<bool(const std::shared_ptr<OctreeNode>&,
681  const std::shared_ptr<OctreeNodeInfo>&)>& f)
682  const {
683  // root_node_'s child index is 0, though it isn't a child node
684  TraverseRecurse(root_node_,
685  std::make_shared<OctreeNodeInfo>(origin_, size_, 0, 0), f);
686 }
687 
688 void Octree::TraverseRecurse(
689  const std::shared_ptr<OctreeNode>& node,
690  const std::shared_ptr<OctreeNodeInfo>& node_info,
691  const std::function<bool(const std::shared_ptr<OctreeNode>&,
692  const std::shared_ptr<OctreeNodeInfo>&)>& f) {
693  if (node == nullptr) {
694  return;
695  } else if (auto internal_node =
696  std::dynamic_pointer_cast<OctreeInternalNode>(node)) {
697  // Allow caller to avoid traversing further down this tree path
698  if (f(internal_node, node_info)) return;
699 
700  double child_size = node_info->size_ / 2.0;
701 
702  for (size_t child_index = 0; child_index < 8; ++child_index) {
703  size_t x_index = child_index % 2;
704  size_t y_index = (child_index / 2) % 2;
705  size_t z_index = (child_index / 4) % 2;
706 
707  auto child_node = internal_node->children_[child_index];
708  Eigen::Vector3d child_node_origin =
709  node_info->origin_ + Eigen::Vector3d(double(x_index),
710  double(y_index),
711  double(z_index)) *
712  child_size;
713  auto child_node_info = std::make_shared<OctreeNodeInfo>(
714  child_node_origin, child_size, node_info->depth_ + 1,
715  child_index);
716  TraverseRecurse(child_node, child_node_info, f);
717  }
718  } else if (auto leaf_node =
719  std::dynamic_pointer_cast<OctreeLeafNode>(node)) {
720  f(leaf_node, node_info);
721  } else {
722  utility::LogError("Internal error: unknown node type");
723  }
724 }
725 
726 std::pair<std::shared_ptr<OctreeLeafNode>, std::shared_ptr<OctreeNodeInfo>>
727 Octree::LocateLeafNode(const Eigen::Vector3d& point) const {
728  std::shared_ptr<OctreeLeafNode> target_leaf_node = nullptr;
729  std::shared_ptr<OctreeNodeInfo> target_leaf_node_info = nullptr;
730  auto f_locate_leaf_node =
731  [&target_leaf_node, &target_leaf_node_info, &point](
732  const std::shared_ptr<OctreeNode>& node,
733  const std::shared_ptr<OctreeNodeInfo>& node_info) -> bool {
734  bool skip_children = false;
735  if (IsPointInBound(point, node_info->origin_, node_info->size_)) {
736  if (auto leaf_node =
737  std::dynamic_pointer_cast<OctreeLeafNode>(node)) {
738  target_leaf_node = leaf_node;
739  target_leaf_node_info = node_info;
740  }
741  } else {
742  skip_children = true;
743  }
744  return skip_children;
745  };
746  Traverse(f_locate_leaf_node);
747  return std::make_pair(target_leaf_node, target_leaf_node_info);
748 }
749 
750 std::shared_ptr<geometry::VoxelGrid> Octree::ToVoxelGrid() const {
751  auto voxel_grid = std::make_shared<geometry::VoxelGrid>();
752  voxel_grid->CreateFromOctree(*this);
753  return voxel_grid;
754 }
755 
757  bool rc = true;
758  value["class_name"] = "Octree";
759  value["size"] = size_;
760  value["max_depth"] = Json::Int64(max_depth_);
761  rc = rc && EigenVector3dToJsonArray(origin_, value["origin"]);
762  if (root_node_ == nullptr) {
763  value["tree"] = Json::objectValue;
764  } else {
765  rc = rc && root_node_->ConvertToJsonValue(value["tree"]);
766  }
767  return rc;
768 }
769 
771  if (value.isObject() == false) {
773  "Octree read JSON failed: unsupported json format.");
774  return false;
775  }
776  if (value.get("class_name", "") != "Octree") {
777  return false;
778  }
779 
780  // Get octree attributes
781  bool rc = true;
782  rc = EigenVector3dFromJsonArray(origin_, value["origin"]);
783  size_ = value.get("size", 0.0).asDouble();
784  max_depth_ = value.get("max_depth", 0).asInt64();
785 
786  // Create nodes
788  return rc;
789 }
790 
792  origin_ = voxel_grid.origin_;
793  size_ = (voxel_grid.GetMaxBound() - origin_).maxCoeff();
794  double half_voxel_size = voxel_grid.voxel_size_ / 2.;
795  for (const auto& voxel_iter : voxel_grid.voxels_) {
796  const geometry::Voxel& voxel = voxel_iter.second;
797  Eigen::Vector3d mid_point = half_voxel_size + origin_.array() +
798  voxel.grid_index_.array().cast<double>() *
799  voxel_grid.voxel_size_;
802  }
803 }
804 
805 } // namespace geometry
806 } // namespace cloudViewer
int size
bool has_colors
std::string name
int64_t size_
Definition: FilePLY.cpp:37
math::float4 color
Eigen::Vector3d origin
Definition: VoxelGridIO.cpp:26
Bounding box structure.
Definition: ecvBBox.h:25
virtual bool IsEmpty() const override
Definition: ecvBBox.h:72
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
virtual Eigen::Vector3d GetMinBound() const override
Returns min bounds for geometry coordinates.
bool hasColors() const override
Returns whether colors are enabled or not.
Eigen::Vector3d getEigenColor(size_t index) const
virtual Eigen::Vector3d GetMaxBound() const override
Returns max bounds for geometry coordinates.
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
unsigned size() const override
Definition: PointCloudTpl.h:38
Eigen::Vector3d getEigenPoint(size_t index) const
OctreeColorLeafNode class is an OctreeLeafNode containing color.
Definition: Octree.h:183
static std::function< std::shared_ptr< OctreeLeafNode >)> GetInitFunction()
Get lambda function for initializing OctreeLeafNode.
Definition: Octree.cpp:197
static std::function< void(std::shared_ptr< OctreeLeafNode >)> GetUpdateFunction(const Eigen::Vector3d &color)
Get lambda function for updating OctreeLeafNode.
Definition: Octree.cpp:204
std::shared_ptr< OctreeLeafNode > Clone() const override
Clone this OctreeLeafNode.
Definition: Octree.cpp:219
bool operator==(const OctreeLeafNode &other) const override
Definition: Octree.cpp:225
bool ConvertFromJsonValue(const Json::Value &value) override
Definition: Octree.cpp:240
bool ConvertToJsonValue(Json::Value &value) const override
Definition: Octree.cpp:235
OctreeLeafNode base class.
Definition: Octree.h:173
static std::shared_ptr< OctreeNode > ConstructFromJsonValue(const Json::Value &value)
Factory function to construct an OctreeNode by parsing the json value.
Definition: Octree.cpp:27
OctreePointColorLeafNode class is an OctreeColorLeafNode containing a list of indices corresponding t...
Definition: Octree.h:217
std::vector< size_t > indices_
Associated point indices with this node.
Definition: Octree.h:243
static std::function< void(std::shared_ptr< OctreeLeafNode >)> GetUpdateFunction(size_t index, const Eigen::Vector3d &color)
Get lambda function for updating OctreeLeafNode.
Definition: Octree.cpp:261
bool ConvertToJsonValue(Json::Value &value) const override
Definition: Octree.cpp:300
static std::function< std::shared_ptr< OctreeLeafNode >)> GetInitFunction()
Get lambda function for initializing OctreeLeafNode.
Definition: Octree.cpp:254
bool ConvertFromJsonValue(const Json::Value &value) override
Definition: Octree.cpp:310
std::shared_ptr< OctreeLeafNode > Clone() const override
Clone this OctreeLeafNode.
Definition: Octree.cpp:280
bool operator==(const OctreeLeafNode &other) const override
Definition: Octree.cpp:287
Octree datastructure.
Definition: Octree.h:250
virtual Octree & Transform(const Eigen::Matrix4d &transformation) override
Apply transformation (4x4 matrix) to the geometry coordinates.
Definition: Octree.cpp:524
void CreateFromVoxelGrid(const geometry::VoxelGrid &voxel_grid)
Convert from voxel grid.
Definition: Octree.cpp:791
Octree(const char *name="Octree2")
Default Constructor.
Definition: Octree.h:253
std::shared_ptr< geometry::VoxelGrid > ToVoxelGrid() const
Convert to VoxelGrid.
Definition: Octree.cpp:750
static bool IsPointInBound(const Eigen::Vector3d &point, const Eigen::Vector3d &origin, const double &size)
Return true if point within bound, that is, origin <= point < origin + size.
Definition: Octree.cpp:663
virtual ecvOrientedBBox GetOrientedBoundingBox() const override
Definition: Octree.cpp:519
bool ConvertToJsonValue(Json::Value &value) const override
Definition: Octree.cpp:756
void ConvertFromPointCloud(const ccPointCloud &point_cloud, double size_expand=0.01)
Convert octree from point cloud.
Definition: Octree.cpp:545
virtual ccBBox getOwnBB(bool withGLFeatures=false) override
Returns the entity's own bounding-box.
Definition: Octree.cpp:479
virtual Octree & 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: Octree.cpp:534
virtual Eigen::Vector3d GetMinBound() const override
Returns min bounds for geometry coordinates.
Definition: Octree.cpp:491
bool operator==(const Octree &other) const
Returns true if the Octree is completely the same, used for testing.
Definition: Octree.cpp:384
virtual ccBBox GetAxisAlignedBoundingBox() const override
Returns an axis-aligned bounding box of the geometry.
Definition: Octree.cpp:511
virtual Eigen::Vector3d GetMaxBound() const override
Returns max bounds for geometry coordinates.
Definition: Octree.cpp:499
virtual bool IsEmpty() const override
Definition: Octree.h:288
void InsertPoint(const Eigen::Vector3d &point, const std::function< std::shared_ptr< OctreeLeafNode >()> &fl_init, const std::function< void(std::shared_ptr< OctreeLeafNode >)> &fl_update, const std::function< std::shared_ptr< OctreeInternalNode >()> &fi_init=nullptr, const std::function< void(std::shared_ptr< OctreeInternalNode >)> &fi_update=nullptr)
Insert a point to the octree.
Definition: Octree.cpp:579
virtual Octree & Translate(const Eigen::Vector3d &translation, bool relative=true) override
Apply translation to the geometry coordinates.
Definition: Octree.cpp:529
std::pair< std::shared_ptr< OctreeLeafNode >, std::shared_ptr< OctreeNodeInfo > > LocateLeafNode(const Eigen::Vector3d &point) const
Returns leaf OctreeNode and OctreeNodeInfo where the querypoint should reside.
Definition: Octree.cpp:727
std::shared_ptr< OctreeNode > root_node_
Root of the octree.
Definition: Octree.h:317
void Traverse(const std::function< bool(const std::shared_ptr< OctreeNode > &, const std::shared_ptr< OctreeNodeInfo > &)> &f)
DFS traversal of Octree from the root, with callback function called for each node.
Definition: Octree.cpp:671
Eigen::Vector3d origin_
Definition: Octree.h:321
virtual Octree & 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: Octree.cpp:539
virtual Eigen::Vector3d GetCenter() const override
Returns the center of the geometry coordinates.
Definition: Octree.cpp:507
bool ConvertFromJsonValue(const Json::Value &value) override
Definition: Octree.cpp:770
VoxelGrid is a collection of voxels which are aligned in grid.
Definition: VoxelGrid.h:64
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
Eigen::Vector3d origin_
Coorindate of the origin point.
Definition: VoxelGrid.h:266
virtual Eigen::Vector3d GetMaxBound() const override
Returns max bounds for geometry coordinates.
Definition: VoxelGrid.cpp:61
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 bool EigenVector3dFromJsonArray(Eigen::Vector3d &vec, const Json::Value &value)
static bool EigenVector3dToJsonArray(const Eigen::Vector3d &vec, Json::Value &value)
static ecvOrientedBBox CreateFromAxisAlignedBoundingBox(const ccBBox &aabox)
#define LogWarning(...)
Definition: Logging.h:72
#define LogError(...)
Definition: Logging.h:60
const Dtype Int64
Definition: Dtype.cpp:47
CLOUDVIEWER_HOST_DEVICE Pair< First, Second > make_pair(const First &_first, const Second &_second)
Definition: SlabTraits.h:49
Generic file read and write utility for python interface.
static vtkPVTrivialProducerStaticInternal Value