1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 07:13:02 +04:00

Merge branch 4.x

This commit is contained in:
Alexander Smorkalov
2025-08-27 15:50:00 +03:00
69 changed files with 7807 additions and 768 deletions
+197
View File
@@ -3,6 +3,8 @@
#ifdef HAVE_OPENCV_CORE
#include "dlpack/dlpack.h"
static PyObject* pycvMakeType(PyObject* , PyObject* args, PyObject* kw) {
const char *keywords[] = { "depth", "channels", NULL };
@@ -20,6 +22,201 @@ static PyObject* pycvMakeTypeCh(PyObject*, PyObject *value) {
return PyInt_FromLong(CV_MAKETYPE(depth, channels));
}
#define CV_DLPACK_CAPSULE_NAME "dltensor"
#define CV_DLPACK_USED_CAPSULE_NAME "used_dltensor"
template<typename T>
bool fillDLPackTensor(const T& src, DLManagedTensor* tensor, const DLDevice& device);
template<typename T>
bool parseDLPackTensor(DLManagedTensor* tensor, T& obj, bool copy);
template<typename T>
int GetNumDims(const T& src);
// source: https://github.com/dmlc/dlpack/blob/7f393bbb86a0ddd71fde3e700fc2affa5cdce72d/docs/source/python_spec.rst#L110
static void dlpack_capsule_deleter(PyObject *self){
if (PyCapsule_IsValid(self, CV_DLPACK_USED_CAPSULE_NAME)) {
return;
}
DLManagedTensor *managed = (DLManagedTensor *)PyCapsule_GetPointer(self, CV_DLPACK_CAPSULE_NAME);
if (managed == NULL) {
PyErr_WriteUnraisable(self);
return;
}
if (managed->deleter) {
managed->deleter(managed);
}
}
static void array_dlpack_deleter(DLManagedTensor *self)
{
if (!Py_IsInitialized()) {
return;
}
PyGILState_STATE state = PyGILState_Ensure();
PyObject *array = (PyObject *)self->manager_ctx;
PyMem_Free(self);
Py_XDECREF(array);
PyGILState_Release(state);
}
template<typename T>
static PyObject* to_dlpack(const T& src, PyObject* self, PyObject* py_args, PyObject* kw)
{
int stream = 0;
PyObject* maxVersion = nullptr;
PyObject* dlDevice = nullptr;
bool copy = false;
const char* keywords[] = { "stream", "max_version", "dl_device", "copy", NULL };
if (!PyArg_ParseTupleAndKeywords(py_args, kw, "|iOOp:__dlpack__", (char**)keywords, &stream, &maxVersion, &dlDevice, &copy))
return nullptr;
DLDevice device = {(DLDeviceType)-1, 0};
if (dlDevice && dlDevice != Py_None && PyTuple_Check(dlDevice))
{
device.device_type = static_cast<DLDeviceType>(PyLong_AsLong(PyTuple_GetItem(dlDevice, 0)));
device.device_id = PyLong_AsLong(PyTuple_GetItem(dlDevice, 1));
}
int ndim = GetNumDims(src);
void* ptr = PyMem_Malloc(sizeof(DLManagedTensor) + sizeof(int64_t) * ndim * 2);
if (!ptr) {
PyErr_NoMemory();
return nullptr;
}
DLManagedTensor* tensor = reinterpret_cast<DLManagedTensor*>(ptr);
tensor->manager_ctx = self;
tensor->deleter = array_dlpack_deleter;
tensor->dl_tensor.ndim = ndim;
tensor->dl_tensor.shape = reinterpret_cast<int64_t*>(reinterpret_cast<char*>(ptr) + sizeof(DLManagedTensor));
tensor->dl_tensor.strides = tensor->dl_tensor.shape + ndim;
fillDLPackTensor(src, tensor, device);
PyObject* capsule = PyCapsule_New(ptr, CV_DLPACK_CAPSULE_NAME, dlpack_capsule_deleter);
if (!capsule) {
PyMem_Free(ptr);
return nullptr;
}
// the capsule holds a reference
Py_INCREF(self);
return capsule;
}
template<typename T>
static PyObject* from_dlpack(PyObject* py_args, PyObject* kw)
{
PyObject* arr = nullptr;
PyObject* device = nullptr;
bool copy = false;
const char* keywords[] = { "device", "copy", NULL };
if (!PyArg_ParseTupleAndKeywords(py_args, kw, "O|Op:from_dlpack", (char**)keywords, &arr, &device, &copy))
return nullptr;
PyObject* capsule = nullptr;
if (PyCapsule_CheckExact(arr))
{
capsule = arr;
}
else
{
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
capsule = PyObject_CallMethodObjArgs(arr, PyString_FromString("__dlpack__"), NULL);
PyGILState_Release(gstate);
}
DLManagedTensor* tensor = reinterpret_cast<DLManagedTensor*>(PyCapsule_GetPointer(capsule, CV_DLPACK_CAPSULE_NAME));
if (tensor == nullptr)
{
if (capsule != arr)
Py_DECREF(capsule);
return nullptr;
}
T retval;
bool success = parseDLPackTensor(tensor, retval, copy);
if (success)
{
PyCapsule_SetName(capsule, CV_DLPACK_USED_CAPSULE_NAME);
}
if (capsule != arr)
Py_DECREF(capsule);
return success ? pyopencv_from(retval) : nullptr;
}
static DLDataType GetDLPackType(size_t elemSize1, int depth) {
DLDataType dtype;
dtype.bits = static_cast<uint8_t>(8 * elemSize1);
dtype.lanes = 1;
switch (depth)
{
case CV_8S: case CV_16S: case CV_32S: dtype.code = kDLInt; break;
case CV_8U: case CV_16U: dtype.code = kDLUInt; break;
case CV_16F: case CV_32F: case CV_64F: dtype.code = kDLFloat; break;
default:
CV_Error(Error::StsNotImplemented, "__dlpack__ data type");
}
return dtype;
}
static int DLPackTypeToCVType(const DLDataType& dtype, int channels) {
if (dtype.code == kDLInt)
{
switch (dtype.bits)
{
case 8: return CV_8SC(channels);
case 16: return CV_16SC(channels);
case 32: return CV_32SC(channels);
default:
{
PyErr_SetString(PyExc_BufferError,
format("Unsupported int dlpack depth: %d", dtype.bits).c_str());
return -1;
}
}
}
if (dtype.code == kDLUInt)
{
switch (dtype.bits)
{
case 8: return CV_8UC(channels);
case 16: return CV_16UC(channels);
default:
{
PyErr_SetString(PyExc_BufferError,
format("Unsupported uint dlpack depth: %d", dtype.bits).c_str());
return -1;
}
}
}
if (dtype.code == kDLFloat)
{
switch (dtype.bits)
{
case 16: return CV_16FC(channels);
case 32: return CV_32FC(channels);
case 64: return CV_64FC(channels);
default:
{
PyErr_SetString(PyExc_BufferError,
format("Unsupported float dlpack depth: %d", dtype.bits).c_str());
return -1;
}
}
}
PyErr_SetString(PyExc_BufferError, format("Unsupported dlpack data type: %d", dtype.code).c_str());
return -1;
}
#define PYOPENCV_EXTRA_METHODS_CV \
{"CV_MAKETYPE", CV_PY_FN_WITH_KW(pycvMakeType), "CV_MAKETYPE(depth, channels) -> retval"}, \
{"CV_8UC", (PyCFunction)(pycvMakeTypeCh<CV_8U>), METH_O, "CV_8UC(channels) -> retval"}, \
+158
View File
@@ -21,17 +21,175 @@ template<> struct pyopencvVecConverter<cuda::GpuMat>
};
CV_PY_TO_CLASS(cuda::GpuMat)
CV_PY_TO_CLASS(cuda::GpuMatND)
CV_PY_TO_CLASS(cuda::Stream)
CV_PY_TO_CLASS(cuda::Event)
CV_PY_TO_CLASS(cuda::HostMem)
CV_PY_TO_CLASS_PTR(cuda::GpuMat)
CV_PY_TO_CLASS_PTR(cuda::GpuMatND)
CV_PY_TO_CLASS_PTR(cuda::GpuMat::Allocator)
CV_PY_FROM_CLASS(cuda::GpuMat)
CV_PY_FROM_CLASS(cuda::GpuMatND)
CV_PY_FROM_CLASS(cuda::Stream)
CV_PY_FROM_CLASS(cuda::HostMem)
CV_PY_FROM_CLASS_PTR(cuda::GpuMat::Allocator)
template<>
bool fillDLPackTensor(const Ptr<cv::cuda::GpuMat>& src, DLManagedTensor* tensor, const DLDevice& device)
{
if ((device.device_type != -1 && device.device_type != kDLCUDA) || device.device_id != 0)
{
PyErr_SetString(PyExc_BufferError, "GpuMat can be exported only on GPU:0");
return false;
}
tensor->dl_tensor.data = src->cudaPtr();
tensor->dl_tensor.device.device_type = kDLCUDA;
tensor->dl_tensor.device.device_id = 0;
tensor->dl_tensor.dtype = GetDLPackType(src->elemSize1(), src->depth());
tensor->dl_tensor.shape[0] = src->rows;
tensor->dl_tensor.shape[1] = src->cols;
tensor->dl_tensor.shape[2] = src->channels();
tensor->dl_tensor.strides[0] = src->step1();
tensor->dl_tensor.strides[1] = src->channels();
tensor->dl_tensor.strides[2] = 1;
tensor->dl_tensor.byte_offset = 0;
return true;
}
template<>
bool fillDLPackTensor(const Ptr<cv::cuda::GpuMatND>& src, DLManagedTensor* tensor, const DLDevice& device)
{
if ((device.device_type != -1 && device.device_type != kDLCUDA) || device.device_id != 0)
{
PyErr_SetString(PyExc_BufferError, "GpuMatND can be exported only on GPU:0");
return false;
}
tensor->dl_tensor.data = src->getDevicePtr();
tensor->dl_tensor.device.device_type = kDLCUDA;
tensor->dl_tensor.device.device_id = 0;
tensor->dl_tensor.dtype = GetDLPackType(src->elemSize1(), CV_MAT_DEPTH(src->flags));
for (int i = 0; i < src->dims; ++i)
tensor->dl_tensor.shape[i] = src->size[i];
for (int i = 0; i < src->dims; ++i)
tensor->dl_tensor.strides[i] = src->step[i];
tensor->dl_tensor.byte_offset = 0;
return true;
}
template<>
bool parseDLPackTensor(DLManagedTensor* tensor, cv::cuda::GpuMat& obj, bool copy)
{
if (tensor->dl_tensor.byte_offset != 0)
{
PyErr_SetString(PyExc_BufferError, "Unimplemented from_dlpack for GpuMat with memory offset");
return false;
}
if (tensor->dl_tensor.ndim != 3)
{
PyErr_SetString(PyExc_BufferError, "cuda_GpuMat.from_dlpack expects a 3D tensor. Use cuda_GpuMatND.from_dlpack instead");
return false;
}
if (tensor->dl_tensor.device.device_type != kDLCUDA)
{
PyErr_SetString(PyExc_BufferError, "cuda_GpuMat.from_dlpack expects a tensor on CUDA device");
return false;
}
if (tensor->dl_tensor.strides[1] != tensor->dl_tensor.shape[2] ||
tensor->dl_tensor.strides[2] != 1)
{
PyErr_SetString(PyExc_BufferError, "Unexpected strides for image. Try use GpuMatND");
return false;
}
int type = DLPackTypeToCVType(tensor->dl_tensor.dtype, (int)tensor->dl_tensor.shape[2]);
if (type == -1)
return false;
obj = cv::cuda::GpuMat(
static_cast<int>(tensor->dl_tensor.shape[0]),
static_cast<int>(tensor->dl_tensor.shape[1]),
type,
tensor->dl_tensor.data,
tensor->dl_tensor.strides[0] * tensor->dl_tensor.dtype.bits / 8
);
if (copy)
obj = obj.clone();
return true;
}
template<>
bool parseDLPackTensor(DLManagedTensor* tensor, cv::cuda::GpuMatND& obj, bool copy)
{
if (tensor->dl_tensor.byte_offset != 0)
{
PyErr_SetString(PyExc_BufferError, "Unimplemented from_dlpack for GpuMat with memory offset");
return false;
}
if (tensor->dl_tensor.device.device_type != kDLCUDA)
{
PyErr_SetString(PyExc_BufferError, "cuda_GpuMat.from_dlpack expects a tensor on CUDA device");
return false;
}
int type = DLPackTypeToCVType(tensor->dl_tensor.dtype, (int)tensor->dl_tensor.shape[2]);
if (type == -1)
return false;
std::vector<size_t> steps(tensor->dl_tensor.ndim - 1);
std::vector<int> sizes(tensor->dl_tensor.ndim);
for (int i = 0; i < tensor->dl_tensor.ndim - 1; ++i)
{
steps[i] = tensor->dl_tensor.strides[i] * tensor->dl_tensor.dtype.bits / 8;
sizes[i] = static_cast<int>(tensor->dl_tensor.shape[i]);
}
sizes.back() = static_cast<int>(tensor->dl_tensor.shape[tensor->dl_tensor.ndim - 1]);
obj = cv::cuda::GpuMatND(sizes, type, tensor->dl_tensor.data, steps);
if (copy)
obj = obj.clone();
return true;
}
template<>
int GetNumDims(const Ptr<cv::cuda::GpuMat>& src) { return 3; }
template<>
int GetNumDims(const Ptr<cv::cuda::GpuMatND>& src) { return src->dims; }
static PyObject* pyDLPackGpuMat(PyObject* self, PyObject* py_args, PyObject* kw) {
Ptr<cv::cuda::GpuMat> * self1 = 0;
if (!pyopencv_cuda_GpuMat_getp(self, self1))
return failmsgp("Incorrect type of self (must be 'cuda_GpuMat' or its derivative)");
return to_dlpack(*(self1), self, py_args, kw);
}
static PyObject* pyDLPackGpuMatND(PyObject* self, PyObject* py_args, PyObject* kw) {
Ptr<cv::cuda::GpuMatND> * self1 = 0;
if (!pyopencv_cuda_GpuMatND_getp(self, self1))
return failmsgp("Incorrect type of self (must be 'cuda_GpuMatND' or its derivative)");
return to_dlpack(*(self1), self, py_args, kw);
}
static PyObject* pyDLPackDeviceCUDA(PyObject*, PyObject*, PyObject*) {
return pyopencv_from(std::tuple<int, int>(kDLCUDA, 0));
}
static PyObject* pyGpuMatFromDLPack(PyObject*, PyObject* py_args, PyObject* kw) {
return from_dlpack<cv::cuda::GpuMat>(py_args, kw);
}
static PyObject* pyGpuMatNDFromDLPack(PyObject*, PyObject* py_args, PyObject* kw) {
return from_dlpack<cv::cuda::GpuMatND>(py_args, kw);
}
#define PYOPENCV_EXTRA_METHODS_cuda_GpuMat \
{"__dlpack__", CV_PY_FN_WITH_KW(pyDLPackGpuMat), ""}, \
{"__dlpack_device__", CV_PY_FN_WITH_KW(pyDLPackDeviceCUDA), ""}, \
{"from_dlpack", CV_PY_FN_WITH_KW_(pyGpuMatFromDLPack, METH_STATIC), ""}, \
#define PYOPENCV_EXTRA_METHODS_cuda_GpuMatND \
{"__dlpack__", CV_PY_FN_WITH_KW(pyDLPackGpuMatND), ""}, \
{"__dlpack_device__", CV_PY_FN_WITH_KW(pyDLPackDeviceCUDA), ""}, \
{"from_dlpack", CV_PY_FN_WITH_KW_(pyGpuMatNDFromDLPack, METH_STATIC), ""}, \
#endif