python opencv 类型错误:输出数组布局与 cv::Mat 不兼容

25 投票
7 回答
66387 浏览
提问于 2025-04-18 07:24

我在这里使用了选择性搜索:http://koen.me/research/selectivesearch/。这个方法可以给出可能的感兴趣区域,也就是物体可能出现的地方。我想对这些区域进行处理,只保留一些,然后去掉重复的边框,最终得到一组整洁的边框。为了去掉不需要的或重复的边框区域,我使用了opencv中的grouprectangles函数来进行筛选。

一旦我从上面链接中的“选择性搜索算法”中获取到有趣的区域,我会把结果保存到一个.mat文件中,然后在Python程序中读取这些结果,像这样:

 import scipy.io as sio
 inboxes = sio.loadmat('C:\\PATH_TO_MATFILE.mat')
 candidates = np.array(inboxes['boxes'])
 # candidates is 4 x N array with each row describing a bounding box like this: 
 # [rowBegin colBegin rowEnd colEnd]
 # Now I will process the candidates and retain only those regions that are interesting
 found = [] # This is the list in which I will retain what's interesting
 for win in candidates: 
     # doing some processing here, and if some condition is met, then retain it:
     found.append(win)

# Now I want to store only the interesting regions, stored in 'found', 
# and prune unnecessary bounding boxes

boxes = cv2.groupRectangles(found, 1, 2) # But I get an error here

出现的错误是:

    boxes = cv2.groupRectangles(found, 1, 2)
TypeError: Layout of the output array rectList is incompatible with cv::Mat (step[ndims-1] != elemsize or step[1] != elemsize*nchannels)

这是怎么回事呢?我在另一段代码中做了非常相似的事情,但没有出现错误。那段没有错误的代码是:

inboxes = sio.loadmat('C:\\PATH_TO_MY_FILE\\boxes.mat')
boxes = np.array(inboxes['boxes'])
pruned_boxes = cv2.groupRectangles(boxes.tolist(), 100, 300)

我能看到的唯一不同之处是,boxes是一个numpy数组,我把它转换成了列表。但在我出问题的代码中,found已经是一个列表了。

7 个回答

5

OpenCV在处理数据类型为np.int64的numpy数组时似乎会遇到一些问题。这个数据类型是像np.arraynp.full这些方法默认返回的类型。

>>> canvas = np.full((256, 256, 3), 255)
>>> canvas
array([[255, 255, 255],
       [255, 255, 255],
       [255, 255, 255]])
>>> canvas.dtype
dtype('int64')
>>> cv2.rectangle(canvas, (0, 0), (2, 2), (0, 0, 0))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Layout of the output array img is incompatible with cv::Mat (step[ndims-1] != elemsize or step[1] != elemsize*nchannels)

解决这个问题的方法是先把数组转换成np.int32类型:

>>> cv2.rectangle(canvas.astype(np.int32), (0, 0), (2, 2), (0, 0, 0))
array([[  0,   0,   0],
       [  0, 255,   0],
       [  0,   0,   0]], dtype=int32)
8

这里有很多提出的解决方案,但问题的根本原因在于数组的内存布局。由于某种原因(编辑:见下面的评论),OpenCV要求输入数据必须是C语言的顺序(行优先),而不是F语言的顺序(列优先)。想了解更多细节,可以查看这里

这里所有提出的解决方案实际上都是在把数组转换成C语言的顺序:

  • 使用 array.copy(),因为它的默认参数是 order='C'
  • 先转换成列表再转换回NumPy数组,因为C语言的顺序是默认的
  • array.astype() 可能会这样做(这似乎取决于原始布局和数据类型)
  • np.ascontiguousarray() 会转换成C语言的顺序。

这里有一个小例子,可以重现这个问题,这里用的是 cv2.line

import cv2
import numpy as np

img = np.zeros((200, 200, 3), dtype=np.uint8)

# Breaks
img = np.require(img, requirements=["F_CONTIGUOUS"])

# img = np.require(img, requirements=["C_CONTIGUOUS"])  # Fixes it
# img = img.copy()  # Also fixes it

cv2.line(img, (10, 10), (100, 100), (255,0,0), 5)
61

另一个原因可能是数组不连续。如果把它变成连续的,也能解决这个问题。

image = np.ascontiguousarray(image, dtype=np.uint8)

86

我自己的解决办法就是请求一份原始数组的副本……(上天和Gary Bradski知道为什么……)

im = dbimg[i]
bb = boxes[i]  
m = im.transpose((1, 2, 0)).astype(np.uint8).copy() 
pt1 = (bb[0],bb[1])
pt2 = (bb[0]+bb[2],bb[1]+bb[3])  
cv2.rectangle(m,pt1,pt2,(0,255,0),2)  
7

解决办法是先把 found 转换成一个 numpy 数组,然后再把它转换回一个列表:

found = np.array(found)
boxes = cv2.groupRectangles(found.tolist(), 1, 2)

撰写回答