您可以查看 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++ 类。注意:我没有测试过这个代码,因为我从一个项目中剥离了它,但我希望它仍然有用。