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

使用Python GDAL多进程读写多波段影像遇Pickle错误及执行异常

问题根源
  • GDAL的GDALDataset是Swig封装的C++对象,属于**SwigPyObject**,无法被Python的pickle机制序列化。而multiprocessing在传递参数给子进程时必须序列化对象,所以直接传递ds和out会触发TypeError: cannot pickle 'SwigPyObject' object错误。
  • 你使用apply_async时的两个核心问题:
    • 参数传递错误:zip(...)返回的是迭代器,而apply_async的args需要是一个参数元组,直接传迭代器会导致worker_function接收到错误的参数类型。
    • 主进程未等待异步任务完成:apply_async是非阻塞调用,主进程执行完后直接退出,子进程还未启动就被终止,因此没有输出也没有写入数据。

可行解决方案

方案1:子进程独立打开输入输出文件(推荐)

不要传递Dataset对象,而是传递文件路径,让每个子进程自行打开输入文件(只读模式)和输出文件(更新模式),彻底规避序列化问题。

修改后的代码示例:

import os
import time
import multiprocessing as mp
import numpy as np
from osgeo import gdal


def worker_function(img_in, band, img_out):
    # 子进程独立打开数据集
    src = gdal.Open(img_in, gdal.GA_ReadOnly)
    dst = gdal.Open(img_out, gdal.GA_Update)
    
    data = src.GetRasterBand(band).ReadAsArray()
    # 在这里添加你的波段处理逻辑
    print(f'\tProcessing band {band} ...')
    
    dst.GetRasterBand(band).WriteArray(data)
    
    # 手动释放资源
    src = None
    dst = None
    return


if __name__ == '__main__':
    start = time.time()
    cores = 3
    img_in = r'c:\Users\myname\input.tif'
    img_out = r'c:\Users\myname\output.tif'
    
    # 主进程负责创建输出文件并写入元数据
    if os.path.exists(img_out):
        os.remove(img_out)
    ds = gdal.Open(img_in, gdal.GA_ReadOnly)
    print(f'Raster Size: {ds.RasterXSize, ds.RasterYSize}')
    print(f'Number of bands: {ds.RasterCount}')
    print('Creating output file ...')
    
    driver = gdal.GetDriverByName('GTiff')
    out = driver.Create(img_out, ds.RasterXSize, ds.RasterYSize, ds.RasterCount, gdal.GDT_Byte)
    out.SetProjection(ds.GetProjection())
    out.SetGeoTransform(ds.GetGeoTransform())
    # 主进程写完元数据后立即关闭数据集,让子进程可以正常打开写入
    out = None
    ds = None
    
    print(f'Setting up multiprocessing for {cores} cores ...')
    mp.freeze_support()
    print('Starting the pool ...')
    
    # 构造任务参数列表:每个任务对应(输入路径, 波段号, 输出路径)
    tasks = [(img_in, band, img_out) for band in range(1, ds.RasterCount + 1)]
    
    with mp.Pool(processes=cores) as pool:
        pool.map(worker_function, tasks)
    
    print(f'\nDone! Took me {np.round(time.time() - start, 4)}s.')

方案2:apply_async的错误修正(仅作问题分析,不推荐)

如果你坚持尝试传递Dataset对象(本质仍会触发pickle错误),需要先修正参数传递和等待逻辑,但这个方案无法解决核心的序列化问题,仅作参考:

# 仅演示apply_async的正确用法,仍会触发pickle错误
if __name__ == '__main__':
    # ... 前面的代码保持不变 ...
    with mp.Pool(processes=cores) as pool:
        # 1. 逐个提交异步任务,保存结果对象
        results = []
        for band in range(1, ds.RasterCount + 1):
            res = pool.apply_async(worker_function, args=(ds, band, out))
            results.append(res)
        # 2. 等待所有任务执行完成
        for res in results:
            res.get()
    # ... 后面的代码保持不变 ...

补充说明
  • GTiff驱动支持多进程同时写入不同波段,只要每个进程仅操作自己负责的波段,不会出现写入冲突。
  • 如果你的波段处理逻辑占用大量内存,可以考虑分块读取/写入(比如按行或固定大小块),避免一次性加载整个波段数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 20:45:31