Как применить маску в форме диска к массиву NumPy? - PullRequest
33 голосов
/ 27 декабря 2011

У меня есть такой массив:

>>> np.ones((8,8))
array([[ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.]])

Я создаю маску в форме диска с радиусом 3, таким образом:

y,x = np.ogrid[-3: 3+1, -3: 3+1]
mask = x**2+y**2 <= 3**2

Это дает:

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

Теперь я хочу иметь возможность применить эту маску к моему массиву, используя любой элемент в качестве центральной точки.Так, например, с центральной точкой в ​​(1,1) я хочу получить массив вроде:

>>> new_arr
array([[ True,  True,  True,  True,    1.,  1.,  1.,  1.],
       [ True,  True,  True,  True,  True,  1.,  1.,  1.],
       [ True,  True,  True,  True,    1.,  1.,  1.,  1.],
       [ True,  True,  True,  True,    1.,  1.,  1.,  1.],
       [ 1.,    True,    1.,    1.,    1.,  1.,  1.,  1.],
       [ 1.,      1.,    1.,    1.,    1.,  1.,  1.,  1.],
       [ 1.,      1.,    1.,    1.,    1.,  1.,  1.,  1.],
       [ 1.,      1.,    1.,    1.,    1.,  1.,  1.,  1.]])

Есть ли простой способ применить эту маску?

Редактировать: я не должен был смешивать булевы и числа с плавающей точкой - это вводило в заблуждение.

>>> new_arr
array([[ 255.,  255.,  255.,  255.,    1.,  1.,  1.,  1.],
       [ 255.,  255.,  255.,  255.,  255.,  1.,  1.,  1.],
       [ 255.,  255.,  255.,  255.,    1.,  1.,  1.,  1.],
       [ 255.,  255.,  255.,  255.,    1.,  1.,  1.,  1.],
       [ 1.,    255.,    1.,    1.,    1.,  1.,  1.,  1.],
       [ 1.,      1.,    1.,    1.,    1.,  1.,  1.,  1.],
       [ 1.,      1.,    1.,    1.,    1.,  1.,  1.,  1.],
       [ 1.,      1.,    1.,    1.,    1.,  1.,  1.,  1.]])

Это больше, чем результат, который мне требуется.

array[mask] = 255 

замаскирует массив, используя центральную точку (0 + радиус, 0 + радиус).

Однако я хотел бы иметь возможность разместить маску любого размера в любой точке (y, x) и автоматически обрезать ее до нужного размера.

Ответы [ 6 ]

59 голосов
/ 28 декабря 2011

Я бы сделал это так, где (a, b) - центр вашей маски:

import numpy as np

a, b = 1, 1
n = 7
r = 3

y,x = np.ogrid[-a:n-a, -b:n-b]
mask = x*x + y*y <= r*r

array = np.ones((n, n))
array[mask] = 255
7 голосов
/ 17 апреля 2013

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

Моя проблема состояла в том, чтобы применить это круговое ядро ​​для вычисления среднего значения всех значений, окружающих каждую точку.в 2D матрице.Сгенерированное ядро ​​может быть передано универсальному фильтру scipy следующим образом:

import numpy as np
from scipy.ndimage.filters import generic_filter as gf

kernel = np.zeros((2*radius+1, 2*radius+1))
y,x = np.ogrid[-radius:radius+1, -radius:radius+1]
mask = x**2 + y**2 <= radius**2
kernel[mask] = 1
circular_mean = gf(data, np.mean, footprint=kernel)

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

4 голосов
/ 22 июня 2017

Вы можете использовать функцию Сволпи Сгибать, которая позволяет вам размещать любую конкретную маску, или ядро, на любое количество заданных координат в вашем массиве, все сразу:

import numpy as np
from scipy.ndimage.filters import convolve

Сначаласоздайте массив координат с координатой, где вы хотите, чтобы маска (ядро) центрировалась, помеченной как 2

background = np.ones((10,10))
background[5,5] = 2
print(background)

[[ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  2.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]]

Создайте свою маску:

y,x = np.ogrid[-3: 3+1, -3: 3+1]
mask = x**2+y**2 <= 3**2
mask = 254*mask.astype(float)
print(mask)

[[   0.    0.    0.  254.    0.    0.    0.]
 [   0.  254.  254.  254.  254.  254.    0.]
 [   0.  254.  254.  254.  254.  254.    0.]
 [ 254.  254.  254.  254.  254.  254.  254.]
 [   0.  254.  254.  254.  254.  254.    0.]
 [   0.  254.  254.  254.  254.  254.    0.]
 [   0.    0.    0.  254.    0.    0.    0.]]

Сверните два изображения:

b = convolve(background, mask)-sum(sum(mask))+1
print(b)

[[   1.    1.    1.    1.    1.    1.    1.    1.    1.    1.]
 [   1.    1.    1.    1.    1.    1.    1.    1.    1.    1.]
 [   1.    1.    1.    1.    1.  255.    1.    1.    1.    1.]
 [   1.    1.    1.  255.  255.  255.  255.  255.    1.    1.]
 [   1.    1.    1.  255.  255.  255.  255.  255.    1.    1.]
 [   1.    1.  255.  255.  255.  255.  255.  255.  255.    1.]
 [   1.    1.    1.  255.  255.  255.  255.  255.    1.    1.]
 [   1.    1.    1.  255.  255.  255.  255.  255.    1.    1.]
 [   1.    1.    1.    1.    1.  255.    1.    1.    1.    1.]
 [   1.    1.    1.    1.    1.    1.    1.    1.    1.    1.]]

Обратите внимание, что записи функции свертки не коммутируют, т. Е. Сворачивают (a, b)! = Convolve (b, a)

Обратите внимание также, что если ваша точка находится около края,алгоритм не воспроизводит ядро ​​по координатам.Чтобы обойти это, вы можете заполнить фон самой большой осью вашего ядра, применить свертку, а затем удалить заполнение.

Теперь вы можете сопоставить любое ядро ​​с любым количеством точек в массиве, но обратите вниманиечто если два ядра перекрываются, они добавляются при перекрытии.Вы можете ограничить это, если вам нужно.

3 голосов
/ 18 августа 2014

Если выразить это одной удобной функцией:

def cmask(index,radius,array):
  a,b = index
  nx,ny = array.shape
  y,x = np.ogrid[-a:nx-a,-b:ny-b]
  mask = x*x + y*y <= radius*radius

  return(sum(array[mask]))

Возвращает сумму пикселей в радиусе или возвращает (массив [маска] = 2) для любых нужд.

2 голосов
/ 27 декабря 2011

Вы пытались создать маску или нули и единицы, а затем использовать умножение массива на элемент? Это канонический путь, более или менее.

Кроме того, вы уверены , хотите ли вы сочетание чисел и логических значений в массиве numpy ? NumPy, как следует из названия, лучше всего работает с числами.

0 голосов
/ 27 декабря 2011

Чтобы получить тот же результат, что и в вашем примере, вы можете сделать что-то вроде этого:

>>> new_arr = np.array(ones, dtype=object)
>>> new_arr[mask[2:, 2:]] = True
>>> print new_arr
array([[True, True, True, True, 1.0, 1.0, 1.0, 1.0],
       [True, True, True, True, True, 1.0, 1.0, 1.0],
       [True, True, True, True, 1.0, 1.0, 1.0, 1.0],
       [True, True, True, True, 1.0, 1.0, 1.0, 1.0],
       [1.0, True, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
       [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
       [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
       [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]], dtype=object)
...