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

4 投票
1 回答
4175 浏览
提问于 2025-04-16 15:15

我正在使用opencv v2.2进行一些模板匹配,遇到了使用它们的封装方法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

撰写回答