从Python调用C ++ opencv函数(发送cv :: Mat到C ++ dll,它使用opencv) [英] Call C++ opencv functions from Python ( Send a cv :: Mat to C++ dll which is using opencv )

查看:5809
本文介绍了从Python调用C ++ opencv函数(发送cv :: Mat到C ++ dll,它使用opencv)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have written a C++ code using opencv, I converted the C++ code as a "DLL" and I need to call a method from this dll in python which receives cv::Mat as datatype. But I am getting error here. The below are the samples of C++ code and python code.

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

On googling I found we need to use Boost library but am not sure how to convert Python mat to C++ cv::Mat and how to make interface between them.

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()


推荐答案

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

You can have a look at the OpenCV Python wrapper. In the OpenCV folder in modules/python/src2/cv2.cpp (depending on the version, I use OpenCV 2.4) there are some functions called pyopencv_to used by the OpenCV Python wrapper. One of those is used to convert PyObject to cv::Mat. Your "FromPython" function needs to get PyObject as input. I personally use boost::python::object to pass the numpy arrays returned by the Python OpenCV functions to the C++ function/class. You should end up having something like this in 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的函数将如下所示:

Then the function where you can access to cv::Mat will look like:

/// 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中。 soemthing like:

This function to be accessible from Python needs to be wrapped in a BOOST_PYTHON_MODULE. Soemthing like:

#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包装器。我使用Linux,所以我编译上面的代码来获得一个动态库(.so)。我不知道在Windows中有多不同。我可以从动态库访问模块:

Then in Python you can call your function from the Python module created by the Boost wrapper. I am using Linux so I compile the code above to get an dynamic library (.so). I am not sure how different it is in Windows. I can access to the module from the dynamic library as:

import myWrapper
import cv2 

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

你可以避免使用Boost,但是我没有尝试其他方法,方便包装C ++类。
注意:我没有测试这个代码,因为我从项目中删除它,但我希望它仍然可以使用。

You can probably avoid the use of Boost but I haven't tried other ways and I found Boost convenient to wrap C++ classes. NOTE: I haven't tested this code as I stripped it from a project but I hope it can still be useful.

这篇关于从Python调用C ++ opencv函数(发送cv :: Mat到C ++ dll,它使用opencv)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆