从Python访问OpenCV CUDA函数(无PyCUDA)

14

我正在编写一个Python应用程序,使用OpenCV的Python绑定进行标记检测和其他图像处理。我想使用OpenCV的CUDA模块来CUDA加速我的应用程序的某些部分,并且在他们的.hpp文件中注意到他们似乎正在使用OpenCV导出宏来支持Python和Java。然而,尽管我正在构建OpenCV WITH_CUDA=ON,但我似乎无法访问那些CUDA函数。

是否有必要使用诸如PyCUDA之类的包装器来访问GPU函数,例如cudaarithm中的阈值(threshold)?或者,如果我在Python代码中调用cv2.threshold()(而不是常规的基于CPU的实现),这些CUDA加速的函数已经被使用了吗?

CV_EXPORTS double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type, Stream& stream = Stream::Null());

我看到的cv2子模块如下:

  • Error
  • aruco
  • detail
  • fisheye
  • flann
  • instr
  • ml
  • ocl
  • ogl
  • videostab

cv2.cudacv2.gpucv2.cudaarithm都会返回AttributeError

我用于构建OpenCV的CMake指令如下:

cmake -DOPENCV_EXTRA_MODULES_PATH=/usr/local/lib/opencv_contrib/modules/ \
    -D WITH_CUDA=ON -D CUDA_FAST_MATH=1 \
    -D ENABLE_PRECOMPILED_HEADERS=OFF \
    -D BUILD_TESTS=OFF -D BUILD_PERF_TESTS=OFF -D BUILD_EXAMPLES=OFF \
    -D BUILD_opencv_java=OFF \
    -DBUILD_opencv_bgsegm=OFF -DBUILD_opencv_bioinspired=OFF -DBUILD_opencv_ccalib=OFF -DBUILD_opencv_cnn_3dobj=OFF -DBUILD_opencv_contrib_world=OFF -DBUILD_opencv_cvv=OFF -DBUILD_opencv_datasets=OFF -DBUILD_openc
v_dnn=OFF -DBUILD_opencv_dnns_easily_fooled=OFF -DBUILD_opencv_dpm=OFF -DBUILD_opencv_face=OFF -DBUILD_opencv_fuzzy=OFF -DBUILD_opencv_hdf=OFF -DBUILD_opencv_line_descriptor=OFF -DBUILD_opencv_matlab=OFF -DBUILD_o
pencv_optflow=OFF -DBUILD_opencv_plot=OFF -DBUILD_opencv_README.md=OFF -DBUILD_opencv_reg=OFF -DBUILD_opencv_rgbd=OFF -DBUILD_opencv_saliency=OFF -DBUILD_opencv_sfm=OFF -DBUILD_opencv_stereo=OFF -DBUILD_opencv_str
uctured_light=OFF -DBUILD_opencv_surface_matching=OFF -DBUILD_opencv_text=OFF -DBUILD_opencv_tracking=OFF -DBUILD_opencv_viz=OFF -DBUILD_opencv_xfeatures2d=OFF -DBUILD_opencv_ximgproc=OFF -DBUILD_opencv_xobjdetect
=OFF -DBUILD_opencv_xphoto=OFF ..

正如您所注意到的,OpenCV有自己的Python绑定到C++函数。据我所知,您不需要pycuda。您使用的是哪个版本的OpenCV?访问OpenCV Cuda函数应该很简单。 - NAmorim
嘿@NAmorim,感谢您的评论!我正在使用OpenCV 3.2.0-dev。然而,当我加载cv2可用的模块时,我没有看到CUDA的子模块(请参见更新的问题)。在Python .so中已经替换了具有CUDA加速对应项的函数吗? - ostrumvulpes
4
从OpenCV 4开始,Python绑定到CUDA加速代码应该可以工作。以下是关于如何实现这一点的文章: 加速OpenCV 4 - 在Windows中使用CUDA 10.0、Intel MKL+TBB和Python绑定构建 - nchaumont
4个回答

29

正如与@NAmorim的回答和评论线程中所确认的那样,没有可访问的Python绑定到OpenCV的各种CUDA模块

我通过使用Cython来绕过这个限制,获得了访问我需要的CUDA函数的权限,并实现了必要的逻辑来将我的Python对象(主要是NumPy数组)转换为OpenCV C/C++对象并返回。

工作代码

我首先编写了一个Cython定义文件GpuWrapper.pxd。该文件的目的是引用外部的C/C++类和方法,例如我感兴趣的CUDA方法。

from libcpp cimport bool
from cpython.ref cimport PyObject

# References PyObject to OpenCV object conversion code borrowed from OpenCV's own conversion file, cv2.cpp
cdef extern from 'pyopencv_converter.cpp':
    cdef PyObject* pyopencv_from(const Mat& m)
    cdef bool pyopencv_to(PyObject* o, Mat& m)

cdef extern from 'opencv2/imgproc.hpp' namespace 'cv':
    cdef enum InterpolationFlags:
        INTER_NEAREST = 0
    cdef enum ColorConversionCodes:
        COLOR_BGR2GRAY

cdef extern from 'opencv2/core/core.hpp':
    cdef int CV_8UC1
    cdef int CV_32FC1

cdef extern from 'opencv2/core/core.hpp' namespace 'cv':
    cdef cppclass Size_[T]:
        Size_() except +
        Size_(T width, T height) except +
        T width
        T height
    ctypedef Size_[int] Size2i
    ctypedef Size2i Size
    cdef cppclass Scalar[T]:
        Scalar() except +
        Scalar(T v0) except +

cdef extern from 'opencv2/core/core.hpp' namespace 'cv':
    cdef cppclass Mat:
        Mat() except +
        void create(int, int, int) except +
        void* data
        int rows
        int cols

cdef extern from 'opencv2/core/cuda.hpp' namespace 'cv::cuda':
    cdef cppclass GpuMat:
        GpuMat() except +
        void upload(Mat arr) except +
        void download(Mat dst) const
    cdef cppclass Stream:
        Stream() except +

cdef extern from 'opencv2/cudawarping.hpp' namespace 'cv::cuda':
    cdef void warpPerspective(GpuMat src, GpuMat dst, Mat M, Size dsize, int flags, int borderMode, Scalar borderValue, Stream& stream)
    # Function using default values
    cdef void warpPerspective(GpuMat src, GpuMat dst, Mat M, Size dsize, int flags)

我们还需要将Python对象转换为OpenCV对象的能力。我从OpenCV的modules/python/src2/cv2.cpp中复制了前几百行代码,可以在附录中找到该代码。
最后,我们可以编写Cython包装器方法来调用OpenCV的CUDA函数!这是Cython实现文件GpuWrapper.pyx的一部分。
import numpy as np  # Import Python functions, attributes, submodules of numpy
cimport numpy as np  # Import numpy C/C++ API

def cudaWarpPerspectiveWrapper(np.ndarray[np.uint8_t, ndim=2] _src,
                               np.ndarray[np.float32_t, ndim=2] _M,
                               _size_tuple,
                               int _flags=INTER_NEAREST):
    # Create GPU/device InputArray for src
    cdef Mat src_mat
    cdef GpuMat src_gpu
    pyopencv_to(<PyObject*> _src, src_mat)
    src_gpu.upload(src_mat)

    # Create CPU/host InputArray for M
    cdef Mat M_mat = Mat()
    pyopencv_to(<PyObject*> _M, M_mat)

    # Create Size object from size tuple
    # Note that size/shape in Python is handled in row-major-order -- therefore, width is [1] and height is [0]
    cdef Size size = Size(<int> _size_tuple[1], <int> _size_tuple[0])

    # Create empty GPU/device OutputArray for dst
    cdef GpuMat dst_gpu = GpuMat()
    warpPerspective(src_gpu, dst_gpu, M_mat, size, INTER_NEAREST)

    # Get result of dst
    cdef Mat dst_host
    dst_gpu.download(dst_host)
    cdef np.ndarray out = <np.ndarray> pyopencv_from(dst_host)
    return out

在运行一个设置脚本来将此代码进行cythonize和编译之后(见附录),我们可以将GpuWrapper作为Python模块导入并运行cudaWarpPerspectiveWrapper。这使我能够通过CUDA运行代码,仅有0.34722222222222854%的不匹配--非常令人兴奋!
参考文献(最多只能发布2个):
- 将ndarray转换为cv::Mat的最简单方法是什么? - 为使用OpenCV的C++代码编写Python绑定 附录: pyopencv_converter.cpp
#include <Python.h>
#include "numpy/ndarrayobject.h"
#include "opencv2/core/core.hpp"

static PyObject* opencv_error = 0;

// === FAIL MESSAGE ====================================================================================================

static int failmsg(const char *fmt, ...)
{
    char str[1000];

    va_list ap;
    va_start(ap, fmt);
    vsnprintf(str, sizeof(str), fmt, ap);
    va_end(ap);

    PyErr_SetString(PyExc_TypeError, str);
    return 0;
}

struct ArgInfo
{
    const char * name;
    bool outputarg;
    // more fields may be added if necessary

    ArgInfo(const char * name_, bool outputarg_)
        : name(name_)
        , outputarg(outputarg_) {}

    // to match with older pyopencv_to function signature
    operator const char *() const { return name; }
};

// === THREADING =======================================================================================================

class PyAllowThreads
{
public:
    PyAllowThreads() : _state(PyEval_SaveThread()) {}
    ~PyAllowThreads()
    {
        PyEval_RestoreThread(_state);
    }
private:
    PyThreadState* _state;
};

class PyEnsureGIL
{
public:
    PyEnsureGIL() : _state(PyGILState_Ensure()) {}
    ~PyEnsureGIL()
    {
        PyGILState_Release(_state);
    }
private:
    PyGILState_STATE _state;
};

// === ERROR HANDLING ==================================================================================================

#define ERRWRAP2(expr) \
try \
{ \
    PyAllowThreads allowThreads; \
    expr; \
} \
catch (const cv::Exception &e) \
{ \
    PyErr_SetString(opencv_error, e.what()); \
    return 0; \
}

// === USING NAMESPACE CV ==============================================================================================

using namespace cv;

// === NUMPY ALLOCATOR =================================================================================================

class NumpyAllocator : public MatAllocator
{
public:
    NumpyAllocator() { stdAllocator = Mat::getStdAllocator(); }
    ~NumpyAllocator() {}

    UMatData* allocate(PyObject* o, int dims, const int* sizes, int type, size_t* step) const
    {
        UMatData* u = new UMatData(this);
        u->data = u->origdata = (uchar*)PyArray_DATA((PyArrayObject*) o);
        npy_intp* _strides = PyArray_STRIDES((PyArrayObject*) o);
        for( int i = 0; i < dims - 1; i++ )
            step[i] = (size_t)_strides[i];
        step[dims-1] = CV_ELEM_SIZE(type);
        u->size = sizes[0]*step[0];
        u->userdata = o;
        return u;
    }

    UMatData* allocate(int dims0, const int* sizes, int type, void* data, size_t* step, int flags, UMatUsageFlags usageFlags) const
    {
        if( data != 0 )
        {
            CV_Error(Error::StsAssert, "The data should normally be NULL!");
            // probably this is safe to do in such extreme case
            return stdAllocator->allocate(dims0, sizes, type, data, step, flags, usageFlags);
        }
        PyEnsureGIL gil;

        int depth = CV_MAT_DEPTH(type);
        int cn = CV_MAT_CN(type);
        const int f = (int)(sizeof(size_t)/8);
        int typenum = depth == CV_8U ? NPY_UBYTE : depth == CV_8S ? NPY_BYTE :
                      depth == CV_16U ? NPY_USHORT : depth == CV_16S ? NPY_SHORT :
                      depth == CV_32S ? NPY_INT : depth == CV_32F ? NPY_FLOAT :
                      depth == CV_64F ? NPY_DOUBLE : f*NPY_ULONGLONG + (f^1)*NPY_UINT;
        int i, dims = dims0;
        cv::AutoBuffer<npy_intp> _sizes(dims + 1);
        for( i = 0; i < dims; i++ )
            _sizes[i] = sizes[i];
        if( cn > 1 )
            _sizes[dims++] = cn;
        PyObject* o = PyArray_SimpleNew(dims, _sizes, typenum);
        if(!o)
            CV_Error_(Error::StsError, ("The numpy array of typenum=%d, ndims=%d can not be created", typenum, dims));
        return allocate(o, dims0, sizes, type, step);
    }

    bool allocate(UMatData* u, int accessFlags, UMatUsageFlags usageFlags) const
    {
        return stdAllocator->allocate(u, accessFlags, usageFlags);
    }

    void deallocate(UMatData* u) const
    {
        if(!u)
            return;
        PyEnsureGIL gil;
        CV_Assert(u->urefcount >= 0);
        CV_Assert(u->refcount >= 0);
        if(u->refcount == 0)
        {
            PyObject* o = (PyObject*)u->userdata;
            Py_XDECREF(o);
            delete u;
        }
    }

    const MatAllocator* stdAllocator;
};

// === ALLOCATOR INITIALIZATION ========================================================================================

NumpyAllocator g_numpyAllocator;

// === CONVERTOR FUNCTIONS =============================================================================================

template<typename T> static
bool pyopencv_to(PyObject* obj, T& p, const char* name = "<unknown>");

template<typename T> static
PyObject* pyopencv_from(const T& src);

enum { ARG_NONE = 0, ARG_MAT = 1, ARG_SCALAR = 2 };

// special case, when the convertor needs full ArgInfo structure
static bool pyopencv_to(PyObject* o, Mat& m, const ArgInfo info)
{
    bool allowND = true;
    if(!o || o == Py_None)
    {
        if( !m.data )
            m.allocator = &g_numpyAllocator;
        return true;
    }

    if( PyInt_Check(o) )
    {
        double v[] = {static_cast<double>(PyInt_AsLong((PyObject*)o)), 0., 0., 0.};
        m = Mat(4, 1, CV_64F, v).clone();
        return true;
    }
    if( PyFloat_Check(o) )
    {
        double v[] = {PyFloat_AsDouble((PyObject*)o), 0., 0., 0.};
        m = Mat(4, 1, CV_64F, v).clone();
        return true;
    }
    if( PyTuple_Check(o) )
    {
        int i, sz = (int)PyTuple_Size((PyObject*)o);
        m = Mat(sz, 1, CV_64F);
        for( i = 0; i < sz; i++ )
        {
            PyObject* oi = PyTuple_GET_ITEM(o, i);
            if( PyInt_Check(oi) )
                m.at<double>(i) = (double)PyInt_AsLong(oi);
            else if( PyFloat_Check(oi) )
                m.at<double>(i) = (double)PyFloat_AsDouble(oi);
            else
            {
                failmsg("%s is not a numerical tuple", info.name);
                m.release();
                return false;
            }
        }
        return true;
    }

    if( !PyArray_Check(o) )
    {
        failmsg("%s is not a numpy array, neither a scalar", info.name);
        return false;
    }

    PyArrayObject* oarr = (PyArrayObject*) o;

    bool needcopy = false, needcast = false;
    int typenum = PyArray_TYPE(oarr), new_typenum = typenum;
    int type = typenum == NPY_UBYTE ? CV_8U :
               typenum == NPY_BYTE ? CV_8S :
               typenum == NPY_USHORT ? CV_16U :
               typenum == NPY_SHORT ? CV_16S :
               typenum == NPY_INT ? CV_32S :
               typenum == NPY_INT32 ? CV_32S :
               typenum == NPY_FLOAT ? CV_32F :
               typenum == NPY_DOUBLE ? CV_64F : -1;

    if( type < 0 )
    {
        if( typenum == NPY_INT64 || typenum == NPY_UINT64 || typenum == NPY_LONG )
        {
            needcopy = needcast = true;
            new_typenum = NPY_INT;
            type = CV_32S;
        }
        else
        {
            failmsg("%s data type = %d is not supported", info.name, typenum);
            return false;
        }
    }

#ifndef CV_MAX_DIM
    const int CV_MAX_DIM = 32;
#endif

    int ndims = PyArray_NDIM(oarr);
    if(ndims >= CV_MAX_DIM)
    {
        failmsg("%s dimensionality (=%d) is too high", info.name, ndims);
        return false;
    }

    int size[CV_MAX_DIM+1];
    size_t step[CV_MAX_DIM+1];
    size_t elemsize = CV_ELEM_SIZE1(type);
    const npy_intp* _sizes = PyArray_DIMS(oarr);
    const npy_intp* _strides = PyArray_STRIDES(oarr);
    bool ismultichannel = ndims == 3 && _sizes[2] <= CV_CN_MAX;

    for( int i = ndims-1; i >= 0 && !needcopy; i-- )
    {
        // these checks handle cases of
        //  a) multi-dimensional (ndims > 2) arrays, as well as simpler 1- and 2-dimensional cases
        //  b) transposed arrays, where _strides[] elements go in non-descending order
        //  c) flipped arrays, where some of _strides[] elements are negative
        // the _sizes[i] > 1 is needed to avoid spurious copies when NPY_RELAXED_STRIDES is set
        if( (i == ndims-1 && _sizes[i] > 1 && (size_t)_strides[i] != elemsize) ||
            (i < ndims-1 && _sizes[i] > 1 && _strides[i] < _strides[i+1]) )
            needcopy = true;
    }

    if( ismultichannel && _strides[1] != (npy_intp)elemsize*_sizes[2] )
        needcopy = true;

    if (needcopy)
    {
        if (info.outputarg)
        {
            failmsg("Layout of the output array %s is incompatible with cv::Mat (step[ndims-1] != elemsize or step[1] != elemsize*nchannels)", info.name);
            return false;
        }

        if( needcast ) {
            o = PyArray_Cast(oarr, new_typenum);
            oarr = (PyArrayObject*) o;
        }
        else {
            oarr = PyArray_GETCONTIGUOUS(oarr);
            o = (PyObject*) oarr;
        }

        _strides = PyArray_STRIDES(oarr);
    }

    // Normalize strides in case NPY_RELAXED_STRIDES is set
    size_t default_step = elemsize;
    for ( int i = ndims - 1; i >= 0; --i )
    {
        size[i] = (int)_sizes[i];
        if ( size[i] > 1 )
        {
            step[i] = (size_t)_strides[i];
            default_step = step[i] * size[i];
        }
        else
        {
            step[i] = default_step;
            default_step *= size[i];
        }
    }

    // handle degenerate case
    if( ndims == 0) {
        size[ndims] = 1;
        step[ndims] = elemsize;
        ndims++;
    }

    if( ismultichannel )
    {
        ndims--;
        type |= CV_MAKETYPE(0, size[2]);
    }

    if( ndims > 2 && !allowND )
    {
        failmsg("%s has more than 2 dimensions", info.name);
        return false;
    }

    m = Mat(ndims, size, type, PyArray_DATA(oarr), step);
    m.u = g_numpyAllocator.allocate(o, ndims, size, type, step);
    m.addref();

    if( !needcopy )
    {
        Py_INCREF(o);
    }
    m.allocator = &g_numpyAllocator;

    return true;
}

template<>
bool pyopencv_to(PyObject* o, Mat& m, const char* name)
{
    return pyopencv_to(o, m, ArgInfo(name, 0));
}

template<>
PyObject* pyopencv_from(const Mat& m)
{
    if( !m.data )
        Py_RETURN_NONE;
    Mat temp, *p = (Mat*)&m;
    if(!p->u || p->allocator != &g_numpyAllocator)
    {
        temp.allocator = &g_numpyAllocator;
        ERRWRAP2(m.copyTo(temp));
        p = &temp;
    }
    PyObject* o = (PyObject*)p->u->userdata;
    Py_INCREF(o);
    return o;
}

setupGpuWrapper.py

import subprocess
import os
import numpy as np
from distutils.core import setup, Extension
from Cython.Build import cythonize
from Cython.Distutils import build_ext

"""
Run setup with the following command:
```
python setupGpuWrapper.py build_ext --inplace
```
"""

# Determine current directory of this setup file to find our module
CUR_DIR = os.path.dirname(__file__)
# Use pkg-config to determine library locations and include locations
opencv_libs_str = subprocess.check_output("pkg-config --libs opencv".split()).decode()
opencv_incs_str = subprocess.check_output("pkg-config --cflags opencv".split()).decode()
# Parse into usable format for Extension call
opencv_libs = [str(lib) for lib in opencv_libs_str.strip().split()]
opencv_incs = [str(inc) for inc in opencv_incs_str.strip().split()]

extensions = [
    Extension('GpuWrapper',
              sources=[os.path.join(CUR_DIR, 'GpuWrapper.pyx')],
              language='c++',
              include_dirs=[np.get_include()] + opencv_incs,
              extra_link_args=opencv_libs)
]

setup(
    cmdclass={'build_ext': build_ext},
    name="GpuWrapper",
    ext_modules=cythonize(extensions)
)

我按照您的描述进行操作,但在应用cudaWarpPerspectiveWrapper函数时,出现了“Segmentation fault (core dumped)”错误,不确定哪里出错了。 当我编译cython时,出现了2个警告: cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++warning: #warning "Using deprecated NumPy API, disable it by " "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-Wcpp] 看起来都没问题。我正在使用opencv 3.3、python 2.7和cython 0.26。 - Xinyao Wang
嘿,Xinyao,我认为我也收到了那些警告——它们应该是无害的。我想我当时使用的是OpenCV 3.2、Python 3.2和Cython 0.25。这可能是Python版本之间的差异吗?你还需要做的一件重要的事情是调用numpy.import_array()——请参见这里:https://docs.scipy.org/doc/numpy-1.10.0/user/c-info.how-to-extend.html#required-subroutine。我记得当我忘记设置它时,会出现令人沮丧的seg错误! - ostrumvulpes
谢谢您的回复!我尝试了不同的Python版本(3.5和2.7)以及OpenCV版本(3.1、3.2和3.3),但仍然无法正常工作。我认为这是由于没有调用numpy.import_array()引起的,因为在您提到之前我对此一无所知。我在这里发布了一个问题:链接 如果您有时间看一下就太好了。我刚刚在GitHub上找到了另一个解决将numpy数组转换为cv mat的代码,但如果您能提供一些可能的解决方案,那就太棒了。 - Xinyao Wang
1
我无法确定在哪里包含numpy.import_array(),因为在Python中numpy没有import_array属性,所以似乎不应该包含在我的.py文件中。对这些东西太新了。 - Xinyao Wang
4
您好,我尝试在GpuWrapper.pyx中添加numpy.import_array(),现在它能够完美地工作了。非常感谢您提供的解决方案,十分感激! - Xinyao Wang
@ostrumvulpes 非常感谢你们两个!这对于Cython新手来说是一个很好的时间节省器。 - nazikus

5

我使用了OpenCV 4.0.0进行测试。@nchaumont提到,从OpenCV 4版本开始,已经包含了Python与CUDA的绑定。

至少在OpenCV 4.1.0中(可能更早),默认的Python绑定包括CUDA,只要OpenCV是使用CUDA支持构建的。

大多数功能似乎都作为cv2.cuda.thing(例如,cv2.cuda.cvtColor())公开。

目前,它们缺乏任何在线文档-例如,GPU Canny边缘检测器没有提及Python。但您可以在Python的REPL中使用help函数查看C++文档,这应该基本相同。


自从OpenCV 4.4.0以来,cv::cuda::CascadeClassifier应该已经回归了,但是我找不到Python绑定。它不在cv2.cuda.CascadeClassifier之下。有没有人发现了这个问题? - Jop Knoppers
cv2.cuda_CascadeClassifier 会导致分段错误。 - Jop Knoppers

4
我使用以下方法在Python中访问OpenCV的C++ CUDA方法:
  1. 创建自定义的opencv_contrib模块
  2. 编写C++代码来包装OpenCV CUDA方法
  3. 使用OpenCV Python绑定,公开您的自定义方法
  4. 使用opencv_contrib构建opencv
  5. 运行Python代码进行测试
我创建了一个小的github repo来演示相同的内容。

0

那么,如果我在Python代码中调用cv2.threshold()(而不是常规的基于CPU的实现),这些CUDA加速函数是否已经被使用了?

不,您必须从GPU加速模块中显式调用它们。调用cv2.threshold()将仅运行CPU版本。

由于OpenCV的Python API包装了C++函数,因此检查C++ API通常会提供有关函数/模块位置的有用提示。

例如,通过this过渡指南,您可以看到从OpenCV 2.X到3.X所做的API更改。在此处,可以通过先前版本上的cv2.cuda和cv2.gpu访问OpenCV 3.X上的GPU模块。而3.X中的cuda模块分为几个小部分:

  • cuda - 基于CUDA的计算机视觉
  • cudaarithm - 矩阵操作
  • cudabgsegm - 背景分割
  • cudacodec - 视频编码/解码
  • cudafeatures2d - 特征检测和描述
  • cudafilters - 图像过滤
  • cudaimgproc - 图像处理
  • cudalegacy - 旧版本支持
  • cudaoptflow - 光流
  • cudastereo - 立体对应
  • cudawarping - 图像变形
  • cudev - 设备层

您应该在cv2中搜索这些模块。


1
很遗憾,我找不到应该存在的这些模块: `>>> cv2.cuda Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'module' object has no attribute 'cuda'
cv2.gpu Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'module' object has no attribute 'gpu' cv2.cudaarithm Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'module' object has no attribute 'cudaarithm'`
- ostrumvulpes
你现在能够使用这个库了吗?你尝试过检查是否成功构建了OpenCV + Cuda吗?例如,如果你在Python中运行print cv2.getBuildInformation(),你应该会得到所有被激活的CMake标志。其中有一行应该是Use Cuda: Yes - NAmorim
4
嗨@NAmorim,我确实启用了“使用Cuda:是”。似乎Python没有与CUDA相关模块的绑定,因为GpuArray类型一开始就没有暴露给Python。我正在研究的解决方案是使用PyCUDA和ctypes从Python调用自己的C++代码来调用OpenCV CUDA函数。我会看看这是否是一个好的解决方案,并尝试保持这篇文章的更新! - ostrumvulpes
@ostrumvulpes 嗯,这对我来说是新鲜事。我已经在C++中使用了CUDA,我认为Python也会有它的绑定(好在OpenCV Python文档几乎不存在...)。祝你好运! - NAmorim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接