【问题标题】:OpenCV Cython bridge leaking memoryOpenCV Cython 桥泄漏内存
【发布时间】:2015-12-05 12:31:36
【问题描述】:

我编写了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)

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

我使用了 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

看起来由 Numpy 分配器创建的 ndarrays 没有被释放,但我不知道如何解决这个问题。谁能告诉我如何正确释放这段内存?

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

【问题讨论】:

  • 我认为(但我不是 100% 确定)您想将 PyObject* pyopencv_from(const Mat&amp;) 更改为 object pyopencv_from(const Mat&amp;)。我相信pyopencv_from 将返回引用计数为 1 的内容,并且强制转换为对象会将其增加到 2,但只会释放一个。见groups.google.com/forum/#!topic/cython-users/pZFurj3rUyg
  • 确实如此!请张贴作为回复,我会接受。

标签: python c++ opencv cython


【解决方案1】:

问题是您需要将pyopencv_from 的定义从PyObject* pyopencv_from(const Mat&amp;) 更改为object pyopencv_from(const Mat&amp;)

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

这是基于a newsgroup post,它引用了我认为不再存在的文档。在此引用:

每当 Py_ 函数返回时 对PyObject*新引用,返回类型为“对象”。 当函数返回借用的引用时,返回 类型是PyObject*。当 Cython 将“对象”视为返回类型时 它不会增加引用计数。当它看到PyObject* 为了使用结果,您必须显式转换为&lt;object&gt;, 当你这样做时,Cython 会增加引用计数 无论您是否愿意,都会强迫您使用明确的DECREF(或泄漏内存)。 为了避免这种情况,我们制定了上述约定。

借来的 如果您对 &lt;object&gt; 进行显式类型转换,则引用,[Cython] 会生成一个 INCREFDECREF 所以你要小心。

所以要点是:

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

  • 1234563指出内存已被释放)。
  • 如果您告诉 Cython 函数返回 PyObject*,它什么也不做,因为它只是将其视为任意指针类型(好的,但您必须进行引用计数)

  • 当您对&lt;object&gt; 执行显式案例时(请参见上面清单中的“问题行”),它会将引用增加 1(因此现在是 2)以声明所有权,但只会减少一次。引用计数永远保持为 1,并且永远不会释放对象。

【讨论】:

  • 感谢详尽的回答!但是,直接从pyopencv_from() 返回对我不起作用(Cannot convert 'PyObject *' to Python object),我必须手动转换为&lt;object&gt;,然后手动执行Py_DECREF()
  • 啊——我觉得你需要做两处修改:直接return,还要修改cdef extern from "cv2.cpp":块中pyopencv_from()的定义(不用担心和header不匹配确切地!)。但是,手动操作 Py_DECREF() 也可以,所以如果它是这样工作的,那就这样吧。
猜你喜欢
  • 1970-01-01
  • 2018-03-03
  • 2013-08-27
  • 1970-01-01
  • 2020-08-15
  • 1970-01-01
  • 1970-01-01
  • 2018-05-20
  • 1970-01-01
相关资源
最近更新 更多