Comment appliquer un masque en forme de disque à un tableau numpy?

J'ai un tableau comme ceci:

>>> 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.]])

Je crée un masque en forme de disque avec Rayon 3 ainsi:

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

Cela donne:

>> 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)

Maintenant, je veux pouvoir appliquer ce masque à mon tableau, en utilisant n'importe quel élément comme point central. Ainsi, par exemple, avec le point central à (1,1), je veux obtenir un tableau comme:

>>> 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.]])

Est-il un moyen facile d'appliquer ce masque?

Edit: Je n'aurais pas dû mélanger les booléens et les flotteurs - c'était trompeur.

>>> 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.]])

C'est plus le résultat que j'ai besoin.

Tableau[masque] = 255

Masquera le tableau en utilisant le point central (0 + rayon, 0 + rayon).

Cependant, j'aimerais pouvoir placer n'importe quel masque de taille à n'importe quel point (y,x) et l'avoir automatiquement coupé pour s'adapter.

31
demandé sur user816555 2011-12-27 20:44:38

6 réponses

Je le ferais comme ceci, où (a, b) est le centre de votre masque:

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
51
répondu Bi Rico 2011-12-28 00:41:46

Je voulais juste partager avec tout le monde une application un peu plus avancée de cette technique que je devais juste faire face.

Mon problème était d'appliquer ce noyau circulaire pour calculer la moyenne de toutes les valeurs entourant chaque point dans une matrice 2D. Le noyau généré peut être passé au filtre générique de scipy de la manière suivante:

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)

Espérons que cela aide!

7
répondu Iñigo Hernáez Corres 2013-04-17 14:02:16

Pour le mettre une fonction pratique:

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]))

Renvoie la somme des pixels dans le rayon, ou return (array[mask] = 2) pour n'importe quel besoin.

3
répondu horst 2014-08-18 17:29:13

Vous pouvez utiliser la fonction convolve de scipy, qui a l'avantage de vous permettre de placer n'importe quel masque particulier, alias noyau, sur n'importe quel nombre de coordonnées données dans votre tableau, tout à la fois:

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

Créez D'abord un tableau de coordonnées avec la coordonnée de l'endroit où vous voulez que le masque (noyau) soit centré marqué comme 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.]]

Créez votre masque:

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.]]

Convolve les deux images:

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.]]

Notez que les entrées de la fonction convolve ne commutent pas, c'est à dire de convolution(a,b) != convolution(b,a)

Notez également que si votre point est proche d'un bord, l'algo ne reproduit pas le noyau à la coordonnée. Pour contourner cela, vous pouvez pad l'arrière-plan par le plus grand axe de votre noyau, appliquer la convolution, puis supprimer le remplissage.

Maintenant, vous pouvez mapper n'importe quel noyau de n'importe quel nombre de points dans un tableau, mais notez que si deux noyaux se chevauchent, ils ajoutent à la chevaucher. Vous pouvez le seuil si vous avez besoin.

3
répondu Michael Varney 2017-08-10 01:13:17

Avez-vous essayé de créer un masque ou des zéros et des uns, puis d'utiliser une multiplication de tableau par élément? C'est la voie canonique, plus ou moins.

Aussi, êtes-vous certains vous voulez un mélange de chiffres et de booléens dans un numpy tableau? NumPy, comme son nom l'indique, fonctionne mieux avec les nombres.

2
répondu 9000 2011-12-27 17:14:53

Pour obtenir le même résultat que dans votre exemple, vous pouvez faire quelque chose comme ceci:

>>> 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)
0
répondu jcollado 2011-12-27 17:10:49