Bilinear interpolasi data titik pada raster dengan Python?

12

Saya memiliki raster yang ingin saya lakukan interpolasi titik dengan. Di sinilah tempat saya:

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)])

Hingga kini, saya sudah mencoba fungsi interp2d SciPy :

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

namun saya mendapatkan kesalahan memori pada sistem Windows 32-bit saya dengan 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

Saya akui, saya memiliki keyakinan terbatas pada fungsi SciPy ini, karena parameter bounds_erroratau fill_valuetidak berfungsi seperti yang didokumentasikan. Saya tidak melihat mengapa saya harus memiliki kesalahan memori, karena raster saya adalah 317 × 301, dan algoritma bilinear seharusnya tidak sulit.

Adakah yang menemukan algoritma interpolasi bilinear yang baik, lebih disukai dengan Python, mungkin disesuaikan dengan NumPy? Ada petunjuk atau saran?


(Catatan: algoritma interpolasi tetangga terdekat adalah kue mudah:

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]

... tapi saya lebih suka metode interpolasi bilinear)

Mike T
sumber
1
Mungkin Anda mendapatkan MemoryErrorkarena NumPy mencoba mengakses di luar Anda band_array? Anda harus memeriksa axdan ay.
olt
1
kapak, ay mungkin punya masalah jika grid diputar sama sekali. Mungkin lebih baik untuk mengubah titik interpolasi Anda menjadi piksel atau koordinat data. Juga, jika ada masalah satu per satu dengan mereka, maka Anda mungkin melampaui ukuran band.
Dave X
Grid yang diputar dan benar membutuhkan transformasi ke ruang-grid kemudian kembali ke ruang-koordinat. Ini membutuhkan kebalikan dari koefisien transformasi affine di gt.
Mike T

Jawaban:

7

Saya telah menerjemahkan rumus di bawah ini (dari Wikipedia ) ke dalam Python-speak untuk menghasilkan algoritma berikut, yang tampaknya berfungsi.

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)

Perhatikan bahwa hasilnya akan dikembalikan dengan ketepatan yang lebih tinggi daripada data sumber, karena ini diklasifikasi ke dtype('float64')tipe data NumPy . Anda dapat menggunakan nilai kembali dengan .astype(band_array.dtype)untuk membuat tipe data output sama dengan array input.

rumus interpolasi bilinear

Mike T
sumber
3

Saya mencobanya secara lokal untuk hasil yang serupa, tetapi saya menggunakan platform 64-bit sehingga tidak mencapai batas memori. Mungkin alih-alih coba interpolasi potongan-potongan kecil array pada suatu waktu, seperti dalam contoh ini .

Anda juga bisa melakukan ini dengan GDAL, dari baris perintah:

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

Untuk melakukan operasi yang setara di Python, gunakan 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...    
scw
sumber
Data poin saya yang ingin saya interpolasi tidak diberi spasi secara teratur, jadi saya tidak bisa menggunakan ReprojectImageteknik bawaan GDAL .
Mike T
1

Saya memiliki masalah yang tepat di masa lalu, dan tidak pernah menyelesaikannya menggunakan interpolate.interp2d. Saya telah berhasil menggunakan scipy.ndimage.map_coordinates . Coba yang berikut ini:

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

Ini tampaknya memberikan hasil yang sama dengan bilinear.

Matthew Snape
sumber
Saya agak terlempar oleh yang satu ini, karena saya tidak yakin bagaimana koordinat raster sumber digunakan untuk digunakan (daripada menggunakan koordinat piksel). Saya melihat itu "vektor" untuk memecahkan banyak hal.
Mike T
Setuju, saya tidak benar-benar mengerti omong kosong. Solusi numpy Anda jauh lebih baik.
Matthew Snape
0

scipy.interpolate.interp2d () berfungsi baik dengan scipy yang lebih modern. Saya pikir versi yang lebih lama menganggap grid tidak teratur dan tidak mengambil keuntungan dari grid biasa. Saya mendapatkan kesalahan yang sama seperti yang Anda lakukan dengan Scipy. versi = 0.11.0, tetapi pada scipy. versi = 0.14.0, itu dengan senang hati bekerja pada beberapa keluaran model 1600x1600.

Terima kasih atas petunjuknya dalam pertanyaan Anda.

#!/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
Dave X
sumber