Найти соседние элементы в двумерной сетке
Поэтому у меня есть двумерный массив 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)