Commit 60046875 authored by James Bowman's avatar James Bowman

#759. memtrack comments, sealed the numpy MatND leak

parent afd42eed
...@@ -14,8 +14,12 @@ static PyObject *opencv_error; ...@@ -14,8 +14,12 @@ static PyObject *opencv_error;
struct memtrack_t { struct memtrack_t {
PyObject_HEAD PyObject_HEAD
int owner;
void *ptr; void *ptr;
int freeptr;
Py_ssize_t size; Py_ssize_t size;
PyObject *backing;
CvArr *backingmat;
}; };
struct iplimage_t { struct iplimage_t {
...@@ -694,6 +698,7 @@ static void cvmatnd_dealloc(PyObject *self) ...@@ -694,6 +698,7 @@ static void cvmatnd_dealloc(PyObject *self)
{ {
cvmatnd_t *pc = (cvmatnd_t*)self; cvmatnd_t *pc = (cvmatnd_t*)self;
Py_DECREF(pc->data); Py_DECREF(pc->data);
cvFree(&pc->a);
PyObject_Del(self); PyObject_Del(self);
} }
...@@ -923,11 +928,29 @@ static void cvlineiterator_specials(void) ...@@ -923,11 +928,29 @@ static void cvlineiterator_specials(void)
/* memtrack */ /* memtrack */
/* Motivation for memtrack is when the storage for a Mat is an array or buffer
object. By setting 'data' to be a memtrack, can deallocate the storage at
object destruction.
For array objects, 'backing' is the actual storage object. memtrack holds the reference,
then DECREF's it at dealloc.
For MatND's, we need to cvDecRefData() on release, and this is what field 'backingmat' is for.
If freeptr is true, then a straight cvFree() of ptr happens.
*/
static void memtrack_dealloc(PyObject *self) static void memtrack_dealloc(PyObject *self)
{ {
memtrack_t *pi = (memtrack_t*)self; memtrack_t *pi = (memtrack_t*)self;
// printf("===> memtrack_dealloc %p!\n", pi->ptr); if (pi->backing)
cvFree(&pi->ptr); Py_DECREF(pi->backing);
if (pi->backingmat)
cvDecRefData(pi->backingmat);
if (pi->freeptr)
cvFree(&pi->ptr);
PyObject_Del(self); PyObject_Del(self);
} }
...@@ -2188,7 +2211,11 @@ static PyObject *pythonize_CvMat(cvmat_t *m) ...@@ -2188,7 +2211,11 @@ static PyObject *pythonize_CvMat(cvmat_t *m)
memtrack_t *o = PyObject_NEW(memtrack_t, &memtrack_Type); memtrack_t *o = PyObject_NEW(memtrack_t, &memtrack_Type);
size_t gap = mat->data.ptr - (uchar*)mat->refcount; size_t gap = mat->data.ptr - (uchar*)mat->refcount;
o->ptr = mat->refcount; o->ptr = mat->refcount;
o->owner = __LINE__;
o->freeptr = true;
o->size = gap + mat->rows * mat->step; o->size = gap + mat->rows * mat->step;
o->backing = NULL;
o->backingmat = NULL;
PyObject *data = PyBuffer_FromReadWriteObject((PyObject*)o, (size_t)gap, mat->rows * mat->step); PyObject *data = PyBuffer_FromReadWriteObject((PyObject*)o, (size_t)gap, mat->rows * mat->step);
if (data == NULL) if (data == NULL)
return NULL; return NULL;
...@@ -2214,11 +2241,14 @@ static PyObject *pythonize_foreign_CvMat(cvmat_t *m) ...@@ -2214,11 +2241,14 @@ static PyObject *pythonize_foreign_CvMat(cvmat_t *m)
#else #else
memtrack_t *o = PyObject_NEW(memtrack_t, &memtrack_Type); memtrack_t *o = PyObject_NEW(memtrack_t, &memtrack_Type);
o->ptr = mat->data.ptr; o->ptr = mat->data.ptr;
o->owner = __LINE__;
o->freeptr = false;
o->size = mat->rows * mat->step; o->size = mat->rows * mat->step;
o->backing = NULL;
o->backingmat = mat;
PyObject *data = PyBuffer_FromReadWriteObject((PyObject*)o, (size_t)0, mat->rows * mat->step); PyObject *data = PyBuffer_FromReadWriteObject((PyObject*)o, (size_t)0, mat->rows * mat->step);
if (data == NULL) if (data == NULL)
return NULL; return NULL;
Py_INCREF(o); // XXX - hack to prevent free of this foreign memory
#endif #endif
m->data = data; m->data = data;
m->offset = 0; m->offset = 0;
...@@ -2242,7 +2272,11 @@ static PyObject *pythonize_IplImage(iplimage_t *cva) ...@@ -2242,7 +2272,11 @@ static PyObject *pythonize_IplImage(iplimage_t *cva)
memtrack_t *o = PyObject_NEW(memtrack_t, &memtrack_Type); memtrack_t *o = PyObject_NEW(memtrack_t, &memtrack_Type);
assert(ipl->imageDataOrigin == ipl->imageData); assert(ipl->imageDataOrigin == ipl->imageData);
o->ptr = ipl->imageDataOrigin; o->ptr = ipl->imageDataOrigin;
o->owner = __LINE__;
o->freeptr = true;
o->size = ipl->height * ipl->widthStep; o->size = ipl->height * ipl->widthStep;
o->backing = NULL;
o->backingmat = NULL;
PyObject *data = PyBuffer_FromReadWriteObject((PyObject*)o, (size_t)0, o->size); PyObject *data = PyBuffer_FromReadWriteObject((PyObject*)o, (size_t)0, o->size);
if (data == NULL) if (data == NULL)
return NULL; return NULL;
...@@ -2253,13 +2287,11 @@ static PyObject *pythonize_IplImage(iplimage_t *cva) ...@@ -2253,13 +2287,11 @@ static PyObject *pythonize_IplImage(iplimage_t *cva)
return (PyObject*)cva; return (PyObject*)cva;
} }
static PyObject *pythonize_CvMatND(cvmatnd_t *m) static PyObject *pythonize_CvMatND(cvmatnd_t *m, PyObject *backing = NULL)
{ {
// //
// Need to make this CvMatND look like any other, with a Python // Need to make this CvMatND look like any other, with a Python
// string as its data. // buffer object as its data.
// So copy the image data into a Python string object, then release
// it.
// //
CvMatND *mat = m->a; CvMatND *mat = m->a;
...@@ -2268,15 +2300,20 @@ static PyObject *pythonize_CvMatND(cvmatnd_t *m) ...@@ -2268,15 +2300,20 @@ static PyObject *pythonize_CvMatND(cvmatnd_t *m)
PyObject *data = PyString_FromStringAndSize((char*)(mat->data.ptr), mat->dim[0].size * mat->dim[0].step); PyObject *data = PyString_FromStringAndSize((char*)(mat->data.ptr), mat->dim[0].size * mat->dim[0].step);
#else #else
memtrack_t *o = PyObject_NEW(memtrack_t, &memtrack_Type); memtrack_t *o = PyObject_NEW(memtrack_t, &memtrack_Type);
o->ptr = cvPtr1D(mat, 0); o->ptr = mat->data.ptr;
o->owner = __LINE__;
o->freeptr = false;
o->size = cvmatnd_size(mat); o->size = cvmatnd_size(mat);
Py_XINCREF(backing);
o->backing = backing;
o->backingmat = mat;
PyObject *data = PyBuffer_FromReadWriteObject((PyObject*)o, (size_t)0, o->size); PyObject *data = PyBuffer_FromReadWriteObject((PyObject*)o, (size_t)0, o->size);
Py_DECREF(o); // Now 'data' holds the only reference to 'o'
if (data == NULL) if (data == NULL)
return NULL; return NULL;
#endif #endif
m->data = data; m->data = data;
m->offset = 0; m->offset = 0;
// cvDecRefData(mat); // Ref count should be zero here, so this is a release
return (PyObject*)m; return (PyObject*)m;
} }
...@@ -2787,6 +2824,8 @@ static PyObject *pycvfromarray(PyObject *self, PyObject *args, PyObject *kw) ...@@ -2787,6 +2824,8 @@ static PyObject *pycvfromarray(PyObject *self, PyObject *args, PyObject *kw)
static PyObject *fromarray(PyObject *o, int allowND) static PyObject *fromarray(PyObject *o, int allowND)
{ {
PyObject *ao = PyObject_GetAttrString(o, "__array_struct__"); PyObject *ao = PyObject_GetAttrString(o, "__array_struct__");
PyObject *retval;
if ((ao == NULL) || !PyCObject_Check(ao)) { if ((ao == NULL) || !PyCObject_Check(ao)) {
PyErr_SetString(PyExc_TypeError, "object does not have array interface"); PyErr_SetString(PyExc_TypeError, "object does not have array interface");
return NULL; return NULL;
...@@ -2847,7 +2886,7 @@ static PyObject *fromarray(PyObject *o, int allowND) ...@@ -2847,7 +2886,7 @@ static PyObject *fromarray(PyObject *o, int allowND)
return (PyObject*)failmsg("cv.fromarray array can be 2D or 3D only, see allowND argument"); return (PyObject*)failmsg("cv.fromarray array can be 2D or 3D only, see allowND argument");
} }
m->a->data.ptr = (uchar*)pai->data; m->a->data.ptr = (uchar*)pai->data;
return pythonize_foreign_CvMat(m); retval = pythonize_foreign_CvMat(m);
} else { } else {
int dims[CV_MAX_DIM]; int dims[CV_MAX_DIM];
int i; int i;
...@@ -2856,8 +2895,11 @@ static PyObject *fromarray(PyObject *o, int allowND) ...@@ -2856,8 +2895,11 @@ static PyObject *fromarray(PyObject *o, int allowND)
cvmatnd_t *m = PyObject_NEW(cvmatnd_t, &cvmatnd_Type); cvmatnd_t *m = PyObject_NEW(cvmatnd_t, &cvmatnd_Type);
ERRWRAP(m->a = cvCreateMatND(pai->nd, dims, type)); ERRWRAP(m->a = cvCreateMatND(pai->nd, dims, type));
m->a->data.ptr = (uchar*)pai->data; m->a->data.ptr = (uchar*)pai->data;
return pythonize_CvMatND(m);
retval = pythonize_CvMatND(m, ao);
} }
Py_DECREF(ao);
return retval;
} }
#endif #endif
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment