Dividere il raster in blocchi più piccoli usando GDAL?


18

Ho un raster (USGS DEM in realtà) e ho bisogno di dividerlo in pezzi più piccoli come mostra l'immagine qui sotto. Ciò è stato realizzato in ArcGIS 10.0 usando lo strumento Split Raster. Vorrei un metodo FOSS per fare questo. Ho guardato GDAL, pensando sicuramente che lo avrebbe fatto (in qualche modo con gdal_translate), ma non riesco a trovare nulla. Alla fine, mi piacerebbe essere in grado di prendere il raster e dire quanto grande (4KM da blocchi 4KM) mi piacerebbe dividere in.

inserisci qui la descrizione dell'immagine


Ho un'utilità che utilizza subprocess.Popen per eseguire più gdal si traduce contemporaneamente che utilizzo per estrarre un grande raster su riquadri utilizzando una rete da pesca, particolarmente utile se l'input e / o l'output sono altamente compressi (ad esempio LZW o deflate GeoTiff ), se nessuno dei due è altamente compresso, il processo raggiunge un picco nell'accesso all'HDD e non è molto più veloce dell'esecuzione uno alla volta. Sfortunatamente non è abbastanza generico da condividere a causa di rigide convenzioni di denominazione ma comunque spunti di riflessione. L'opzione -multi per GDALWarp causa spesso problemi e utilizza solo 2 thread (uno letto, uno scritto) non tutti disponibili.
Michael Stimson,

Risposte:


18

gdal_translate funzionerà usando le opzioni -srcwin o -projwin.

-srcwin xoff yoff xsize ysize: seleziona una sottofinestra dall'immagine di origine per la copia in base alla posizione di pixel / linea.

-projwin ulx uly lrx lry: seleziona una sottofinestra dall'immagine sorgente per la copia (come -srcwin) ma con gli angoli indicati in coordinate georeferenziate.

Dovresti trovare le posizioni dei pixel / linee o le coordinate degli angoli e poi passare sopra i valori con gdal_translate. Qualcosa come il pitone veloce e sporco qui sotto funzionerà se usare i valori di pixel e -srcwin è adatto a te, sarà un po 'più di lavoro per risolvere le coordinate.

import os, gdal
from gdalconst import *

width = 512
height = 512
tilesize = 64

for i in range(0, width, tilesize):
    for j in range(0, height, tilesize):
        gdaltranString = "gdal_translate -of GTIFF -srcwin "+str(i)+", "+str(j)+", "+str(tilesize)+", " \
            +str(tilesize)+" utm.tif utm_"+str(i)+"_"+str(j)+".tif"
        os.system(gdaltranString)

1
Salve, quando provo l'opzione -projwin con un'immagine geotiff, ricevo un avviso che dice "Avviso: calcolato -srcwin -3005000 1879300 50 650 non rientra completamente nell'estensione raster. Continuando comunque" Non sono sicuro di dove sto sbagliando sembra che non lo faccia usando le sue coordinate georeferenziate.
ncelik,

@ncelik probabilmente perché stai usando le coordinate della cella nel tuo projwin e dovresti invece usare srcwin. In caso di difficoltà, si prega di inviare una nuova domanda con tutte le informazioni pertinenti in modo che possiamo fornire suggerimenti sul problema specifico.
Michael Stimson,

15

La mia soluzione, basata su quella di @wwnick, legge le dimensioni raster dal file stesso e copre l'intera immagine rendendo più piccoli i riquadri del bordo, se necessario:

import os, sys
from osgeo import gdal

dset = gdal.Open(sys.argv[1])

width = dset.RasterXSize
height = dset.RasterYSize

print width, 'x', height

tilesize = 5000

for i in range(0, width, tilesize):
    for j in range(0, height, tilesize):
        w = min(i+tilesize, width) - i
        h = min(j+tilesize, height) - j
        gdaltranString = "gdal_translate -of GTIFF -srcwin "+str(i)+", "+str(j)+", "+str(w)+", " \
            +str(h)+" " + sys.argv[1] + " " + sys.argv[2] + "_"+str(i)+"_"+str(j)+".tif"
        os.system(gdaltranString)

Penso che dovrebbe essere sys.argv [1] dove dice sys.argv [2], giusto?
Oskarlin,

3
credo che sys.argv [2] sia usato come prefisso per i file di output. Super utile-- grazie @Ries!
Charlie Hofmann,

4

Esiste uno script python in bundle specifico per la ricalcatura di raster, gdal_retile :

gdal_retile.py [-v] [-co NAME=VALUE]* [-of out_format] [-ps pixelWidth pixelHeight]
               [-overlap val_in_pixel]
               [-ot  {Byte/Int16/UInt16/UInt32/Int32/Float32/Float64/
                      CInt16/CInt32/CFloat32/CFloat64}]'
               [ -tileIndex tileIndexName [-tileIndexField tileIndexFieldName]]
               [ -csv fileName [-csvDelim delimiter]]
               [-s_srs srs_def]  [-pyramidOnly]
               [-r {near/bilinear/cubic/cubicspline/lanczos}]
               -levels numberoflevels
               [-useDirForEachRow]
               -targetDir TileDirectory input_files

per esempio:

gdal_retile.py -ps 512 512 -targetDir C:\example\dir some_dem.tif


4

Per @Aaron che ha chiesto:

Spero di trovare una versione gdalwarp della risposta di @ wwnick che utilizza l'opzione -multi per operazioni multicore e multithread ottimizzate

Disclaimer lieve

Questo usa gdalwarp, anche se non sono del tutto convinto che ci saranno molti miglioramenti delle prestazioni. Finora sono stato associato all'I / O: l'esecuzione di questo script su un grande raster tagliandolo in molte parti più piccole non sembra impegnativo per la CPU, quindi presumo che il collo di bottiglia stia scrivendo sul disco. Se stai pianificando di riproiettare contemporaneamente le tessere o qualcosa di simile, questo potrebbe cambiare. Ci sono suggerimenti per la messa a punto qui . Una breve interpretazione non ha prodotto alcun miglioramento per me e la CPU non è mai stata il fattore limitante.

Disclaimer a parte, ecco uno script che userà gdalwarpper dividere un raster in più tessere più piccole. Potrebbero esserci delle perdite dovute alla divisione del pavimento, ma questo può essere risolto selezionando il numero di tessere che desideri. Sarà n+1dove si ntrova il numero diviso per ottenere le variabili tile_widthe tile_height.

import subprocess
import gdal
import sys


def gdalwarp(*args):
    return subprocess.check_call(['gdalwarp'] + list(args))


src_path = sys.argv[1]
ds = gdal.Open(src_path)

try:
    out_base = sys.argv[2]
except IndexError:
    out_base = '/tmp/test_'

gt = ds.GetGeoTransform()

width_px = ds.RasterXSize
height_px = ds.RasterYSize

# Get coords for lower left corner
xmin = int(gt[0])
xmax = int(gt[0] + (gt[1] * width_px))

# get coords for upper right corner
if gt[5] > 0:
    ymin = int(gt[3] - (gt[5] * height_px))
else:
    ymin = int(gt[3] + (gt[5] * height_px))

ymax = int(gt[3])

# split height and width into four - i.e. this will produce 25 tiles
tile_width = (xmax - xmin) // 4
tile_height = (ymax - ymin) // 4

for x in range(xmin, xmax, tile_width):
    for y in range(ymin, ymax, tile_height):
        gdalwarp('-te', str(x), str(y), str(x + tile_width),
                 str(y + tile_height), '-multi', '-wo', 'NUM_THREADS=ALL_CPUS',
                 '-wm', '500', src_path, out_base + '{}_{}.tif'.format(x, y))

3

È possibile utilizzare r.tile di GRASS GIS. r.tile genera una mappa raster separata per ogni riquadro con nomi di mappe numerati in base al prefisso definito dall'utente. È possibile definire la larghezza delle piastrelle (colonne) e l'altezza delle piastrelle (righe).

Utilizzando l' API Python grass-session sono necessarie solo poche righe di codice Python per chiamare la funzionalità r.tile dall'esterno, ovvero per scrivere uno script autonomo. Utilizzando r.external e r.external.out anche durante la fase di elaborazione GIS di GRASS non si verifica alcuna duplicazione dei dati.

Pseudo codice:

  1. inizializza grass-session
  2. definire il formato di output con r.external.out
  3. collegare il file di input con r.external
  4. eseguire r.tile che genera i riquadri nel formato sopra definito
  5. scollegare r.external.out
  6. chiudi erba
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.