Субрегионы логического 2d массива - PullRequest
7 голосов
/ 10 января 2020

Скажем, у меня есть двумерный логический массив. Я хотел бы получить список фрагментов / или аналогичных, где каждый фрагмент представляет наименьший (по размеру) субрегион массива, содержащий значения True, в то время как его граница содержит все значения False.

Я мог бы l oop для каждая строка и столбец и хранить индексы, когда такое условие выполнено, но мне интересно, если вы знаете другой способ или библиотека, которая делает это эффективно? Можно предположить, что граница исходного логического массива всегда равна False / 0.

Пример 1

enter image description here

Пример 2

enter image description here

Редактировать! Добавлены новые примеры с правильными решениями. Извините за путаницу.

Ответы [ 2 ]

4 голосов
/ 10 января 2020

Это анализ связанных компонентов, о котором спрашивали и отвечали раньше . Адаптируя принятый оттуда ответ к вашим потребностям, возможное решение довольно короткое:

import numpy as np
from scipy.ndimage.measurements import label


def analysis(array):
    labeled, _ = label(array, np.ones((3, 3), dtype=np.int))
    for i in np.arange(1, np.max(labeled)+1):
        pixels = np.array(np.where(labeled == i))
        x1 = np.min(pixels[1, :])
        x2 = np.max(pixels[1, :])
        y1 = np.min(pixels[0, :])
        y2 = np.max(pixels[0, :])
        print(str(i) + ' | slice: array[' + str(y1) + ':' + str(y2) + ', ' + str(x1) + ':' + str(x2) + ']')


example1 = np.array([
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
    [0, 0, 0, 1, 0, 1, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 1, 1, 0],
    [0, 0, 0, 0, 0, 0, 1, 1, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]).astype(bool)

example2 = np.array([
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 1, 0, 1, 0, 0],
    [0, 0, 0, 1, 0, 0, 1, 0, 1, 0],
    [0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
    [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 1, 0, 0, 1, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 1, 1, 0, 0, 1, 0],
    [0, 0, 0, 0, 1, 0, 1, 0, 0, 0],
    [0, 0, 0, 1, 0, 0, 0, 0, 1, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]).astype(bool)

for a in [example1, example2]:
    print(a, '\n')
    analysis(a)
    print('\n')

Вот результат (без примеров):

[[...]] 

1 | slice: array[1:2, 3:5]
2 | slice: array[4:6, 6:8]
3 | slice: array[8:8, 2:2]

[[...]] 

1 | slice: array[1:3, 5:8]
2 | slice: array[2:2, 3:3]
3 | slice: array[4:5, 1:1]
4 | slice: array[5:8, 3:6]
5 | slice: array[6:6, 8:8]
6 | slice: array[8:8, 8:8]

Надеюсь, что поможет!

------------------
System information
------------------
Python:  3.8.1
SciPy:   1.4.1
------------------
1 голос
/ 10 января 2020

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

from itertools import combinations

def find_squares(a):
    # Find ones
    ones = [(i, j) for i, row in enumerate(a) for j, val in enumerate(row) if val]
    # Make graph of connected ones
    graph = {a: [] for a in ones}
    for a, b in combinations(ones, 2):
        if abs(a[0] - b[0]) <= 1 and abs(a[1] - b[1]) <= 1:
            graph[a].append(b)
            graph[b].append(a)
    # Find connected components in graph
    components = []
    for a, a_neigh in graph.items():
        if any(a in c for c in components):
            continue
        component = {a, *a_neigh}
        pending = [*a_neigh]
        visited = {a}
        while pending:
            b = pending.pop()
            if b in visited:
                continue
            visited.add(b)
            component.add(b)
            b_neigh = graph[b]
            component.update(b_neigh)
            pending.extend(c for c in b_neigh if c not in visited)
        components.append(component)
    # Find bounds for each component
    bounds = [((min(a[0] for a in c), min(a[1] for a in c)),
               (max(a[0] for a in c), max(a[1] for a in c)))
              for c in components]
    return bounds

a = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 0, 1, 0, 1, 0, 0],
     [0, 0, 0, 1, 0, 0, 1, 0, 1, 0],
     [0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
     [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
     [0, 1, 0, 0, 1, 0, 0, 0, 0, 0],
     [0, 0, 0, 0, 1, 1, 0, 0, 1, 0],
     [0, 0, 0, 0, 1, 0, 1, 0, 0, 0],
     [0, 0, 0, 1, 0, 0, 0, 0, 1, 0],
     [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
square_bounds = find_squares(a)
print(*square_bounds, sep='\n')
# ((1, 5), (3, 8))
# ((2, 3), (2, 3))
# ((4, 1), (5, 1))
# ((5, 3), (8, 6))
# ((6, 8), (6, 8))
# ((8, 8), (8, 8))
...