Question

J'ai une image avec des valeurs allant de 0 à 1.Ce que j'aime faire, c'est faire une simple moyenne.
Mais, plus précisément, pour une cellule située au bord de l'image, j'aimerais calculer la moyenne des pixels pour cette partie du quartier/noyau qui se trouve dans l'étendue de l'image.En fait, cela revient à adapter le dénominateur de la « formule moyenne », le nombre de pixels par lequel on divise la somme.

J'ai réussi à faire cela comme indiqué ci-dessous avec scipy.ndimage.generic_filter, mais c'est loin d'être efficace en termes de temps.

def fnc(buffer, count):
    n = float(sum(buffer < 2.0))
    sum = sum(buffer) - ((count - b) * 2.0)
    return (sum / n)

avg = scipy.ndimage.generic_filter(image, fnc, footprint = kernel, \
                                   mode = 'constant', cval = 2.0,   \
                                   extra_keywords = {'count': countkernel})

Détails

  • kernel = tableau carré (cercle représenté par des unités)
  • Remplissage avec des 2 et non par des zéros depuis lors, je ne pouvais pas séparer correctement les zéros de la zone remplie et les zéros du raster réel
  • countkernel = nombre de un dans le kernel
  • n = nombre de cellules qui se trouvent à l'intérieur image en excluant les cellules de la zone rembourrée identifiées par des valeurs de 2
  • Corriger le sum en soustrayant (nombre de cellules rembourrées * 2,0) de la somme totale du quartier d'origine

Mises à jour)

1) Le remplissage avec NaN augmente le calcul d'environ 30 % :

    def fnc(buffer):
        return (numpy.nansum(buffer) / numpy.sum([~numpy.isnan(buffer)]))

    avg = scipy.ndimage.generic_filter(image, fnc, footprint = kernel, \
                                       mode = 'constant', cval = float(numpy.nan)

2) Application de la solution proposée par Yves Daoust (réponse acceptée), réduit définitivement le temps de traitement au minimum :

    def fnc(buffer):
        return numpy.sum(buffer)

    sumbigimage = scipy.ndimage.generic_filter(image, fnc, \
                                               footprint = kernel, \
                                               mode = 'constant', \
                                               cval = 0.0)
    summask     = scipy.ndimage.generic_filter(mask, fnc, \
                                               footprint = kernel, \
                                               mode = 'constant', \
                                               cval = 0.0)
    avg = sumbigimage / summask

3) S'appuyer sur Celle d'Yves astuce pour utiliser une image binaire supplémentaire, qui en fait applique un masque, je suis tombé sur le principe de tableaux masqués.En tant que tel, un seul tableau doit être traité car un tableau masqué « mélange » les tableaux d’images et de masques.
Un petit détail sur le tableau des masques :au lieu de remplir la partie intérieure (étendue de l'image originale) avec des 1 et de remplir la partie extérieure (bordure) avec des 0 comme dans la mise à jour précédente, vous devez faire l'inverse.Un 1 dans un tableau masqué signifie « invalide », un 0 signifie « valide ».
Ce code est même 50% plus rapide que le code fourni dans la mise à jour 2) :

    maskedimg = numpy.ma.masked_array(imgarray, mask = maskarray)

    def fnc(buffer):
        return numpy.mean(buffer)

    avg = scipy.ndimage.generic_filter(maskedimg, fnc, footprint = kernel, \
                                       mode = 'constant', cval = 0.0)

--> Je dois me corriger ici !
J'ai dû me tromper lors de la validation, car après quelques calculs, il semblait que scipy.ndimage.<filters> ne peut pas gérer les masked_arrays dans le sens où lors de l'opération de filtrage, le masque n'est pas pris en compte.
D'autres personnes l'ont également mentionné, comme ici et ici.


Le pouvoir d'une image...

  • gris:étendue de l'image à traiter
  • blanc:zone rembourrée (dans mon cas remplie de 2.0)
  • nuances de rouge :étendue du noyau
    • rouge foncé:quartier efficace
    • rouge clair:partie du quartier à ignorer

enter image description here


Comment modifier ce morceau de code plutôt pragmatique pour améliorer les performances du calcul ?

Merci d'avance!

Était-ce utile?

La solution

Je ne sais pas si cela va aider, car je ne maîtrise pas scipy :utilisez une image auxiliaire de 1 dans la zone grise et de 0 dans la zone blanche (des 0 également dans l'image source).Appliquez ensuite le filtre aux deux images avec une simple somme.

Il y a un certain espoir d'accélération si scipy fournit une version spécialisée du filtre avec une fonction intégrée de sommation.

Ceci fait, vous devrez diviser les deux images pixel par pixel.

Autres conseils

Je ne suis pas sûr de l'efficacité de cette méthode, mais j'utilise une formulation plus simple avec nanc'est qui gère à la fois les bordures et les masques.

Pas d'étui à masque :

avg = scipy.ndimage.generic_filter(image, np.nanmean, mode='constant', cval=np.nan, footprint=kernel)

Étui à masque :

masked_image = np.where(mask, image, np.nan)
avg = scipy.ndimage.generic_filter(masked_image, np.nanmean, mode='constant', cval=np.nan, footprint=kernel)

Vous pouvez tout utiliser numpy le nan les fonctions.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top