Wie puffere ich Rasterpixel nach ihren Werten?

28

Die Pixel auf der linken Seite stellen Baumpositionen und die zugehörigen Kronenradien dar (dh Pixelwerte im Bereich von 2 bis 5). Ich möchte diese Rasterpixel nach ihrem Kronenradiuswert puffern. Das Bild rechts ist das, was ich nur mit Rasterverarbeitungsmethoden erreichen möchte .

Ich würde anfänglich überlegen, in ArcGIS eine kreisförmige Brennpunktsumme zu verwenden, obwohl die Nachbarschaftseinstellung ein fester Wert ist, der den variablen Kronenradius nicht berücksichtigt.

Was ist eine gute Methode, um Pixel nach ihren Werten zu "puffern"?

Bildbeschreibung hier eingeben

Aaron
quelle
2
Haben Sie versucht, Raster in Punkte zu konvertieren, dann feldweise zu puffern und dann wieder in Raster umzuwandeln?
2
Es hilft zu erkennen, dass dies eine nicht lokale Operation ist, da diese Nichtlokalität zeigt, dass es inhärente Grenzen dafür gibt, wie die Berechnung ausgeführt werden kann. Beispielsweise würde sich Ihre Ausgabe fast überall radikal ändern, wenn sich nur ein einzelnes isoliertes Pixel in der Eingabe auf einen großen Wert ändern würde. Wenn Sie also Einschränkungen bei den Eingabewerten kennen, teilen Sie diese bitte mit, da dies zu verbesserten Lösungen führen kann. Befinden sich zum Beispiel alle Ihre Eingabewerte immer in der Menge {2,3,4}?
whuber
@ Dan Patterson So bin ich auf das Bild rechts gekommen. Ich versuche jedoch, Vektoroperationen vollständig zu vermeiden und diese Schritte zu vermeiden.
Aaron
2
@whuber Dieser Datensatz repräsentiert Bäume mit variablen Kronendurchmessern. Angesichts dessen können die Baumkronenradiusmessungen realistisch von 1 bis 10 variieren. Ich sollte auch hinzufügen, dass die gepufferte Ausgabe nur Nullen für die Abwesenheit von Kronen und Einsen für die Anwesenheit von Kronen sein muss.
Aaron
1
OK danke. Es sieht so aus, als hätten Sie die Beispielausgabe erstellt, indem Sie die 3-Puffer der Punkte mit dem Wert 3, die 4-Puffer der Punkte mit dem Wert 4 und die 5-Puffer der Punkte mit dem Wert 5 vereinigt haben. (Sie scheinen es vergessen zu haben.) 2.) Dieser Prozess beantwortet nicht nur Ihre Frage, sondern (glaube ich) ist die einfachste Lösung mit den in Spatial Analyst verfügbaren Tools.
whuber

Antworten:

14

Hier ist eine reine Rasterlösung für die Python 2.7Verwendung von numpyund 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()

Eingang: Bildbeschreibung hier eingeben

Ausgabe: Bildbeschreibung hier eingeben

jatobat
quelle
1
+1 für den Dilatationsansatz! Es funktioniert auch mit Nahpunkten.
Antonio Falciano
Dies ist ein großartiges Beispiel dafür, warum dieses alte Jet-Farbschema schrecklich war. Mit viridis sieht das viel klarer aus.
naught101
8

Vektorbasierter Ansatz

Diese Aufgabe kann in drei Schritten ausgeführt werden:

Hinweis: Wenn Sie das Pufferfeld verwenden, wird die Berechnung eines Puffers für jeden Kronenradiuswert vermieden.


Raster-basierter Ansatz

Dieses Problem vermeidet die vektorbasierte Lösung und schlägt vor, eine Art zellularer Automaten zu verwenden, die auf den nächsten Nachbarn basieren. Angenommen, alle schwarzen Pixel sind Nullen, die Pixel sind quadriert und ihre Größe ist gleich 1 (oder alternativ werden sie angemessen skaliert), sind die zu übernehmenden Regeln sehr einfach:

  1. Wenn der Pixelwert ( VALUE) größer als 1 ist, wird sein Wert zu VALUE-1den umgebenden Pixeln und berücksichtigt diese. Wenn ihre Werte kleiner als sind VALUE-1, werden diese Pixel geboren oder wachsen und ihr Wert wird VALUE-1. Ansonsten überleben diese Pixel und bleiben unverändert.
  2. Wenn VALUE<=1, nichts tun (das Pixel ist tot!).

Diese Regeln müssen angewendet werden, bis alle Pixel tot sind, dh ihre Werte sind gleich 0 oder 1. Also N-1mal, wo Nist der maximale Wert, den Sie im Eingabe-Raster haben. Dieser Ansatz lässt sich mit etwas Python und Numpy recht einfach umsetzen.

Antonio Falciano
quelle
1
Danke für die Antwort afalciano. Auf diese Weise habe ich das Bild rechts erstellt und einen Vektoransatz verwendet - einen, den ich zu vermeiden versuche.
Aaron
1
Ok Aaron, hier ist ein rasterbasierter Ansatz. Hoffe das hilft.
Antonio Falciano
7

Eine andere Option wäre, separate Raster für jeden Pixelwert zu erstellen, in diesem Fall 4 Raster mit einer Bedingung. Erweitern Sie dann die Raster um eine Pixelanzahl, die dem Rasterwert entspricht (indem Sie möglicherweise über eine Werteliste iterieren). Verbinden Sie zuletzt die Raster (entweder algebraisch oder räumlich), um ein binäres Raster für die Baumkronen zu erstellen.

HDunn
quelle
1
Diese Idee ist die richtige. Die Details können verbessert werden: (1) Die Auswahl erstellt einen binären (0,1) Indikator für die Bäume eines bestimmten Kronenradius. (2) Eine Brennpunktsumme dieser Auswahl - unter Verwendung einer kreisförmigen Nachbarschaft des gegebenen Radius - ist unter Verwendung der FFT schnell zu berechnen. (3) Addieren der Brennsummen (punktweise) und Vergleichen dieser mit 0 ergibt den gewünschten Puffer.
Whuber
7

Es ist eine schwierige Frage, dies im Raster zu tun, da Sie nicht die Möglichkeit haben, den Wert des Pixels zum Definieren der Größe des Puffers zu verwenden. Aus diesem Grund müssten Sie, wie bereits erwähnt, für jeden Wert den Fokusfilter verwenden.

Hier ist eine mögliche Antwort, um es mit nur 3 Filtern zu tun (ich konnte nicht weniger finden), aber nicht perfekt, wie von Whuber erwähnt: Ihre Puffer werden abgeschnitten, wenn Bäume nahe beieinander stehen.

1) BEARBEITEN: Euklidische Zuordnung (dies löst das Problem nicht vollständig, da es die Puffer in der Nähe kleinerer Bäume schneidet, aber es ist besser als die Artefakte meiner ersten Lösung).

2) Euklidianabstand um jedes Pixel

3) Rasterrechner (Kartenalgebra) mit einer bedingten Anweisung

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

Beachten Sie, dass Sie den Zustand in Bezug auf den Radius (mit oder ohne das zentrale Pixel) an Ihre Bedürfnisse anpassen können.

radouxju
quelle
+1 Dies ist ein kreativer Ansatz. Ich werde testen, ob es möglich ist, mit diesem Ansatz zu skalieren.
Aaron
2
Der euklidische Entfernungsansatz funktioniert nicht, da nur die Entfernung zum nächsten Baum berechnet wird. Dies ist nicht unbedingt die Entfernung zu einem Baum, dessen Krone den Punkt abdeckt.
Whuber
2

Fragen Sie sich, warum Sie nicht das Erweiterungstool von ArcGIS verwenden?

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)

Bei Überlappung: Der letzte expandBefehl deckt die vorherigen ab.

Mr. Che
quelle
2

Wenn Sie die Pixelposition haben, geben Ihnen der Radius und der Mittelpunktkreis-Algorithmus (eine Variante des Bresenham Alg.) Einen Hinweis. IMO ist es einfach, ein Polygon aus diesem Ansatz zu erstellen, und ich denke, es ist einfach, dies in Python zu implementieren. Eine Vereinigung dieser Menge von Polygonen ergibt den Abdeckungsbereich.

huckfinn
quelle
Ich weiß, dass dies keine Frage ist, aber möchten Sie mehr über grafische Grundelemente und das Füllen von Linien mit Polygonen erfahren? Für Kreise ist es sehr einfach. Konvexe Kombination ist ein Schlagwort und so weiter ....
huckfinn
Wie würde dies mit grundlegenden Rasteroperationen angewendet?
Whuber
Wenn Sie versuchen, dies im Rasterraum zu handhaben, bestimmen Sie die Kreispunkte, sortieren Sie sie nach y oder x und füllen Sie den Raum mit einer geraden Linie (Scan-Linie), um den Kreis zu füllen. Wenn Sie im dreieckigen Ansatz den Kreis durch eine Annäherung dreieckiger Sektoren aufbauen und versuchen, das Dreieck zu füllen, müssen Sie prüfen, ob der Punkt innen oder außen liegt (konvexe Kombination) und umgekehrt. Und im "GIS" -Ansatz ist das Bilden von Polygonen (im Uhrzeigersinn ausgerichtete Polygone) und das Bilden einer Vereinigung das dritte (IMO das rechnerisch teuerste).
Huckfinn
Um es klar auszudrücken: Und im "GIS" -Ansatz ... mache eine algebrarische Operation wie Vereinigung, Schnittmenge, Berührung .... ist die dritte IMO die rechnerisch teuerste.
huckfinn