ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
eigen.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 
9 #include "pybind/docstring.h"
10 
11 namespace pybind11 {
12 
13 template <typename Vector,
14  typename holder_type = std::unique_ptr<Vector>,
15  typename... Args>
16 py::class_<Vector, holder_type> bind_vector_without_repr(
17  py::module &m, std::string const &name, Args &&...args) {
18  // hack function to disable __repr__ for the convenient function
19  // bind_vector()
20  using Class_ = py::class_<Vector, holder_type>;
21  Class_ cl(m, name.c_str(), std::forward<Args>(args)...);
22  cl.def(py::init<>());
23  cl.def(
24  "__bool__", [](const Vector &v) -> bool { return !v.empty(); },
25  "Check whether the list is nonempty");
26  cl.def("__len__", &Vector::size);
27  return cl;
28 }
29 
30 // - This function is used by Pybind for std::vector<SomeEigenType> constructor.
31 // This optional constructor is added to avoid too many Python <-> C++ API
32 // calls when the vector size is large using the default biding method.
33 // Pybind matches np.float64 array to py::array_t<double> buffer.
34 // - Directly using templates for the py::array_t<double> and py::array_t<int>
35 // and etc. doesn't work. The current solution is to explicitly implement
36 // bindings for each py array types.
37 template <typename EigenVector>
38 std::vector<EigenVector> py_array_to_vectors_double(
39  py::array_t<double> array) { // remove the restrictive flags here
40  // Ensure array is contiguous (C-style)
41  if (!(array.flags() & (py::array::c_style))) {
42  // Make a contiguous copy if necessary
43  array = py::array_t<double, py::array::c_style>(array);
44  }
45  int64_t eigen_vector_size = EigenVector::SizeAtCompileTime;
46  if (array.ndim() != 2 || array.shape(1) != eigen_vector_size) {
47  throw py::cast_error();
48  }
49  std::vector<EigenVector> eigen_vectors(array.shape(0));
50  auto array_unchecked = array.mutable_unchecked<2>();
51  for (auto i = 0; i < array_unchecked.shape(0); ++i) {
52  // The EigenVector here must be a double-typed eigen vector, since only
53  // cloudViewer::Vector3dVector binds to py_array_to_vectors_double.
54  // Therefore, we can use the memory map directly.
55  eigen_vectors[i] = Eigen::Map<EigenVector>(&array_unchecked(i, 0));
56  }
57  return eigen_vectors;
58 }
59 
60 template <typename EigenVector>
61 std::vector<EigenVector> py_array_to_vectors_int(py::array_t<int> array) {
62  if (!(array.flags() & (py::array::c_style))) {
63  array = py::array_t<int, py::array::c_style>(array);
64  }
65  int64_t eigen_vector_size = EigenVector::SizeAtCompileTime;
66  if (array.ndim() != 2 || array.shape(1) != eigen_vector_size) {
67  throw py::cast_error();
68  }
69  std::vector<EigenVector> eigen_vectors(array.shape(0));
70  auto array_unchecked = array.mutable_unchecked<2>();
71  for (auto i = 0; i < array_unchecked.shape(0); ++i) {
72  eigen_vectors[i] = Eigen::Map<EigenVector>(&array_unchecked(i, 0));
73  }
74  return eigen_vectors;
75 }
76 
77 template <typename EigenVector,
78  typename EigenAllocator = Eigen::aligned_allocator<EigenVector>>
79 std::vector<EigenVector, EigenAllocator>
80 py_array_to_vectors_int_eigen_allocator(py::array_t<int> array) {
81  if (!(array.flags() & (py::array::c_style))) {
82  array = py::array_t<int, py::array::c_style>(array);
83  }
84  int64_t eigen_vector_size = EigenVector::SizeAtCompileTime;
85  if (array.ndim() != 2 || array.shape(1) != eigen_vector_size) {
86  throw py::cast_error();
87  }
88  std::vector<EigenVector, EigenAllocator> eigen_vectors(array.shape(0));
89  auto array_unchecked = array.mutable_unchecked<2>();
90  for (auto i = 0; i < array_unchecked.shape(0); ++i) {
91  eigen_vectors[i] = Eigen::Map<EigenVector>(&array_unchecked(i, 0));
92  }
93  return eigen_vectors;
94 }
95 
96 template <typename EigenVector,
97  typename EigenAllocator = Eigen::aligned_allocator<EigenVector>>
98 std::vector<EigenVector, EigenAllocator>
99 py_array_to_vectors_int64_eigen_allocator(py::array_t<int64_t> array) {
100  if (!(array.flags() & (py::array::c_style))) {
101  array = py::array_t<int64_t, py::array::c_style>(array);
102  }
103  int64_t eigen_vector_size = EigenVector::SizeAtCompileTime;
104  if (array.ndim() != 2 || array.shape(1) != eigen_vector_size) {
105  throw py::cast_error();
106  }
107  std::vector<EigenVector, EigenAllocator> eigen_vectors(array.shape(0));
108  auto array_unchecked = array.mutable_unchecked<2>();
109  for (auto i = 0; i < array_unchecked.shape(0); ++i) {
110  eigen_vectors[i] = Eigen::Map<EigenVector>(&array_unchecked(i, 0));
111  }
112  return eigen_vectors;
113 }
114 
115 } // namespace pybind11
116 
117 namespace {
118 
119 template <typename Scalar,
120  typename Vector = std::vector<Scalar>,
121  typename holder_type = std::unique_ptr<Vector>>
122 py::class_<Vector, holder_type> pybind_eigen_vector_of_scalar(
123  py::module &m, const std::string &bind_name) {
124  auto vec = py::bind_vector<std::vector<Scalar>>(m, bind_name,
125  py::buffer_protocol());
126  vec.def_buffer([](std::vector<Scalar> &v) -> py::buffer_info {
127  return py::buffer_info(v.data(), sizeof(Scalar),
129  {v.size()}, {sizeof(Scalar)});
130  });
131  vec.def("__copy__",
132  [](std::vector<Scalar> &v) { return std::vector<Scalar>(v); });
133  vec.def("__deepcopy__", [](std::vector<Scalar> &v, py::dict &memo) {
134  return std::vector<Scalar>(v);
135  });
136  // We use iterable __init__ by default
137  // vec.def("__init__", [](std::vector<Scalar> &v,
138  // py::array_t<Scalar, py::array::c_style> b) {
139  // py::buffer_info info = b.request();
140  // if (info.format != py::format_descriptor<Scalar>::format() ||
141  // info.ndim != 1)
142  // throw std::runtime_error("Incompatible buffer format!");
143  // new (&v) std::vector<Scalar>(info.shape[0]);
144  // memcpy(v.data(), info.ptr, sizeof(Scalar) * v.size());
145  //});
146  return vec;
147 }
148 
149 template <typename EigenVector,
150  typename Vector = std::vector<EigenVector>,
151  typename holder_type = std::unique_ptr<Vector>,
152  typename InitFunc>
153 py::class_<Vector, holder_type> pybind_eigen_vector_of_vector(
154  py::module &m,
155  const std::string &bind_name,
156  const std::string &repr_name,
157  InitFunc init_func) {
158  typedef typename EigenVector::Scalar Scalar;
159  auto vec = py::bind_vector_without_repr<std::vector<EigenVector>>(
160  m, bind_name, py::buffer_protocol());
161  vec.def(py::init(init_func));
162  vec.def_buffer([](std::vector<EigenVector> &v) -> py::buffer_info {
163  size_t rows = EigenVector::RowsAtCompileTime;
164  return py::buffer_info(v.data(), sizeof(Scalar),
166  {v.size(), rows},
167  {sizeof(EigenVector), sizeof(Scalar)});
168  });
169  vec.def("__repr__", [repr_name](const std::vector<EigenVector> &v) {
170  return repr_name + std::string(" with ") + std::to_string(v.size()) +
171  std::string(" elements.\n") +
172  std::string("Use numpy.asarray() to access data.");
173  });
174  vec.def("__copy__", [](std::vector<EigenVector> &v) {
175  return std::vector<EigenVector>(v);
176  });
177  vec.def("__deepcopy__", [](std::vector<EigenVector> &v, py::dict &memo) {
178  return std::vector<EigenVector>(v);
179  });
180 
181  // py::detail must be after custom constructor
182  using Class_ = py::class_<Vector, std::unique_ptr<Vector>>;
183  py::detail::vector_if_copy_constructible<Vector, Class_>(vec);
184  py::detail::vector_if_equal_operator<Vector, Class_>(vec);
185  py::detail::vector_modifiers<Vector, Class_>(vec);
186  py::detail::vector_accessor<Vector, Class_>(vec);
187 
188  return vec;
189 
190  // Bare bones interface
191  // We choose to disable them because they do not support slice indices
192  // such as [:,:]. It is recommended to convert it to numpy.asarray()
193  // to access raw data.
194  // v.def("__getitem__", [](const std::vector<Eigen::Vector3d> &v,
195  // std::pair<size_t, size_t> i) {
196  // if (i.first >= v.size() || i.second >= 3)
197  // throw py::index_error();
198  // return v[i.first](i.second);
199  //});
200  // v.def("__setitem__", [](std::vector<Eigen::Vector3d> &v,
201  // std::pair<size_t, size_t> i, double x) {
202  // if (i.first >= v.size() || i.second >= 3)
203  // throw py::index_error();
204  // v[i.first](i.second) = x;
205  //});
206  // We use iterable __init__ by default
207  // vec.def("__init__", [](std::vector<EigenVector> &v,
208  // py::array_t<Scalar, py::array::c_style> b) {
209  // py::buffer_info info = b.request();s
210  // if (info.format !=
211  // py::format_descriptor<Scalar>::format() ||
212  // info.ndim != 2 ||
213  // info.shape[1] != EigenVector::RowsAtCompileTime)
214  // throw std::runtime_error("Incompatible buffer format!");
215  // new (&v) std::vector<EigenVector>(info.shape[0]);
216  // memcpy(v.data(), info.ptr, sizeof(EigenVector) * v.size());
217  //});
218 }
219 
220 template <typename EigenVector,
221  typename EigenAllocator = Eigen::aligned_allocator<EigenVector>,
222  typename Vector = std::vector<EigenVector, EigenAllocator>,
223  typename holder_type = std::unique_ptr<Vector>,
224  typename InitFunc>
225 py::class_<Vector, holder_type> pybind_eigen_vector_of_vector_eigen_allocator(
226  py::module &m,
227  const std::string &bind_name,
228  const std::string &repr_name,
229  InitFunc init_func) {
230  typedef typename EigenVector::Scalar Scalar;
231  auto vec = py::bind_vector_without_repr<
232  std::vector<EigenVector, EigenAllocator>>(m, bind_name,
233  py::buffer_protocol());
234  vec.def(py::init(init_func));
235  vec.def_buffer(
236  [](std::vector<EigenVector, EigenAllocator> &v) -> py::buffer_info {
237  size_t rows = EigenVector::RowsAtCompileTime;
238  return py::buffer_info(v.data(), sizeof(Scalar),
240  2, {v.size(), rows},
241  {sizeof(EigenVector), sizeof(Scalar)});
242  });
243  vec.def("__repr__",
244  [repr_name](const std::vector<EigenVector, EigenAllocator> &v) {
245  return repr_name + std::string(" with ") +
246  std::to_string(v.size()) + std::string(" elements.\n") +
247  std::string("Use numpy.asarray() to access data.");
248  });
249  vec.def("__copy__", [](std::vector<EigenVector, EigenAllocator> &v) {
250  return std::vector<EigenVector, EigenAllocator>(v);
251  });
252  vec.def("__deepcopy__",
253  [](std::vector<EigenVector, EigenAllocator> &v, py::dict &memo) {
254  return std::vector<EigenVector, EigenAllocator>(v);
255  });
256 
257  // py::detail must be after custom constructor
258  using Class_ = py::class_<Vector, std::unique_ptr<Vector>>;
259  py::detail::vector_if_copy_constructible<Vector, Class_>(vec);
260  py::detail::vector_if_equal_operator<Vector, Class_>(vec);
261  py::detail::vector_modifiers<Vector, Class_>(vec);
262  py::detail::vector_accessor<Vector, Class_>(vec);
263 
264  return vec;
265 }
266 
267 template <typename EigenMatrix,
268  typename EigenAllocator = Eigen::aligned_allocator<EigenMatrix>,
269  typename Vector = std::vector<EigenMatrix, EigenAllocator>,
270  typename holder_type = std::unique_ptr<Vector>>
271 py::class_<Vector, holder_type> pybind_eigen_vector_of_matrix(
272  py::module &m,
273  const std::string &bind_name,
274  const std::string &repr_name) {
275  typedef typename EigenMatrix::Scalar Scalar;
276  auto vec = py::bind_vector_without_repr<
277  std::vector<EigenMatrix, EigenAllocator>>(m, bind_name,
278  py::buffer_protocol());
279  vec.def_buffer(
280  [](std::vector<EigenMatrix, EigenAllocator> &v) -> py::buffer_info {
281  // We use this function to bind Eigen default matrix.
282  // Thus they are all column major.
283  size_t rows = EigenMatrix::RowsAtCompileTime;
284  size_t cols = EigenMatrix::ColsAtCompileTime;
285  return py::buffer_info(v.data(), sizeof(Scalar),
287  3, {v.size(), rows, cols},
288  {sizeof(EigenMatrix), sizeof(Scalar),
289  sizeof(Scalar) * rows});
290  });
291  vec.def("__repr__",
292  [repr_name](const std::vector<EigenMatrix, EigenAllocator> &v) {
293  return repr_name + std::string(" with ") +
294  std::to_string(v.size()) + std::string(" elements.\n") +
295  std::string("Use numpy.asarray() to access data.");
296  });
297  vec.def("__copy__", [](std::vector<EigenMatrix, EigenAllocator> &v) {
298  return std::vector<EigenMatrix, EigenAllocator>(v);
299  });
300  vec.def("__deepcopy__",
301  [](std::vector<EigenMatrix, EigenAllocator> &v, py::dict &memo) {
302  return std::vector<EigenMatrix, EigenAllocator>(v);
303  });
304 
305  // py::detail must be after custom constructor
306  using Class_ = py::class_<Vector, std::unique_ptr<Vector>>;
307  py::detail::vector_if_copy_constructible<Vector, Class_>(vec);
308  py::detail::vector_if_equal_operator<Vector, Class_>(vec);
309  py::detail::vector_modifiers<Vector, Class_>(vec);
310  py::detail::vector_accessor<Vector, Class_>(vec);
311 
312  return vec;
313 }
314 
315 } // unnamed namespace
316 
317 namespace cloudViewer {
318 namespace utility {
319 
320 void pybind_eigen(py::module &m) {
321  auto intvector = pybind_eigen_vector_of_scalar<int>(m, "IntVector");
322  intvector.attr("__doc__") = docstring::static_property(
323  py::cpp_function([](py::handle arg) -> std::string {
324  return R"(Convert int32 numpy array of shape ``(n,)`` to CloudViewer format.)";
325  }),
326  py::none(), py::none(), "");
327 
328  auto doublevector =
329  pybind_eigen_vector_of_scalar<double>(m, "DoubleVector");
330  doublevector.attr("__doc__") = docstring::static_property(
331  py::cpp_function([](py::handle arg) -> std::string {
332  return R"(Convert float64 numpy array of shape ``(n,)`` to CloudViewer format.)";
333  }),
334  py::none(), py::none(), "");
335 
336  auto vector3dvector = pybind_eigen_vector_of_vector<Eigen::Vector3d>(
337  m, "Vector3dVector", "std::vector<Eigen::Vector3d>",
338  py::py_array_to_vectors_double<Eigen::Vector3d>);
339  vector3dvector.attr("__doc__") = docstring::static_property(
340  py::cpp_function([](py::handle arg) -> std::string {
341  return R"(Convert float64 numpy array of shape ``(n, 3)`` to CloudViewer format.
342 
343 Example usage
344 
345 .. code-block:: python
346 
347  import cloudViewer
348  import numpy as np
349 
350  pcd = cloudViewer.geometry.ccPointCloud()
351  np_points = np.random.rand(100, 3)
352 
353  # From numpy to CloudViewer
354  pcd.set_points(cloudViewer.utility.Vector3dVector(np_points))
355 
356  # From CloudViewer to numpy
357  np_points = np.asarray(pcd.get_points())
358 )";
359  }),
360  py::none(), py::none(), "");
361 
362  auto vector3ivector = pybind_eigen_vector_of_vector<Eigen::Vector3i>(
363  m, "Vector3iVector", "std::vector<Eigen::Vector3i>",
364  py::py_array_to_vectors_int<Eigen::Vector3i>);
365  vector3ivector.attr("__doc__") = docstring::static_property(
366  py::cpp_function([](py::handle arg) -> std::string {
367  return R"(Convert int32 numpy array of shape ``(n, 3)`` to CloudViewer format..
368 
369 Example usage
370 
371 .. code-block:: python
372 
373  import cloudViewer
374  import numpy as np
375 
376  # Example mesh
377  # x, y coordinates:
378  # [0: (-1, 2)]__________[1: (1, 2)]
379  # \ /\
380  # \ (0) / \
381  # \ / (1)\
382  # \ / \
383  # [2: (0, 0)]\/________\[3: (2, 0)]
384  #
385  # z coordinate: 0
386 
387  mesh = cloudViewer.geometry.ccMesh()
388  mesh.create_internal_cloud()
389  np_vertices = np.array([[-1, 2, 0],
390  [1, 2, 0],
391  [0, 0, 0],
392  [2, 0, 0]])
393  np_triangles = np.array([[0, 2, 1],
394  [1, 2, 3]]).astype(np.int32)
395  mesh.set_vertices(cloudViewer.Vector3dVector(np_vertices))
396 
397  # From numpy to CloudViewer
398  mesh.set_triangles(cloudViewer.Vector3iVector(np_triangles))
399 
400  # From CloudViewer to numpy
401  np_triangles = np.asarray(mesh.triangles)
402 )";
403  }),
404  py::none(), py::none(), "");
405 
406  auto vector2ivector = pybind_eigen_vector_of_vector<Eigen::Vector2i>(
407  m, "Vector2iVector", "std::vector<Eigen::Vector2i>",
408  py::py_array_to_vectors_int<Eigen::Vector2i>);
409  vector2ivector.attr("__doc__") = docstring::static_property(
410  py::cpp_function([](py::handle arg) -> std::string {
411  return "Convert int32 numpy array of shape ``(n, 2)`` to "
412  "CloudViewer format.";
413  }),
414  py::none(), py::none(), "");
415 
416  auto vector2dvector = pybind_eigen_vector_of_vector<Eigen::Vector2d>(
417  m, "Vector2dVector", "std::vector<Eigen::Vector2d>",
418  py::py_array_to_vectors_double<Eigen::Vector2d>);
419  vector2dvector.attr("__doc__") = docstring::static_property(
420  py::cpp_function([](py::handle arg) -> std::string {
421  return "Convert float64 numpy array of shape ``(n, 2)`` to "
422  "CloudViewer format.";
423  }),
424  py::none(), py::none(), "");
425 
426  auto matrix3dvector =
427  pybind_eigen_vector_of_matrix<Eigen::Matrix3d,
428  std::allocator<Eigen::Matrix3d>>(
429  m, "Matrix3dVector", "std::vector<Eigen::Matrix3d>");
430  matrix3dvector.attr("__doc__") = docstring::static_property(
431  py::cpp_function([](py::handle arg) -> std::string {
432  return "Convert float64 numpy array of shape ``(n, 3, 3)`` to "
433  "CloudViewer format.";
434  }),
435  py::none(), py::none(), "");
436 
437  auto matrix4dvector = pybind_eigen_vector_of_matrix<Eigen::Matrix4d>(
438  m, "Matrix4dVector", "std::vector<Eigen::Matrix4d>");
439  matrix4dvector.attr("__doc__") = docstring::static_property(
440  py::cpp_function([](py::handle arg) -> std::string {
441  return "Convert float64 numpy array of shape ``(n, 4, 4)`` to "
442  "CloudViewer format.";
443  }),
444  py::none(), py::none(), "");
445 
446  auto vector4ivector = pybind_eigen_vector_of_vector_eigen_allocator<
448  m, "Vector4iVector", "std::vector<Eigen::Vector4i>",
449  py::py_array_to_vectors_int_eigen_allocator<Eigen::Vector4i>);
450  vector4ivector.attr("__doc__") = docstring::static_property(
451  py::cpp_function([](py::handle arg) -> std::string {
452  return "Convert int numpy array of shape ``(n, 4)`` to "
453  "CloudViewer format.";
454  }),
455  py::none(), py::none(), "");
456 }
457 
458 } // namespace utility
459 } // namespace cloudViewer
filament::Texture::InternalFormat format
int size
std::string name
py::handle static_property
Definition: docstring.cpp:24
void pybind_eigen(py::module &m)
Definition: eigen.cpp:320
Generic file read and write utility for python interface.
Eigen::Matrix< Index, 4, 1 > Vector4i
Definition: knncpp.h:31
std::string to_string(const T &n)
Definition: Common.h:20
std::vector< EigenVector, EigenAllocator > py_array_to_vectors_int_eigen_allocator(py::array_t< int > array)
Definition: eigen.cpp:80
py::class_< Vector, holder_type > bind_vector_without_repr(py::module &m, std::string const &name, Args &&...args)
Definition: eigen.cpp:16
std::vector< EigenVector > py_array_to_vectors_int(py::array_t< int > array)
Definition: eigen.cpp:61
std::vector< EigenVector > py_array_to_vectors_double(py::array_t< double > array)
Definition: eigen.cpp:38
std::vector< EigenVector, EigenAllocator > py_array_to_vectors_int64_eigen_allocator(py::array_t< int64_t > array)
Definition: eigen.cpp:99