基于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
相关产品推荐
相关产品推荐

