Cv::Mat:Python
lastmod 2025-02-14created 2016-06-2212 edits
How to make cv::Mat compatible with the NumPy Array used in OpenCV-Python can be found in the source code1 of OpenCV.
Example
Below is an example.
#include <iostream>
#include <string>
#include <vector>
#include <Python.h>
#define MODULESTR "cv2"
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/ndarrayobject.h>
#include <opencv2/core/core.hpp>
#include <opencv2/core/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#define ERRWRAP2(expr) \
try \
{ \
PyAllowThreads allowThreads; \
expr; \
} \
catch (const cv::Exception &e) \
{ \
PyErr_SetString(opencv_error, e.what()); \
return 0; \
}
using namespace cv;
static PyObject* opencv_error = 0;
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;
};
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
{
//std::cout << "call NumpyAllocator::allocate() 1\n";
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
{
//std::cout << "call NumpyAllocator::allocate() 2\n";
if( data != 0 ) {
std::cout << "numpy_all_2_1\n";
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;
//std::cout << "numpy_all_2_2\n";
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;
//std::cout << "numpy_all_2_3(depth:" << depth << ", cn:" << cn << ")\n";
cv::AutoBuffer<npy_intp> _sizes(dims + 1);
//std::cout << "numpy_all_2_4\n";
for( i = 0; i < dims; i++ ) {
//std::cout << "numpy_all_2_4 :: size:" << sizes[i] << std::endl;
_sizes[i] = sizes[i];
}
if( cn > 1 ) {
_sizes[dims++] = cn;
}
//std::cout << "numpy_all_2_5(dims:" << dims << ", _sizes.size():" << _sizes.size() << ", typenum:" << typenum << ")\n";
PyObject* o = PyArray_SimpleNew(dims, _sizes, typenum);
//std::cout << "numpy_all_2_6\n";
if(!o) {
CV_Error_(Error::StsError, ("The numpy array of typenum=%d, ndims=%d can not be created", typenum, dims));
}
//std::cout << "numpy_all_2_7\n";
return allocate(o, dims0, sizes, type, step);
}
bool allocate(UMatData* u, int accessFlags, UMatUsageFlags usageFlags) const
{
//std::cout << "call NumpyAllocator::allocate() 3\n";
return stdAllocator->allocate(u, accessFlags, usageFlags);
}
void deallocate(UMatData* u) const
{
//std::cout << "call NumpyAllocator::deallocate()\n";
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;
};
NumpyAllocator g_numpyAllocator;
PyObject * pyopencv_from(const cv::Mat & m)
{
//std::cout << "call pyopencv_from()\n";
if (!m.data) {
Py_RETURN_NONE;
}
//std::cout << "Py_RETURN_NONE()\n";
cv::Mat temp;
cv::Mat * p = (cv::Mat*) &m;
//std::cout << "!! 1.\n";
if (!p->u || p->allocator != &g_numpyAllocator) {
//std::cout << "!! 2.\n";
temp.allocator = &g_numpyAllocator;
//std::cout << "!! 3.\n";
// ERRWRAP2(m.copyTo(temp));
try {
//std::cout << "!! 3-1.\n";
PyAllowThreads allowThreads;
//std::cout << "!! 3-2.\n";
m.copyTo(temp);
//std::cout << "!! 3-3.\n";
} catch (const cv::Exception &e) {
//std::cout << "!! 3-4.\n";
PyErr_SetString(opencv_error, e.what());
//std::cout << "!! 3-5.\n";
return nullptr;
}
//std::cout << "!! 4.\n";
p = &temp;
}
//std::cout << "!! 5.\n";
PyObject * o = (PyObject*)p->u->userdata;
Py_INCREF(o);
return o;
}
///
/// 아래 부터 Python 코드 호출부:
///
int testDic(PyObject * pModule)
{
Mat image = imread("../data/demo/000542.jpg", CV_LOAD_IMAGE_COLOR);
if (image.cols <= 0 || image.rows <= 0) {
std::cout << "[WARNING] Image is 0x0.\n";
return -1;
}
if (!image.data) {
std::cout << "Could not open or find the image" << std::endl ;
return -1;
}
//namedWindow( "Display window", WINDOW_AUTOSIZE );
//imshow( "Display window", image );
//waitKey(0);
std::cout << "[STEP] 02 Get Attribue from Module.\n";
PyObject * pFunc = PyObject_GetAttrString(pModule, "runFasterRcnn");
std::cout << "[STEP] 03 Create attribues.\n";
PyObject * py_mat = pyopencv_from(image);
PyObject * class_number = PyInt_FromLong(100);
PyObject * pArgs = PyTuple_New(2);
PyTuple_SetItem(pArgs, 0, py_mat);
PyTuple_SetItem(pArgs, 1, class_number);
std::cout << "[STEP] 04 Call function.\n";
PyObject * pResult = PyObject_CallObject(pFunc, pArgs);
Py_ssize_t list_size = PyList_Size(pResult);
std::cout << "LIST SIZE: " << list_size << std::endl;
for (int i = 0; i < list_size; ++i) {
PyObject * item = PyList_GetItem(pResult, i);
PyObject * item_x = PyDict_GetItemString(item, "x");
PyObject * item_y = PyDict_GetItemString(item, "y");
PyObject * item_w = PyDict_GetItemString(item, "w");
PyObject * item_h = PyDict_GetItemString(item, "h");
int x = PyInt_AsLong(item_x);
int y = PyInt_AsLong(item_y);
int w = PyInt_AsLong(item_w);
int h = PyInt_AsLong(item_h);
std::cout << "x:" << x << ", "
<< "y:" << y << ", "
<< "w:" << w << ", "
<< "h:" << h << std::endl;
// Py_DECREF(item_x); // Don't use this code.
// Py_DECREF(item_y); // Don't use this code.
// Py_DECREF(item_w); // Don't use this code.
// Py_DECREF(item_h); // Don't use this code.
// Py_DECREF(item); // Don't use this code.
}
std::cout << "[STEP] 05 Release.\n";
Py_DECREF(pResult);
// Py_DECREF(class_number); // Don't use this code.
// Py_DECREF(py_mat); // Don't use this code.
Py_DECREF(pArgs);
Py_DECREF(pFunc);
}
void importNumPy()
{
import_array();
}
int python_demo(int v1, int v2)
{
Py_SetProgramName((char*)"FASTER_RCNN_DEMO");
Py_Initialize();
importNumPy(); ////////////////////////////////////// IMPORTENT!!
PyRun_SimpleString(
"import sys;\n"
"sys.path.append('.');\n"
"sys.path.append('..');");
// PySys_SetPath((char*)"..");
std::cout << "[STEP] 01 Import Module.\n";
PyObject * pModule = PyImport_ImportModule("temp"); // Python 스크립트 이름 (.py는 제거)
if (pModule == NULL) {
std::cout << "[WARNING] pModule is NULL\n";
return -1;
}
// PySys_SetPath((char*)"..");
// PySys_SetPath((char*)"/home/user/Project/americano");
// PySys_SetPath((char*)"/home/user/Project/americano/demo");
// PyObject * pModule = PyImport_ImportModule("faster");
// PyObject * pModule = loadModule("/home/user/Project/americano/demo", "faster");
// PyObject * pModule = PyImport_ImportModule("demo");
std::cout << "[STEP] 02 Get Attribue from Module.\n";
PyObject * pFunc = PyObject_GetAttrString(pModule, "runTest");
std::cout << "[STEP] 03 Create attribues.\n";
PyObject * pValue1 = PyInt_FromLong(v1);
PyObject * pValue2 = PyInt_FromLong(v2);
PyObject * pArgs = PyTuple_New(2);
PyTuple_SetItem(pArgs, 0, pValue1);
PyTuple_SetItem(pArgs, 1, pValue2);
std::cout << "[STEP] 04 Call function.\n";
// PyObject_CallObject(pFunc, nullptr); // if not parameters.
PyObject * pResult = PyObject_CallObject(pFunc, pArgs);
int result = PyInt_AsLong(pResult);
std::cout << "[STEP] 05 Release.\n";
Py_DECREF(pResult);
// Py_DECREF(pValue1); // Don't use this code.
// Py_DECREF(pValue2); // Don't use this code.
Py_DECREF(pArgs);
Py_DECREF(pFunc);
Py_DECREF(pModule);
testMat(pModule);
testDic(pModule);
Py_Finalize();
return result;
}
The calling code is as follows.
import cv2
def runTest(v1, v2):
print "HAHA"
return v1 + v2
def showMatImage(title, frame):
cv2.imshow(title, frame)
cv2.waitKey(0)
def getRectDic(x, y, w, h):
return {'x': x, 'y': y, 'w': w, 'h': h}
def runFasterRcnn(frame, class_number):
tuple_array = []
tuple_array.append(getRectDic(0, 0, 5, 5))
tuple_array.append(getRectDic(5, 5, 10, 10))
return tuple_array
Troubleshooting
Segmentation fault
When using the implementation above, accessing cv::Mat from Python may cause a segmentation fault. For reference, the author ran into this when using it together with Caffe, and the OS was Ubuntu 14.04.
In this case Boost version 1.6.0 was used, and switching to version 1.5.x solved it.
See also
Favorite site
- Stackoverflow: Call C++ opencv functions from Python ( Send a cv :: Mat to C++ dll which is using opencv )
- Cython wrapper between opencv Mat in C++ and numpy
References
-
Located at
$OPENCV_HOME/modules/python/src2/cv2.cpp. ↩
Pages that link here