python opencv TypeError: расположение выходного массива, несовместимого с cv::Mat

Я использую выборочный поиск здесь: http://koen.me/research/selectivesearch/ Это дает возможные области интереса, где объект может быть. Я хочу выполнить некоторую обработку и сохранить только некоторые области, а затем удалить дублирующие ограничивающие блоки, чтобы получить окончательный аккуратный набор ограничивающих прямоугольников. Чтобы удалить ненужные / дублированные области ограничивающих рамок, я использую grouprectangles функция opencv для обрезки.

Как только я получаю интересные области от Matlab из "алгоритма выборочного поиска" по ссылке выше, я сохраняю результаты в .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 был просто массив, который я затем преобразовал в список. Но в моем проблемном коде, found это уже список.

7 ответов

Решение

Решение было преобразовать found сначала в массив NumPy, а затем преобразовать его в список:

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

Мое собственное решение состояло в том, чтобы просто попросить копию оригинального массива...(бог и Гэри Брадски знает почему...)

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)  

Другая причина может заключаться в том, что массив не является смежным. Преодоление этого также решило бы проблему

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

Здесь есть много предлагаемых решений, но первопричиной является расположение памяти массива. По какой-то причине (отредактируйте: см. комментарий ниже) OpenCV требует, чтобы его ввод был в порядке C (основная строка), а не в порядке F (основной столбец), подробности см. здесь .

Все предлагаемые здесь решения неявно изменяют массив на порядок C:

  • с использованиемarray.copy()делает это, потому что есть параметр по умолчаниюorder='C'
  • преобразование в список и обратно в NumPy, поскольку порядок C является значением по умолчанию
  • array.astype() может сделать это (кажется, зависит от исходного макета и dtype)
  • np.ascontiguousarray()преобразуется в порядок С.

Вот небольшой пример, который воспроизводит проблему, с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)

Похоже, что в Opencv возникают проблемы с рисованием в массивах с типом данных np.int64, который является типом данных по умолчанию, возвращаемым такими методами, как np.array а также np.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)

Была такая же проблема при запуске

image = cv2.putText(изображение, 'текст', организация, шрифт, fontScale, цвет, толщина, cv2.LINE_AA)

Это сработало для меня

image = cv2.putText(image.astype(np.uint8).copy(), 'текст', организация, шрифт, fontScale, цвет, толщина, cv2.LINE_AA)

Просто для полноты картины кажется, что многие из нас использовали решение Этьена Перо выше, минус .copy(). Достаточно преобразования типа массива в int. Например, при использовании преобразования Хафа:

    # Define the Hough transform parameters
    rho,theta,threshold,min,max = 1, np.pi/180, 30, 40, 60  

    image = ima.astype(np.uint8) # assuming that ima is an image.

    # Run Hough on edge detected image
    lines = cv2.HoughLinesP(sob, rho, theta, threshold, np.array([]), min, max)

    # Iterate over the output "lines" and draw lines on the blank 
    line_image = np.array([[0 for col in range(x)] for row in range(y)]).astype(np.uint8)

    for line in lines: # lines are series of (x,y) coordinates
        for x1,y1,x2,y2 in line:
            cv2.line(line_image, (x1,y1), (x2,y2), (255,0,0), 10)

Только после этого данные можно было построить с помощью plt.imshow()

Другие вопросы по тегам