2017-01-28 1 views
5

J'essaie d'intégrer certaines classes et fonctions C++ à Python en utilisant Cython. Jusqu'à présent, j'ai enveloppé 2 classes, et maintenant je veux envelopper une fonction.Retour d'un objet complexe contenant PyObject à partir de la fonction C++ Cython

La signature de la fonction est

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

J'ai enveloppé avec succès les cours CLNF et FaceModelParameters, et je ne parviens pas à envelopper cette fonction analyze.

La fonction traite PyObject* s car elle traite avec opencv, et j'aimerais pouvoir les passer facilement entre les langues. J'utilise these functions afin d'effectuer le casting entre cv::Point aux objets python et entre python Mat à cv::Mat.

Ceci est mon fichier ciboire:

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

Mais en essayant de le compiler je reçois le message d'erreur

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

(qui fait référence à la ligne map[string, vector[object]] analyze [...])

Répondre

2

Malheureusement vous pouvez N'utilisez pas les conversions std::map ->dict et std::vector ->list de Cython ici. Je pense que le problème fondamental de l'écriture d'une telle conversion est que Cython ne sait pas ce que C++ fait avec le comptage des références, donc il aurait du mal à le faire correctement.

A la place, vous devez modéliser le vecteur comme PyObject* et écrire vos propres fonctions de conversion. Il y a une légère complication qui templating avec PyObject* semble confondre Cython mais qui peut être travaillé autour d'un 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 

Essentiellement, nous itérer sur la carte, et itérer sur les vecteurs en son sein, la coulée du PyObject* à <object> . J'ai fait une hypothèse sur le comptage des références ici - je suppose que votre code C++ ne diminue jamais le nombre de référence des PyObject s dans les vecteurs (et donc vous devez le diminuer vous-même pour tenir compte de la destruction du vecteur). Une deuxième hypothèse est qu'aucun des PyObject* sont NULL.

(Ceci est testé pour compiler en Cython mais je n'ai aucun moyen de tester qu'il compile en C++ ou s'exécute correctement).


Edit: je réalisais que j'avais fait une petite erreur dans le comptage de référence qui devrait maintenant être corrigée.