Cv::Mat:Python
cv::Mat
와 OpenCV-Python에서 사용하는 NumPy Array와 호환 시키는 방법은 OpenCV의 소스코드1에서 확인할 수 있다.
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;
}
호출 코드는 아래와 같다.
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
위의 구현을 사용했을 경우. Python에서 cv::Mat에 접근할 경우 세그먼테이션 오류가 발생될 수 있다. 참고로 필자의 경우 Caffe와 함께 사용했을 때 위의 현상을 격었으며, OS는 Ubuntu 14.04 였다.
이 경우 Boost 1.6.0버전을 사용했으며, 1.5.x 버전으로 변경하여 해결하였다.
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
-
$OPENCV_HOME/modules/python/src2/cv2.cpp
에 위치해 있다. ↩