2017-01-28 41 views
5

Próbuję zawinąć niektóre klasy i funkcje C++ do Pythona za pomocą Cythona. Do tej pory zawinąłem 2 klasy, a teraz chcę zawinąć funkcję.Zwracanie złożonego obiektu zawierającego PyObject z funkcji C++ Cython

Podpis funkcja jest

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

Mam powodzeniem owinięty klas CLNF i FaceModelParameters, i mam problemy z zawijania ten analyze funkcję.

Funkcja zajmuje się PyObject* s, ponieważ zajmuje się opencv i chciałbym móc je łatwo przekazywać między językami. Używam these functions w celu wykonania rzutowania między cv::Point na obiekty Pythona i między pytonem Mat a numerem cv::Mat.

To jest mój plik 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)) 

Ale po próbują go skompilować otrzymuję komunikat o błędzie

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

(który odnosi się do linii map[string, vector[object]] analyze [...])

Odpowiedz

2

Niestety można Używaj tutaj automatycznych konwersji Cythona std::map->i ->. Myślę, że podstawową kwestią związaną z pisaniem takiej konwersji jest to, że Cython nie wie, co robi C++ z liczeniem odwołań, więc starałoby się to naprawić w sposób niezawodny.

Zamiast tego należy szablonować wektor jako PyObject* i pisać własne funkcje konwersji. Jest niewielka komplikacja, która szablonów z PyObject* wydaje się mylić Cython ale że można pracować rundę z 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 

Zasadniczo mamy iteracyjne nad mapą i iteracyjne nad wektorami w nim, odlewanie PyObject* do <object> . Założyłem tutaj założenie o liczeniu odwołań - zakładam, że twój kod C++ nigdy nie zmniejsza liczby odwołań w wektorach o wartości PyObject (a więc musisz samemu go zmniejszyć, aby uwzględnić zniszczenie wektora). Drugie założenie jest takie, że żaden z PyObject* nie ma wartości NULL.

(Jest testowany pod kątem kompilacji w Cython, ale nie mam możliwości sprawdzenia, czy kompiluje się w C++ lub działa poprawnie).


Edit: zdałem sobie sprawę, że będę się niewielki błąd w liczeniu odniesienia, który powinien być skorygowany.