Returning a complex object containing PyObject from C ++ - a Cython function

I am trying to port some C ++ classes and functions to Python using Cython. So far I have wrapped 2 classes, and now I want to wrap a function.

Function Signature

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

I successfully wrapped the CLNF and FaceModelParameters , and I was unable to wrap this analyze function.

The function deals with PyObject* because it deals with opencv, and I would like to be able to easily transfer them between languages. I use these functions to cast between cv::Point python objects and between the python pressure gauge to cv::Mat .

This is my pyx file:

 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)) 

But when I try to compile it, I get an error

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

(which refers to the line map[string, vector[object]] analyze [...] )

+5
source share
1 answer

Unfortunately, you cannot use the Cython automatic std::mapdict and std::vectorlist conversions here. I think that the fundamental problem with writing such a conversion is that Cython does not know what C ++ does with reference counting, so it would be difficult to get this right reliably.

Instead, you need to PyObject* vector as PyObject* and write your own conversion functions. There's a slight complication that templating with PyObject* seems to confuse Cython, but this can be worked out with a typedef:

 # 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 

Essentially, we iterate over the map and PyObject* over the vectors, discarding PyObject* to <object> . I made the assumption of link counting here - I assume that your C ++ code never decreases the PyObject reference PyObject in vectors (and therefore you need to decrease it yourself to allow for the destruction of the vector). The second assumption is that none from PyObject* not NULL.

(This is checked for compilation in Cython, but I have no way to verify that it compiles in C ++ or works correctly).


Edit: I realized that I made a small mistake in link counting, which now needs to be fixed.

+2
source

Source: https://habr.com/ru/post/1263496/


All Articles