【问题标题】:Call C++ opencv functions from Python ( Send a cv :: Mat to C++ dll which is using opencv )从 Python 调用 C++ opencv 函数(将 cv :: Mat 发送到使用 opencv 的 C++ dll)
【发布时间】:2013-10-08 20:07:02
【问题描述】:

我使用 opencv 编写了一个 C++ 代码,我将 C++ 代码转换为“DLL”,我需要在 python 中从这个 dll 调用一个方法,该方法接收 cv::Mat 作为数据类型。但我在这里遇到错误。以下是C++代码和python代码示例。

在谷歌上我发现我们需要使用 Boost 库,但不知道如何将 Python mat 转换为 C++ cv::Mat 以及如何在它们之间建立接口。

C++ dll 代码:

DLLEXPORT int FromPython ( cv :: Mat InputSrc) {

    imshow ( "FromPython", InputSrc );

        return 0;
}

Python 代码

import cv2 as cv
from ctypes import cdll

cap = cv.VideoCapture(0)

while(1):
    ret, frame = cap.read()

    cv.imshow('frame',frame)
    mydll = cdll.LoadLibrary('C:\Users\Documents\FromPythonDLL.dll')
    i = mydll.FromPython(frame)
    print(i)

    k = cv.waitKey(1) & 0xff
    if k == 27:
        break

cap.release()
cv.destroyAllWindows()

【问题讨论】:

    标签: c++ python opencv image-processing dll


    【解决方案1】:

    您可以查看 OpenCV Python 包装器。在 modules/python/src2/cv2.cpp 的 OpenCV 文件夹中(取决于版本,我使用 OpenCV 2.4),有一些名为 pyopencv_to 的函数被 OpenCV Python 包装器使用。其中之一用于将 PyObject 转换为 cv::Mat。您的“FromPython”函数需要获取 PyObject 作为输入。我个人使用 boost::python::object 将 Python OpenCV 函数返回的 numpy 数组传递给 C++ 函数/类。你最终应该在 C++ 中有这样的东西:

    ///PythonToOCV.h
    
    #ifndef __PYTHONTOOCV_H_INCLUDED__
    #define __PYTHONTOOCV_H_INCLUDED__
    
    #include <iostream>
    #include <Python.h>
    #include <boost/python.hpp>
    #include "numpy/ndarrayobject.h"
    #include "opencv2/core/core.hpp"
    
    /////////////////////////////////////////////////////////////////////////////
    /// \brief Import Numpy array. Necessary to avoid PyArray_Check() to crash
    void doImport( );
    
    int failmsg( const char *fmt, ... );
    
    static size_t REFCOUNT_OFFSET = ( size_t )&((( PyObject* )0)->ob_refcnt ) +
    ( 0x12345678 != *( const size_t* )"\x78\x56\x34\x12\0\0\0\0\0" )*sizeof( int );
    
    static inline PyObject* pyObjectFromRefcount( const int* refcount )
    {
    return ( PyObject* )(( size_t )refcount - REFCOUNT_OFFSET );
    }
    
    static inline int* refcountFromPyObject( const PyObject* obj )
    {
    return ( int* )(( size_t )obj + REFCOUNT_OFFSET );
    }
    
    class NumpyAllocator : public cv::MatAllocator
    {
    public:
    NumpyAllocator( ) { }
    ~NumpyAllocator( ) { }
    
    void allocate( int dims, const int* sizes, int type, int*& refcount,
    uchar*& datastart, uchar*& data, size_t* step );
    
    void deallocate( int* refcount, uchar* datastart, uchar* data );
    };
    
    
    /////////////////////////////////////////////////////////////////////////////
    /// \brief Convert a numpy array to a cv::Mat. This is used to import images
    /// from Python.
    /// This function is extracted from opencv/modules/python/src2/cv2.cpp
    /// in OpenCV 2.4
    int pyopencv_to( const PyObject* o, cv::Mat& m, const char* name = "<unknown>", bool allowND=true );
    #endif //__PYTHONTOOCV_H_INCLUDED__
    
    ///PythonToOCV.cpp
    
    #include "PythonToOpenCV.h"
    
    void doImport( )
    {
        import_array( );
    }
    
    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;
    }
    
    void NumpyAllocator::allocate( int dims, const int* sizes, int type, int*& refcount, uchar*& datastart, uchar*& data, size_t* step )
    {
        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;
        npy_intp _sizes[CV_MAX_DIM+1];
        for( i = 0; i < dims; i++ )
            _sizes[i] = sizes[i];
        if( cn > 1 )
        {
        /*if( _sizes[dims-1] == 1 )
             _sizes[dims-1] = cn;
        else*/
            _sizes[dims++] = cn;
        }
        PyObject* o = PyArray_SimpleNew( dims, _sizes, typenum );
        if( !o )
        CV_Error_(CV_StsError, ("The numpy array of typenum=%d, ndims=%d can not be created", typenum, dims));
        refcount = refcountFromPyObject(o);
        npy_intp* _strides = PyArray_STRIDES(o);
        for( i = 0; i < dims - (cn > 1); i++ )
            step[i] = (size_t)_strides[i];
        datastart = data = (uchar*)PyArray_DATA(o);
    
    }
    
    void NumpyAllocator::deallocate( int* refcount, uchar* datastart, uchar* data )
    {
        if( !refcount )
           return;
        PyObject* o = pyObjectFromRefcount(refcount);
        Py_INCREF(o);
        Py_DECREF(o);
    }
    
    // Declare the object
    NumpyAllocator g_numpyAllocator;
    
    int pyopencv_to(const PyObject* o, cv::Mat& m, const char* name, bool allowND )
    {
        // to avoid PyArray_Check() to crash even with valid array
        doImport( );
    
        if(!o || o == Py_None)
        {
            if( !m.data )
                m.allocator = &g_numpyAllocator;
            return true;
        }
    
        if( !PyArray_Check(o) )
        {
            failmsg("%s is not a numpy array", name);
            return false;
        }
    
        // NPY_LONG (64 bit) is converted to CV_32S (32 bit)
        int typenum = PyArray_TYPE(o);
        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 || typenum == NPY_LONG ? CV_32S :
            typenum == NPY_FLOAT ? CV_32F :
            typenum == NPY_DOUBLE ? CV_64F : -1;
    
        if( type < 0 )
        {
            failmsg("%s data type = %d is not supported", name, typenum);
            return false;
        }
    
        int ndims = PyArray_NDIM(o);
        if(ndims >= CV_MAX_DIM)
        {
            failmsg("%s dimensionality (=%d) is too high", name, ndims);
            return false;
        }
    
        int size[CV_MAX_DIM+1];
        size_t step[CV_MAX_DIM+1], elemsize = CV_ELEM_SIZE1(type);
        const npy_intp* _sizes = PyArray_DIMS(o);
        const npy_intp* _strides = PyArray_STRIDES(o);
        bool transposed = false;
    
        for(int i = 0; i < ndims; i++)
        {
            size[i] = (int)_sizes[i];
            step[i] = (size_t)_strides[i];
        }
    
        if( ndims == 0 || step[ndims-1] > elemsize ) {
            size[ndims] = 1;
            step[ndims] = elemsize;
            ndims++;
        }
    
        if( ndims >= 2 && step[0] < step[1] )
        {
            std::swap(size[0], size[1]);
            std::swap(step[0], step[1]);
            transposed = true;
        }
    
        if( ndims == 3 && size[2] <= CV_CN_MAX && step[1] == elemsize*size[2] )
        {
            ndims--;
            type |= CV_MAKETYPE(0, size[2]);
        }
    
        if( ndims > 2 && !allowND )
        {
            failmsg("%s has more than 2 dimensions", name);
            return false;
        }
    
        m = cv::Mat(ndims, size, type, PyArray_DATA(o), step);
    
        if( m.data )
        {
            m.refcount = refcountFromPyObject(o);
            m.addref(); // protect the original numpy array from deallocation
            // (since Mat destructor will decrement the reference counter)
        };
        m.allocator = &g_numpyAllocator;
    
        if( transposed )
        {
            cv::Mat tmp;
            tmp.allocator = &g_numpyAllocator;
            transpose(m, tmp);
            m = tmp;
        }
        return true;
    }
    

    那么您可以访问 cv::Mat 的函数将如下所示:

    /// fromPython.h
    
    #ifndef __FROMPYTHON_H_INCLUDED__
    #define __FROMPYTHON_H_INCLUDED__
    
    #include "PythonToOCV.h"
    #include <boost/python.hpp>
    
    int fromPython( boost::python::object &frame );
    #endif //__FROMPYTHON_H_INCLUDED__
    
    
    
    /// fromPython.cpp
    
    #include "fromPython.h"
    
    int fromPython( boost::python::object &frame )
    {
         cv::Mat image;
         // this is the function from modules/python/src2/cv2.cpp (the third parameter might be ArgInfo in later OpenCV versions)
         pyopencv_to( frame.ptr( ), image, "info", true );
    
         ///
         ///  HERE code using cv::Mat image          
         ///
    
         return 1;
    } 
    

    要从 Python 访问该函数,需要将其包装在 BOOST_PYTHON_MODULE 中。有点像:

    #include "fromPython.h"
    using namespace boost::python; 
    
    /// This function needs to be included to pass PyObjects as numpy array ( http://mail.python.org/pipermail/cplusplus-sig/2006-September/011021.html )
    void* extract_pyarray( PyObject* x )
    {
        return PyObject_TypeCheck( x, &PyArray_Type ) ? x : 0;
    }
    
    BOOST_PYTHON_MODULE( myWrapper )
    {
         // This function needs to be included to pass PyObjects as numpy array ( http://mail.python.org/pipermail/cplusplus-sig/2006-September/011021.html )
         boost::python::converter::registry::insert( &extract_pyarray, type_id<PyArrayObject>( ) ); 
         def fromPython( "fromPython", &fromPython );
    } 
    

    然后在 Python 中,您可以从 Boost 包装器创建的 Python 模块中调用您的函数。我使用的是 Linux,所以我编译上面的代码来获得一个动态库 (.so)。我不确定它在 Windows 中有何不同。我可以从动态库中访问模块:

    import myWrapper
    import cv2 
    
    def myFunct():
        cap = cv2.VideoCapture(0)
        while(1):
            ret,frame = cap.read()
            myWrapper.fromPython(frame)
    

    您可能可以避免使用 Boost,但我还没有尝试过其他方法,我发现 Boost 可以方便地包装 C++ 类。 注意:我没有测试此代码,因为我从项目中剥离了它,但我希望它仍然有用。

    【讨论】:

    • 感谢您的回答。我会再等几个小时让人们查看并接受作为回答。
    • 我按照此链接stackoverflow.com/questions/12008536/… 中提到的这种方式进行操作,但遇到了我在同一个链接中提到的错误
    • 对不起,我已经尝试过你的答案但无法理解,我应该如何从 python 调用“frompython”方法?您的答案中代码的第二部分是什么。我按照上面提到的链接,因为它似乎很容易。
    • 从动态库中导入 boost Python 模块,然后调用该函数。我用 Python 代码编辑我的答案
    • 非常感谢您的编辑。 “pyopencv_to(frame.ptr(),图像,“信息”,真);”我需要在我的代码中包含这个方法的定义吗?这个方法看起来像很多依赖项?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-23
    • 2016-12-30
    • 1970-01-01
    • 2016-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多