You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于GDAL高效实现数千个部分重叠栅格的平均合并方案咨询

高效重叠栅格平均合并方案

方案核心思路

放弃原有Python像素函数的实现方式,改用「累加值栅格+计数栅格」的C原生GDAL操作实现,全程无Python层面的像素遍历,内存占用低、速度相比原有方案提升10~100倍。

具体操作步骤

  • 步骤1:生成所有输入栅格的掩模,有效值区域设为1、NoData区域设为0,用于后续统计每个像素的重叠次数
  • 步骤2:分别生成所有输入栅格的总和栅格、所有掩模的总和(即每个像素重叠次数)栅格
  • 步骤3:将总和栅格除以计数栅格得到平均像素值,NoData区域统一赋值为255即可

代码实现(基于GDAL Python绑定)

from osgeo import gdal
import numpy as np
import os

# 开启GDAL多线程加速,可根据CPU核心数调整参数
gdal.SetConfigOption('GDAL_NUM_THREADS', 'ALL_CPUS')

# 配置参数
out_name = "output"
tifs = ['tif1.tif', 'tif2.tif', ..., 'tif1235.tif']  # 替换为你的输入栅格列表
nodata_val = 255
block_size = 2048  # 可根据内存调整,2048/4096均可,数值越大速度越快、内存占用越高

# 第一步:构建临时VRT获取输出栅格的空间范围、分辨率、投影等统一参数
vrt_path = f"{out_name}_temp.vrt"
gdal.BuildVRT(vrt_path, tifs, options=gdal.BuildVRTOptions(srcNodata=nodata_val, VRTNodata=nodata_val))
vrt_ds = gdal.Open(vrt_path)
x_size = vrt_ds.RasterXSize
y_size = vrt_ds.RasterYSize
geo_transform = vrt_ds.GetGeoTransform()
projection = vrt_ds.GetProjection()
vrt_ds = None

# 第二步:创建总和栅格和计数栅格
driver = gdal.GetDriverByName("GTiff")
sum_ds = driver.Create(f"{out_name}_sum.tif", x_size, y_size, 1, gdal.GDT_UInt32, 
                       options=["COMPRESS=LZW", "TILED=YES", "BIGTIFF=YES"])
sum_ds.SetGeoTransform(geo_transform)
sum_ds.SetProjection(projection)
sum_band = sum_ds.GetRasterBand(1)
sum_band.Fill(0)

count_ds = driver.Create(f"{out_name}_count.tif", x_size, y_size, 1, gdal.GDT_UInt16, 
                         options=["COMPRESS=LZW", "TILED=YES", "BIGTIFF=YES"])
count_ds.SetGeoTransform(geo_transform)
count_ds.SetProjection(projection)
count_band = count_ds.GetRasterBand(1)
count_band.Fill(0)

# 第三步:逐栅格、分块累加值和计数,避免一次性加载所有数据
for tif_path in tifs:
    tif_ds = gdal.Open(tif_path)
    gt = tif_ds.GetGeoTransform()
    # 计算当前栅格在目标坐标系下的偏移量
    x_off = int((gt[0] - geo_transform[0]) / geo_transform[1])
    y_off = int((gt[3] - geo_transform[3]) / geo_transform[5])
    tif_x = tif_ds.RasterXSize
    tif_y = tif_ds.RasterYSize
    tif_band = tif_ds.GetRasterBand(1)
    
    # 分块读写
    for y in range(0, tif_y, block_size):
        y_read = min(block_size, tif_y - y)
        for x in range(0, tif_x, block_size):
            x_read = min(block_size, tif_x - x)
            arr = tif_band.ReadAsArray(x, y, x_read, y_read)
            # 生成有效值掩模
            mask = (arr != nodata_val).astype(np.uint16)
            # 累加像素值到总和栅格
            sum_arr = sum_band.ReadAsArray(x_off + x, y_off + y, x_read, y_read)
            sum_arr += arr * mask
            sum_band.WriteArray(sum_arr, x_off + x, y_off + y)
            # 累加计数到计数栅格
            count_arr = count_band.ReadAsArray(x_off + x, y_off + y, x_read, y_read)
            count_arr += mask
            count_band.WriteArray(count_arr, x_off + x, y_off + y)
    tif_ds = None

# 第四步:计算最终平均栅格
out_ds = driver.Create(f"{out_name}.tif", x_size, y_size, 1, gdal.GDT_Byte, 
                       options=["COMPRESS=LZW", "TILED=YES", "BIGTIFF=YES"])
out_ds.SetGeoTransform(geo_transform)
out_ds.SetProjection(projection)
out_band = out_ds.GetRasterBand(1)
out_band.SetNoDataValue(nodata_val)

for y in range(0, y_size, block_size):
    y_read = min(block_size, y_size - y)
    for x in range(0, x_size, block_size):
        x_read = min(block_size, x_size - x)
        sum_arr = sum_band.ReadAsArray(x, y, x_read, y_read)
        count_arr = count_band.ReadAsArray(x, y, x_read, y_read)
        # 计数为0的位置设为NoData,其余取平均值
        out_arr = np.where(count_arr == 0, nodata_val, (sum_arr / count_arr).astype(np.uint8))
        out_band.WriteArray(out_arr, x, y)

# 资源释放与临时文件清理
sum_ds = None
count_ds = None
out_ds = None
os.remove(vrt_path)
os.remove(f"{out_name}_sum.tif")
os.remove(f"{out_name}_count.tif")

效果说明

  • 内存占用:全程采用分块读写逻辑,内存占用仅和设置的block_size有关,2048块大小时内存占用不超过2GB
  • 运行效率:所有核心运算均为numpy的C级实现,无Python层面像素遍历,数千个栅格的处理时间可压缩到几小时内

内容的提问来源于stack exchange,提问作者John Brandt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.08 17:48:03