为啥 cv2.rectangle 有时会返回 np.ndarray,而有时会返回 cv2.UMat
Posted
技术标签:
【中文标题】为啥 cv2.rectangle 有时会返回 np.ndarray,而有时会返回 cv2.UMat【英文标题】:Why cv2.rectangle sometimes return np.ndarray, while sometimes cv2.UMat为什么 cv2.rectangle 有时会返回 np.ndarray,而有时会返回 cv2.UMat 【发布时间】:2019-12-26 10:14:58 【问题描述】:我目前正在对一些图像进行可视化,并发现了 opencv 的 cv2.rectangle 的这种奇怪行为:
当输入图像是 np.ndarray 时,比如 arr,cv2.rectangle() 返回 np.ndarray,arr 用矩形绘制。
当输入图像是 arr 的某种变体时,例如 arr[:, :, [2, 0, 1]],cv2.rectangle() 返回一个 cv2.UMat,并且不绘制矩形。
我现在的环境是:
Python 3.7 Opencv 4.1代码如下:
-
首先生成一张随机图片。
import numpy as np
import cv2
import copy
img = np.random.randint(0, 255, (100, 120, 3)).astype("uint8")
-
现在添加一个矩形
a = copy.deepcopy(img)
ret = cv2.rectangle(a, (0, 0), (10, 10), color=(255, 255, 255), thickness=2)
你会发现:
ret 是一个 np.ndarray ret 的可视化和显示一个矩形被绘制尝试其他方式:
b = copy.deepcopy(img)
c = b[:, :, [2, 1, 0]]
ret = cv2.rectangle(c, (0, 0), (10, 10), color=(255, 255, 255), thickness=2)
你会发现:
ret 是一个 cv2.UMat ret 或 c 的可视化显示 没有绘制矩形我真的很好奇我的代码有什么问题吗?还是背后隐藏着什么?
【问题讨论】:
有趣的行为,对我来说这听起来像是一个错误......也许你应该把它放在 OpenCV github 问题中......另外,如果你用 numpy copy 复制数据,它看起来像正常...像c = img[:, :, [2, 1, 0]].copy()
@api55 我以前见过这个。主要问题是 numpy 数组在数据在内存中的布局方式方面更加灵活。 img[:, :, [2, 1, 0]
只是为同一个缓冲区创建一个视图,并设置元数据,以便以相反的顺序解释第三个轴。不幸的是,无法为该缓冲区创建等效的 cv::Mat
标头。因此,尝试使用 Mat
等效调用函数失败。然而,在 Python 包装器中完成了重载解析,因此它尝试从缓冲区创建一个 UMat...
这(因为我们得到了一个 UMat 并且没有错误消息)似乎成功了。当我尝试result = ret.get();
并可视化结果时,我看到那里有一个白色矩形。然而,原始输入(OPs 代码中的c
)不包含矩形,这表明在创建 UMat 对象时涉及到一个副本(某种预期)。 |像您一样使用ndarray.copy()
制作数组的深层副本似乎会重新排列过程中的缓冲区,以便元数据再次“正常”(与cv::Mat
兼容)。
@DanMašek 你应该把它写成答案,这听起来很完整并且很好地解释了问题:) 我知道 ndarray 的“视图”,但我猜 OpenCV 可以转换它或至少发出警告。不过很高兴知道
@api55 是的,虽然我还不能真正解释为什么它能够把它变成UMat
...我得预留一些时间来理解这一点。
【参考方案1】:
我会努力回答这个问题,因为我经常偶然发现这个问题,并且在 cmets 中我看到了很多正确的东西!
OpenCV 只能处理连续数组,这意味着它们必须以某种方式在内存中布局。在对np.array
进行切片时,numpy
只是更改读取顺序以提高速度(而不是耗时的复制),并因此使其不连续(找到 here)。
@Das Masek 和@Eric 的陈述都是正确的。使用索引数组对np.array
进行切片会始终创建一个副本,如文档中的here 所述。但是,不幸的是,numpy
复制了数组,但不会将其更改回连续数组(在我看来,这似乎是不好的行为)。
解决方案是以下之一:
copy()
np.array
;通过显式复制,numpy
将布局更改回连续,而不是使用索引数组切片。您可以使用a.flags
等检查阵列的flags
。如果您想自动化某些东西,这显然是最昂贵的,因为您字面上每次都在复制。
对我来说更优雅的版本是使用np.ascontiguousarray()
。此函数仅在数组已经不连续时才更改其布局,并且不会copy
它。
另外说明:根据documentation,所有OpenCV 绘图函数实际上都有一个None
返回值,因为它们是inplace 函数。因此我会推荐使用它们。
【讨论】:
以上是关于为啥 cv2.rectangle 有时会返回 np.ndarray,而有时会返回 cv2.UMat的主要内容,如果未能解决你的问题,请参考以下文章
利用cv2.rectangle()绘制矩形框(python)
cv2.rectangle() 调用重载方法,虽然我给出了其他参数