Comment tamponner les pixels raster par leurs valeurs?

28

Les pixels à gauche représentent les emplacements des arbres et leurs rayons de couronne associés (c'est-à-dire des valeurs de pixels allant de 2 à 5). Je voudrais tamponner ces pixels raster par leur valeur de rayon de couronne. L'image à droite est ce que j'espère accomplir en utilisant uniquement des méthodes de traitement raster .

Je penserais initialement à utiliser une somme focale circulaire dans ArcGIS, bien que le paramètre de voisinage soit une valeur fixe, qui ne tiendrait pas compte du rayon de couronne de taille variable.

Quelle est la bonne méthode pour "tamponner" les pixels en fonction de leurs valeurs?

entrez la description de l'image ici

Aaron
la source
2
Avez-vous essayé de convertir le raster en points, puis de le tamponner par champ, puis de le reconvertir en raster?
2
Cela aide à réaliser qu'il s'agit d'une opération non locale , car cette non-localité montre qu'il existe des limites inhérentes à la façon dont le calcul peut être effectué. Par exemple, votre sortie changerait radicalement presque partout si un seul pixel isolé dans l'entrée devait changer en une grande valeur. Ainsi, si vous connaissez des restrictions sur les valeurs d'entrée, veuillez les partager, car cela peut conduire à des solutions améliorées. Par exemple, toutes vos valeurs d'entrée seront-elles toujours dans l'ensemble {2,3,4}?
whuber
@ Dan Patterson C'est ainsi que j'ai trouvé l'image de droite. Cependant, j'essaie d'éviter complètement les opérations vectorielles et d'éviter ces étapes.
Aaron
2
@whuber Cet ensemble de données représente des arbres avec des diamètres de cime variables. Compte tenu de cela, les mesures du rayon de la cime des arbres peuvent varier de manière réaliste de 1 à 10. Je dois également ajouter que la sortie tamponnée n'a besoin que de 0 pour l'absence de couronne et de 1 pour la présence de couronne.
Aaron
1
OK merci. Il semble que vous ayez créé l'exemple de sortie en réunissant les 3 tampons des points avec la valeur 3, les 4 tampons des points avec la valeur 4 et les 5 tampons des points avec la valeur 5. (Vous semblez avoir oublié pour traiter les points avec la valeur 2.) Ce processus répond non seulement à votre question, mais (je crois) c'est la solution la plus simple en utilisant les outils disponibles dans Spatial Analyst.
whuber

Réponses:

14

Voici une solution de trame pure en Python 2.7utilisant numpyet scipy:

import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt

#create tree location matrix with values indicating crown radius
A = np.zeros((120,320))
A[60,40] = 1
A[60,80] = 2
A[60,120] = 3
A[60,160] = 4
A[60,200] = 5
A[60,240] = 6
A[60,280] = 7

#plot tree locations
fig = plt.figure()
plt.imshow(A, interpolation='none')
plt.colorbar()

#find unique values
unique_vals = np.unique(A)
unique_vals = unique_vals[unique_vals > 0]

# create circular kernel
def createKernel(radius):
    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
    return kernel

#apply binary dilation sequentially to each unique crown radius value 
C = np.zeros(A.shape).astype(bool)   
for k, radius in enumerate(unique_vals):  
    B = ndimage.morphology.binary_dilation(A == unique_vals[k], structure=createKernel(radius))
    C = C | B #combine masks

#plot resulting mask   
fig = plt.figure()
plt.imshow(C, interpolation='none')
plt.show()

Contribution: entrez la description de l'image ici

Sortie: entrez la description de l'image ici

jatobat
la source
1
+1 pour l'approche de dilatation! Cela fonctionne aussi avec les points proches.
Antonio Falciano
Ceci est un excellent exemple de la raison pour laquelle ce vieux schéma de couleurs de jet était terrible. Cela semble beaucoup plus clair avec viridis.
naught101
8

Approche vectorielle

Cette tâche peut être effectuée en trois étapes:

Remarque: l'utilisation du champ tampon évite le calcul d'un tampon pour chaque valeur de rayon de couronne.


Approche basée sur raster

En évitant la solution vectorielle, ce problème suggère d'utiliser une sorte d' automates cellulaires basés sur les voisins les plus proches. En supposant que tous les pixels noirs sont des zéros, les pixels sont au carré et leur taille est égale à 1 (ou, à défaut, sont mis à l'échelle de manière appropriée), les règles à adopter sont très simples:

  1. Si la valeur de pixel ( VALUE) est supérieure à 1, sa valeur devient VALUE-1, puis considère ses pixels environnants. Si leurs valeurs sont inférieures à VALUE-1, ces pixels naissent ou grandissent et leur valeur devient VALUE-1. Sinon, ces pixels survivent et restent inchangés.
  2. Si VALUE<=1, ne faites rien (le pixel est mort!).

Ces règles doivent être appliquées jusqu'à ce que tous les pixels soient morts, c'est-à-dire que leurs valeurs soient égales à 0 ou 1. Donc, N-1fois, où Nest la valeur maximale que vous avez dans le raster en entrée. Cette approche peut être implémentée assez facilement avec un peu de Python et numpy.

Antonio Falciano
la source
1
Merci pour la réponse afalciano. Cette méthode est la façon dont j'ai créé l'image à droite et utilise une approche vectorielle - celle que j'essaie d'éviter.
Aaron
1
Ok Aaron, voici maintenant une approche basée sur un raster. J'espère que cela t'aides.
Antonio Falciano
7

Une autre option serait de créer des rasters séparés pour chaque valeur de pixel, dans ce cas 4 rasters, avec une condition. Développez ensuite les rasters d'un nombre de pixels correspondant à la valeur du raster (en parcourant éventuellement une liste de valeurs). Enfin, rejoignez les rasters (algébriques ou spatialement), pour créer un raster binaire pour les cimes des arbres.

HDunn
la source
1
Cette idée est la bonne. Les détails peuvent être améliorés: (1) la sélection crée un indicateur binaire (0,1) des arbres d'un rayon de couronne donné. (2) Une somme focale de cette sélection - en utilisant un voisinage circulaire du rayon donné - est rapide à calculer en utilisant la FFT. (3) Ajouter les sommes focales (point par point) et les comparer à 0 donne le tampon souhaité.
whuber
7

Il est difficile de le faire en mode raster car vous n'avez pas la possibilité d'utiliser la valeur du pixel pour définir la taille du tampon. Par conséquent, vous devrez faire le filtre focal pour chaque valeur, comme vous l'avez déjà dit.

Voici une réponse possible pour le faire avec seulement 3 filtres (je n'ai pas pu trouver moins), mais pas parfaitement comme mentionné par Whuber: vos tampons seront tronqués lorsque les arbres seront proches les uns des autres.

1) EDIT: allocation euclidienne (cela ne résout pas complètement le problème, car il coupe les tampons à proximité d'arbres plus petits, mais c'est mieux que les artefacts de ma première solution).

2) distance euclidienne autour de chaque pixel

3) calculatrice raster (algèbre cartographique) avec une instruction conditionnelle

Con("allocation_result" > ("distance_result" / pixel_size) , 1 , 0)

Notez que vous pouvez ajuster la condition en fonction de vos besoins en termes de rayon (avec ou sans le pixel central)

radouxju
la source
+1 Il s'agit d'une approche créative. Je vais tester pour voir s'il est possible de passer à l'échelle en utilisant cette approche.
Aaron
2
L'approche de la distance euclidienne ne fonctionnera pas, car elle calcule uniquement la distance à l' arbre le plus proche , qui n'est pas nécessairement la distance à un arbre dont la couronne couvre le point.
whuber
2

Vous vous demandez pourquoi vous n'utilisez pas l' outil d' extension d'ArcGIS ?

import arcpy
from arcpy.sa import *

raster_in  = r'c:\test.tif'
raster_out = r'c:\test_out.tif'

outExpand1 = Expand(raster_in, 2, 2)
outExpand2 = Expand(outExpand1, 3, 3)
outExpand3 = Expand(outExpand3, 4, 4)
outExpand4 = Expand(outExpand4, 5, 5)

outExpand4.save(raster_out)

En cas de chevauchement: la dernière expandcommande couvrira les précédentes.

M. Che
la source
2

Si vous avez la position du pixel, le rayon et l' algorithme du cercle médian (une variante de l'alg. De Bresenham) vous donne un indice. OMI, il est facile de créer un polygone à partir de cette approche et je pense qu'il est facile de l'implémenter en Python. Une union de cet ensemble de polygones vous donne la zone de couverture.

huckfinn
la source
Je sais que ce n'est pas une question, mais, voulez-vous en savoir plus sur les primitives graphiques et le remplissage de polygones de lignes de balayage? Pour les cercles, c'est très facile. La combinaison convexe est un mot à la mode et ainsi de suite ....
huckfinn
Comment cela serait-il appliqué à l'aide d'opérations raster de base?
whuber
Si vous essayez de gérer cela dans l'espace raster, déterminez les points du cercle, en les triant par le y ou le x et remplissez l'espace par une ligne droite (ligne de balayage) est en passe de remplir le cercle. Dans l'approche triangulaire, si vous construisez le cercle par une approximation des secteurs tringulaires et essayez de remplir le triangle, vous avez besoin d'un test si le point est à l'intérieur ou à l'extérieur (combinaison convexe) et est dans l'autre sens. Et dans l'approche "SIG", la construction de polygones (polygones orientés dans le sens des aiguilles d'une montre) et la réalisation d'une union est la troisième (l'OMI la plus coûteuse en calcul).
huckfinn
Pour être clair: Et dans l'approche "SIG" ... faire une opération algébraire comme l'union, l'intersection, le toucher ... est le troisième IMO le plus coûteux en calcul.
huckfinn