Question

I am asking here because I haven't gotten any help from the OpenCV developers so far. I reduced the problem to a very simple test case so probably anyone with some background with CPython could help here.

This C code does not leak:

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);
    }
}

This Python code does leak:

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

I searched through the CPython code (of OpenCVs current SVN trunk code) and found this:

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;
}

And from the OpenCV C headers:

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;

I don't exactly understand everything because I never worked with the C-interface to Python before. But probably the bug I am searching for is somewhere in this code.

Am I right? Or where should I search for the bug? How would I fix it?

(Note for people who have seen an earlier version of this question: I looked at the wrong code. Their SWIG interface was deprecated and not used anymore (but the code was still there in SVN, this is why I confused it. So don't look into interfaces/swig, this code is old and not used. The current code lives in modules/python.)


Upstream bug report: memleak in OpenCV Python CreateHist

Was it helpful?

Solution

It has been fixed.

Changed 3 weeks ago by jamesb

  • status changed from accepted to closed
  • resolution set to fixed

Fixed in r4526

The ranges parameters were not being freed, and the iterator over ranges was not being DECREF'ed. Regressions now pass, and original loop does not leak.

OTHER TIPS

I think you have garbage collection issue, in that you never leave the loop.

Does this work more as expected?

while True: 
    cv.CreateHist([40], cv.CV_HIST_ARRAY, [[0,255]], 1)
    cv = None
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top