Python3.8下使用GDAL分块上采样栅格时最后一块TIF存储不完整
异常触发原因
- 核心问题是GeoTIFF文件头写入被进程退出流程打断:GDAL的GeoTIFF驱动默认将图像索引、文件头写在存储文件的末尾位置,完整的写入流程是:先逐块写入所有像素数据到磁盘,最后再把完整的文件头追加到文件末尾。你现在的代码在写完第10块的所有像素数据后,循环立刻结束,Python进程马上触发退出流程,刚好这一步截断在“准备写文件头”的节点,导致磁盘上的文件只保留了像素数据部分(1.7M),缺少末尾的文件头块,因此文件是不完整的。
- 两次运行后旧残文件被补全的原因:第一次运行结束后,系统还持有没正常关闭的第10块文件的句柄,文件头内容还存在系统缓存里。当用户第二次启动脚本时,进程初始化过程中会释放上一轮残留的文件句柄,系统缓存自动把之前暂存的文件头刷到磁盘上,因此原本1.7M的残文件会被补全到2M的完整大小;但本轮新生成的第10块文件,还是会因为同样的进程退出打断问题,在写完像素数据、准备写文件头的时候被截断,再次出现不完整的情况。
- 隐含的分块尺寸硬编码问题:你写死了每块读取的行列数为25852,没有根据全局影像的实际尺寸判断最后一块的有效读取范围。当当前块的偏移量靠近全局影像边界时,固定的读取尺寸会超出影像边界,GDAL会自动将越界部分截断,导致最后一块的数组尺寸和其他块不一致,进一步放大写入不全的问题。
代码调整方案
- 补全全链路刷盘逻辑:在原来仅对栅格带执行
FlushCache的基础上,对整个输出数据集对象也执行刷盘,并且在刷盘完成后主动将所有关联的写对象、读对象置空,强制GDAL立即完成文件全流程写入(包括末尾文件头),不依赖Python的垃圾回收时机。 - 修正分块读取的越界截断问题:不要硬编码固定的读取行列数,每次读取块前计算当前位置下的有效读取尺寸,保证最后一块的读取内容完整、不越界。
- 移除循环内重复的对象初始化:将驱动、空间参考系统的初始化移到循环外,减少不必要的对象创建开销。
核心修改代码片段
# 循环外提前初始化全局对象 src = gdal.Open('Global_database.tif') ds_xform = src.GetGeoTransform() ds_driver = gdal.GetDriverByName('Gtiff') srs = osr.SpatialReference() srs.ImportFromEPSG(4326) # 读取块时,修正越界问题 x_read_count = min(xcount, src.RasterXSize - int(xoff[p])) y_read_count = min(ycount, src.RasterYSize - int(yoff[q])) data = src.GetRasterBand(1).ReadAsArray( int(xoff[p]), int(yoff[q]), x_read_count, y_read_count ).astype(np.float32) # 每个块的写入流程末尾,替换原来的释放逻辑 band.WriteArray(zero_array) band.FlushCache() # 对整个数据集执行刷盘 resized_array.FlushCache() # 主动释放写侧所有对象,触发GDAL完整关闭流程 band = None resized_array = None # 主动释放读侧对象 src = None del zero_array del Var o=o+1
注意:不要依赖Python进程退出时的自动资源回收机制处理大尺寸Tiff的写入,必须在循环内逐块完成刷盘和资源释放,否则长循环运行过程中很容易因为缓存溢出、回收顺序随机导致文件写入不完整。
内容的提问来源于stack exchange,提问作者Zara_prs
相关产品推荐
相关产品推荐

