Анализ местности на карте: альтернатива функции numpy.roll? - PullRequest
0 голосов
/ 08 января 2019

Я пытаюсь проанализировать ландшафт карты, заданный StarCraft 2 bot API .

Задачей новичка для этого анализа было найти скалы для жнецов, которые являются специальными единицами в SC2, которые могут прыгать вверх и вниз по скалам.

Чтобы решить эту проблему, я анализирую точки, в которых сама точка не подлежит изменению (= обрыв), а северная и южная две точки являются изменяемыми. Пропускаемые точки помечаются как 1, а не как 0 в массиве.

Карта местности существует в виде двумерного массива. Ниже приведен небольшой отрывок из более крупного массива 200x200:

import numpy as np
example = np.array([[0, 0, 0, 0],
                   [0, 1, 1, 0],
                   [0, 0, 0, 0],
                   [0, 1, 1, 0],
                   [0, 0, 0, 0]])

Здесь точки [2, 1] и [2, 2] будут соответствовать критериям, при которых сами точки не подлежат изменению (= 0), а точки выше и ниже их можно исправлять (= 1).

Это может быть достигнуто с помощью следующего кода:

above = np.roll(example, 1, axis=0) # Shift rows downwards
below = np.roll(example, -1, axis=0) # Shift rows upwards
result = np.zeros_like(example) # Create array with zeros
result[(example == 0) & (above == 1) & (below == 1)] = 1 # Set cells to 1 that match condition
print(repr(result))
#   array([[0, 0, 0, 0],
           [0, 0, 0, 0],
           [0, 1, 1, 0],
           [0, 0, 0, 0],
           [0, 0, 0, 0]])

Теперь мой вопрос: можно ли добиться того же с помощью меньшего количества кода?

Функция np.roll каждый раз создает новый объект np.array, поэтому анализ сотен соседних точек может привести к 100 строкам ненужного кода и высокому использованию памяти.

Я пытаюсь найти что-то похожее на

result = np.zeros_like(example)
result[(example == 0) & (example[-1, 0] == 1) & (example[1, 0 == 1)] = 1
# or
result[(example == 0) & ((example[-1:2, 0].sum() == 2)] = 1

Здесь числа в скобках отображают относительное положение к анализируемой в данный момент точке, но я не знаю, есть ли способ заставить это работать с numpy.

Кроме того, результат для нулевой строки будет неясным при проверке точки «над» ней: он может получить доступ либо к последней строке, вызвать ошибку или вернуть значение по умолчанию (0 или 1).

Edit: Я нашел этот пост , и он указал мне на функцию scipy convolve2d , которую можно применить здесь, что может быть тем, что я ищу:

import numpy as np
from scipy import signal
example = np.array([[0, 0, 0, 0],
                   [0, 1, 1, 0],
                   [0, 0, 0, 0],
                   [0, 1, 1, 0],
                   [0, 0, 0, 0]])
kernel = np.zeros((3, 3), dtype=int)
kernel[::2, 1] = 1
print(repr(kernel))
# array([[0, 1, 0],
#        [0, 0, 0],
#        [0, 1, 0]])
result2 = signal.convolve2d(example, kernel, mode="same")
print(repr(result2))
# array([[0, 1, 1, 0],
#        [0, 0, 0, 0],
#        [0, 2, 2, 0],
#        [0, 0, 0, 0],
#        [0, 1, 1, 0]])
result2[result2 < 2] = 0
result2[result2 == 2] = 1
print(repr(result2))
# array([[0, 0, 0, 0],
#        [0, 0, 0, 0],
#        [0, 1, 1, 0],
#        [0, 0, 0, 0],
#        [0, 0, 0, 0]])

Edit2: Другое решение может быть scipy.ndimage.minimum_filter , которое, похоже, работает аналогично:

import numpy as np
from scipy import ndimage
example = np.array([[0, 0, 0, 0],
                   [0, 1, 1, 0],
                   [0, 0, 0, 0],
                   [0, 1, 1, 0],
                   [0, 0, 0, 0]])
kernel = np.zeros((3, 3), dtype=int)
kernel[::2, 1] = 1
print(repr(kernel))
# array([[0, 1, 0],
#        [0, 0, 0],
#        [0, 1, 0]])
result3 = ndimage.minimum_filter(example, footprint=kernel_vertical, mode="constant")
print(repr(result3))
# array([[0, 0, 0, 0],
#        [0, 0, 0, 0],
#        [0, 1, 1, 0],
#        [0, 0, 0, 0],
#        [0, 0, 0, 0]])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...