ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
tensormap.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 "t/geometry/TensorMap.h"
9 
10 #include "pybind/docstring.h"
12 #include "t/geometry/PointCloud.h"
13 
14 namespace cloudViewer {
15 namespace t {
16 namespace geometry {
17 
18 // This is a copy of py::bind_map function, with `__delitem__` function
19 // removed. The same is defined in `pybind_tensormap` to use
20 // `TensorMap::Erase(key)`.
21 template <typename Map,
22  typename holder_type = std::unique_ptr<Map>,
23  typename... Args>
24 static py::class_<Map, holder_type> bind_tensor_map(py::handle scope,
25  const std::string &name,
26  Args &&...args) {
27  using KeyType = typename Map::key_type;
28  using MappedType = typename Map::mapped_type;
29  using Class_ = py::class_<Map, holder_type>;
30 
31  // If either type is a non-module-local bound type then make the map binding
32  // non-local as well; otherwise (e.g. both types are either module-local or
33  // converting) the map will be module-local.
34  auto tinfo = py::detail::get_type_info(typeid(MappedType));
35  bool local = !tinfo || tinfo->module_local;
36  if (local) {
37  tinfo = py::detail::get_type_info(typeid(KeyType));
38  local = !tinfo || tinfo->module_local;
39  }
40 
41  Class_ cl(scope, name.c_str(), pybind11::module_local(local),
42  std::forward<Args>(args)...);
43 
44  cl.def(py::init<>());
45 
46  // Register stream insertion operator (if possible)
47  py::detail::map_if_insertion_operator<Map, Class_>(cl, name);
48 
49  cl.def(
50  "__bool__", [](const Map &m) -> bool { return !m.empty(); },
51  "Check whether the map is nonempty");
52 
53  // Essential: keep list alive while iterator exists
54  cl.def(
55  "__iter__",
56  [](Map &m) { return py::make_key_iterator(m.begin(), m.end()); },
57  py::keep_alive<0, 1>()
58 
59  );
60 
61  // Essential: keep list alive while iterator exists
62  cl.def(
63  "items",
64  [](Map &m) { return py::make_iterator(m.begin(), m.end()); },
65  py::keep_alive<0, 1>());
66 
67  cl.def(
68  "__getitem__",
69  [](Map &m, const KeyType &k) -> MappedType & {
70  auto it = m.find(k);
71  if (it == m.end())
72  throw py::key_error(
73  fmt::format("Key {} not found in TensorMap", k));
74  return it->second;
75  },
76  // py::return_value_policy::copy is used as the safest option.
77  // The goal is to make TensorMap works similarly as putting Tensors
78  // into a python dict, i.e., {"a": Tensor(xx), "b": Tensor(XX)}.
79  // Accessing a value in the map will return a shallow copy of the
80  // tensor that shares the same underlying memory.
81  //
82  // - automatic : works, different id
83  // - automatic_reference: works, different id
84  // - take_ownership : doesn't work, segfault
85  // - copy : works, different id
86  // - move : doesn't work, blob is null
87  // - reference : doesn't work, when a key is deleted, the
88  // alias becomes invalid
89  // - reference_internal : doesn't work, value in map overwritten
90  // when assigning to alias
92 
93  cl.def("__setitem__", [](Map &m, const KeyType &k, const MappedType &v) {
95  m[k] = v;
96  } else {
97  throw py::key_error(
98  fmt::format("Cannot assign to reserved key \"{}\"", k));
99  }
100  });
101 
102  cl.def("__contains__", [](Map &m, const KeyType &k) -> bool {
103  auto it = m.find(k);
104  if (it == m.end()) return false;
105  return true;
106  });
107 
108  // Assignment provided only if the type is copyable
109  py::detail::map_assignment<Map, Class_>(cl);
110 
111  // Deleted the "__delitem__" function.
112  // This will be implemented in `pybind_tensormap()`.
113 
114  cl.def("__len__", [](const Map &m) -> size_t { return m.size(); });
115 
116  return cl;
117 }
118 
119 void pybind_tensormap(py::module &m) {
120  // Bind to the generic dictionary interface such that it works the same as a
121  // regular dictionay in Python, except that types are enforced. Supported
122  // functions include `__bool__`, `__iter__`, `items`, `__getitem__`,
123  // `__contains__`, `__len__` and map assignment.
124  // The `__delitem__` function is removed from bind_map, in bind_tensor_map,
125  // and defined in this function, to use TensorMap::Erase, in order to
126  // protect users from deleting the `private_key`.
127  auto tm = bind_tensor_map<TensorMap>(
128  m, "TensorMap", "Map of String to Tensor with a primary key.");
129 
130  tm.def("__delitem__",
131  [](TensorMap &m, const std::string &k) { return m.Erase(k); });
132 
133  tm.def("erase",
134  [](TensorMap &m, const std::string &k) { return m.Erase(k); });
135 
136  // Constructors.
137  tm.def(py::init<const std::string &>(), "primary_key"_a);
138  tm.def(py::init<const std::string &,
139  const std::unordered_map<std::string, core::Tensor> &>(),
140  "primary_key"_a, "map_keys_to_tensors"_a);
141 
142  // Member functions. Some C++ functions are ignored since the
143  // functionalities are already covered in the generic dictionary interface.
144  tm.def_property_readonly("primary_key", &TensorMap::GetPrimaryKey);
145  tm.def("is_size_synchronized", &TensorMap::IsSizeSynchronized);
146  tm.def("assert_size_synchronized", &TensorMap::AssertSizeSynchronized);
147 
148  // Pickle support.
149  tm.def(py::pickle(
150  [](const TensorMap &m) {
151  // __getstate__
152  std::unordered_map<std::string, core::Tensor> map;
153  for (const auto &kv : m) {
154  map[kv.first] = kv.second;
155  }
156 
157  return py::make_tuple(m.GetPrimaryKey(), map);
158  },
159  [](py::tuple t) {
160  // __setstate__
161  if (t.size() != 2) {
163  "Cannot unpickle TensorMap! Expecting a tuple of "
164  "size 2.");
165  }
166  return TensorMap(t[0].cast<std::string>(),
167  t[1].cast<std::unordered_map<std::string,
168  core::Tensor>>());
169  }));
170 
171  tm.def("__setattr__",
172  [](TensorMap &m, const std::string &key, const core::Tensor &val) {
173  if (!TensorMap::GetReservedKeys().count(key)) {
174  m[key] = val;
175  } else {
176  throw py::key_error(fmt::format(
177  "Cannot assign to reserved key \"{}\"", key));
178  }
179  });
180 
181  tm.def("__getattr__",
182  [](TensorMap &m, const std::string &key) -> core::Tensor {
183  auto it = m.find(key);
184  if (it == m.end()) {
185  throw py::key_error(
186  fmt::format("Key {} not found in TensorMap", key));
187  }
188  return it->second;
189  });
190 
191  tm.def("__delattr__", [](TensorMap &m, const std::string &key) {
192  auto it = m.find(key);
193  if (it == m.end()) {
194  throw py::key_error(
195  fmt::format("Key {} not found in TensorMap", key));
196  }
197  return m.Erase(key);
198  });
199 
200  tm.def("__str__", &TensorMap::ToString);
201 
202  tm.def("__repr__", &TensorMap::ToString);
203 
204  tm.def("__dir__", [](TensorMap &m) {
205  auto keys = py::list();
206  for (const auto &kv : m) {
207  keys.append(kv.first);
208  }
209  return keys;
210  });
211 }
212 
213 } // namespace geometry
214 } // namespace t
215 } // namespace cloudViewer
filament::Texture::InternalFormat format
std::string name
int count
bool copy
Definition: VtkUtils.cpp:74
bool IsSizeSynchronized() const
Returns true if all tensors in the map have the same size.
Definition: TensorMap.cpp:21
std::string ToString() const
Print the TensorMap to string.
Definition: TensorMap.cpp:135
void AssertSizeSynchronized() const
Assert IsSizeSynchronized().
Definition: TensorMap.cpp:48
static std::unordered_set< std::string > GetReservedKeys()
Get reserved keys for the map. A map cannot contain any of these keys.
Definition: TensorMap.cpp:83
std::string GetPrimaryKey() const
Returns the primary key of the TensorMap.
Definition: TensorMap.h:159
#define LogError(...)
Definition: Logging.h:60
void pybind_tensormap(py::module &m)
Definition: tensormap.cpp:119
static py::class_< Map, holder_type > bind_tensor_map(py::handle scope, const std::string &name, Args &&...args)
Definition: tensormap.cpp:24
Generic file read and write utility for python interface.
#define local
Definition: unzip.c:93