将Cython内存视图传递给OpenCV函数

4
我正在使用cython memoryviews来引用一些灰度图像。我已经成功地在我编写的一些图像处理代码中使用了它。现在,我需要使用一些OpenCV函数。不幸的是,我发现我不能将memoryviews作为图像参数传递给OpenCV函数。代码可以编译,但当它运行时,它在调用OpenCV函数时停止,并显示“TypeError: is not a numpy array”。
我可以使用np.asarray(my_memoryview)将memoryview转换回numpy数组。这个方法可以工作,但它会复制数据并且速度很慢。
在memoryview文档中,他们谈到了强制转换为numpyhttp://docs.cython.org/src/userguide/memoryviews.html#coercion-to-numpy,看起来我应该能够将memoryview强制转换为numpy数组而不复制内存。然而,如果我写:
im = np.asarray(<np.uint8_t[:, :]> my_memoryview)

这会导致编译错误:"只能从指针或数组创建cython.array"

如何将memoryview传递给OpenCV函数,或者如何强制将memoryview转换为numpy数组而不复制数据,任何帮助都将不胜感激!


你从哪里获取到原始的memoryview? - r3m0t
我从一个numpy数组(即openCV图像)中获取原始的memoryview,方法如下: cdef np.uint8_t[:, ::1] im2 = im 然后我对im2进行操作,基本上是提取一个子窗口(memoryview),然后将其传递给cv2.matchTemplate。目前我正在使用np.asarray(subwindow),因为我无法直接传递memoryview。 我认为我可能误解了转换为numpy文档示例中的强制转换,在该示例中,他们将指针转换为memoryview,然后使用np.asarray将其转换为numpy数组,这与我所做的相同。但是,我不确定它是强制转换还是复制。 - martinako
1个回答

5
Numpy/OpenCV不支持memoryviews,但支持旧版前身。您可以创建一个包装器类:
from cython.view cimport memoryview

cdef extern from "Python.h":
    object PyLong_FromVoidPtr(void *p)

cdef class OpenCVMemoryView:
    cdef object arr
    cdef object underlying_object
    def __init__(OpenCVMemoryView self, np.uint8_t[:, :] my_memoryview):
        self.underlying_object = my_memoryview # prevents GC of my_memoryview
        cdef memoryview my_memoryview_c = my_memoryview
        self.arr = dict(version=3,
            typestr='<u1', #typestr=np.uint8,
            data=(PyLong_FromVoidPtr(<void*>my_memoryview_c.view.buf), False),
            strides=my_memoryview.strides,
            shape=my_memoryview.shape)
    def __array_interface__(self):
        return self.arr

Cython memoryview object有属性,返回的元组与__array_interface__所需的元组类似。

如果这样做没有更快,那么我推断你的解决方案已经不会复制数据了。


谢谢你的回答。我想尝试你建议的方法,但是当我编译你的代码时,我遇到了一个错误,我无法通过,错误在data=PyLong_FromVoidPtr(<void*>my_memoryview.view.buf)中。我得到了这个错误:memoryview_opencv.pyx:13:51: Casting temporary Python object to non-numeric non-Python type你知道如何解决这个问题吗? - martinako
嗨,martinako,使用中间变量my_memoryview_c(具有不同的Cython类型),那部分可以工作。我稍微编辑了一下我的答案,但在我让它工作之前,我的电脑崩溃了。如果你得到了“expected a readable buffer interface”的错误,你可能需要使用被注释掉的typestr - r3m0t
嗨,现在它已经构建成功了,但是当我将OpenCVMemoryView传递给cv2.matchTemplate时,我遇到了运行时错误:TypeError:<unknown>不是numpy数组。我尝试了注释的typestr,但没有任何区别。 - martinako
现在我相信np.asarray没有复制数据。 我使用np.asarray将memoryview转换为ndarray,然后修改了原始的memoryview,修改后的结果显示在转换后的ndarray中。@r3m0t非常感谢您的帮助,我仍然想测试这个OpenCVMemoryView类,也许这个memoryview的包装比使用asarray创建的更快?另一方面,我的原始问题是错误的,因为我认为np.asarray会复制数据,这是提出问题的动机。我想我需要编辑问题并解释这不是情况。 - martinako

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