2017-01-28 11 views
5

Cython을 사용하여 Python에 일부 C++ 클래스 및 함수를 래핑하려고합니다. 지금까지 2 개의 클래스를 래핑했습니다. 이제는 함수를 감싸고 싶습니다.C++ 함수에서 PyObject를 포함하는 복잡한 객체 반환 Cython

기능의 서명이

std::map<std::string, std::vector<PyObject*>> analyze(PyObject* img, LandmarkDetector::CLNF& clnf_model, LandmarkDetector::FaceModelParameters& params);

이다 나는 성공적으로 CLNFFaceModelParameters 클래스를 포장했다, 나는 문제가이 analyze 기능을 포장하는 데 문제.

이 함수는 opencv를 다루기 때문에 PyObject*을 다룹니다. 언어간에 쉽게 전달할 수 있기를 바랍니다. 에서 python 개체 사이, 그리고 python Mat에서 cv::Mat 사이의 캐스팅을 수행하기 위해 these functions을 사용하고 있습니다.

내 PYX 파일입니다

from libcpp.vector cimport vector 
from libcpp.map cimport map 
from libcpp.string cimport string 
from cpython.ref cimport PyObject 
from cython.operator cimport dereference as deref 

cdef extern from "LandmarkDetectorModel.h" namespace "LandmarkDetector": 
    cdef cppclass CLNF: 
     CLNF(string) except + 

cdef extern from "LandmarkDetectorParameters.h" namespace "LandmarkDetector": 
    cdef cppclass FaceModelParameters: 
     FaceModelParameters(vector[string] &) except + 

cdef class PyCLNF: 
    cdef CLNF *thisptr 
    def __cinit__(self, arg): 
     self.thisptr = new CLNF(<string> arg) 

cdef class PyLandmarkDetectorParameters: 
    cdef FaceModelParameters *thisptr 
    def __cinit__(self, args): 
     self.thisptr = new FaceModelParameters(args) 

cdef extern from "FaceLandmarkVid.h": 
    map[string, vector[object]] analyze(object, CLNF&, FaceModelParameters&) 

cdef PyAnalyze(object img, PyCLNF clnf, PyLandmarkDetectorParameters facemodel): 
    return analyze(img, deref(clnf.thisptr), deref(facemodel.thisptr)) 

그러나 컴파일하려고에 나는 오류 메시지가 얻을 불행하게도

답변

2

을 수행 할 수 있습니다 (라인 map[string, vector[object]] analyze [...]을 의미)

landmarks.pyx:26:23: Python object type 'Python object' cannot be used as a template argument 

Cython의 자동 std::map ->dictstd::vector ->list 변환을 사용하지 마십시오. 이러한 변환을 쓰는 데있어 근본적인 문제는 Cython이 참조 카운팅을 통해 C++이 수행하는 작업을 알지 못한다는 것입니다. 따라서이 작업을 안정적으로 수행하기 위해 노력할 것입니다.

대신 벡터를 PyObject*으로 템플릿을 만들고 고유 한 변환 함수를 작성해야합니다. 이 PyObject*하여 해당 템플릿이 사이 썬를 혼동하는 것 같다 약간의 합병증이다하지만 그건 타입 정의와 함께 라운드를 작업 할 수 있습니다 다음 PyObject*<object>에 캐스팅,

# unchanged [...] 
from cpython.ref cimport PyObject, Py_DECREF 
from cython.operator cimport dereference as deref, preincrement 
# unchanged [...] 

ctypedef PyObject* PyObjectPtr # I run into a bug templaing vector otherwise 

cdef extern from "FaceLandmarkVid.h": 
    map[string, vector[PyObjectPtr]] analyze(object, CLNF&, FaceModelParameters&) 

# an extra function to convert the vector to a list 
cdef convertVector(vector[PyObjectPtr]& v): 
    cdef vector[PyObjectPtr].iterator i = v.begin() 
    cdef vector[PyObjectPtr].iterator end = v.end() 

    cdef list out = [] 

    while i != end: 
     out.append(<object>deref(i)) 
     # reduce reference count to account for destruction of the 
     # vector at the end of the conversion process 
     Py_DECREF(<object>deref(i)) 
     preincrement(i) 

    return out 

cdef PyAnalyze(object img, PyCLNF clnf, PyLandmarkDetectorParameters facemodel): 
    cdef map[string, vector[PyObjectPtr]] res = analyze(img, deref(clnf.thisptr), deref(facemodel.thisptr)) 
    cdef map[string, vector[PyObjectPtr]].iterator i = res.begin() 
    cdef map[string, vector[PyObjectPtr]].iterator end = res.end() 

    cdef dict out = {}  

    while i!=end: 

     out[deref(i).first] = convertVector(deref(i).second) 
     preincrement(i) 
    return out 

은 기본적으로 우리가지도를 반복하고, 그 안에 벡터 반복 . 나는 여기서 참조 카운팅에 대한 가정을했다. 나는 C++ 코드가 벡터에서 PyObject의 레퍼런스 카운트를 결코 감소시키지 않는다고 가정하고있다. 그래서 벡터의 소멸을 설명하기 위해 그것을 줄여야한다. 두 번째 가정은 PyObject* 중 하나도 NULL이 아니라는 것입니다.

(이 테스트는 Cython으로 컴파일되었지만 C++로 컴파일되거나 올바르게 실행되는지 테스트 할 방법이 없습니다.


편집 : 나는 내가 지금 수정해야 참조 횟수에 약간의 실수를했던 것을 깨달았다.