NVIDIA DeepStream SDK API Reference

9.2 Release
service-maker/sources/python/src/bindings/tensor.h
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3  * SPDX-License-Identifier: Apache-2.0
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #include "tensordoc.h"
19 
20 #include <dlpack/dlpack.h>
21 #include <iostream>
22 #include <vector>
23 #include <pybind11/pybind11.h>
24 #include <pybind11/numpy.h>
25 #include <memory>
26 
27 namespace py = pybind11;
28 
29 class __attribute__ ((visibility("hidden"))) DlpackTensorContext : public Tensor::Context {
30  public:
31  DlpackTensorContext(py::object dlm_tensor): dlm_tensor_(dlm_tensor) {}
32  virtual ~DlpackTensorContext() {}
33  protected:
34  py::object dlm_tensor_;
35 };
36 
37 enum class ColorFormat {
41 };
42 
44 public:
46 
47  explicit TensorWrapper(Tensor* tensor)
48  : tensor_(tensor)
49  {}
50 
51  explicit TensorWrapper(py::object object, std::string format) {
52  py::tuple dlpack_device = object.attr("__dlpack_device__")().cast<py::tuple>();
53  auto device_type = static_cast<DLDeviceType>(dlpack_device[0].cast<int>());
54  py::capsule cap;
55  if (device_type == kDLCUDAHost || device_type == kDLCUDA || device_type == kDLCUDAManaged) {
56  auto stream_id = 1;
57  cap = object.attr("__dlpack__")(py::arg("stream") = stream_id).cast<py::capsule>();
58  } else {
59  cap = object.attr("__dlpack__")().cast<py::capsule>();
60  }
61  if (!PyCapsule_IsValid(cap.ptr(), "dltensor")) {
62  throw std::runtime_error("Invalid capsule for dltensor");
63  }
64  DLManagedTensor *dl_managed_tensor = cap.get_pointer<DLManagedTensor>();
65  const DLTensor& dl_tensor = dl_managed_tensor->dl_tensor;
66  unsigned int device_id = (unsigned int )dl_tensor.device.device_id;
67  auto dtype_code = dl_tensor.dtype.code;
68  auto device = Tensor::DeviceType::NONE;
69  if (device_type == kDLCPU) {
70  device = Tensor::DeviceType::CPU;
71  } else if (device_type == kDLCUDAHost || device_type == kDLCUDA || device_type == kDLCUDAManaged) {
72  device = Tensor::DeviceType::GPU;
73  } else {
74  throw std::runtime_error("Unsupported Tensor Device Type");
75  }
76 
77  Tensor::DataType dtype = Tensor::INVALID;
78  if (dtype_code == kDLInt) {
79  dtype = Tensor::SIGNED;
80  } else if (dtype_code == kDLUInt || dtype_code == kDLBool) {
81  dtype = Tensor::UNSIGNED;
82  } else if (dtype_code == kDLFloat) {
84  } else if (dtype_code == kDLComplex) {
85  dtype = Tensor::COMPLEX;
86  } else {
87  std::string dtype_str = std::to_string(dtype_code);
88  throw std::runtime_error("Unsupported Tensor dtype: " + dtype_str);
89  }
90 
91  tensor_ = make_unique<Tensor>(
92  dl_tensor.ndim,
93  dtype,
94  dl_tensor.dtype.bits,
95  dl_tensor.shape,
96  dl_tensor.strides,
97  dl_tensor.data,
98  format,
99  device_id,
100  device,
101  new DlpackTensorContext(object)
102  );
103  }
104 
105  std::vector<uint64_t> shape_vec() const {
106  return tensor_->shape();
107  }
108 
109  std::vector<int64_t> strides_vec() const {
110  std::vector<int64_t> strides;
111  for (size_t i = 0; i < tensor_->rank(); i++) {
112  strides.push_back(tensor_->stride(i));
113  }
114  return strides;
115  }
116 
117  py::tuple shape() const {
118  auto ndim = tensor_->rank();
119  auto tensor_shape = tensor_->shape();
120  py::tuple shape(ndim);
121  for (size_t i = 0; i < ndim; i++) {
122  shape[i] = tensor_shape[i];
123  }
124  return shape;
125  }
126 
127  py::tuple strides() const {
128  auto ndim = tensor_->rank();
129  py::tuple strides(ndim);
130  for (size_t d = 0; d < ndim; d++) {
131  strides[d] = tensor_->stride(d);
132  }
133  return strides;
134  }
135 
137  return tensor_->dtype();
138  }
139 
140  py::dtype dtype() const {
141  auto dtype = tensor_->dtype();
142  std::string dtype_str;
143  if (dtype == Tensor::UNSIGNED) {
144  dtype_str = "uint";
145  } else if (dtype == Tensor::SIGNED) {
146  dtype_str = "int";
147  } else if (dtype == Tensor::FLOAT) {
148  dtype_str = "float";
149  } else if (dtype == Tensor::COMPLEX) {
150  dtype_str = "complex";
151  }
152  if (dtype_str.length()) {
153  dtype_str += std::to_string(tensor_->bits());
154  return py::dtype(dtype_str);
155  }
156 
157  return py::dtype();
158  }
159 
160  py::capsule dlpack(PyObject* py_object_ptr, py::object stream) {
161  unique_ptr<DLManagedTensor> dl_managed_tensor(new DLManagedTensor);
162  dl_managed_tensor->manager_ctx = (void*) py_object_ptr;
163  dl_managed_tensor->deleter = [](DLManagedTensor *tensor) {
164  py::gil_scoped_acquire acquire;
165  delete tensor->dl_tensor.shape;
166  delete tensor->dl_tensor.strides;
167  auto ctx = static_cast<PyObject*>(tensor->manager_ctx);
168  assert(ctx != NULL);
169  Py_DECREF(ctx);
170  tensor->manager_ctx = NULL;
171  delete tensor;
172  };
173 
174  DLTensor &dl_tensor = dl_managed_tensor->dl_tensor;
175  dl_tensor.device.device_type = tensor_->deviceType() == Tensor::DeviceType::GPU ? kDLCUDA : kDLCPU;
176  dl_tensor.device.device_id = tensor_->deviceId();
177  dl_tensor.ndim = tensor_->rank();
178 
179  unique_ptr<int64_t[]> shape_ptr(new int64_t[dl_tensor.ndim]);
180  copy_n(tensor_->shape().begin(), dl_tensor.ndim, shape_ptr.get());
181  dl_tensor.shape = shape_ptr.release();
182 
183  unique_ptr<int64_t[]> strides_ptr(new int64_t[dl_tensor.ndim]);
184  for (int n = 0; n < dl_tensor.ndim; n++) {
185  strides_ptr[n] = tensor_->stride(n);
186  }
187  dl_tensor.strides = strides_ptr.release();
188 
189  auto dtype = tensor_->dtype();
190  uint8_t dtype_code = 0;
191  if (dtype == Tensor::UNSIGNED) {
192  dtype_code = kDLUInt;
193  } else if (dtype == Tensor::SIGNED) {
194  dtype_code = kDLInt;
195  } else if (dtype == Tensor::FLOAT) {
196  dtype_code = kDLFloat;
197  } else if (dtype == Tensor::COMPLEX) {
198  dtype_code = kDLComplex;
199  }
200  dl_tensor.dtype = {dtype_code, (uint8_t)tensor_->bits(), 1};
201 
202  dl_tensor.data = tensor_->data();
203  dl_tensor.byte_offset = 0;
204 
205  py::capsule cap(dl_managed_tensor.release(), "dltensor", [](PyObject *ptr) {
206  if (PyCapsule_IsValid(ptr, "dltensor")) {
207  if(auto *tensor = static_cast<DLManagedTensor *>(PyCapsule_GetPointer(ptr, "dltensor"))) {
208  if (tensor->deleter) {
209  tensor->deleter(tensor);
210  }
211  }
212  }
213  });
214 
215  return cap;
216  }
217 
218  std::pair<int, int> dlpackDevice() const {
219  return {kDLCUDA, tensor_->deviceId()};
220  }
221 
223  if (tensor_) {
224  return tensor_->wrap(static_cast<NvBufSurfaceColorFormat>(format));
225  } else {
226  return Buffer();
227  }
228  }
229 
230  TensorWrapper* clone() const {
231  if (tensor_) {
232  return new TensorWrapper(tensor_->clone());
233  } else {
234  return nullptr;
235  }
236  }
237 
239  return tensor_.release();
240  }
241 
242  uint64_t size() const {
243  if (tensor_) {
244  return tensor_->size();
245  } else {
246  return 0;
247  }
248  }
249 
250  TensorWrapper* toGPU(unsigned int device_id) const {
251  if (tensor_) {
252  return new TensorWrapper(tensor_->toGPU(device_id));
253  } else {
254  return nullptr;
255  }
256  }
257 
258  std::string deviceType() const {
259  if (tensor_) {
260  auto dtype = tensor_->deviceType();
261  if (dtype == Tensor::DeviceType::GPU) return "GPU";
262  if (dtype == Tensor::DeviceType::CPU) return "CPU";
263  }
264  return "NONE";
265  }
266 
267  void* data() const {
268  return tensor_->data();
269  }
270 
271  bool hasTensor() const { return tensor_ != nullptr; }
272 
273 protected:
274  unique_ptr<Tensor> tensor_;
275 };
276 
277 template<typename T>
278 void print_nd_summary(const void* base, const std::vector<uint64_t>& shape, const std::vector<int64_t>& strides, size_t dim = 0, ssize_t offset = 0) {
279  if (dim == shape.size()) {
280  const T* ptr = reinterpret_cast<const T*>(reinterpret_cast<const char*>(base) + offset);
281  std::cout << *ptr;
282  return;
283  }
284  std::cout << "[";
285  ssize_t n = shape[dim];
286  ssize_t print_head = std::min<ssize_t>(3, n);
287  ssize_t print_tail = (n > 6) ? 3 : std::max<ssize_t>(0, n - 3);
288 
289  // Print first 3
290  for (ssize_t i = 0; i < print_head; ++i) {
291  if (i > 0) std::cout << ", ";
292  print_nd_summary<T>(base, shape, strides, dim + 1, offset + i * strides[dim]);
293  }
294  // Ellipsis if needed
295  if (n > 6) std::cout << ", ...";
296  // Print last 3
297  for (ssize_t i = n - print_tail; i < n; ++i) {
298  if (i > 0) std::cout << ", ";
299  print_nd_summary<T>(base, shape, strides, dim + 1, offset + i * strides[dim]);
300  }
301  std::cout << "]";
302 }
303 
304 void module_tensor_bind(py::module &m);
305 
306 void module_tensor_bind(py::module &m) {
307  py::enum_<ColorFormat>(m, "ColorFormat", pydeepstreamdoc::tensor::ColorFormatDoc::descr)
308  .value("RGB", ColorFormat::RGB)
309  .value("I420", ColorFormat::I420)
310  .value("RGBA", ColorFormat::RGBA);
311  py::class_<TensorWrapper>(m, "Tensor", pydeepstreamdoc::tensor::TensorDoc::descr)
312  .def_property_readonly("shape", &TensorWrapper::shape)
313  .def_property_readonly("strides", &TensorWrapper::strides)
314  .def_property_readonly("dtype", &TensorWrapper::dtype)
315  .def_property_readonly("device_type", &TensorWrapper::deviceType)
316  .def("__dlpack__", [](py::object& self, py::object stream){
317  TensorWrapper* tensor = self.cast<TensorWrapper*>();
318  Py_INCREF(self.ptr());
319  return tensor->dlpack(self.ptr(), stream);
320  }, pydeepstreamdoc::tensor::TensorDoc::dlpack, py::arg("stream") = py::none())
323  .def("clone", &TensorWrapper::clone, py::call_guard<py::gil_scoped_release>(), py::return_value_policy::take_ownership, pydeepstreamdoc::tensor::TensorDoc::clone)
324  .def("to_gpu", &TensorWrapper::toGPU, py::arg("device_id"), py::return_value_policy::take_ownership, pydeepstreamdoc::tensor::TensorDoc::to_gpu)
326  .def("__bool__", [](TensorWrapper& self) { return self.hasTensor(); })
327  .def("__repr__", [](const TensorWrapper& self) {
328  if (self.hasTensor()) {
329  if (self.deviceType() == "CPU") {
330  return "Tensor(shape=" + py::repr(self.shape()).cast<std::string>() +
331  ", strides=" + py::repr(self.strides()).cast<std::string>() +
332  ", dtype=" + py::repr(self.dtype()).cast<std::string>() +
333  ", __dlpack_device__=" + py::repr(py::cast(self.dlpackDevice())).cast<std::string>() +
334  ", device_type=" + self.deviceType() +
335  ", size=" + std::to_string(self.size()) +
336  ", data=" + py::repr(py::array(self.dtype(), self.shape_vec(), self.strides_vec(), self.data())).cast<std::string>() +
337  ")";
338  }
339  return "Tensor(shape=" + py::repr(self.shape()).cast<std::string>() +
340  ", strides=" + py::repr(self.strides()).cast<std::string>() +
341  ", dtype=" + py::repr(self.dtype()).cast<std::string>() +
342  ", __dlpack_device__=" + py::repr(py::cast(self.dlpackDevice())).cast<std::string>() +
343  ", device_type=" + self.deviceType() +
344  ", size=" + std::to_string(self.size()) +
345  ", data=" + py::repr(py::cast(self.data())).cast<std::string>() +
346  ")";
347  }
348  return "Tensor(shape=" + py::repr(self.shape()).cast<std::string>() +
349  ", strides=" + py::repr(self.strides()).cast<std::string>() +
350  ", dtype=" + py::repr(self.dtype()).cast<std::string>() +
351  ", __dlpack_device__=" + py::repr(py::cast(self.dlpackDevice())).cast<std::string>() +
352  ", size=" + std::to_string(self.size()) +
353  ", data=[])";
354  });
355  m.def("as_tensor", [](py::object object, std::string format) {
356  if (hasattr(object, "__dlpack_device__")) {
357  return make_unique<TensorWrapper>(object, format);
358  }
359  return make_unique<TensorWrapper>();
361 ;
362 }
TensorWrapper::clone
TensorWrapper * clone() const
Definition: service-maker/sources/python/src/bindings/tensor.h:230
TensorWrapper::dtype
py::dtype dtype() const
Definition: service-maker/sources/python/src/bindings/tensor.h:140
TensorWrapper::TensorWrapper
TensorWrapper(Tensor *tensor)
Definition: service-maker/sources/python/src/bindings/tensor.h:47
ds3d::DataType
DataType
Definition: includes/ds3d/common/idatatype.h:82
Tensor::release
bool release()
Release GPU memory associated with this tensor.
Definition: sources/gst-plugins/gst-nvdsvideotemplate/ds-sparse4d/utils/tensor.h:130
pydeepstreamdoc::tensor::TensorDoc::dlpack_device
constexpr const char * dlpack_device
Definition: service-maker/sources/python/src/bindings/docstrings/tensordoc.h:38
NONE
@ NONE
Indicates that no message is available.
Definition: sources/libs/nvmultiobjecttracker/include/modules/MultiViewAssociator/ICommunicator.hpp:38
pydeepstreamdoc::tensor::TensorDoc::clone
constexpr const char * clone
Definition: service-maker/sources/python/src/bindings/docstrings/tensordoc.h:44
print_nd_summary
void print_nd_summary(const void *base, const std::vector< uint64_t > &shape, const std::vector< int64_t > &strides, size_t dim=0, ssize_t offset=0)
Definition: service-maker/sources/python/src/bindings/tensor.h:278
module_tensor_bind
void module_tensor_bind(py::module &m)
Definition: service-maker/sources/python/src/bindings/tensor.h:306
TensorWrapper::toGPU
TensorWrapper * toGPU(unsigned int device_id) const
Definition: service-maker/sources/python/src/bindings/tensor.h:250
TensorWrapper::hasTensor
bool hasTensor() const
Definition: service-maker/sources/python/src/bindings/tensor.h:271
ColorFormat::RGBA
@ RGBA
TensorWrapper::TensorWrapper
TensorWrapper()
Definition: service-maker/sources/python/src/bindings/tensor.h:45
TensorWrapper::c_dtype
Tensor::DataType c_dtype() const
Definition: service-maker/sources/python/src/bindings/tensor.h:136
TensorWrapper
Definition: service-maker/sources/python/src/bindings/tensor.h:43
TensorWrapper::shape_vec
std::vector< uint64_t > shape_vec() const
Definition: service-maker/sources/python/src/bindings/tensor.h:105
ColorFormat::RGB
@ RGB
__attribute__
void __attribute__((constructor)) nvmsgbroker_init(void)
pydeepstreamdoc::tensor::TensorDoc::to_gpu
constexpr const char * to_gpu
Definition: service-maker/sources/python/src/bindings/docstrings/tensordoc.h:45
TensorWrapper::dlpackDevice
std::pair< int, int > dlpackDevice() const
Definition: service-maker/sources/python/src/bindings/tensor.h:218
TensorWrapper::size
uint64_t size() const
Definition: service-maker/sources/python/src/bindings/tensor.h:242
FLOAT
@ FLOAT
Specifies FP32 format.
Definition: includes/nvdsinfer.h:80
NVBUF_COLOR_FORMAT_RGB
@ NVBUF_COLOR_FORMAT_RGB
Specifies RGB-8-8-8 single plane.
Definition: includes/nvbufsurface.h:167
TensorWrapper::shape
py::tuple shape() const
Definition: service-maker/sources/python/src/bindings/tensor.h:117
pydeepstreamdoc::tensor::TensorDoc::wrap
constexpr const char * wrap
Definition: service-maker/sources/python/src/bindings/docstrings/tensordoc.h:39
pydeepstreamdoc::tensor::TensorDoc::size
constexpr const char * size
Definition: service-maker/sources/python/src/bindings/docstrings/tensordoc.h:46
Tensor
Structure to store tensor information.
Definition: sources/gst-plugins/gst-nvdsvideotemplate/ds-sparse4d/utils/tensor.h:40
TensorWrapper::tensor_
unique_ptr< Tensor > tensor_
Definition: service-maker/sources/python/src/bindings/tensor.h:274
pydeepstreamdoc::tensor::TensorDoc::descr
constexpr const char * descr
Definition: service-maker/sources/python/src/bindings/docstrings/tensordoc.h:31
pydeepstreamdoc::tensor::TensorDoc::as_tensor
constexpr const char * as_tensor
Definition: service-maker/sources/python/src/bindings/docstrings/tensordoc.h:47
pydeepstreamdoc::tensor::TensorDoc::dlpack
constexpr const char * dlpack
Definition: service-maker/sources/python/src/bindings/docstrings/tensordoc.h:37
pybind11
Definition: service-maker/sources/python/src/bindings/value.h:18
TensorWrapper::release
Tensor * release()
Definition: service-maker/sources/python/src/bindings/tensor.h:238
TensorWrapper::wrap
Buffer wrap(ColorFormat format)
Definition: service-maker/sources/python/src/bindings/tensor.h:222
TensorWrapper::data
void * data() const
Definition: service-maker/sources/python/src/bindings/tensor.h:267
data
JSAMPIMAGE data
Definition: sources/libs/nvll_batch_jpeg_encode/oss/jpeglib.h:967
pydeepstreamdoc::tensor::ColorFormatDoc::descr
constexpr const char * descr
Definition: service-maker/sources/python/src/bindings/docstrings/tensordoc.h:26
TensorWrapper::strides_vec
std::vector< int64_t > strides_vec() const
Definition: service-maker/sources/python/src/bindings/tensor.h:109
NVBUF_COLOR_FORMAT_RGBA
@ NVBUF_COLOR_FORMAT_RGBA
Specifies RGBA-8-8-8-8 single plane.
Definition: includes/nvbufsurface.h:151
ColorFormat::I420
@ I420
TensorWrapper::dlpack
py::capsule dlpack(PyObject *py_object_ptr, py::object stream)
Definition: service-maker/sources/python/src/bindings/tensor.h:160
ColorFormat
ColorFormat
Definition: service-maker/sources/python/src/bindings/tensor.h:37
TensorWrapper::deviceType
std::string deviceType() const
Definition: service-maker/sources/python/src/bindings/tensor.h:258
TensorWrapper::strides
py::tuple strides() const
Definition: service-maker/sources/python/src/bindings/tensor.h:127
NVBUF_COLOR_FORMAT_YUV420
@ NVBUF_COLOR_FORMAT_YUV420
Specifies BT.601 colorspace - YUV420 multi-planar.
Definition: includes/nvbufsurface.h:117
NvBufSurfaceColorFormat
NvBufSurfaceColorFormat
Defines color formats for NvBufSurface.
Definition: includes/nvbufsurface.h:110
ds3d::v2xinfer::format
static std::string format(const char *fmt,...)
Definition: sources/libs/ds3d/inference_custom_lib/ds3d_v2x_infer_custom_preprocess/tensor.hpp:31
TensorWrapper::TensorWrapper
TensorWrapper(py::object object, std::string format)
Definition: service-maker/sources/python/src/bindings/tensor.h:51