OpenCV:Python接口存在内存泄漏,而C版本没有

7 投票
2 回答
1040 浏览
提问于 2025-04-16 07:03

我在这里提问是因为到现在为止还没有得到OpenCV开发者的帮助。我把问题简化成了一个非常简单的测试案例,所以可能任何对CPython有一点了解的人都能帮我。

这段C代码没有内存泄漏:

int main() {
    while(true) {
        int hist_size[] = {40};
        float range[] = {0.0f,255.0f};
        float* ranges[] = {range};
        CvHistogram* hist = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);
        cvReleaseHist(&hist);
    }
}

这段Python代码却有内存泄漏:

while True: cv.CreateHist([40], cv.CV_HIST_ARRAY, [[0,255]], 1)

我查阅了CPython的代码(OpenCV当前的SVN主干代码),发现了这些:

struct cvhistogram_t {
  PyObject_HEAD
  CvHistogram h;
  PyObject *bins;
};

...

/* cvhistogram */

static void cvhistogram_dealloc(PyObject *self)
{
  cvhistogram_t *cvh = (cvhistogram_t*)self;
  Py_DECREF(cvh->bins);
  PyObject_Del(self);
}

static PyTypeObject cvhistogram_Type = {
  PyObject_HEAD_INIT(&PyType_Type)
  0,                                      /*size*/
  MODULESTR".cvhistogram",                /*name*/
  sizeof(cvhistogram_t),                  /*basicsize*/
};

static PyObject *cvhistogram_getbins(cvhistogram_t *cvh)
{
  Py_INCREF(cvh->bins);
  return cvh->bins;
}

static PyGetSetDef cvhistogram_getseters[] = {
  {(char*)"bins", (getter)cvhistogram_getbins, (setter)NULL, (char*)"bins", NULL},
  {NULL}  /* Sentinel */
};

static void cvhistogram_specials(void)
{
  cvhistogram_Type.tp_dealloc = cvhistogram_dealloc;
  cvhistogram_Type.tp_getset = cvhistogram_getseters;
}

...

static PyObject *pycvCreateHist(PyObject *self, PyObject *args, PyObject *kw)
{
  const char *keywords[] = { "dims", "type", "ranges", "uniform", NULL };
  PyObject *dims;
  int type;
  float **ranges = NULL;
  int uniform = 1;

  if (!PyArg_ParseTupleAndKeywords(args, kw, "Oi|O&i", (char**)keywords, &dims, &type, convert_to_floatPTRPTR, (void*)&ranges, &uniform)) {
    return NULL;
  }
  cvhistogram_t *h = PyObject_NEW(cvhistogram_t, &cvhistogram_Type);
  args = Py_BuildValue("Oi", dims, CV_32FC1);
  h->bins = pycvCreateMatND(self, args);
  Py_DECREF(args);
  if (h->bins == NULL) {
    return NULL;
  }
  h->h.type = CV_HIST_MAGIC_VAL;
  if (!convert_to_CvArr(h->bins, &(h->h.bins), "bins"))
    return NULL;

  ERRWRAP(cvSetHistBinRanges(&(h->h), ranges, uniform));

  return (PyObject*)h;
}

还有OpenCV的C头文件:

typedef struct CvHistogram
{
    int     type;
    CvArr*  bins;
    float   thresh[CV_MAX_DIM][2];  /* For uniform histograms.                      */
    float** thresh2;                /* For non-uniform histograms.                  */
    CvMatND mat;                    /* Embedded matrix header for array histograms. */
}
CvHistogram;

我并不是完全理解这些内容,因为我之前从未使用过C接口与Python交互。但我觉得我正在寻找的那个bug可能就在这些代码里。

我这样想对吗?或者我应该在哪里寻找这个bug?我该如何修复它呢?

(给那些看过这个问题早期版本的人一个说明:我之前看错了代码。他们的SWIG接口已经不再使用(但代码仍然保留在SVN里,所以我搞混了。所以不要去看interfaces/swig,那段代码是旧的,不再使用。现在的代码在modules/python里。)


上游bug报告:OpenCV Python CreateHist中的内存泄漏

2 个回答

0

我觉得你可能遇到了垃圾回收的问题,因为你一直没有退出循环。

这样做是不是更符合你的预期呢?

while True: 
    cv.CreateHist([40], cv.CV_HIST_ARRAY, [[0,255]], 1)
    cv = None
2

这个问题已经解决了。

三周前由 jamesb 修改

  • 状态从“已接受”变为“已关闭”
  • 解决方案设置为“已修复”

r4526 中修复

之前的范围参数没有被释放,而且遍历范围的迭代器没有被正确处理。现在回归测试通过了,原来的循环也不会再出现内存泄漏的问题。

撰写回答