การแก้ไข Bilinear ของข้อมูลจุดบนแรสเตอร์ใน Python หรือไม่?


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

ถึงตอนนี้ฉันได้ลองใช้ฟังก์ชั่นinterp2d ของ SciPy แล้ว :

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

แต่ฉันได้รับข้อผิดพลาดของหน่วยความจำในระบบ Windows 32 บิตที่มีแรสเตอร์ 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

ฉันจะยอมรับว่าฉันมีความมั่นใจ จำกัด ในฟังก์ชั่น SciPy นี้เนื่องจากพารามิเตอร์bounds_errorหรือfill_valueพารามิเตอร์ไม่ทำงานตามที่บันทึกไว้ ฉันไม่เห็นสาเหตุที่ฉันควรมีข้อผิดพลาดของหน่วยความจำเนื่องจากแรสเตอร์ของฉันคือ 317 × 301 และอัลกอริทึม bilinearไม่ควรยาก

มีใครเจออัลกอริธึมการแก้ไข bilinear ที่ดีโดยเฉพาะอย่างยิ่งใน Python ที่เหมาะกับ 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
บางทีคุณอาจจะได้รับMemoryErrorเพราะ NumPy พยายามที่จะเข้าถึงเกินกว่าของคุณband_array? คุณควรตรวจสอบและax ay
olt

1
ขวาน ay อาจมีปัญหาหากกริดหมุนไปเลย อาจเป็นการดีกว่าที่จะแปลงจุดแทรกเป็นพิกเซลหรือพิกัดข้อมูล นอกจากนี้หากมีปัญหาแบบออฟไลน์โดยคุณอาจจะเกินขนาดของวงดนตรี
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)

โปรดทราบว่าผลลัพธ์จะถูกส่งคืนพร้อมความแม่นยำสูงกว่าข้อมูลต้นฉบับเนื่องจากมีการจัดประเภทไว้ในdtype('float64')ประเภทข้อมูลของ NumPy คุณสามารถใช้ค่าส่งคืนด้วย.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...    

ข้อมูลจุดของฉันที่ฉันต้องการแก้ไขไม่ได้เว้นระยะอย่างสม่ำเสมอดังนั้นฉันจึงไม่สามารถใช้ReprojectImageเทคนิคในตัวของ GDAL ได้
Mike T

1

ฉันมีปัญหาที่แน่นอนในอดีตและไม่เคยแก้ไขด้วย interpolate.interp2d ฉันมีความสำเร็จโดยใช้scipy.ndimage.map_coordinates ลองทำสิ่งต่อไปนี้:

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

สิ่งนี้ดูเหมือนว่าจะให้ผลเช่นเดียวกับ bilinear


ฉันถูกโยนออกไปเล็กน้อยเพราะฉันไม่แน่ใจว่าจะใช้พิกัดแรสเตอร์ต้นทางได้อย่างไร (แทนที่จะใช้พิกัดพิกเซล) ฉันเห็นว่ามันเป็น "vectorized" เพื่อแก้ปัญหาหลายจุด
Mike T

เห็นด้วยฉันไม่เข้าใจจริง ๆ scipy วิธีแก้ปัญหาของคุณดีขึ้นมาก
Matthew Snape

0

scipy.interpolate.interp2d () ทำงานได้ดีกับ scipy ที่ทันสมัยกว่า ฉันคิดว่าเวอร์ชันเก่าจะใช้กริดที่ผิดปกติและไม่ใช้ประโยชน์จากกริดปกติ ฉันได้รับข้อผิดพลาดเช่นเดียวกับที่คุณทำกับ scipy รุ่น = 0.11.0 แต่ใช้กับ scipy version = 0.14.0 สามารถใช้งานได้กับ 1600x1600 model output อย่างมีความสุข

ขอบคุณสำหรับคำแนะนำในคำถามของคุณ

#!/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.