如何将一个numpy数组视图转换为opencv矩阵?

4

我正在使用OpenCV v2.2在ndarrays上进行一些模板匹配,当使用他们的包装方法cv.fromarray()时,我遇到了严重的内存泄漏问题。为了避免这些内存泄漏,我避免使用fromarray()函数,而是直接使用cv.SetData,如下所示:

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

这似乎解决了内存泄漏问题,在foo_cv退出作用域时,似乎已正确地释放。然而,现在我有一个问题,如果foo_numpy只是一个更大数组的切片/视图,则我不被允许使用foo_numpy.data (无法获取非连续数组的单一段缓冲区)。目前我通过在foo_numpy.base != None时制作foo_numpy.copy()来解决这个问题,这样可以在新副本上获取缓冲区。但我觉得这是不必要的,因为这个切片具有__array_struct____array_interface__,所以我应该能够用适当的步长轻松地跨越它?我不确定如何以好的方式做到这一点,因为这个数组的基础也可以是另一个更大的数组的视图,进一步无限延伸。
1个回答

2
我认为你尝试做的事情存在问题,因为你感兴趣的数组数据(即foo_np_view)实际上只存储在一个地方,即foo_np.data,而OpenCV的SetData方法没有提供任何方式来指定步幅设置,以便跳过不属于foo_np_view的字节。
但是,你可以通过Numpy的tostring()方法解决这个问题,该方法将数组(或其中的视图)转换为字节字符串:
>>> 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 )

重新制造原始问题:

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

使用tostring()方法(有关步幅设置的说明请参见下文):
>>> 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

w * d * foo_np_view.dtype.itemsize 给我们一个步幅值,与 foo_np_view.copy() 相同,这是必要的,因为视图和它的副本的字符串表示形式是相同的:

>>> foo_np_view.copy().tostring() == foo_np_view.tostring()
True
>>> foo_np_view.copy().strides[0] == w * d * foo_np_view.dtype.itemsize
True

谢谢,那似乎可以工作,但我不认为我能比当前的解决方案(复制数组)获得更好的性能,因为tostring()方法似乎已经返回了一个副本。如果我错了,请纠正我。 - wim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接