Python OpenCV Cython桥内存泄漏

Python OpenCV Cython桥内存泄漏,python,c++,opencv,cython,Python,C++,Opencv,Cython,我已经编写了一个视频捕获类的实现,该类可以与Basler摄像机一起工作。它是这样使用的: import cv2 import PyBaslerCamera video = PyBaslerCamera.PyBaslerCamera() video.open(0) while True: ret, image = video.read() cv2.imshow("Test", image) cv2.waitKey(1) # distutils: language = c

我已经编写了一个
视频捕获
类的实现,该类可以与Basler摄像机一起工作。它是这样使用的:

import cv2
import PyBaslerCamera

video = PyBaslerCamera.PyBaslerCamera()
video.open(0)
while True:
    ret, image = video.read()
    cv2.imshow("Test", image)
    cv2.waitKey(1)
# distutils: language = c++
# distutils: sources = BaslerCamera.cpp

from cython.operator cimport dereference as deref
from cpython.ref cimport PyObject
from libcpp cimport bool

cdef extern from "opencv2/core/core.hpp" namespace "cv":    
    cdef cppclass Mat:
        bool empty() const
        void release() const

    cdef cppclass _OutputArray:
        Mat getMat(int idx=-1) const



cdef extern from "cv2.cpp":
    void import_array()
    PyObject* pyopencv_from(const Mat&)
    int pyopencv_to(PyObject*, Mat&)

cdef Mat np2mat(object array):
    cdef Mat mat
    cdef PyObject* pyobject = <PyObject*> array
    pyopencv_to(pyobject, mat)
    return <Mat>mat

cdef object mat2np(const Mat &mat):
    return <object> pyopencv_from(mat)

cdef extern from "BaslerCamera.h" namespace "cv":
    cdef cppclass BaslerCamera:
        BaslerCamera()
        bool open(int index)
        bool isOpened()
        void release()
        bool grab()
        Mat retrieve()
        bool read(_OutputArray image)
        Mat read()
        bool set(int propId, double value)
        double get(int propId)
        BaslerCamera &operator>>(Mat &image)

cdef class PyBaslerCamera:
    cdef BaslerCamera *thisptr
    cdef Mat mat

    def __cinit__(self):
        print("PyBaslerCamera init")
        import_array()
        self.thisptr = new BaslerCamera()

    def __dealloc__(self):
        del self.thisptr

    def open(self, int index = 0):
        self.thisptr.open(index)

    def read(self):
        mat = self.thisptr.read()

        if mat.empty():
            return (False, None)
        else:
            out = mat2np(mat)
            return (True, out)
我的Cython文件如下所示:

import cv2
import PyBaslerCamera

video = PyBaslerCamera.PyBaslerCamera()
video.open(0)
while True:
    ret, image = video.read()
    cv2.imshow("Test", image)
    cv2.waitKey(1)
# distutils: language = c++
# distutils: sources = BaslerCamera.cpp

from cython.operator cimport dereference as deref
from cpython.ref cimport PyObject
from libcpp cimport bool

cdef extern from "opencv2/core/core.hpp" namespace "cv":    
    cdef cppclass Mat:
        bool empty() const
        void release() const

    cdef cppclass _OutputArray:
        Mat getMat(int idx=-1) const



cdef extern from "cv2.cpp":
    void import_array()
    PyObject* pyopencv_from(const Mat&)
    int pyopencv_to(PyObject*, Mat&)

cdef Mat np2mat(object array):
    cdef Mat mat
    cdef PyObject* pyobject = <PyObject*> array
    pyopencv_to(pyobject, mat)
    return <Mat>mat

cdef object mat2np(const Mat &mat):
    return <object> pyopencv_from(mat)

cdef extern from "BaslerCamera.h" namespace "cv":
    cdef cppclass BaslerCamera:
        BaslerCamera()
        bool open(int index)
        bool isOpened()
        void release()
        bool grab()
        Mat retrieve()
        bool read(_OutputArray image)
        Mat read()
        bool set(int propId, double value)
        double get(int propId)
        BaslerCamera &operator>>(Mat &image)

cdef class PyBaslerCamera:
    cdef BaslerCamera *thisptr
    cdef Mat mat

    def __cinit__(self):
        print("PyBaslerCamera init")
        import_array()
        self.thisptr = new BaslerCamera()

    def __dealloc__(self):
        del self.thisptr

    def open(self, int index = 0):
        self.thisptr.open(index)

    def read(self):
        mat = self.thisptr.read()

        if mat.empty():
            return (False, None)
        else:
            out = mat2np(mat)
            return (True, out)
<代码> diditul:语言= C++ #distutils:sources=BaslerCamera.cpp 从cython.operator cimport将解引用作为解引用 从cpython.ref cimport PyObject 来自libcpp cimport bool 来自“opencv2/core/core.hpp”命名空间“cv”的cdef外部: cdef CPP类材料: bool empty()常量 无效释放()常量 cdef CPP类输出阵列: Mat getMat(int idx=-1)常量 来自“cv2.cpp”的cdef外部: 无效导入_数组() PyObject*pyopencv_from(const Mat&) int pyopencv_to(PyObject*,Mat&) cdef Mat np2mat(对象阵列): cdef垫 cdef PyObject*PyObject=数组 pyopencv_to(pyobject,mat) 回程垫 cdef对象mat2np(常数Mat和Mat): 从(mat)返回pyopencv_ “BaslerCamera.h”命名空间“cv”中的cdef外部: cdef CPP类BaslerCamera: 巴斯勒照相机() 布尔开放(整数索引) bool isOpened() 无效释放() bool-grab() Mat检索() 布尔读取(输出阵列图像) Mat read() 布尔集合(int-propId,双值) 双get(int-propId) BaslerCamera&operator>>(材料和图像) cdef类PyBaslerCamera: cdef BaslerCamera*此PTR cdef垫 定义(自我): 打印(“PyBaslerCamera初始化”) 导入数组() self.thisptr=新BaslerCamera() def uu dealoc uu(自我): del self.thisptr def打开(自身,int索引=0): self.thisptr.open(索引) def读取(自): mat=self.thisptr.read() 如果mat.empty(): 返回(假,无) 其他: out=mat2np(mat) 返回(True,out) 我使用了OpenCV中的cv2.cpp文件:

现在,一切都正常了,我正在从摄像机获取视频流,但问题是它泄漏了很多(在几秒钟内它会填满我的内存,这让我相信它只是泄漏了所有的帧)。Valgrind似乎证实了这一点

==21435== 1,050,624,000 bytes in 152 blocks are possibly lost in loss record 5,939 of 5,939
==21435==    at 0x4C2BBA0: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==21435==    by 0x20D7F3AB: ??? (in /usr/lib/python3/dist-packages/numpy/core/multiarray.cpython-34m-x86_64-linux-gnu.so)
==21435==    by 0x20D1BD89: ??? (in /usr/lib/python3/dist-packages/numpy/core/multiarray.cpython-34m-x86_64-linux-gnu.so)
==21435==    by 0x251D55E1: NumpyAllocator::allocate(int, int const*, int, void*, unsigned long*, int, cv::UMatUsageFlags) const (cv2.cpp:156)
==21435==    by 0xB983720: cv::Mat::create(int, int const*, int) (in /usr/local/lib/libopencv_core.so.3.0.0)
==21435==    by 0xB9B54C7: cv::_OutputArray::create(int, int, int, int, bool, int) const (in /usr/local/lib/libopencv_core.so.3.0.0)
==21435==    by 0xB810A7C: cv::Mat::copyTo(cv::_OutputArray const&) const (in /usr/local/lib/libopencv_core.so.3.0.0)
==21435==    by 0x251D44F9: pyopencv_from<cv::Mat> (cv2.cpp:211)
==21435==    by 0x251D44F9: __pyx_f_14PyBaslerCamera_mat2np (PyBaslerCamera.cpp:662)
==21435==    by 0x251D44F9: __pyx_pf_14PyBaslerCamera_14PyBaslerCamera_6read(__pyx_obj_14PyBaslerCamera_PyBaslerCamera*) [clone .isra.9] (PyBaslerCamera.cpp:973)
==21435==    by 0x503F5C: PyEval_EvalFrameEx (in /usr/bin/python3.4)
==21435==    by 0x5A9CB4: PyEval_EvalCodeEx (in /usr/bin/python3.4)
==21435==    by 0x5E7104: ??? (in /usr/bin/python3.4)
==21435==    by 0x5E71C8: PyRun_FileExFlags (in /usr/bin/python3.4)
==21435== 
==21435== LEAK SUMMARY:
==21435==    definitely lost: 165,107 bytes in 262 blocks
==21435==    indirectly lost: 179,724,840 bytes in 205 blocks
==21435==      possibly lost: 1,057,720,529 bytes in 646 blocks
==21435==    still reachable: 9,399,307 bytes in 10,288 blocks
==21435==         suppressed: 0 bytes in 0 blocks
==21435== Reachable blocks (those to which a pointer was found) are not shown.
==21435== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==21435==1050624000字节(152个块)可能在5939的丢失记录5939中丢失
==21435==0x4C2BBA0:malloc(在/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so中)
==21435==0x20D7F3AB:???(在/usr/lib/python3/dist-packages/numpy/core/multiarray.cpython-34m-x86_64-linux-gnu.so中)
==21435==0x20D1BD89:???(在/usr/lib/python3/dist-packages/numpy/core/multiarray.cpython-34m-x86_64-linux-gnu.so中)
==21435==by 0x251D55E1:NumpyAllocator::allocate(int,int const*,int,void*,unsigned long*,int,cv::UMatUsageFlags)const(cv2.cpp:156)
==21435==by 0xB983720:cv::Mat::create(int,int const*,int)(在/usr/local/lib/libopencv_core.so.3.0.0中)
==21435==by 0xB9B54C7:cv:_OutputArray::create(int,int,int,int,bool,int)const(在/usr/local/lib/libopencv_core.so.3.0.0中)
==21435==by 0xB810A7C:cv::Mat::copyTo(cv:_outputarrayconst&)const(in/usr/local/lib/libopencv_core.so.3.0.0)
==21435==0x251D44F9:pyopencv_from(cv2.cpp:211)
==21435==0x251D44F9:\uuuuPyx\uF\u14PybaslerCamera\uMat2NP(PyBaslerCamera.cpp:662)
==21435==0x251D44F9:[clone.isra.9](PyBaslerCamera.cpp:973)
==21435==0x503F5C:PyEval_EvalFrameEx(在/usr/bin/python3.4中)
==21435==0x5A9CB4:PyEval_evalcodex(在/usr/bin/python3.4中)
==21435==0x5E7104:???(in/usr/bin/python3.4)
==21435==by 0x5E71C8:PyRun_FileExFlags(在/usr/bin/python3.4中)
==21435== 
==21435==泄漏汇总:
==21435==肯定丢失:262个块中有165107个字节
==21435==间接丢失:205个块中179724840字节
==21435==可能丢失:646个块中的1057720529字节
==21435==仍然可访问:10288块中的9399307字节
==21435==抑制:0个块中有0个字节
==21435==未显示可访问块(找到指针的块)。
==21435==若要查看它们,请使用--leak check=full--show leak kinds=all重新运行
看起来Numpy分配器创建的
ndarray
s没有被释放,但我不知道如何解决这个问题。有人能告诉我如何正确释放这个内存吗


或者,如果有人对如何处理整个
cv::Mat
np array
业务有更好的建议,我愿意接受想法。

问题是,您需要将
pyopencv\u的定义从
PyObject*pyopencv\u从
const-Mat&
更改为
对象pyopencv\u从(const-Mat&)

#刚刚演示到位
来自“cv2.cpp”的cdef外部:
无效导入_数组()
对象pyopencv_from(const Mat&)
#等
#还有一个稍后出现的函数。。。
cdef对象mat2np(常数Mat和Mat):
#从(mat)问题行返回pyopencv#u!
#现在可以成为:
从(mat)返回pyopencv_
这是基于我认为不再存在的参考文献。引述如下:

当返回Py_uu函数时 对
PyObject*
的新引用,返回类型为“object”。 当函数返回借用的引用时,返回 类型为
PyObject*
。当Cython将“object”视为返回类型时 它不会增加引用计数。当它看到
PyObject*
为了使用结果,必须显式强制转换为
, 当你这样做时,Cython会增加引用计数 无论您是否希望,都会强制您执行显式的
DECREF
(或泄漏内存)。 为了避免这种情况,我们制定了上述惯例

借 引用如果您对
执行显式类型转换,[Cython]将生成
INCREF
DECREF
因此您必须小心

因此,要点是:

pyopencv_返回的对象