Skip to content

Cv::Mat:Python

cv::MatOpenCV-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

References


  1. $OPENCV_HOME/modules/python/src2/cv2.cpp에 위치해 있다.