代码之家  ›  专栏  ›  技术社区  ›  Carpetfizz

在Cython中返回结构数组

  •  6
  • Carpetfizz  · 技术社区  · 9 年前

    // .pyx
    
    from libc.stdint cimport uint8_t
    
    cdef extern from "<apriltag.h>":
        cdef struct apriltag_detection:
            int id
            double c[2]
            double p[4][2]
    
        ctypedef apriltag_detection apriltag_detection_t
    
    cdef extern from "tag36h11_detector/tag36h11_detector.h":
        apriltag_detection_t* scan_frame(int width, int height, uint8_t* data);
    
    cdef class Detection:
        # how do I "link" this to the struct defined above?
        def __cinit__(self):
            pass
        def __dealloc__(self):
            pass
    
    def detect(width, height, frame):
        return scan_frame(width, height, frame)
    

    detect 函数,并获取 Detection 对象,其中 侦查 是C结构的包装类 apriltag_detection 类型定义为 apriltag_detection_t

    我收到以下编译错误:

    tag36h11_检测器。pyx:22:21:无法将“apriltag\u detection\u t*”转换为Python对象

    // .h
    typedef struct detection_payload {
        int size;
        apriltag_detection_t** detections;
    } detection_payload_t;
    

    我试图将上述结构转换为一个Python对象,该对象包含 size 还有一个Python列表 物体。

    // .pyx
    
    cdef extern from "<apriltag.h>":
        cdef struct apriltag_detection:
            int id
            double c[2]
            double p[4][2]
    
        ctypedef apriltag_detection apriltag_detection_t
    
    cdef extern from "tag36h11_detector/tag36h11_detector.h":
        cdef struct detection_payload:
            int size
            apriltag_detection_t** detections
        ctypedef detection_payload detection_payload_t
        detection_payload* scan_frame(int width, int height, uint8_t* data)
    
    ...
    
    
    cdef class Detection:
        cdef apriltag_detection* _d
    
        def __cinit__(self):
            self._d = NULL
        cdef _setup(self, apriltag_detection* d):
            self._d = d
        def __dealloc__(self):
            self._d = NULL
        property id:
            def __get__(self):
                return self._d.id
        property c:
            def __get__(self):
                return self._d.c
        property p:
            def __get__(self):
                return self._d.p
    
    cdef Detection_create(apriltag_detection_t* d):
        return Detection()._setup(d)
    
    cdef class DetectionPayload:
        cdef detection_payload* _p
    
        def __cinit__(self):
            self._p = NULL
        cdef _setup(self, detection_payload* p):
            self._p = p
            self.size = p.size
            self.detections = []
            for i in range(0, self.size):
                apriltag_detection_t* detection = self._p.detections[i]
                d = Detection_create(detection)
                self.detections+=[d]
        def __dealloc__(self):
            _p = NULL
        property size:
            def __get__(self):
                return self.size
        property detections:
            def __get__(self):
                return self.detections
    

    apriltag_detection_t* detection = self._p.detections[I]
    

    apriltag_detection_t*

    更新2

    现在编译和导入都很好。阵列方面仍然没有进展

    from libc.stdint cimport uint8_t
    
    cdef extern from "<apriltag.h>":
        cdef struct apriltag_detection:
            int id
            double c[2]
            double p[4][2]
    
        ctypedef apriltag_detection apriltag_detection_t
    
    cdef extern from "tag36h11_detector/tag36h11_detector.h":
        apriltag_detection_t* scan_frame(int width, int height, uint8_t* data);
    
    cdef class Detection:
        cdef apriltag_detection* _d
    
        def __cinit__(self):
            self._d = NULL
        cdef _setup(self, apriltag_detection* d):
            self._d = d
        def __dealloc__(self):
            self._d = NULL
        property id:
            def __get__(self):
                return self._d.id
        property c:
            def __get__(self):
                return self._d.c
        property p:
            def __get__(self):
                return self._d.p
    
    cdef Detection_create(apriltag_detection_t* d):
        return Detection()._setup(d)
    
    def detect(width, height, frame):
        cdef apriltag_detection_t* detection = scan_frame(width, height, frame)
        return Detection_create(detection)
    

    我试着跟随下面链接的帖子,这就是我到目前为止所做的。

    from libc.stdint cimport uint8_t
    
    cdef extern from "<apriltag.h>":
        cdef struct apriltag_detection:
            int id
            double c[2]
            double p[4][2]
    
        ctypedef apriltag_detection apriltag_detection_t
    
    cdef extern from "tag36h11_detector/tag36h11_detector.h":
        apriltag_detection_t* scan_frame(int width, int height, uint8_t* data);
    
    cdef class Detection:
        cdef apriltag_detection* _d;
        def __cinit__(self):
            self._d = NULL
        def _setup(self, apriltag_detection* d):
            self._d = d
        def __dealloc__(self):
            self._d = NULL
        property id:
            def __get__(self):
                return self._t.id
        property c:
            def __get__(self):
                return self._t.c
        property p:
            def __get__(self):
                return self._t.p
    
    cdef Detection_create(apriltag_detection_t* d):
        return Detection()._setup(d)
    
    def detect(width, height, frame):
        return <Detection>scan_frame(width, height, frame)
    

    tag36h11_检测器。pyx:33:30:无法将“apriltag\u detection\u t*”转换为Python对象

    在线上

    cdef Detection_create(apriltag_detection_t* d):
        return Detection()._setup(d)
    

    此外,我不知道如何返回Python列表。。。

    1 回复  |  直到 9 年前
        1
  •  4
  •   ead    9 年前

    你似乎已经解决了你的问题,但我还是想回答这个问题。第一个原因是我想尝试一下,第二个原因是我认为你在内存管理方面有一些问题,我想指出一下。

    我们将包装以下简单的C接口:

    //creator.h
    typedef struct {
       int mult;
       int add;
    } Result;
    
    typedef struct {
       int size;
       Result *arr;
    } ResultArray;
    
    ResultArray create(int size, int *input){
       //whole file at the end of the answer
    }
    

    我们的包装pyx文件如下所示:

    #result_import.pyx (verion 0)
    cdef extern from "creator.h":
         ctypedef struct Result:
              int mult
              int add
         ctypedef struct ResultArray:
              int size
              Result *arr
         ResultArray create(int size, int *input)
    
    def create_structs(int[::1] input_vals):
        pass
    

    int[::1] )传递我的输入数组,这有两个优点:

    1. numpy , array 由于Python3),使用numpy数组更灵活
    2. 确保(通过 [::1]

    在测试脚本中,我使用numpy,但也可以使用内置数组:

    #test.py
    import result_import
    import numpy as np
    
    a=np.array([1,2,3],dtype='int32')
    result=result_import.create_structs(a)
    for i,el in enumerate(result):
        print  i, ": mult:", el.mult, " add:", el.add
    

    现在一切都还没有开始,但一切都准备好了。

    第一种情况:

    #result_import.pyx (verion 1)
    #from cpython cimport array needed for array.array in Python2
    from libc.stdlib cimport free
    ....
    class PyResult:
        def __init__(self, mult, add):
           self.mult=mult
           self.add=add
    
    
    def create_structs(int[::1] input_vals):
        cdef ResultArray res=create(len(input_vals), &input_vals[0])
        try:
            lst=[]
            for i in range(res.size):
                lst.append(PyResult(res.arr[i].mult, res.arr[i].add))
        finally:
            free(res.arr)
        return lst  
    

    我将整个数据转换为python对象,并使用一个简单的列表。非常简单,但有两件事值得注意:

    1. 内存管理:我负责释放res.arr中分配的内存 try...finally
    2. 仅将此指针设置为是不够的 NULL ,我必须打电话 free -功能。

    现在我们的 test.py

    第二种情况: 然而,如果我只需要其中的一些元素并将它们全部转换,那就是效率低下。此外,我在内存中保留了所有元素两次(至少一段时间)-这是天真方法的缺点。所以我想创造 PyResult -在程序中稍后的某个地方按需访问对象。

    让我们写一个包装器列表:

    #result_import.pyx (verion 2)
    ...
    cdef class WrappingList:
        cdef int size
        cdef Result *arr
    
        def __cinit__(self):
            self.size=0
            self.arr=NULL
    
        def __dealloc__(self):
            free(self.arr)
            print "deallocated"#just a check
    
        def __getitem__(self, index):
            if index<0 or index>=self.size:
                raise IndexError("list index out of range")
            return PyResult(self.arr[index].mult, self.arr[index].add)
    
    
    def create_structs(int[::1] input_vals):
        cdef ResultArray res=create(len(input_vals), &input_vals[0])
        lst=WrappingList()
        lst.size, lst.arr=res.size, res.arr
        return lst 
    

    所以这个班 WrappingList 其行为非常类似于列表,保留整个C数组而不复制并创建 PyResult公司 -对象仅在需要时。值得一提的是:

    1. __dealloc__ 当 WrapperingList -对象被销毁-这是我们释放C代码提供给我们的内存的地方。在…的结尾 测验py公司 我们应该看到“解除分配”,否则就会出问题。。。
    2. __getitem__

    第三种情况: Python代码不仅应该读取结果,还应该对其进行更改,因此更改后的数据可以传递回C代码。为此,让我们 PyResult公司 代理人:

    #result_import.pyx (verion 3, last)
    ...
    cdef class PyResult:
        cdef Result *ptr #ptr to my element
        def __init__(self):
           self.ptr=NULL
    
        @property
        def mult(self):
            return self.ptr.mult
    
        @mult.setter
        def mult(self, value):
            self.ptr.mult = value
    
        @property
        def add(self):
            return self.ptr.add
    
        @add.setter
        def add(self, value):
            self.ptr.add = value
    
    
    cdef class WrappingList:  
        ...
        def __getitem__(self, index):
            if index>=self.size:
                raise IndexError("list index out of range")
            res=PyResult()
            res.ptr=&self.arr[index]
            return res
    

    -对象持有一个指向相应元素的指针,可以在C数组中直接对其进行更改。然而,我要提到的是,存在一些陷阱:

    1. PyResult公司 不应活得比父母长- 包装英语 PyResult公司 -类。
    2. 访问元素( add mult )成本相当高,因为每次都必须创建、注册然后删除一个新的Python对象。

    #test.py(second version)
    import result_import
    import numpy as np
    
    a=np.array([1,2,3],dtype='int32')
    result=result_import.create_structs(a)
    for i,el in enumerate(result):
        print  i, ": mult:", el.mult, " add:", el.add
        el.mult, el.add=42*i,21*i
    
    # now print changed values:
    for i,el in enumerate(result):
        print  i, ": mult:", el.mult, " add:", el.add
    


    附件:

    马虎的 creator.h -需要检查 malloc

    //creator.h
    typedef struct {
       int mult;
       int add;
    } Result;
    
    typedef struct {
       int size;
       Result *arr;
    } ResultArray;
    
    ResultArray create(int size, int *input){
       ResultArray res;
       res.size=size;
       res.arr=(Result *)malloc(size*sizeof(Result));//todo: check !=0
       for(int i=0;i<size;i++){
           res.arr[i].mult=2*input[i];
           res.arr[i].add=2+input[i]; 
       }
       return res;
    }
    

    设置。py:

    from distutils.core import setup, Extension
    from Cython.Build import cythonize
    
    setup(ext_modules=cythonize(Extension(
                name='result_import',
                sources = ["result_import.pyx"]
        )))