Interpolazione bilineare dei dati dei punti su un raster in Python?


12

Ho un raster con cui mi piacerebbe fare alcune interpolazioni puntuali. Ecco dove sono:

from osgeo import gdal
from numpy import array

# Read raster
source = gdal.Open('my_raster.tif')
nx, ny = source.RasterXSize, source.RasterYSize
gt = source.GetGeoTransform()
band_array = source.GetRasterBand(1).ReadAsArray()
# Close raster
source = None

# Compute mid-point grid spacings
ax = array([gt[0] + ix*gt[1] + gt[1]/2.0 for ix in range(nx)])
ay = array([gt[3] + iy*gt[5] + gt[5]/2.0 for iy in range(ny)])

Fino ad ora, ho provato la funzione interp2d di SciPy :

from scipy import interpolate
bilinterp = interpolate.interp2d(ax, ay, band_array, kind='linear')

tuttavia ricevo un errore di memoria sul mio sistema Windows a 32 bit con un raster 317 × 301:

Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "C:\Python25\Lib\site-packages\scipy\interpolate\interpolate.py", line 125, in __init__
    self.tck = fitpack.bisplrep(self.x, self.y, self.z, kx=kx, ky=ky, s=0.)
  File "C:\Python25\Lib\site-packages\scipy\interpolate\fitpack.py", line 873, in bisplrep
tx,ty,nxest,nyest,wrk,lwrk1,lwrk2)
MemoryError

Lo ammetto, ho una fiducia limitata in questa funzione SciPy, poiché i parametri bounds_erroro fill_valuenon funzionano come documentato. Non vedo perché dovrei avere un errore di memoria, poiché il mio raster è 317 × 301 e l' algoritmo bilineare non dovrebbe essere difficile.

Qualcuno ha trovato un buon algoritmo di interpolazione bilineare, preferibilmente in Python, possibilmente su misura con NumPy? Qualche suggerimento o consiglio?


(Nota: l' algoritmo di interpolazione più vicino è semplice:

from numpy import argmin, NAN

def nearest_neighbor(px, py, no_data=NAN):
    '''Nearest Neighbor point at (px, py) on band_array
    example: nearest_neighbor(2790501.920, 6338905.159)'''
    ix = int(round((px - (gt[0] + gt[1]/2.0))/gt[1]))
    iy = int(round((py - (gt[3] + gt[5]/2.0))/gt[5]))
    if (ix < 0) or (iy < 0) or (ix > nx - 1) or (iy > ny - 1):
        return no_data
    else:
        return band_array[iy, ix]

... ma preferisco di gran lunga i metodi di interpolazione bilineare)


1
Forse hai MemoryErrorcapito perché NumPy prova ad accedere oltre il tuo band_array? Dovresti controllare axe ay.
olt

1
ax, ay potrebbe avere un problema se la griglia viene ruotata affatto. Potrebbe essere meglio trasformare i punti interpolati in coordinate pixel o dati. Inoltre, se c'è un problema unico con loro, allora potresti andare oltre le dimensioni della band.
Dave X,

Le griglie corrette e ruotate richiedono la trasformazione in griglia-spazio, quindi di nuovo in coordinate-spazio. Ciò richiede l'inverso dei coefficienti di trasformazione affine in gt.
Mike T,

Risposte:


7

Ho tradotto la formula seguente (da Wikipedia ) in Python-speak per produrre il seguente algoritmo, che sembra funzionare.

from numpy import floor, NAN

def bilinear(px, py, no_data=NAN):
    '''Bilinear interpolated point at (px, py) on band_array
    example: bilinear(2790501.920, 6338905.159)'''
    ny, nx = band_array.shape
    # Half raster cell widths
    hx = gt[1]/2.0
    hy = gt[5]/2.0
    # Calculate raster lower bound indices from point
    fx = (px - (gt[0] + hx))/gt[1]
    fy = (py - (gt[3] + hy))/gt[5]
    ix1 = int(floor(fx))
    iy1 = int(floor(fy))
    # Special case where point is on upper bounds
    if fx == float(nx - 1):
        ix1 -= 1
    if fy == float(ny - 1):
        iy1 -= 1
    # Upper bound indices on raster
    ix2 = ix1 + 1
    iy2 = iy1 + 1
    # Test array bounds to ensure point is within raster midpoints
    if (ix1 < 0) or (iy1 < 0) or (ix2 > nx - 1) or (iy2 > ny - 1):
        return no_data
    # Calculate differences from point to bounding raster midpoints
    dx1 = px - (gt[0] + ix1*gt[1] + hx)
    dy1 = py - (gt[3] + iy1*gt[5] + hy)
    dx2 = (gt[0] + ix2*gt[1] + hx) - px
    dy2 = (gt[3] + iy2*gt[5] + hy) - py
    # Use the differences to weigh the four raster values
    div = gt[1]*gt[5]
    return (band_array[iy1,ix1]*dx2*dy2/div +
            band_array[iy1,ix2]*dx1*dy2/div +
            band_array[iy2,ix1]*dx2*dy1/div +
            band_array[iy2,ix2]*dx1*dy1/div)

Si noti che il risultato verrà restituito con una precisione apparentemente superiore rispetto ai dati di origine, poiché è aggiornato al dtype('float64')tipo di dati di NumPy . È possibile utilizzare il valore restituito con .astype(band_array.dtype)per rendere il tipo di dati di output uguale all'array di input.

formula di interpolazione bilineare


3

L'ho provato localmente con risultati simili, ma sono su una piattaforma a 64 bit, quindi non ha raggiunto il limite di memoria. Forse invece prova a interpolare piccoli pezzi dell'array alla volta, come in questo esempio .

Potresti anche farlo con GDAL, dalla riga di comando sarebbe:

gdalwarp -ts $XSIZE*2 0 -r bilinear input.tif interp.tif

Per eseguire l'operazione equivalente in Python, usa ReprojectImage () :

mem_drv = gdal.GetDriverByName('MEM')
dest = mem_drv.Create('', nx, ny, 1)

resample_by = 2
dt = (gt[0], gt[1] * resample_by, gt[2], gt[3], gt[4], gt[5] * resample_by)
dest.setGeoTransform(dt)

resampling_method = gdal.GRA_Bilinear    
res = gdal.ReprojectImage(source, dest, None, None, resampling_method)

# then, write the result to a file of your choice...    

I dati dei miei punti che vorrei interpolare non sono distribuiti regolarmente, quindi non posso usare la ReprojectImagetecnica integrata di GDAL .
Mike T,

1

Ho avuto il problema esatto in passato e non l'ho mai risolto usando interpolate.interp2d. Ho avuto successo usando scipy.ndimage.map_coordinates . Prova quanto segue:

scipy.ndimage.map_coordinates (band_array, [ax, ay]], order = 1)

Questo sembra dare lo stesso output di quello bilineare.


Sono stato un po 'scartato da questo, poiché non sono sicuro di come vengano utilizzate le coordinate raster di origine (anziché utilizzare le coordinate pixel). Vedo che è "vettorializzato" per risolvere molti punti.
Mike T

D'accordo, non capisco davvero Scipy. La tua soluzione intorpidita molto meglio.
Matthew Snape,

0

scipy.interpolate.interp2d () funziona bene con scipy più moderno. Penso che le versioni precedenti assumano griglie irregolari e non sfruttino le griglie normali. Ottengo lo stesso errore che fai con Scipy. versione = 0.11.0, ma su scipy. versione = 0.14.0, funziona felicemente su alcuni output del modello 1600x1600.

Grazie per i suggerimenti nella tua domanda.

#!/usr/bin/env python

from osgeo import gdal
from numpy import array
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("filename",help='raster file from which to interpolate a (1/3,1/3) point from from')
args = parser.parse_args()

# Read raster
source = gdal.Open(args.filename)
nx, ny = source.RasterXSize, source.RasterYSize
gt = source.GetGeoTransform()
band_array = source.GetRasterBand(1).ReadAsArray()
# Close raster
source = None

# Compute mid-point grid spacings
ax = array([gt[0] + ix*gt[1] + gt[1]/2.0 for ix in range(nx)])
ay = array([gt[3] + iy*gt[5] + gt[5]/2.0 for iy in range(ny)])

from scipy import interpolate
bilinterp = interpolate.interp2d(ax, ay, band_array, kind='linear')

x1 = gt[0] + gt[1]*nx/3
y1 = gt[3] + gt[5]*ny/3.

print(nx, ny, x1,y1,bilinterp(x1,y1))

####################################

$ time ./interp2dTesting.py test.tif 
(1600, 1600, -76.322, 30.70889, array([-8609.27777778]))

real    0m4.086s
user    0m0.590s
sys 0m0.252s
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.