Wie durchlaufe ich jede Zelle in einem fortlaufenden Raster?


13

Siehe diesen Link für weitere Details.

Das Problem:

Ich möchte ein kontinuierliches Raster (eines ohne Attributtabelle) Zelle für Zelle durchlaufen und den Wert der Zelle abrufen. Ich möchte diese Werte übernehmen und Bedingungen für sie ausführen und die unten beschriebenen Schritte der Kartenalgebra emulieren, ohne den Raster-Rechner tatsächlich zu verwenden.

Auf Anfrage von Kommentaren unten habe ich Details hinzugefügt, die Hintergrundinformationen zum Problem liefern und die Notwendigkeit der Implementierung einer Methode als solche im Abschnitt "Die erforderliche Analyse" begründen.

Die unten vorgeschlagene Analyse ist zwar für mein Problem relevant, muss aber nicht in eine Antwort umgesetzt werden. Der Umfang der Frage bezieht sich nur auf die Iteration durch ein kontinuierliches Raster zum Abrufen / Festlegen der Zellenwerte.

Die Analyse benötigt:

Wenn eine der folgenden Bedingungen erfüllt ist, geben Sie der Ausgabezelle den Wert 1. Geben Sie der Ausgabezelle nur dann den Wert 0, wenn keine der Bedingungen erfüllt ist.

Bedingung 1: Wenn der Zellenwert größer ist als die oberen und unteren Zellen, geben Sie den Wert 1 an:

Con("raster" > FocalStatistics("raster", NbrIrregular("C:\filepath\kernel_file.txt"), "MAXIMUM"), 1, 0)

Wo die Kerneldatei so aussieht:

3 3 
0 1 0
0 0 0
0 1 0

Bedingung 2: Wenn der Zellenwert größer als der Wert der linken und rechten Zelle ist, geben Sie den Wert 1 an:

Con("raster" > FocalStatistics("raster", NbrIrregular("C:\filepath\kernel_file.txt"), "MAXIMUM"), 1, 0)

Wo die Kerneldatei so aussieht:

3 3 
0 0 0
1 0 1
0 0 0  

Bedingung 3: Wenn der Zellenwert größer als die oberen und unteren rechten Zellen ist, geben Sie den Wert 1 an:

Con("raster" > FocalStatistics("raster", NbrIrregular("C:\filepath\kernel_file.txt"), "MAXIMUM"), 1, 0)

Wo die Kerneldatei so aussieht:

3 3 
1 0 0
0 0 0
0 0 1 

Bedingung 4: Wenn der Zellenwert größer ist als die unteren linken und oberen rechten Zellen, geben Sie den Wert 1 an:

Con("raster" > FocalStatistics("raster", NbrIrregular("C:\filepath\kernel_file.txt"), "MAXIMUM"), 1, 0)

Wo die Kerneldatei so aussieht:

3 3 
0 0 1
0 0 0
1 0 0 

Bedingung 5: Wenn jede einer der benachbarten Zellen , die einen Wert gleich die mittleren Zelle hat, gibt die Ausgabe - Raster einen Wert von 1 ( unter Verwendung von fokaler Varietät mit zwei nächsten Nachbarschafts Berechnungen )

Warum nicht Kartenalgebra verwenden?

Im Folgenden wurde festgestellt, dass mein Problem mithilfe der Kartenalgebra gelöst werden kann. Wie oben dargestellt, handelt es sich jedoch um insgesamt sechs Raster-Berechnungen und eine, um alle erstellten Raster zusammenzuführen. Es scheint mir, dass es viel effizienter ist, Zelle für Zelle alle Vergleiche gleichzeitig durchzuführen, anstatt jede einzelne sieben Mal zu durchlaufen und eine ganze Menge Speicher für die Erstellung von sieben Rastern zu verwenden.

Wie soll das Problem angegriffen werden?

Der obige Link rät zur Verwendung der IPixelBlock-Schnittstelle. Aus der ESRI-Dokumentation geht jedoch nicht hervor, ob Sie tatsächlich über IPixelBlock auf einen einzelnen Zellenwert zugreifen oder ob Sie über die Größe des von Ihnen festgelegten IPixelBlocks auf mehrere Zellenwerte zugreifen. Eine gute Antwort sollte eine Methode für den Zugriff auf die Zellenwerte eines kontinuierlichen Rasters vorschlagen und eine Erklärung der Methodik hinter dem Code liefern, wenn nicht offensichtlich.

In Summe:

Was ist die beste Methode zum Durchlaufen jeder Zelle in einem CONTINUOUS-Raster (das keine Attributtabelle enthält ), um auf ihre Zellenwerte zuzugreifen?

Eine gute Antwort muss nicht die oben beschriebenen Analyseschritte implementieren, sondern muss nur eine Methode zum Zugreifen auf Zellenwerte eines Rasters bereitstellen.


4
Es ist fast immer unnötig, jede Zelle in einem Raster zu durchlaufen. Können Sie weitere Informationen darüber bereitstellen, was Sie versuchen zu tun?
user2856

2
@Luke ist richtig: Um eine iterative Rasterberechnung in einem GIS durchzuführen, ist es bei weitem am besten, eine explizite Schleife durch die Zellen zu vermeiden , da unter der Haube eine eventuell durchzuführende Schleife bereits optimiert wurde. Suchen Sie stattdessen nach einer Möglichkeit, die vom GIS bereitgestellte Kartenalgebra-Funktionalität zu verwenden, sofern dies überhaupt möglich ist. Wenn Sie Ihre Analyse beschreiben würden, könnten Sie nützliche Antworten erhalten, die einen solchen Ansatz verwenden.
Whuber

@ Luke Ich habe Details der Analyse hinzugefügt.
Conor

1
Vielen Dank für die Klarstellung, Conor. Ich bin damit einverstanden, dass das Schreiben einer eigenen Schleife effizienter sein kann, wenn Ihr GIS bei jeder Rasterberechnung einen erheblichen Mehraufwand verursacht. Was ist aus Neugier die beabsichtigte Interpretation dieser (ungewöhnlichen) Bedingungen?
Whuber

1
@whuber Für Kantenerkennungsoperationen werden Vektorpolygone aus meinem Raster erstellt. Die Anwendung ähnelt konzeptionell der Identifizierung von Hydrologiebecken anhand eines DEM (in der oben aufgeführten Nachbarschaftsstatistik wird die mittlere Zelle als der "Peak" betrachtet, von dem das Wasser am Gefälle abfließt), liegt jedoch außerhalb des Bereichs der Hydrologie. Zuvor habe ich für diesen Zweck Flow Direction und Basin Rasters verwendet, aber diese sind in meiner endgültigen Analyse fehleranfällig, da die Eigenschaften dieser Methoden nicht genau den Anforderungen entsprechen.
Conor

Antworten:


11

Ich sehe, dass dies bereits durch das Original Poster (OP) gelöst wurde, aber ich werde eine einfache Lösung in Python veröffentlichen, nur für den Fall, dass jemand in der Zukunft an verschiedenen Möglichkeiten interessiert ist, dieses Problem zu lösen. Ich bin ein Teil der Open-Source-Software, daher hier eine Lösung mit GDAL in Python:

import gdal

#Set GeoTiff driver
driver = gdal.GetDriverByName("GTiff")
driver.Register()

#Open raster and read number of rows, columns, bands
dataset = gdal.Open(filepath)
cols = dataset.RasterXSize
rows = dataset.RasterYSize
allBands = dataset.RasterCount
band = dataset.GetRasterBand(1)

#Get array of raster cell values.  The two zeros tell the 
#iterator which cell to start on and the 'cols' and 'rows' 
#tell the iterator to iterate through all columns and all rows.
def get_raster_cells(band,cols,rows):
    return band.ReadAsArray(0,0,cols,rows)

Implementieren Sie die Funktion wie folgt:

#Bind array to a variable
rasterData = get_raster_cells(band,cols,rows)

#The array will look something like this if you print it
print rasterData
> [[ 1, 2, 3 ],
   [ 4, 5, 6 ],
   [ 7, 8, 9 ]]

Durchlaufen Sie dann Ihre Daten mit einer verschachtelten Schleife:

for row in rasterData:
    for val in row:
        print val
> 1
  2
  3
  4...

Oder Sie möchten Ihr 2-D-Array mit einem Listenverständnis abflachen:

flat = [val for row in rasterData for val in row]

Wie auch immer, während Sie die Daten Zelle für Zelle durchlaufen, ist es möglich, einige Bedingungen in Ihre Schleife zu werfen, um Werte zu ändern / zu bearbeiten. In diesem Skript habe ich verschiedene Möglichkeiten für den Zugriff auf die Daten beschrieben: https://github.com/azgs/hazards-viewer/blob/master/python/zonal_stats.py .


Ich mag die Einfachheit und Eleganz dieser Lösung. Ich werde noch ein paar Tage warten und wenn niemand eine Lösung mit gleicher oder höherer Qualität findet, werde ich Tags hinzufügen, um den Umfang der Frage zum Wohle der Community zu erweitern und Ihnen das Kopfgeld zu gewähren.
Conor

Vielen Dank, @Conor! Wir sind Anfang dieser Woche auf ein ähnliches Problem an meinem Arbeitsplatz gestoßen, und ich habe es gelöst, indem ich eine Klasse mit GDAL / Python geschrieben habe. Insbesondere benötigten wir eine serverseitige Methode zum Berechnen des Mittelwerts einer Fläche eines Rasters, wenn nur ein Begrenzungsrahmen von einem Benutzer in unserer clientseitigen Anwendung angegeben wurde. Glaubst du, es wäre von Vorteil, wenn ich den Rest der Klasse, die ich geschrieben habe, hinzufüge?
Asonnenschein

Das Hinzufügen von Code zum Lesen des abgerufenen 2-D-Arrays und zum Bearbeiten seiner Werte ist hilfreich.
Conor

9

Aktualisieren! Die numpy Lösung:

import arcpy
import numpy as np

in_ras = path + "/rastername"

raster_Array = arcpy.RasterToNumPyArray(in_ras)
row_num = raster_Array.shape[0]
col_num = raster_Array.shape[1]
cell_count = row_num * row_num

row = 0
col = 0
temp_it = 0

while temp_it < cell_count:
    # Insert conditional statements
    if raster_Array[row, col] > 0:
        # Do something
        val = raster_Array[row, col]
        print val
    row+=1
    if col > col_num - 1:
        row = 0
        col+=1

Daher ist es mühsam, das fertige Array mit arcpy wieder in das Raster zu bringen. arcpy.NumPyArrayToRaster ist squirrelly und neigt dazu, Ausmaße neu zu definieren, selbst wenn Sie Ihre LL-Koordinaten eingeben.

Ich speichere lieber als Text.

np.savetxt(path + "output.txt", output, fmt='%.10f', delimiter = " ")

Ich verwende Python aus Geschwindigkeitsgründen als 64-Bit-Version. Dies bedeutet, dass ich numpy.savetxt derzeit nicht mit einem Header versorgen kann. Also muss ich die Ausgabe öffnen und den ASCII-Header hinzufügen, den Arc haben möchte, bevor ich ASCII in Raster konvertiere

File_header = "NCOLS xxx" + '\n'+ "NROWS xxx" + '\n' + "XLLCORNER xxx"+'\n'+"YLLCORNER xxx"+'\n'+"CELLSIZE xxx"+'\n'+"NODATA_VALUE xxx"+'\n'

Die numpy-Version führt mein Schichtraster, Multiplikationen und Additionen viel schneller aus (1000 Iterationen in 2 Minuten) als die arcpy-Version (1000 Iterationen in 15 Minuten).

ALTE VERSION Ich kann dies später löschen. Ich habe gerade ein ähnliches Skript geschrieben. Ich habe versucht, in Punkte umzuwandeln und den Suchcursor zu verwenden. Ich habe in 12 Stunden nur 5000 Iterationen erhalten. Also suchte ich einen anderen Weg.

Meine Methode besteht darin, die Zellmittelpunktkoordinaten jeder Zelle zu durchlaufen. Ich beginne in der oberen linken Ecke und gehe von rechts nach links. Am Ende der Reihe gehe ich eine Reihe runter und beginne wieder links. Ich habe ein 240 m Raster mit 2603 Spalten und 2438 Zeilen, also insgesamt 6111844 Zellen. Ich benutze eine Iteratorvariable und eine while-Schleife. Siehe unten

Ein paar Anmerkungen: 1 - Sie müssen die Koordinaten der Ausdehnung kennen

2 - Mit Punktkoordinaten für Zellenmitte ausführen - Verschieben Sie die Hälfte der Zellengröße von den Ausdehnungswerten

3 - Mein Skript verwendet den Zellenwert, um ein wertspezifisches Raster zu ziehen, und verschiebt dieses Raster dann in die Mitte der ursprünglichen Zelle. Dadurch wird ein Null-Raster hinzugefügt, um die Ausdehnung zu erweitern, bevor ein endgültiges Raster erstellt wird. Dies ist nur ein Beispiel. Hier können Sie Ihre bedingten Anweisungen einfügen (zweite if-Anweisung in der while-Schleife).

4 - In diesem Skript wird davon ausgegangen, dass alle Rasterwerte als Ganzzahlen umgewandelt werden können. Dies bedeutet, dass Sie zuerst die Daten loswerden müssen. Con IsNull.

6 - Ich bin immer noch nicht glücklich damit und arbeite daran, dies vollständig aus arcpy herauszuholen. Ich würde lieber als Numpy-Arrays gecastet und die Mathe dort ausführen und sie dann zu Arc zurückbringen.

ULx = 959415 ## coordinates for the Upper Left of the entire raster 
ULy = 2044545
x = ULx ## I redefine these if I want to run over a smaller area
y = ULy
temp_it = 0

while temp_it < 6111844: # Total cell count in the data extent
        if x <= 1583895 and y >= 1459474: # Coordinates for the lower right corner of the raster
           # Get the Cell Value
           val_result = arcpy.GetCellValue_management(inraster, str(x)+" " +str(y), "1")
           val = int(val_result.getOutput(0))
        if val > 0: ## Here you could insert your conditional statements
            val_pdf = Raster(path + "pdf_"str(val))
            shift_x  =  ULx - x # This will be a negative value
            shift_y = ULy - y # This will be a positive value
            arcpy.Shift_management(val_pdf, path+ "val_pdf_shift", str(-shift_x), str(-shift_y))
            val_pdf_shift = Raster(path + "val_pdf_shift")
            val_pdf_sh_exp = CellStatistics([zeros, val_pdf_shift], "SUM", "DATA")
            distr_days = Plus(val_pdf_sh_exp, distr_days)
        if temp_it % 20000 == 0: # Just a print statement to tell me how it's going
                print "Iteration number " + str(temp_it) +" completed at " + str(time_it)
        x += 240 # shift x over one column
        if x > 1538295: # if your at the right hand side of a row
            y = y-240 # Shift y down a row
            x = 959415 # Shift x back to the first left hand column
        temp_it+=1

distr_days.save(path + "Final_distr_days")

4

Versuchen Sie es mit IGridTable, ICursor, IRow. Dieses Code-Snippet dient zum Aktualisieren von Rasterzellenwerten. Es zeigt jedoch die Grundlagen der Iteration:

Wie kann ich ein neues Feld in eine Rasterattributtabelle einfügen und diese durchlaufen?

Public Sub CalculateArea(raster As IRaster, areaField As String)
    Dim bandCol As IRasterBandCollection
    Dim band As IRasterBand

    Set bandCol = raster
    Set band = bandCol.Item(0)

    Dim hasTable As Boolean
    band.hasTable hasTable
    If (hasTable = False) Then
        Exit Sub
    End If    

    If (AddVatField(raster, areaField, esriFieldTypeDouble, 38) = True) Then
        ' calculate cell size
        Dim rstProps As IRasterProps
        Set rstProps = raster

        Dim pnt As IPnt
        Set pnt = rstProps.MeanCellSize

        Dim cellSize As Double
        cellSize = (pnt.X + pnt.Y) / 2#

        ' get fields index
        Dim attTable As ITable
        Set attTable = band.AttributeTable

        Dim idxArea As Long, idxCount As Long
        idxArea = attTable.FindField(areaField)
        idxCount = attTable.FindField("COUNT")

        ' using update cursor
        Dim gridTableOp As IGridTableOp
        Set gridTableOp = New gridTableOp

        Dim cellCount As Long, cellArea As Double

        Dim updateCursor As ICursor, updateRow As IRow
        Set updateCursor = gridTableOp.Update(band.RasterDataset, Nothing, False)
        Set updateRow = updateCursor.NextRow()
        Do Until updateRow Is Nothing
            cellCount = CLng(updateRow.Value(idxCount))
            cellArea = cellCount * (cellSize * cellSize)

            updateRow.Value(idxArea) = cellArea
            updateCursor.updateRow updateRow

            Set updateRow = updateCursor.NextRow()
        Loop

    End If
End Sub

Sobald Sie die Tabelle durchlaufen haben, können Sie den spezifischen Feldzeilenwert mit abrufen row.get_Value(yourfieldIndex). Wenn Sie Google

arcobjects row.get_Value

Sie sollten in der Lage sein, viele Beispiele zu bekommen, die dies zeigen.

Ich hoffe, das hilft.


1
Leider habe ich es versäumt, zu beachten, und ich werde in meiner obigen ursprünglichen Frage darauf eingehen, dass mein Raster viele fortlaufende Werte enthält, die aus großen Doppelwerten bestehen. Daher funktioniert diese Methode nicht, da mein Raster keine Attributtabellenwerte enthält.
Conor,

4

Wie wäre es damit als radikale Idee, Sie müssten in Python oder ArcObjects programmieren.

  1. Konvertieren Sie Ihr Raster in Punkt-Feature-Classes.
  2. Erstellen Sie XY-Felder und füllen Sie sie aus.
  3. Laden Sie die Punkte in ein Wörterbuch, in dem key eine Zeichenfolge aus X, Y und item ein Zellenwert ist.
  4. Blättern Sie durch Ihr Wörterbuch und berechnen Sie für jeden Punkt die 8 umliegenden Zellen-XYs.
  5. Rufen Sie diese aus Ihrem Wörterbuch ab und testen Sie sie mit Ihren Regeln. Sobald Sie einen zutreffenden Wert gefunden haben, können Sie den Rest der Tests überspringen.
  6. Schreiben Sie die Ergebnisse in ein anderes Wörterbuch und konvertieren Sie sie anschließend wieder in ein Raster, indem Sie zuerst eine Punkt-FeatureClass erstellen und dann Punkte in ein Raster konvertieren.

2
Durch die Konvertierung in eine Reihe von Punkt-Features werden die beiden Eigenschaften der rasterbasierten Datendarstellung beseitigt, die sie so effektiv machen: (1) das Finden von Nachbarn ist eine äußerst einfache Operation mit konstanter Zeit und (2) das explizite Speichern von Standorten Nicht benötigt, RAM, Festplatte und E / A-Anforderungen sind minimal. Obwohl dieser Ansatz funktioniert, ist es daher schwierig, einen Grund zu finden, ihn zu empfehlen.
whuber

Danke für deine Antwort Hornbydd. Ich bin mit der Implementierung einer solchen Methode einverstanden, aber die Schritte 4 und 5 scheinen in rechnerischer Hinsicht nicht sehr effektiv zu sein. Meine Raster haben mindestens 62.500 Zellen (die von mir festgelegte Mindestauflösung für mein Raster beträgt 250 Zellen x 250 Zellen, aber die Auflösung kann und besteht in der Regel aus viel mehr), und ich muss eine räumliche Abfrage durchführen für jede Bedingung, um meine Vergleiche durchzuführen ... Da ich 6 Bedingungen habe, wäre das 6 * 62500 = 375000 räumliche Abfragen. Mit Kartenalgebra wäre ich besser dran. Aber danke für diese neue Sichtweise des Problems. Upvoted.
Conor

Können Sie es nicht einfach in ASCII konvertieren und dann mit einem Programm wie R rechnen?
Oliver Burdekin

Außerdem habe ich ein Java-Applet, das ich geschrieben habe und das leicht geändert werden kann, um Ihre obigen Bedingungen zu erfüllen. Es war nur ein Glättungsalgorithmus, aber die Aktualisierungen wären ziemlich einfach durchzuführen.
Oliver Burdekin

Solange das Programm von der .NET-Plattform für einen Benutzer aufgerufen werden kann, auf dem nur .NET Framework 3.5 und ArcGIS 10 installiert sind. Das Programm ist Open Source und ich beabsichtige, dass dies die einzigen Softwareanforderungen sind, wenn es an Endbenutzer ausgeliefert wird. Wenn Ihre Antwort implementiert werden kann, um diese beiden Anforderungen zu erfüllen, wird sie als gültige Antwort betrachtet. Ich werde der Frage zur Verdeutlichung auch ein Versions-Tag hinzufügen.
Conor

2

Eine Lösung:

Ich habe das heute früher gelöst. Der Code ist eine Anpassung dieser Methode . Das Konzept dahinter war nicht sonderlich schwierig, als ich herausgefunden hatte, was die Objekte, die für die Schnittstelle zum Raster verwendet wurden, tatsächlich tun. Die folgende Methode verwendet zwei Eingabedatensätze (inRasterDS und outRasterDS). Sie sind beide der gleiche Datensatz. Ich habe gerade eine Kopie von inRasterDS erstellt und sie als outRasterDS an die Methode übergeben. Auf diese Weise haben beide dieselbe Ausdehnung, denselben Raumbezug usw. Die Methode liest die Werte aus inRasterDS Zelle für Zelle und vergleicht sie mit den nächsten Nachbarn. Die Ergebnisse dieser Vergleiche werden als gespeicherte Werte in outRasterDS verwendet.

Der Prozess:

Ich habe IRasterCursor -> IPixelBlock -> SafeArray verwendet, um die Pixelwerte zu ermitteln, und IRasterEdit, um neue Werte in das Raster zu schreiben. Wenn Sie IPixelBlock erstellen, teilen Sie dem Gerät die Größe und Position des Bereichs mit, in den Sie lesen / schreiben möchten. Wenn Sie nur die untere Hälfte eines Rasters bearbeiten möchten, legen Sie dies als IPixelBlock-Parameter fest. Wenn Sie das gesamte Raster durchlaufen möchten, müssen Sie IPixelBlock auf die Größe des gesamten Rasters einstellen. In der folgenden Methode übergebe ich dazu die Größe an IRasterCursor (pSize) und erhalte dann den PixelBlock vom Rastercursor.

Der andere Schlüssel ist, dass Sie SafeArray verwenden müssen, um mit den Werten in dieser Methode zu kommunizieren. Sie erhalten IPixelBlock von IRasterCursor, dann SafeArray von IPixelBlock. Dann lesen und schreiben Sie an SafeArray. Wenn Sie mit dem Lesen / Schreiben in SafeArray fertig sind, schreiben Sie Ihr gesamtes SafeArray zurück in IPixelBlock, schreiben Sie dann Ihren IPixelBlock in IRasterCursor und verwenden Sie schließlich IRasterCursor, um den Speicherort für den Start des Schreibvorgangs festzulegen und IRasterEdit, um den Schreibvorgang selbst durchzuführen. In diesem letzten Schritt bearbeiten Sie die Werte des Datensatzes.

    public static void CreateBoundaryRaster(IRasterDataset2 inRasterDS, IRasterDataset2 outRasterDS)
    {
        try
        {
            //Create a raster. 
            IRaster2 inRaster = inRasterDS.CreateFullRaster() as IRaster2; //Create dataset from input raster
            IRaster2 outRaster = outRasterDS.CreateFullRaster() as IRaster2; //Create dataset from output raster
            IRasterProps pInRasterProps = (IRasterProps)inRaster;
            //Create a raster cursor with a pixel block size matching the extent of the input raster
            IPnt pSize = new DblPnt();
            pSize.SetCoords(pInRasterProps.Width, pInRasterProps.Height); //Give the size of the raster as a IPnt to pass to IRasterCursor
            IRasterCursor inrasterCursor = inRaster.CreateCursorEx(pSize); //Create IRasterCursor to parse input raster 
            IRasterCursor outRasterCursor = outRaster.CreateCursorEx(pSize); //Create IRasterCursor to parse output raster
            //Declare IRasterEdit, used to write the new values to raster
            IRasterEdit rasterEdit = outRaster as IRasterEdit;
            IRasterBandCollection inbands = inRasterDS as IRasterBandCollection;//set input raster as IRasterBandCollection
            IRasterBandCollection outbands = outRasterDS as IRasterBandCollection;//set output raster as IRasterBandCollection
            IPixelBlock3 inpixelblock3 = null; //declare input raster IPixelBlock
            IPixelBlock3 outpixelblock3 = null; //declare output raster IPixelBlock
            long blockwidth = 0; //store # of columns of raster
            long blockheight = 0; //store # of rows of raster

            //create system array for input/output raster. System array is used to interface with values directly. It is a grid that overlays your IPixelBlock which in turn overlays your raster.
            System.Array inpixels; 
            System.Array outpixels; 
            IPnt tlc = null; //set the top left corner

            // define the 3x3 neighborhood objects
            object center;
            object topleft;
            object topmiddle;
            object topright;
            object middleleft;
            object middleright;
            object bottomleft;
            object bottommiddle;
            object bottomright;

            long bandCount = outbands.Count; //use for multiple bands (only one in this case)

            do
            {

                inpixelblock3 = inrasterCursor.PixelBlock as IPixelBlock3; //get the pixel block from raster cursor
                outpixelblock3 = outRasterCursor.PixelBlock as IPixelBlock3;
                blockwidth = inpixelblock3.Width; //set the # of columns in raster
                blockheight = inpixelblock3.Height; //set the # of rows in raster
                outpixelblock3.Mask(255); //set any NoData values

                for (int k = 0; k < bandCount; k++) //for every band in raster (will always be 1 in this case)
                {
                    //Get the pixel array.
                    inpixels = (System.Array)inpixelblock3.get_PixelData(k); //store the raster values in a System Array to read
                    outpixels = (System.Array)outpixelblock3.get_PixelData(k); //store the raster values in a System Array to write
                    for (long i = 1; i < blockwidth - 1; i++) //for every column (except outside columns)
                    {
                        for (long j = 1; j < blockheight - 1; j++) //for every row (except outside rows)
                        {
                            //Get the pixel values of center cell and  neighboring cells

                            center = inpixels.GetValue(i, j);

                            topleft = inpixels.GetValue(i - 1, j + 1);
                            topmiddle = inpixels.GetValue(i, j + 1);
                            topright = inpixels.GetValue(i + 1, j + 1);
                            middleleft = inpixels.GetValue(i - 1, j);
                            middleright = inpixels.GetValue(i + 1, j);
                            bottomleft = inpixels.GetValue(i - 1, j - 1);
                            bottommiddle = inpixels.GetValue(i, j - 1);
                            bottomright = inpixels.GetValue(i - 1, j - 1);


                            //compare center cell value with middle left cell and middle right cell in a 3x3 grid. If true, give output raster value of 1
                            if ((Convert.ToDouble(center) >= Convert.ToDouble(middleleft)) && (Convert.ToDouble(center) >= Convert.ToDouble(middleright)))
                            {
                                outpixels.SetValue(1, i, j);
                            }


                            //compare center cell value with top middle and bottom middle cell in a 3x3 grid. If true, give output raster value of 1
                            else if ((Convert.ToDouble(center) >= Convert.ToDouble(topmiddle)) && (Convert.ToDouble(center) >= Convert.ToDouble(bottommiddle)))
                            {
                                outpixels.SetValue(1, i, j);
                            }

                            //if neither conditions are true, give raster value of 0
                            else
                            {

                                outpixels.SetValue(0, i, j);
                            }
                        }
                    }
                    //Write the pixel array to the pixel block.
                    outpixelblock3.set_PixelData(k, outpixels);
                }
                //Finally, write the pixel block back to the raster.
                tlc = outRasterCursor.TopLeft;
                rasterEdit.Write(tlc, (IPixelBlock)outpixelblock3);
            }
            while (inrasterCursor.Next() == true && outRasterCursor.Next() == true);
            System.Runtime.InteropServices.Marshal.ReleaseComObject(rasterEdit);


        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }

    }

1

AFAIK-Rasterdaten können auf drei Arten gelesen werden:

  • nach Zelle (ineffizient);
  • vom Bild her (ziemlich effizient);
  • nach Blöcken (der effizienteste Weg).

Ohne das Rad neu zu erfinden, empfehle ich, diese aufschlussreichen Folien zu lesen von Chris Garrard .

Die effizienteste Methode ist also das blockweise Lesen von Daten. Dies würde jedoch zu einem Datenverlust bei der Entsprechung von Pixeln führen, die sich während der Anwendung des Filters über den Blockgrenzen befinden. Eine sichere Alternative sollte also darin bestehen, das gesamte Bild auf einmal zu lesen und den Numpy-Ansatz zu verwenden.

Auf der Rechnerseite sollte ich stattdessen gdalfilter.py und implizit den VRT KernelFilteredSource-Ansatz verwenden, um die erforderlichen Filter anzuwenden und vor allem schwere Berechnungen zu vermeiden.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.