使用NumPy操作数组后,Python OpenCV绘制出现错误

8

我正在使用OpenCV读取图像,并尝试在numpy中进行一些操作(旋转90度)。使用matplotlib的imshow查看结果,似乎一切都很正常 - 图像已经旋转。然而,我不能在新图像上使用OpenCV的绘图方法。在下面的代码中(我在sagemath云工作表中运行这个代码):

%python
import cv2
import matplotlib.pyplot as plt
import numpy as np
import os, sys
image = np.array( cv2.imread('imagename.png') )
plt.imshow(image,cmap='gray')
image = np.array(np.rot90(image,3) ) # put it right side up
plt.imshow(image,cmap='gray')
cv2.rectangle(image,(0,0),(100,100),(255,0,0),2)
plt.imshow(image,cmap='gray')

我在 cv2.rectangle() 命令中遇到了以下错误:
TypeError: Layout of the output array img is incompatible with cv::Mat (step[ndims-1] != elemsize or step[1] != elemsize*nchannels)

如果我使用 np.array(np.rot90(image,4) ) 而非旋转 90 度,错误就会消失(即旋转 360 度)。因此似乎是因为尺寸的变化导致出现问题。OpenCV 是否在内部存储了尺寸,需要更新或其他操作?编辑:rot90() 后添加 image = image.copy() 解决了这个问题。请参见 rayryeng 的答案。
3个回答

10

显然这是Python OpenCV包装器中的一个bug。如果您看一下这里的问题:np.rot90() corrupts an opencv image,显然进行不会回到原始尺寸的旋转将破坏图像,并且在该帖子中的OP遇到了与您有相同的错误。顺便说一下,我也遇到了同样的错误...不知道为什么。

解决此问题的方法是在旋转后制作图像副本,然后显示该图像。我无法解释这个,但它似乎有效。此外,请确保在代码末尾调用plt.show()显示图像:

import cv2
import matplotlib.pyplot as plt
import numpy as np
import os, sys
image = np.array( cv2.imread('imagename.png') )
plt.imshow(image,cmap='gray')
image = np.array(np.rot90(image,3) ) # put it right side up

image = image.copy() # Change

plt.imshow(image,cmap='gray')
cv2.rectangle(image,(0,0),(100,100),(255,0,0),2)
plt.imshow(image,cmap='gray')

plt.show() # Show image

很有趣,cv2.imread()只返回一个ndarray。该ndarray与OpenCV无关,并且我没有看到它以任何方式被破坏。唯一有意义的是,OpenCV单独或其他方式跟踪“图像”(ndarray)的尺寸。虽然这样做就不合理了,为什么复制解决方法会起作用呢? - argentum2f
我真的不知道为什么它能工作。我正在考虑提交一个错误请求来弄清楚为什么需要这样做才能使旋转工作...而且我也没有看到任何操作之间的损坏。如果你复制一份,它就会简单地工作! - rayryeng
2
我猜这与Python如何存储数组有关。numpy.rotate()实际上并不改变数据,它只是改变了对数据的视图。如果在复制(旋转后)之前和之后执行'print image.flags',我认为会明白发生了什么。由于OpenCV全部使用C语言编写,C例程无法正确处理numpy数组的新视图。因此,基本上这是OpenCV Python包装器将信息传递给C例程时出现的错误。不确定是否真的有解决方案,但如果数据格式是“非连续”的或者其他无法工作的格式,至少可以抛出一个错误。 - argentum2f

2

我曾经遇到过使用numpy 1.11.2和opencv 3.3.0时出现的相同问题。不确定为什么,但是以下方法对我有用。在使用cv2.rectangle之前,请添加以下代码:

image1 = image1.transpose((1,0)).astype(np.uint8).copy()

Reference


这个答案与被接受的答案相同,只是您将旋转实现为转置操作后跟随一次复制。 - rayryeng

1

数据类型转换对我的问题有效。 在转换之前,图像的类型为np.int64。

image = image.astype(np.int32) # convert data type

请详细说明 - 您是在旋转之前还是之后进行此操作?如果是之后,那么这与先前的答案相同,因为强制转换会导致numpy创建一个新数组。 - argentum2f
在使用cv2.putText之前,我会这样做。cv2.putText是导致我的问题出现TypeError的那一行。'image.copy()'对我不起作用。我猜_Layout_是指numpy数组内存或数据布局,这是np.float64和float32之间的区别。 - dwSun

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