Reputation: 363073
I'm using opencv v2.2 to do some template matching on ndarrays, and I had great trouble with memory leaks when using their wrapped method cv.fromarray()
. Rather than plug the memory leaks I avoided the fromarray()
function and used cv.SetData
directly, like this:
assert foo_numpy.dtype == 'uint8'
assert foo_numpy.ndim == 3
h, w = foo_numpy.shape[:2]
foo_cv = cv.CreateMat(h, w, cv.CV_8UC3)
cv.SetData(foo_cv, foo_numpy.data, foo_numpy.strides[0])
This seems to solve the memory leaks and foo_cv
seems to be deallocated properly when it goes out of scope. However, now I have the issue where if foo_numpy
is just a slice/view on a bigger array, I'm not permitted foo_numpy.data
(cannot get single-segment buffer for discontiguous array). At the moment I'm working around this by making foo_numpy.copy()
if foo_numpy.base != None
, which permits getting the buffer on the new copy. But I have the feeling this is unnecessary, the slice has the __array_struct__
and __array_interface__
so I should be able to just stride it with the appropriate stepsizes somehow? I'm not sure how to do it in a nice way, because the base of this one can also be a view on another larger array ad infinitum.
Upvotes: 4
Views: 4205
Reputation: 3958
I think the problem with what you were trying to do is that the array data you're interested in (ie. foo_np_view
) is actually only stored in one place i.e. foo_np.data
, and the OpenCV SetData
method doesn't provide any way to specify stride settings that would allow you to skip the bytes that are not part of foo_np_view
.
You can, however, get around this problem using Numpy’s tostring()
method, which turns an array (or views therein) into a byte string:
>>> import numpy as np
>>> import cv
>>> foo_np = np.array( 255 * np.random.rand( 200 , 300 , 3 ), dtype = 'uint8' )
>>> foo_np_view = foo_np [ 50:150:2 , 10:290:5 , : ]
>>> h,w,d = foo_np_view.shape
>>> foo_cv = cv.CreateMat( h , w , cv.CV_8UC3 )
Recreating the original problem:
>>> cv.SetData( foo_cv , foo_np_view.data, foo_np_view.strides[0] )
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: cannot get single-segment buffer for discontiguous array
Using the tostring()
method (see below for explanation of the stride setting):
>>> cv.SetData( foo_cv , foo_np_view.tostring() , w * d * foo_np_view.dtype.itemsize )
>>> np.array_equal( np.asarray( foo_cv ) , foo_np_view )
True
The value w * d * foo_np_view.dtype.itemsize
gives us a stride value identical to that of foo_np_view.copy()
, which is necessary as the string representations of the view and its copy are identical:
>>> foo_np_view.copy().tostring() == foo_np_view.tostring()
True
>>> foo_np_view.copy().strides[0] == w * d * foo_np_view.dtype.itemsize
True
Upvotes: 2