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 // clang-format off
9 #include <sstream>
10 #include <unordered_map>
11 
12 #include <Octree.h>
13 #include <VoxelGrid.h>
14 #include <ecvPointCloud.h>
15 
16 #include "pybind/docstring.h"
17 #include "pybind/geometry/octree.h"
20 // clang-format on
21 
22 namespace cloudViewer {
23 namespace geometry {
24 
25 static const std::unordered_map<std::string, std::string>
27  {"origin", "Origin coordinates."},
28  {"size", "Size of the Octree."},
29  {"color", "Color of the point."},
30  {"point", "Coordinates of the point."},
31  {"max_depth", "Maximum depth of the octree."},
32  {"point_cloud", "Input point cloud."},
33  {"size_expand",
34  "A small expansion size such that the octree is slightly "
35  "bigger than the original point cloud bounds to accomodate "
36  "all points."}};
37 
38 void pybind_octree(py::module &m) {
39  // OctreeNodeInfo
40  // Binds std::shared_ptr<...> to avoid non-allocated free in python code
41  py::class_<OctreeNodeInfo, std::shared_ptr<OctreeNodeInfo>>
42  octree_node_info(m, "OctreeNodeInfo",
43  "OctreeNode's information. OctreeNodeInfo is "
44  "computed on the fly, "
45  "not stored with the Node.");
46  octree_node_info.def(py::init([](const Eigen::Vector3d &origin, double size,
47  size_t depth, size_t child_index) {
48  return new OctreeNodeInfo(origin, size, depth,
49  child_index);
50  }),
51  "origin"_a, "size"_a, "depth"_a, "child_index"_a);
52  octree_node_info
53  .def("__repr__",
54  [](const OctreeNodeInfo &node_info) {
55  std::ostringstream repr;
56  repr << "OctreeNodeInfo with origin ["
57  << node_info.origin_(0) << ", "
58  << node_info.origin_(1) << ", "
59  << node_info.origin_(2) << "]";
60  repr << ", size " << node_info.size_;
61  repr << ", depth " << node_info.depth_;
62  repr << ", child_index " << node_info.child_index_;
63  return repr.str();
64  })
65  .def_readwrite(
66  "origin", &OctreeNodeInfo::origin_,
67  "(3, 1) float numpy array: Origin coordinate of the node.")
68  .def_readwrite("size", &OctreeNodeInfo::size_,
69  "float: Size of the node.")
70  .def_readwrite("depth", &OctreeNodeInfo::depth_,
71  "int: Depth of the node to the root. The root is of "
72  "depth 0.")
73  .def_readwrite(
74  "child_index", &OctreeNodeInfo::child_index_,
75  "int: Node's child index of itself. For non-root nodes, "
76  "child_index is 0~7; root node's child_index is -1.");
77  docstring::ClassMethodDocInject(m, "OctreeNodeInfo", "__init__");
78 
79  // OctreeNode
80  py::class_<OctreeNode, PyOctreeNode<OctreeNode>,
81  std::shared_ptr<OctreeNode>>
82  octree_node(m, "OctreeNode", "The base class for octree node.");
83  octree_node.def("__repr__", [](const OctreeNode &octree_node) {
84  return "OctreeNode instance.";
85  });
86  docstring::ClassMethodDocInject(m, "OctreeNode", "__init__");
87 
88  // OctreeInternalNode
89  py::class_<OctreeInternalNode, PyOctreeNode<OctreeInternalNode>,
90  std::shared_ptr<OctreeInternalNode>, OctreeNode>
91  octree_internal_node(m, "OctreeInternalNode",
92  "OctreeInternalNode class, containing "
93  "OctreeNode children.");
94  octree_internal_node
95  .def("__repr__",
96  [](const OctreeInternalNode &internal_node) {
97  size_t num_children = 0;
98  for (const std::shared_ptr<OctreeNode> &child :
99  internal_node.children_) {
100  if (child != nullptr) {
101  num_children++;
102  }
103  }
104  std::ostringstream repr;
105  repr << "OctreeInternalNode with " << num_children
106  << " non-empty child nodes";
107  return repr.str();
108  })
109  .def_static(
110  "get_init_function", &OctreeInternalNode::GetInitFunction,
111  "Get lambda function for initializing OctreeInternalNode. "
112  "When the init function is called, an empty "
113  "OctreeInternalNode is created.")
114  .def_static("get_update_function",
115  &OctreeInternalNode::GetUpdateFunction,
116  "Get lambda function for updating OctreeInternalNode. "
117  "This update function does nothing.");
118  py::detail::bind_default_constructor<OctreeInternalNode>(
119  octree_internal_node);
120  py::detail::bind_copy_functions<OctreeInternalNode>(octree_internal_node);
121  octree_internal_node.def_readwrite("children",
122  &OctreeInternalNode::children_,
123  "List of children Nodes.");
124  docstring::ClassMethodDocInject(m, "OctreeInternalNode", "__init__");
125 
126  // OctreeInternalPointNode
127  py::class_<OctreeInternalPointNode, PyOctreeNode<OctreeInternalPointNode>,
128  std::shared_ptr<OctreeInternalPointNode>, OctreeInternalNode>
129  octree_internal_point_node(
130  m, "OctreeInternalPointNode",
131  "OctreeInternalPointNode class is an "
132  "OctreeInternalNode with a list of point "
133  "indices (from point cloud) belonging to "
134  "children of this node.");
135  octree_internal_point_node
136  .def("__repr__",
137  [](const OctreeInternalPointNode &internal_point_node) {
138  size_t num_children = 0;
139  for (const std::shared_ptr<OctreeNode> &child :
140  internal_point_node.children_) {
141  if (child != nullptr) {
142  num_children++;
143  }
144  }
145  std::ostringstream repr;
146  repr << "OctreeInternalPointNode with " << num_children
147  << " non-empty child nodes and "
148  << internal_point_node.indices_.size() << " points";
149  return repr.str();
150  })
151  .def_readwrite("indices", &OctreeInternalPointNode::indices_,
152  "List of point cloud point indices "
153  "contained in children nodes.")
154  .def_static("get_init_function",
155  &OctreeInternalPointNode::GetInitFunction,
156  "Get lambda function for initializing "
157  "OctreeInternalPointNode. "
158  "When the init function is called, an empty "
159  "OctreeInternalPointNode is created.")
160  .def_static(
161  "get_update_function",
162  &OctreeInternalPointNode::GetUpdateFunction,
163  "Get lambda function for updating OctreeInternalPointNode. "
164  "When called, the update function adds the input "
165  "point index to the corresponding node's list of "
166  "indices of children points.");
167  py::detail::bind_default_constructor<OctreeInternalPointNode>(
168  octree_internal_point_node);
169  py::detail::bind_copy_functions<OctreeInternalPointNode>(
170  octree_internal_point_node);
171  octree_internal_point_node.def_readwrite(
172  "children", &OctreeInternalPointNode::children_,
173  "List of children Nodes.");
174  docstring::ClassMethodDocInject(m, "OctreeInternalPointNode", "__init__");
175 
176  // OctreeLeafNode
177  py::class_<OctreeLeafNode, PyOctreeLeafNode<OctreeLeafNode>,
178  std::shared_ptr<OctreeLeafNode>, OctreeNode>
179  octree_leaf_node(m, "OctreeLeafNode", "OctreeLeafNode base class.");
180  octree_leaf_node
181  .def("__repr__",
182  [](const OctreeLeafNode &leaf_node) {
183  std::ostringstream repr;
184  repr << "OctreeLeafNode base class";
185  return repr.str();
186  })
187  .def("__eq__", &OctreeLeafNode::operator==, "other"_a,
188  "Check equality of OctreeLeafNode.")
189  .def("clone", &OctreeLeafNode::Clone, "Clone this OctreeLeafNode.");
190 
191  docstring::ClassMethodDocInject(m, "OctreeLeafNode", "__init__");
192 
193  // OctreeColorLeafNode
194  py::class_<OctreeColorLeafNode, PyOctreeLeafNode<OctreeColorLeafNode>,
195  std::shared_ptr<OctreeColorLeafNode>, OctreeLeafNode>
196  octree_color_leaf_node(m, "OctreeColorLeafNode",
197  "OctreeColorLeafNode class is an "
198  "OctreeLeafNode containing color.");
199  octree_color_leaf_node
200  .def("__repr__",
201  [](const OctreeColorLeafNode &color_leaf_node) {
202  std::ostringstream repr;
203  repr << "OctreeColorLeafNode with color ["
204  << color_leaf_node.color_(0) << ", "
205  << color_leaf_node.color_(1) << ", "
206  << color_leaf_node.color_(2) << "]";
207  return repr.str();
208  })
209  .def_readwrite("color", &OctreeColorLeafNode::color_,
210  "(3, 1) float numpy array: Color of the node.")
211  .def_static("get_init_function",
212  &OctreeColorLeafNode::GetInitFunction,
213  "Get lambda function for initializing OctreeLeafNode. "
214  "When the init function is called, an empty "
215  "OctreeColorLeafNode is created.")
216  .def_static("get_update_function",
217  &OctreeColorLeafNode::GetUpdateFunction, "color"_a,
218  "Get lambda function for updating OctreeLeafNode. When "
219  "called, the update function updates the corresponding "
220  "node with the input color.");
221 
222  py::detail::bind_default_constructor<OctreeColorLeafNode>(
223  octree_color_leaf_node);
224  py::detail::bind_copy_functions<OctreeColorLeafNode>(
225  octree_color_leaf_node);
226 
227  // OctreePointColorLeafNode
228  py::class_<OctreePointColorLeafNode,
229  PyOctreeLeafNode<OctreePointColorLeafNode>,
230  std::shared_ptr<OctreePointColorLeafNode>, OctreeLeafNode>
231  octree_point_color_leaf_node(m, "OctreePointColorLeafNode",
232  "OctreePointColorLeafNode class is an "
233  "OctreeLeafNode containing color.");
234  octree_point_color_leaf_node
235  .def("__repr__",
236  [](const OctreePointColorLeafNode &color_leaf_node) {
237  std::ostringstream repr;
238  repr << "OctreePointColorLeafNode with color ["
239  << color_leaf_node.color_(0) << ", "
240  << color_leaf_node.color_(1) << ", "
241  << color_leaf_node.color_(2) << "] "
242  << "containing " << color_leaf_node.indices_.size()
243  << " points.";
244  return repr.str();
245  })
246  .def_readwrite("color", &OctreePointColorLeafNode::color_,
247  "(3, 1) float numpy array: Color of the node.")
248  .def_readwrite("indices", &OctreePointColorLeafNode::indices_,
249  "List of point cloud point indices "
250  "contained in this leaf node.")
251  .def_static("get_init_function",
252  &OctreePointColorLeafNode::GetInitFunction,
253  "Get lambda function for initializing OctreeLeafNode. "
254  "When the init function is called, an empty "
255  "OctreePointColorLeafNode is created.")
256  .def_static("get_update_function",
257  &OctreePointColorLeafNode::GetUpdateFunction, "idx"_a,
258  "color"_a,
259  "Get lambda function for updating OctreeLeafNode. When "
260  "called, the update function updates the corresponding "
261  "node with the new point index and the input color.");
262 
263  py::detail::bind_default_constructor<OctreePointColorLeafNode>(
264  octree_point_color_leaf_node);
265  py::detail::bind_copy_functions<OctreePointColorLeafNode>(
266  octree_point_color_leaf_node);
267 
268  // Octree
269  py::class_<Octree, PyGeometry<Octree>, std::shared_ptr<Octree>, ccHObject>
270  octree(m, "Octree", "Octree datastructure.");
271  py::detail::bind_default_constructor<Octree>(octree);
272  py::detail::bind_copy_functions<Octree>(octree);
273  octree.def(py::init([](size_t max_depth) { return new Octree(max_depth); }),
274  "max_depth"_a)
275  .def(py::init([](size_t max_depth, const Eigen::Vector3d &origin,
276  double size) {
277  return new Octree(max_depth, origin, size);
278  }),
279  "max_depth"_a, "origin"_a, "size"_a)
280  .def("__repr__",
281  [](const Octree &octree) {
282  std::ostringstream repr;
283  repr << "Octree with ";
284  repr << "origin: [" << octree.origin_(0) << ", "
285  << octree.origin_(1) << ", " << octree.origin_(2)
286  << "]";
287  repr << ", size: " << octree.size_;
288  repr << ", max_depth: " << octree.max_depth_;
289  return repr.str();
290  })
291  .def("insert_point", &Octree::InsertPoint, "point"_a, "f_init"_a,
292  "f_update"_a, "fi_init"_a = nullptr, "fi_update"_a = nullptr,
293  "Insert a point to the octree.")
294  .def("traverse",
295  py::overload_cast<const std::function<bool(
296  const std::shared_ptr<OctreeNode> &,
297  const std::shared_ptr<OctreeNodeInfo> &)> &>(
298  &Octree::Traverse, py::const_),
299  "f"_a,
300  "DFS traversal of the octree from the root, with a "
301  "callback function f being called for each node.")
302  .def("locate_leaf_node", &Octree::LocateLeafNode, "point"_a,
303  "Returns leaf OctreeNode and OctreeNodeInfo where the query"
304  "point should reside.")
305  .def_static("is_point_in_bound", &Octree::IsPointInBound, "point"_a,
306  "origin"_a, "size"_a,
307  "Return true if point within bound, that is, origin<= "
308  "point < origin + size")
309  .def("convert_from_point_cloud", &Octree::ConvertFromPointCloud,
310  "point_cloud"_a, "size_expand"_a = 0.01,
311  "Convert octree from point cloud.")
312  .def("to_voxel_grid", &Octree::ToVoxelGrid, "Convert to VoxelGrid.")
313  .def("create_from_voxel_grid", &Octree::CreateFromVoxelGrid,
314  "voxel_grid"_a
315  "Convert from VoxelGrid.")
316  .def_readwrite("root_node", &Octree::root_node_,
317  "OctreeNode: The root octree node.")
318  .def_readwrite("origin", &Octree::origin_,
319  "(3, 1) float numpy array: Global min bound "
320  "(include). A point is within bound iff origin <= "
321  "point < origin + size.")
322  .def_readwrite("size", &Octree::size_,
323  "float: Outer bounding box edge size for the whole "
324  "octree. A point is within bound iff origin <= "
325  "point < origin + size.")
326  .def_readwrite("max_depth", &Octree::max_depth_,
327  "int: Maximum depth of the octree. The depth is "
328  "defined as the distance from the deepest leaf node "
329  "to root. A tree with only the root node has depth "
330  "0.");
331 
332  docstring::ClassMethodDocInject(m, "Octree", "__init__");
333  docstring::ClassMethodDocInject(m, "Octree", "insert_point",
335  docstring::ClassMethodDocInject(m, "Octree", "locate_leaf_node",
337  docstring::ClassMethodDocInject(m, "Octree", "is_point_in_bound",
339  docstring::ClassMethodDocInject(m, "Octree", "convert_from_point_cloud",
341  docstring::ClassMethodDocInject(m, "Octree", "to_voxel_grid",
344  m, "Octree", "create_from_voxel_grid",
345  {{"voxel_grid", "geometry.VoxelGrid: The source voxel grid."}});
346 }
347 
348 void pybind_octree_methods(py::module &m) {}
349 
350 } // namespace geometry
351 } // namespace cloudViewer
int size
int64_t size_
Definition: FilePLY.cpp:37
Hierarchical CLOUDVIEWER Object.
Definition: ecvHObject.h:25
OctreeNode's information.
Definition: Octree.h:33
double size_
Size of the node.
Definition: Octree.h:60
Eigen::Vector3d origin_
Origin coordinate of the node.
Definition: Octree.h:58
size_t depth_
Depth of the node to the root. The root is of depth 0.
Definition: Octree.h:62
The base class for octree node.
Definition: Octree.h:75
void ClassMethodDocInject(py::module &pybind_module, const std::string &class_name, const std::string &function_name, const std::unordered_map< std::string, std::string > &map_parameter_body_docs)
Definition: docstring.cpp:27
void pybind_octree_methods(py::module &m)
Definition: octree.cpp:348
static const std::unordered_map< std::string, std::string > map_octree_argument_docstrings
Definition: octree.cpp:26
void pybind_octree(py::module &m)
Definition: octree.cpp:38
Generic file read and write utility for python interface.
cloudViewer::DgmOctree * octree