ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
tensor_accessor.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 <Optional.h>
9 
10 #include <vector>
11 
12 #include "core/Tensor.h"
13 #include "core/TensorKey.h"
14 #ifdef _MSC_VER
15 #pragma warning(disable : 4996) // Use of [[deprecated]] feature
16 #endif
18 #include "pybind/core/core.h"
20 #include "pybind/docstring.h"
21 #include "pybind/pybind_utils.h"
22 
23 namespace cloudViewer {
24 namespace core {
25 
26 static TensorKey ToTensorKey(int key) { return TensorKey::Index(key); }
27 
28 static TensorKey ToTensorKey(const py::slice& key) {
29  Py_ssize_t start;
30  Py_ssize_t stop;
31  Py_ssize_t step;
32  PySlice_Unpack(key.ptr(), &start, &stop, &step);
33  PySliceObject* slice_key = reinterpret_cast<PySliceObject*>(key.ptr());
34 
35  utility::optional<int64_t> start_opt = None;
36  if (!py::detail::PyNone_Check(slice_key->start)) {
37  start_opt = static_cast<int64_t>(start);
38  }
40  if (!py::detail::PyNone_Check(slice_key->stop)) {
41  stop_opt = static_cast<int64_t>(stop);
42  }
44  if (!py::detail::PyNone_Check(slice_key->step)) {
45  step_opt = static_cast<int64_t>(step);
46  }
47  return TensorKey::Slice(start_opt, stop_opt, step_opt);
48 }
49 
50 static TensorKey ToTensorKey(const py::list& key) {
51  Tensor key_tensor = PyTupleToTensor(key);
52  if (key_tensor.GetDtype() != core::Bool) {
53  key_tensor = key_tensor.To(core::Int64);
54  }
55  return TensorKey::IndexTensor(key_tensor);
56 }
57 
58 static TensorKey ToTensorKey(const py::tuple& key) {
59  Tensor key_tensor = PyTupleToTensor(key);
60  if (key_tensor.GetDtype() != core::Bool) {
61  key_tensor = key_tensor.To(core::Int64);
62  }
63  return TensorKey::IndexTensor(key_tensor);
64 }
65 
66 static TensorKey ToTensorKey(const py::array& key) {
67  Tensor key_tensor = PyArrayToTensor(key, /*inplace=*/false);
68  if (key_tensor.GetDtype() != core::Bool) {
69  key_tensor = key_tensor.To(core::Int64);
70  }
71  return TensorKey::IndexTensor(key_tensor);
72 }
73 
74 static TensorKey ToTensorKey(const Tensor& key_tensor) {
75  if (key_tensor.GetDtype() != core::Bool) {
76  return TensorKey::IndexTensor(key_tensor.To(core::Int64));
77  } else {
78  return TensorKey::IndexTensor(key_tensor);
79  }
80 }
81 
90 static TensorKey PyHandleToTensorKey(const py::handle& item) {
91  if (py::isinstance<py::int_>(item)) {
92  return ToTensorKey(
93  static_cast<int64_t>(py::reinterpret_borrow<py::int_>(item)));
94  } else if (py::isinstance<py::slice>(item)) {
95  return ToTensorKey(py::reinterpret_borrow<py::slice>(item));
96  } else if (py::isinstance<py::list>(item)) {
97  return ToTensorKey(py::reinterpret_borrow<py::list>(item));
98  } else if (py::isinstance<py::tuple>(item)) {
99  return ToTensorKey(py::reinterpret_borrow<py::tuple>(item));
100  } else if (py::isinstance<py::array>(item)) {
101  return ToTensorKey(py::reinterpret_borrow<py::array>(item));
102  } else if (py::isinstance<Tensor>(item)) {
103  try {
104  return ToTensorKey(*item.cast<Tensor*>());
105  } catch (...) {
106  utility::LogError("Cannot cast index to Tensor.");
107  }
108  } else {
110  "PyHandleToTensorKey has invalid key type {}.",
111  static_cast<std::string>(py::str(py::type::of(item))));
112  }
113 }
114 
115 static void pybind_getitem(py::class_<Tensor>& tensor) {
116  tensor.def("__getitem__", [](const Tensor& tensor, bool key) {
117  return tensor.GetItem(ToTensorKey(Tensor::Init(key)));
118  });
119 
120  tensor.def("__getitem__", [](const Tensor& tensor, int key) {
121  return tensor.GetItem(ToTensorKey(key));
122  });
123 
124  tensor.def("__getitem__", [](const Tensor& tensor, const py::slice& key) {
125  return tensor.GetItem(ToTensorKey(key));
126  });
127 
128  tensor.def("__getitem__", [](const Tensor& tensor, const py::array& key) {
129  return tensor.GetItem(ToTensorKey(key));
130  });
131 
132  tensor.def("__getitem__", [](const Tensor& tensor, const Tensor& key) {
133  return tensor.GetItem(ToTensorKey(key));
134  });
135 
136  // List is interpreted as one TensorKey object, which calls
137  // Tensor::GetItem(const TensorKey&).
138  // E.g. a[[3, 4, 5]] is a list. It indices the first dimension of a.
139  // E.g. a[(3, 4, 5)] does very different things. It indices the first three
140  // dimensions of a.
141  tensor.def("__getitem__", [](const Tensor& tensor, const py::list& key) {
142  return tensor.GetItem(ToTensorKey(key));
143  });
144 
145  // Tuple is interpreted as a vector TensorKey objects, which calls
146  // Tensor::GetItem(const std::vector<TensorKey>&).
147  // E.g. a[1:2, [3, 4, 5], 3:10] results in a tuple of size 3.
148  tensor.def("__getitem__", [](const Tensor& tensor, const py::tuple& key) {
149  std::vector<TensorKey> tks;
150  for (const py::handle item : key) {
151  tks.push_back(PyHandleToTensorKey(item));
152  }
153  return tensor.GetItem(tks);
154  });
155 }
156 
157 static void pybind_setitem(py::class_<Tensor>& tensor) {
158  tensor.def("__setitem__", [](Tensor& tensor, bool key,
159  const py::handle& value) {
160  return tensor.SetItem(
162  PyHandleToTensor(value, tensor.GetDtype(), tensor.GetDevice(),
163  /*force_copy=*/false));
164  });
165 
166  tensor.def("__setitem__", [](Tensor& tensor, int key,
167  const py::handle& value) {
168  return tensor.SetItem(
169  ToTensorKey(key),
170  PyHandleToTensor(value, tensor.GetDtype(), tensor.GetDevice(),
171  /*force_copy=*/false));
172  });
173 
174  tensor.def("__setitem__", [](Tensor& tensor, const py::slice& key,
175  const py::handle& value) {
176  return tensor.SetItem(
177  ToTensorKey(key),
178  PyHandleToTensor(value, tensor.GetDtype(), tensor.GetDevice(),
179  /*force_copy=*/false));
180  });
181 
182  tensor.def("__setitem__", [](Tensor& tensor, const py::array& key,
183  const py::handle& value) {
184  return tensor.SetItem(
185  ToTensorKey(key),
186  PyHandleToTensor(value, tensor.GetDtype(), tensor.GetDevice(),
187  /*force_copy=*/false));
188  });
189 
190  tensor.def("__setitem__", [](Tensor& tensor, const Tensor& key,
191  const py::handle& value) {
192  return tensor.SetItem(
193  ToTensorKey(key),
194  PyHandleToTensor(value, tensor.GetDtype(), tensor.GetDevice(),
195  /*force_copy=*/false));
196  });
197 
198  // List is interpreted as one TensorKey object, which calls
199  // Tensor::SetItem(const TensorKey&, xxx).
200  // E.g. a[[3, 4, 5]] = xxx is a list. It indices the first dimension of a.
201  // E.g. a[(3, 4, 5)] = xxx does very different things. It indices the first
202  // three dimensions of a.
203  tensor.def("__setitem__", [](Tensor& tensor, const py::list& key,
204  const py::handle& value) {
205  return tensor.SetItem(
206  ToTensorKey(key),
207  PyHandleToTensor(value, tensor.GetDtype(), tensor.GetDevice(),
208  /*force_copy=*/false));
209  });
210 
211  // Tuple is interpreted as a vector TensorKey objects, which calls
212  // Tensor::SetItem(const std::vector<TensorKey>&, xxx).
213  // E.g. a[1:2, [3, 4, 5], 3:10] = xxx results in a tuple of size 3.
214  tensor.def("__setitem__", [](Tensor& tensor, const py::tuple& key,
215  const py::handle& value) {
216  std::vector<TensorKey> tks;
217  for (const py::handle item : key) {
218  tks.push_back(PyHandleToTensorKey(item));
219  }
220  return tensor.SetItem(tks, PyHandleToTensor(value, tensor.GetDtype(),
221  tensor.GetDevice(),
222  /*force_copy=*/false));
223  });
224 }
225 
226 void pybind_core_tensor_accessor(py::class_<Tensor>& tensor) {
227  pybind_getitem(tensor);
228  pybind_setitem(tensor);
229 }
230 
231 } // namespace core
232 } // namespace cloudViewer
TensorKey is used to represent single index, slice or advanced indexing on a Tensor.
Definition: TensorKey.h:26
static TensorKey IndexTensor(const Tensor &index_tensor)
Definition: TensorKey.cpp:144
static TensorKey Index(int64_t index)
Definition: TensorKey.cpp:134
static TensorKey Slice(utility::optional< int64_t > start, utility::optional< int64_t > stop, utility::optional< int64_t > step)
Definition: TensorKey.cpp:138
Dtype GetDtype() const
Definition: Tensor.h:1164
static Tensor Init(const T val, const Device &device=Device("CPU:0"))
Definition: Tensor.h:275
Tensor To(Dtype dtype, bool copy=false) const
Definition: Tensor.cpp:739
#define LogError(...)
Definition: Logging.h:60
Tensor PyHandleToTensor(const py::handle &handle, utility::optional< Dtype > dtype, utility::optional< Device > device, bool force_copy)
static void pybind_getitem(py::class_< Tensor > &tensor)
const Dtype Bool
Definition: Dtype.cpp:52
const Dtype Int64
Definition: Dtype.cpp:47
void pybind_core_tensor_accessor(py::class_< Tensor > &t)
static TensorKey PyHandleToTensorKey(const py::handle &item)
static TensorKey ToTensorKey(int key)
static void pybind_setitem(py::class_< Tensor > &tensor)
constexpr utility::nullopt_t None
Definition: TensorKey.h:20
Tensor PyArrayToTensor(py::array array, bool inplace)
Tensor PyTupleToTensor(const py::tuple &tuple, utility::optional< Dtype > dtype, utility::optional< Device > device)
Generic file read and write utility for python interface.