Извлечь группы ненулевых значений в массив numpy - PullRequest
0 голосов
/ 14 июля 2020

Я пытаюсь извлечь группы rect angular ненулевых значений из массива numpy. Массив может выглядеть так (но намного больше):

a = np.array([
     [0,0,0,0,0,0,0,0,0,0,0],
     [0,0,0,0,0,1,1,1,1,1,0],
     [0,0,0,0,6,1,1,1,3,1,0],
     [0,0,0,0,0,1,1,1,1,1,0],
     [0,0,0,0,2,2,2,0,1,0,0],
     [0,0,0,0,2,2,0,0,0,0,0],
     [0,0,0,0,0,0,0,0,0,0,0],
     [1,1,1,1,0,0,0,0,0,0,0],
     [1,1,1,1,0,0,0,0,7,2,0],
     [1,1,1,1,0,0,0,0,0,0,0]])

, и я хочу извлечь группы / блоки ненулевых значений больше заданного размера (например, больше 3x3), то есть координаты минимальный и максимальный углы этих блоков. В этом примере я должен получить следующее:

res = [[(7,0), (10,4)],
       [(1,5), (4,10)]]

, чтобы

In [12]: xmin, ymin = res[0][0]

In [13]: xmax, ymax = res[0][1]

In [14]: a[xmin:xmax, ymin:ymax]
Out[14]:
array([[1, 1, 1, 1],
       [1, 1, 1, 1],
       [1, 1, 1, 1]])

In [15]: xmin, ymin = res[1][0]

In [16]: xmax, ymax = res[1][1]

In [17]: a[xmin:xmax, ymin:ymax]
Out[17]:
array([[1, 1, 1, 1, 1],
       [1, 1, 1, 3, 1],
       [1, 1, 1, 1, 1]])

Я пробовал смотреть на каждое ненулевое значение массива и увеличивать форму требуемый размер с этого момента, пока он не будет содержать ноль. Это работает, но довольно медленно. Для этого примера массива это занимает около 1,17 мс, а в реальных приложениях (например, массивы 600x1000) это занимает около 18 секунд, что слишком медленно. Есть ли функция или трюк numpy или OpenCV, чтобы сделать это быстрее?

Ответы [ 2 ]

1 голос
/ 14 июля 2020

Я думаю, что есть очень простое решение этой проблемы, используя морфологические преобразования . opening (erosion, за которым следует dilation) просто сокращает области меньше, чем ваш желаемый размер (3x3), а затем восстанавливает оставшиеся. Вот вид a после преобразования в uint8:

f1: a

Now I'll apply opening on it:

out = cv2.morphologyEx(a, cv2.MORPH_OPEN, np.ones((3,3), dtype=np.uint8))

Visualizing out:

f2: out

As you can see, it took just a single line of code to identify the rectangular regions. You can use this output as a bitmask to filter out the original image as well.

a_ = a.copy()
a_[np.logical_not(out.astype('bool'))] = 0

Now a bit more challenging part would be if you need to figure out the corner coordinates of the rectangles. You could break out the big guns and apply contour detection, but I feel as though a simpler подключенных компонентов анализ также должен работать.

from skimage.measure import label
out_ = label(out, connectivity=1)

f3: out_

Теперь каждый регион в массиве out_ отмечен отдельным числом от 0 до N_regions-1 (где 0 - фоновая область). В остальном работа очень проста. Вы можете перебрать каждое число и выполнить простое сравнение numpy, чтобы определить координаты каждой пронумерованной области.

Мы можем выполнить работу еще быстрее, воспользовавшись преимуществами regionprops скимажа. Мы применим его к out_, изображению метки, которое мы вычислили ранее.

from skimage.measure import regionprops

for r in regionprops(out_):
  print('({},{}), ({},{})'.format(*r.bbox))

Out:

(1,5), (4,10)
(7,0), (10,4)
1 голос
/ 14 июля 2020

Похоже, ваша Проблема - типичная проблема компьютерного зрения. Вы ищете области, которые не являются фоном и имеют определенную c форму (прямоугольник) и размер (мин. 3x3).

Для такого рода задач мы используем анализ blob .

Я не хочу писать конкретный пример c, потому что в него включено намного больше функций, которые также могут быть интересны для вашей работы. Есть много примеров для анализа больших двоичных объектов. Вот один из них, который может быть хорошей отправной точкой: https://www.learnopencv.com/blob-detection-using-opencv-python-c/

Краткое расширение моей информации: Пример веб-сайта основан на более старой версии opencv. Следующий код - это реализация в более новых версиях. На данный момент conda предоставляет новую версию OpenCV 3.4.2:

# Standard imports
import cv2
import numpy as np;

# Read image
im = cv2.imread("blob.png", cv2.IMREAD_GRAYSCALE)

# Set up the detector with default parameters.
detector = cv2.SimpleBlobDetector_create()

# Detect blobs.
keypoints = detector.detect(im)

# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the circle corresponds to the size of blob
im_with_keypoints = cv2.drawKeypoints(im, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

# Show keypoints
cv2.imshow("Keypoints", im_with_keypoints)
cv2.waitKey(0)

Важным изменением является создание детектора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...