使用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
相关产品推荐
相关产品推荐

