Найти соседние элементы в двумерной сетке

Поэтому у меня есть двумерный массив Numpy, который выглядит примерно так:

[[1,1,1,2,2],
 [1,1,1,2,2],
 [1,2,2,2,2]]

где каждое число в массиве представляет регион. Я хочу сгенерировать логический массив, который показывает True на позициях, в которых соседние элементы НЕ все равны, т.е. на границе региона. Таким образом, результат должен выглядеть примерно так:

[[False, False, True, False],
...etc

Я знаю, что простой двойной цикл может помочь, но мне нужно что-то быстрее.

1 ответ

Решение

Вообще говоря, вы ищете фильтр обнаружения края.

Существует несколько способов справиться с этим, но основная идея заключается в том, что вы сворачиваете простой фильтр, такой как [-1, 1] или же [-1, 0, 1] с вашими данными. scipy.ndimage и scikit-image - хорошие места для этого. Имейте в виду, что существует более одного способа обработки общего случая.

Тем не менее, если вы хотите простейший случай, вы можете использовать базовые операции:

import numpy as np

x = np.array([[1,1,1,2,2],
              [1,1,1,2,2],
              [1,2,2,2,2]])

edges = np.abs(np.diff(x, axis=1)) > 0

Это дает:

array([[False, False,  True, False],
       [False, False,  True, False],
       [ True, False, False, False]], dtype=bool)

Если вы хотите, чтобы выходные данные имели ту же форму, что и входные, у вас есть несколько разных вариантов. Вы можете дополнить входной массив значениями левого или правого края:

# Pad the right-hand side with the edge values
# Use [(0, 0), (1, 0)] to pad the left instead.
xpad = np.pad(x, [(0, 0), (0, 1)], mode='edge')

edges = np.abs(np.diff(xpad, axis=1)) > 0

Что даст:

array([[False, False,  True, False, False],
       [False, False,  True, False, False],
       [ True, False, False, False, False]], dtype=bool)

В этом конкретном случае вы можете использовать np.gradient вместо заполнения массива. gradient будет использовать другой алгоритм по краям, чтобы гарантировать сохранение размера массива:

dy, dx = np.gradient(x)
edges = np.abs(dx) > 0

Который дает немного более толстую границу, так как использует немного иной алгоритм, чем простое вычитание соседних элементов:

array([[False, False,  True,  True, False],
       [False, False,  True,  True, False],
       [ True,  True, False, False, False]], dtype=bool)

Наконец, в зависимости от того, что вы подразумеваете под "двойной рамкой", вы можете попробовать более сложный фильтр границ, чем простую одностороннюю разницу. Например:

In [4]: import scipy.ndimage

In [5]: abs(scipy.ndimage.laplace(x)) > 0
Out[5]:
array([[False, False,  True,  True, False],
       [False,  True,  True,  True, False],
       [ True,  True,  True, False, False]], dtype=bool)
Другие вопросы по тегам