Nội suy song phương của dữ liệu điểm trên raster trong Python?


12

Tôi có một raster mà tôi muốn thực hiện một số phép nội suy điểm. Đây là nơi tôi đang ở:

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

Cho đến nay, tôi đã thử chức năng interp2d của SciPy :

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

tuy nhiên tôi gặp lỗi bộ nhớ trên hệ thống Windows 32 bit của mình với 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

Tôi sẽ thừa nhận, tôi đã hạn chế niềm tin vào chức năng SciPy này, vì bounds_errorhoặc fill_valuecác tham số không hoạt động như tài liệu. Tôi không thấy lý do tại sao tôi nên có lỗi bộ nhớ, vì raster của tôi là 317 × 301 và thuật toán song tuyến không khó.

Có ai bắt gặp một thuật toán nội suy song tuyến tốt, tốt nhất là bằng Python, có thể được điều chỉnh bằng NumPy không? Bất kỳ gợi ý hoặc lời khuyên?


(Lưu ý: thuật toán nội suy lân cận gần nhất là bánh dễ:

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]

... nhưng tôi rất thích các phương pháp nội suy song tuyến tính)


1
Có lẽ bạn nhận được MemoryErrorbởi vì NumPy cố gắng truy cập ngoài của bạn band_array? Bạn nên kiểm tra axay.
olt

1
ax, ay có thể có vấn đề nếu lưới được xoay hoàn toàn. Có thể tốt hơn để chuyển đổi các điểm nội suy của bạn thành các tọa độ pixel hoặc dữ liệu. Ngoài ra, nếu có vấn đề ngoài lề với họ, thì bạn có thể vượt quá kích thước của ban nhạc.
Dave X

Các lưới chính xác, xoay yêu cầu chuyển đổi sang không gian lưới sau đó quay lại không gian tọa độ. Điều này đòi hỏi nghịch đảo của các hệ số biến đổi affine trong gt.
Mike T

Câu trả lời:


7

Tôi đã dịch công thức dưới đây (từ Wikipedia ) sang Python-speak để mang lại thuật toán sau, có vẻ như hoạt động.

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)

Lưu ý rằng kết quả sẽ được trả về với độ chính xác cao hơn rõ ràng so với dữ liệu nguồn, vì nó được phân loại theo loại dtype('float64')dữ liệu NumPy . Bạn có thể sử dụng giá trị trả về với .astype(band_array.dtype)để làm cho kiểu dữ liệu đầu ra giống như mảng đầu vào.

công thức nội suy song tuyến


3

Tôi đã thử nó cục bộ với kết quả tương tự, nhưng tôi đang ở trên nền tảng 64 bit nên nó không đạt giới hạn bộ nhớ. Có lẽ thay vào đó hãy thử nội suy các phần nhỏ của mảng tại một thời điểm, như trong ví dụ này .

Bạn cũng có thể làm điều này với GDAL, từ dòng lệnh sẽ là:

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

Để thực hiện thao tác tương đương trong Python, hãy sử dụng 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...    

Dữ liệu điểm của tôi mà tôi muốn nội suy không thường xuyên cách nhau, vì vậy tôi không thể sử dụng ReprojectImagekỹ thuật tích hợp của GDAL .
Mike T

1

Tôi đã có vấn đề chính xác trong quá khứ và chưa bao giờ giải quyết vấn đề này bằng cách sử dụng interpolate.interp2d. Tôi đã thành công khi sử dụng scipy.ndimage.map_coordins . Hãy thử như sau:

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

Điều này dường như cho đầu ra tương tự như song tuyến.


Tôi đã hơi thất vọng vì điều này, vì tôi không chắc cách sử dụng tọa độ raster nguồn được sử dụng (thay vì sử dụng tọa độ pixel). Tôi thấy đó là "véc tơ" để giải quyết nhiều điểm.
Mike T

Đồng ý, tôi không thực sự hiểu scipy. Giải pháp numpy của bạn tốt hơn nhiều.
Matthew Snape

0

scipy.interpolate.interp2d () hoạt động tốt với scipy hiện đại hơn. Tôi nghĩ rằng các phiên bản cũ hơn giả định lưới không đều và không tận dụng lợi thế của lưới thông thường. Tôi nhận được lỗi tương tự như bạn làm với scipy. phiên bản = 0.11.0, nhưng trên scipy. phiên bản = 0.14.0, nó vui vẻ hoạt động trên một số đầu ra mô hình 1600x1600.

Cảm ơn bạn đã gợi ý trong câu hỏi của bạn.

#!/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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.