OpenCV Cython桥漏泄内存

mir*_*val 5 c++ python opencv cython

我编写了一个VideoCapture与Basler相机一起使用的类的实现.它是这样使用的:

import cv2
import PyBaslerCamera

video = PyBaslerCamera.PyBaslerCamera()
video.open(0)
while True:
    ret, image = video.read()
    cv2.imshow("Test", image)
    cv2.waitKey(1)
Run Code Online (Sandbox Code Playgroud)

我的Cython文件如下所示:

# 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)
Run Code Online (Sandbox Code Playgroud)

我使用了OpenCV的cv2.cpp文件:https: //github.com/Itseez/opencv/blob/master/modules/python/src2/cv2.cpp

现在,一切正常,我从相机获取视频流,但问题是它泄漏了很多(在几秒钟内它会填满我的ram,这让我相信它只是泄漏了所有帧) .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
Run Code Online (Sandbox Code Playgroud)

看起来ndarrayNumpy分配器创建的s不会被释放,但我对如何解决这个问题感到茫然.谁能告诉我如何正确释放这段记忆?

或者,如果有人有关于如何这整个接近更好的建议cv::Matnp array经营我打开思路.

Dav*_*idW 6

问题是您需要将pyopencv_fromfrom 的定义更改PyObject* pyopencv_from(const Mat&)object pyopencv_from(const Mat&):

# just illustrated in place
cdef extern from "cv2.cpp":
    void import_array()
    object pyopencv_from(const Mat&)
    # etc

# and a function that appears a bit later...
cdef object mat2np(const Mat &mat):
    # return <object> pyopencv_from(mat) # Problem line!
    # can now become:
    return pyopencv_from(mat)
Run Code Online (Sandbox Code Playgroud)

这是基于一个新闻组帖子,它引用了我认为不再存在的文档.引用到这里:

当Py_函数返回对a的新引用PyObject*,返回类型为"object".当函数返回借用的引用时,返回类型为PyObject*.当Cython将"object"视为返回类型时,它不会增加引用计数.当它看到PyObject* 为了使用结果时你必须明确地转换为<object>,当你这样做时,Cython会增加你想要它的引用计数,迫使你显式DECREF(或泄漏内存).为避免这种情况,我们制定了上述惯例.

如果你做一个明确的类型转换<object>,借用引用,[Cython]生成一个 INCREF,DECREF所以你必须小心.

所以要点是:

  • 返回的对象pyopencv_from的引用数为1.

  • 如果告诉Cython该函数返回object它会适当地重新计算它(生成的代码可能会显示GOTREF哪个是无操作,除了调试目的,DECREF稍后稍后将释放内存).

  • 如果告诉Cython该函数返回PyObject*它什么都不做,因为它只是将它视为任意指针类型(好的 - 但你必须进行引用计数)

  • 当您使用显式大小写<object>(参见上面列表中的"问题行")时,它会将引用增加1(因此它现在为2)来声明所有权,但只会减少一次.引用计数永远保持为1,并且永远不会释放对象.