Определить соседние регионы в массиве numpy

Я ищу следующее. У меня есть массив numpy, который помечен как регионы. Массив numpy представляет собой сегментированное изображение. Регион — это ряд соседних ячеек с одинаковым значением. Каждый регион имеет свою уникальную ценность. Упрощенная версия с 3 регионами будет выглядеть так:

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

выход:

array([[1, 1, 1],
       [1, 1, 2],
       [2, 2, 2],
       [3, 3, 3]])

В приведенном выше примере у нас есть 3 отдельных региона, каждый из которых помечен уникальным значением (в данном случае 1,2,3).

Я хочу значение соседних (соседних) регионов для каждого отдельного региона. Итак, в этом случае:

  • Район 1 примыкает к региону 2
  • Регион 2 примыкает к регионам 1 и 3.
  • Район 3 примыкает к региону 2

Каким будет самый элегантный и быстрый способ добиться этого?

Большое спасибо!


person cf2    schedule 28.06.2016    source источник
comment
Не могли бы вы объяснить, что вы подразумеваете под регионами? Вы имеете в виду столбцы?   -  person mkarts    schedule 28.06.2016
comment
Я добавил некоторые дополнительные пояснения по поводу определения регионов в этом случае.   -  person cf2    schedule 28.06.2016


Ответы (2)


Я понимаю, что задача состоит в том, чтобы вернуть все отдельные элементы массива, которые находятся рядом с заданным числом (например, 2). Один из способов добиться этого с помощью методов NumPy — использовать roll, чтобы сдвинуть заданную область на одну единицу вверх, вниз, влево и вправо. Выполняется логическое ИЛИ сдвинутых областей, и возвращаются все отдельные элементы, соответствующие этому условию. Дальше остается удалить сам регион, так как он не считается своим соседом.

Поскольку roll повторно вводит значения, которые выходят за границы массива на противоположных концах (что здесь нежелательно), дополнительным шагом является замена этой строки или столбца на False.

import numpy as np

x = np.array([[1, 1, 1], [1, 1, 2], [2, 2, 2], [3, 3, 3]], np.int32)
region = 2   # number of region whose neighbors we want

y = x == region  # convert to Boolean

rolled = np.roll(y, 1, axis=0)          # shift down
rolled[0, :] = False             
z = np.logical_or(y, rolled)

rolled = np.roll(y, -1, axis=0)         # shift up 
rolled[-1, :] = False
z = np.logical_or(z, rolled)

rolled = np.roll(y, 1, axis=1)          # shift right
rolled[:, 0] = False
z = np.logical_or(z, rolled)

rolled = np.roll(y, -1, axis=1)         # shift left
rolled[:, -1] = False
z = np.logical_or(z, rolled)

neighbors = set(np.unique(np.extract(z, x))) - set([region])
print(neighbors)
person Community    schedule 28.06.2016
comment
Это работает отлично. Я также протестировал его на большом наборе данных, где регионы пронумерованы случайным образом, и там он также работает как надо. Большое спасибо за ваше решение! - person cf2; 29.06.2016

Если регионы помечены небольшими целыми числами (в идеале от 0 до n), метки можно использовать для индексации массива результатов:

n = x.max()
tmp = np.zeros((n+1, n+1), bool)

# check the vertical adjacency
a, b = x[:-1, :], x[1:, :]
tmp[a[a!=b], b[a!=b]] = True

# check the horizontal adjacency
a, b = x[:, :-1], x[:, 1:]
tmp[a[a!=b], b[a!=b]] = True

# register adjacency in both directions (up, down) and (left,right)
result = (tmp | tmp.T)

Для примера массива в вопросе:

In [58]: result.astype(int)
Out[58]: 
array([[0, 0, 0, 0],
       [0, 0, 1, 0],
       [0, 1, 0, 1],
       [0, 0, 1, 0]])

In [60]: np.column_stack(np.nonzero(result))
Out[60]: 
array([[1, 2],
       [2, 1],
       [2, 3],
       [3, 2]])

In [361]: # Assuming labels start from `1`
          [np.flatnonzero(row) for row in result[1:]]
Out[361]: [array([2]), array([1, 3]), array([2])]
person Community    schedule 28.06.2016
comment
Мне очень нравится ваше решение, так как оно возвращает индексированный массив результатов для всех регионов. Однако мои регионы пронумерованы случайным образом, и поэтому ваше решение, к сожалению, не работает с моим набором данных. Спасибо за усилия, хотя! Я сохраню это решение, когда у меня будет набор данных с упорядоченными областями. - person cf2; 29.06.2016