파이썬의 래스터에서 점 데이터의 이중 선형 보간?


12

포인트 보간을하고 싶은 래스터가 있습니다. 내가있는 곳은 다음과 같습니다.

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

지금까지 SciPy의 interp2d 기능을 시도했습니다 .

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

그러나 317 × 301 래스터가있는 32 비트 Windows 시스템에서 메모리 오류가 발생합니다.

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

bounds_error또는 fill_value매개 변수가 문서화 된대로 작동하지 않으므로이 SciPy 기능에 대한 신뢰가 제한적임을 인정 합니다. 래스터가 317 × 301이므로 이중 선형 알고리즘 이 어렵지 않기 때문에 왜 메모리 오류가 발생 해야하는지 알 수 없습니다.

누구든지 NumPy에 적합하게 파이썬에서 좋은 쌍 선형 보간 알고리즘을 경험 한 적이 있습니까? 힌트 나 조언이 있습니까?


(참고 : 가장 가까운 이웃 보간 알고리즘 은 쉬운 케이크입니다.

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]

...하지만 나는 이중 선형 보간 방법을 선호합니다)


1
아마도 당신은 MemoryErrorNumPy가 너의 너머에 액세스하려고하기 때문에 얻을 수 band_array있습니까? ax및 을 확인해야합니다 ay.
olt

1
ax, 그리드가 전혀 회전하지 않으면 문제가있을 수 있습니다. 보간 점을 픽셀 또는 데이터 좌표로 변환하는 것이 좋습니다. 또한, 그들에게 하나씩 문제가 있다면, 당신은 밴드의 크기를 넘어 설 수 있습니다.
Dave X

올바른 회전 그리드는 그리드 공간으로 변환 한 다음 다시 좌표 공간으로 변환해야합니다. 이것은의 아핀 변환 계수의 역수가 필요합니다 gt.
Mike T

답변:


7

아래 공식을 Wikipedia 에서 Python-speak로 변환하여 다음 알고리즘을 생성했습니다.

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)

결과는 NumPy의 dtype('float64')데이터 유형 으로 업 클래스되므로 소스 데이터보다 훨씬 높은 정밀도로 결과가 리턴됩니다 . return 값 .astype(band_array.dtype)을 사용하여 출력 데이터 유형을 입력 배열과 동일하게 만들 수 있습니다 .

쌍 선형 보간 공식


3

비슷한 결과를 로컬로 시도했지만 64 비트 플랫폼에 있으므로 메모리 제한에 도달하지 않았습니다. 대신 이 예제 와 같이 한 번에 작은 배열 조각을 보간 해보십시오 .

커맨드 라인에서 GDAL을 사용 하여이 작업을 수행 할 수도 있습니다.

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

Python에서 동등한 작업을 수행하려면 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...    

보간하려는 내 포인트 데이터가 정기적으로 간격이 없으므로 GDAL의 내장 ReprojectImage기술을 사용할 수 없습니다 .
Mike T

1

과거에 정확한 문제가 있었으며 interpolate.interp2d를 사용하여 해결하지 못했습니다. scipy.ndimage.map_coordinates를 사용하여 성공 했습니다 . 다음을 시도하십시오 :

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

이것은 bilinear와 동일한 출력을주는 것으로 보입니다.


소스 래스터 좌표가 픽셀 좌표를 사용하는 대신 어떻게 사용되는지 확실하지 않기 때문에 이것에 약간 빠져 버렸습니다. 많은 점을 해결하기 위해 "벡터화"되어 있습니다.
Mike T

동의합니다, 나는 scipy를 정말로 이해하지 못합니다. 당신의 numpy 솔루션이 훨씬 좋습니다.
Matthew Snape

0

scipy.interpolate.interp2d ()는 더 현대적인 scipy와 잘 작동합니다. 구형 버전은 불규칙한 그리드를 가정하고 일반 그리드를 사용하지 않는다고 생각합니다. scipy와 동일한 오류가 발생합니다. 버전 = 0.11.0이지만 scipy입니다. version = 0.14.0, 일부 1600x1600 모델 출력에서 ​​행복하게 작동합니다.

질문에 힌트를 주셔서 감사합니다.

#!/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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.