☰
Arcpy批量遥感影像裁剪与掩膜:从脚本到工程化实战
2026/10/14 1:11:09 网站建设 项目流程

1. 遥感影像批处理的核心痛点与Arcpy的破局思路

搞遥感的人手里没攒过几百上千景影像,那都不好意思说自己做过项目。我最早接触遥感影像处理的时候,一景一景在软件里点裁剪、点掩膜,鼠标点到手腕发酸,一整个下午就处理了二十来景,还容易漏掉或者参数设错。后来开始用Arcpy写脚本,才算真正从重复劳动里解放出来。这篇文章就围绕Arcpy批处理遥感影像的裁剪与掩膜这个主题,把我这些年在实际项目中反复打磨过的思路、代码和踩坑经验完整梳理一遍。

先说清楚这个内容适合谁看。如果你手头有几十景甚至上千景遥感影像,需要按研究区边界统一裁剪,或者按矢量范围做掩膜提取,又不想一景一景手动操作,那这篇就是写给你的。哪怕你之前没怎么写过Python,只要跟着思路走,把参数换成你自己的数据路径,基本就能跑起来。我会把每一步为什么这么做讲透,不只是丢一段代码让你抄。

Arcpy是ArcGIS桌面端自带的Python站点包,安装ArcGIS之后就能直接调用,不需要额外配置环境。它的优势在于和ArcGIS的工具体系完全打通,arcpy.sa模块里的栅格处理函数和工具箱里的工具是一一对应的,你在工具箱里怎么设参数,脚本里就怎么传参数。对于遥感影像的裁剪和掩膜来说,核心用到的是ExtractByMask、Clip、ExtractByRectangle这几个函数,配合ListRasters做批量遍历,再套一个循环,就能实现全自动处理。

为什么强调批处理而不是单次处理?因为遥感影像的体量摆在那里。一景Landsat 8的OLI影像大概1GB左右,一景Sentinel-2的L2A产品解压后接近1GB,一个县级行政区的年度监测项目动辄上百景。单次处理的时间成本不只是操作本身,还有等待软件响应、切换文件、检查结果这些隐性开销。批处理把这些全部压缩成一次参数配置加一次运行,中间不需要人工干预,晚上挂着跑,第二天早上收结果,这才是正经的干活方式。

还有一个容易被忽略的点:一致性。手动处理的时候,不同时间、不同状态下设的参数可能有细微差异,比如输出像元大小、重采样方法、NoData值,这些差异在后期做时序分析或者镶嵌的时候会变成大麻烦。批处理脚本把参数固定下来,每一景走的都是同一套逻辑,结果的一致性有保障。这一点在做多时相对比或者变化检测的时候尤其重要。

2. 环境准备与数据组织:磨刀不误砍柴工

2.1 Arcpy运行环境的确认与常见配置问题

Arcpy的运行环境有两个选择:一是ArcGIS自带的Python环境,通常在C:\Python27\ArcGIS10.x或者ArcGIS Pro对应的C:\Program Files\ArcGIS\Pro\bin\Python\envs\arcgispro-py3下面;二是自己配一个conda环境然后装arcpy,但这个比较折腾,不推荐新手走这条路。最省事的办法就是直接用ArcGIS自带的Python,打开IDLE或者PyCharm把解释器指向那个路径就行。

这里有个坑要提前说:ArcGIS Desktop(就是ArcMap那一套)用的是Python 2.7,ArcGIS Pro用的是Python 3.x,两者的arcpy语法有差异。比如Python 2里print是语句不用加括号,Python 3里必须加括号。如果你在网上找的代码跑不通,先检查一下版本对不对。我下面给的代码以Python 3为准,ArcGIS Pro用户可以直接用,ArcMap用户把print改一下就行。

还有一个常见问题是许可证。arcpy.sa模块需要Spatial Analyst扩展许可,如果你在脚本里调用了ExtractByMask但没检查许可,会直接报错。保险的做法是在脚本开头加一句arcpy.CheckOutExtension("Spatial"),处理完再arcpy.CheckInExtension("Spatial")还回去。虽然桌面端一般会自动借,但显式写出来更稳妥,尤其是在多脚本并行或者长时间运行的时候。

2.2 输入数据的组织方式与命名规范

批处理的前提是数据组织得有规律。我见过太多人把影像全部堆在一个文件夹里,文件名五花八门,有的带日期有的不带,有的用横杠有的用下划线,结果写脚本的时候光写文件名匹配规则就写了一上午。正确的做法是在项目开始之前就把目录结构定好,比如:

Project/ ├── input_rasters/ # 原始影像,按日期或轨道号分文件夹 │ ├── 20230115/ │ ├── 20230220/ │ └── ... ├── mask/ # 掩膜矢量文件 │ └── study_area.shp ├── output/ # 输出结果 │ ├── clipped/ │ └── masked/ └── scripts/ # 脚本存放 └── batch_process.py

命名规范上,我建议影像文件名里包含日期和轨道号,比如LC08_123032_20230115.tif,这样在脚本里可以用字符串切片或者正则表达式提取信息,方便后续按条件筛选。如果文件名没有规律,那就得靠文件夹来区分,脚本里用os.walk递归遍历,但这样代码会复杂一些,不如一开始就规范命名来得省事。

掩膜矢量文件要注意坐标系。如果矢量和栅格的坐标系不一致,ExtractByMask会报错或者结果错位。稳妥的做法是在脚本里加一步坐标系检查,或者提前把矢量投影到和栅格一致的坐标系。我一般会在脚本里用arcpy.Describe读取栅格的spatialReference,然后判断矢量的坐标系是否匹配,不匹配就自动投影一份临时文件出来用。

2.3 输出路径与覆盖策略

输出路径不要和输入路径混在一起,否则批量处理的时候容易把自己生成的中间文件又当成输入读进去,造成死循环或者结果混乱。我习惯在输出文件夹里再按处理类型分子文件夹,比如clipped放裁剪结果,masked放掩膜结果,这样后期查找和整理都方便。

覆盖策略上,ExtractByMask和Clip默认如果输出文件已存在会直接覆盖,不会弹窗提示。这在批处理里是好事,但也要小心——如果你不小心把输出路径设成了输入路径,那就直接把原始数据覆盖了。我踩过这个坑,一晚上的处理结果把原始影像全毁了,还好有备份。所以脚本里最好加一个判断,如果输出路径和输入路径相同就报错退出。

3. 裁剪与掩膜的核心原理与函数选型

3.1 裁剪和掩膜到底有什么区别

很多人把裁剪和掩膜混着说,其实在ArcGIS的语境里它们有细微但重要的区别。裁剪(Clip)是按矩形范围或者矢量外接矩形来切割栅格,输出的是矩形区域内的像元,矢量边界之外的像元被保留但可能包含在矩形范围内。掩膜(Mask)则是严格按照矢量边界来提取,边界之外的像元全部设为NoData,边界之内保留原始值。简单说,裁剪是“切矩形”,掩膜是“抠形状”。

在实际项目中怎么选?如果你的研究区是矩形或者接近矩形,用裁剪就够了,速度快,结果规整。如果研究区是不规则形状,比如流域边界、行政区划边界,那就必须用掩膜,否则边界外的像元会混进来影响统计。还有一种情况是两者结合:先用裁剪把大范围影像切成小矩形,再用掩膜抠出精确边界,这样处理效率更高,因为裁剪后的影像体量小,掩膜处理起来更快。

Arcpy里对应的函数是:

  • 裁剪:arcpy.Clip_management()或者arcpy.sa.ExtractByRectangle()
  • 掩膜:arcpy.sa.ExtractByMask()

Clip_management是工具箱里的裁剪工具,功能更全,支持设置NoData值、保持裁剪范围等选项。ExtractByMask是Spatial Analyst里的掩膜提取,专门做按形状提取。我一般批量处理用ExtractByMask,因为它对矢量的支持更好,而且输出自动把边界外设为NoData,不需要额外设置。

3.2 ExtractByMask函数的参数详解

ExtractByMask的完整签名是:

ExtractByMask(in_raster, in_mask_data)

只有两个必填参数,看起来很简单,但实际用的时候有几个细节要注意。

第一个是in_raster,可以是栅格文件路径,也可以是栅格对象。批量处理的时候一般传文件路径,让arcpy自己去读。如果传的是栅格对象,要注意对象释放的问题,不然处理大量影像的时候内存会爆。

第二个是in_mask_data,可以是矢量文件路径,也可以是栅格。传矢量的时候,arcpy会自动把矢量转成栅格掩膜,转换的分辨率默认和输入栅格一致。这里有个隐藏参数是analysis_extent,如果不设,默认用输入栅格的范围,但有时候矢量的范围比栅格大,就会导致输出范围不对。稳妥的做法是显式设置arcpy.env.extent为输入栅格的范围。

还有一个容易忽略的是arcpy.env.snapRaster。如果掩膜矢量的坐标系和栅格不一致,或者像元对齐有问题,设置snapRaster可以让输出像元和输入栅格严格对齐,避免出现半个像元的偏移。这个在做多景影像镶嵌的时候特别重要,不对齐的话镶嵌出来会有缝隙。

3.3 批量遍历的实现方式对比

批量遍历文件有三种常见方式:arcpy.ListRasters()、os.listdir()、glob.glob()。三者各有适用场景。

arcpy.ListRasters()是arcpy自带的,可以直接按栅格格式筛选,比如arcpy.ListRasters("*", "TIF")只列出TIF文件。它还会自动识别栅格数据集,不管文件扩展名是什么。缺点是只能列出工作空间下的栅格,不支持递归子文件夹,而且对文件名模式的支持比较弱。

os.listdir()是Python标准库的,列出文件夹下所有文件,需要自己判断扩展名。优点是灵活,可以配合os.path.join拼路径,也可以递归。缺点是要自己处理文件名编码问题,中文路径有时候会出问题。

glob.glob()支持通配符匹配,比如glob.glob("*.tif")列出所有TIF文件,glob.glob("**/*.tif", recursive=True)递归列出所有子文件夹下的TIF。优点是模式匹配强大,代码简洁。缺点是在文件数量特别多的时候性能不如前两者。

我一般用glob.glob(),因为写起来最直观,而且支持递归,适合数据分文件夹存放的情况。如果文件数量上万,那就换成os.scandir(),性能更好。

4. 完整批处理脚本的逐段拆解与实操

4.1 脚本整体框架与参数配置区

先看整体框架。一个好的批处理脚本应该把参数配置集中放在开头,后面是函数定义和主循环,这样修改参数的时候不用满脚本找。下面是我常用的框架:

# -*- coding: utf-8 -*- import arcpy import os import glob import time # ============ 参数配置区 ============ INPUT_DIR = r"D:\Project\input_rasters" MASK_VECTOR = r"D:\Project\mask\study_area.shp" OUTPUT_DIR = r"D:\Project\output\masked" RASTER_EXT = "*.tif" OVERWRITE = True # =================================== def check_license(): """检查并借用Spatial Analyst许可""" if arcpy.CheckExtension("Spatial") == "Available": arcpy.CheckOutExtension("Spatial") print("Spatial Analyst许可已借用") else: raise RuntimeError("Spatial Analyst许可不可用") def get_raster_list(input_dir, ext): """获取待处理栅格列表""" pattern = os.path.join(input_dir, "**", ext) rasters = glob.glob(pattern, recursive=True) return rasters def batch_extract_by_mask(raster_list, mask, output_dir): """批量掩膜提取""" total = len(raster_list) success = 0 failed = [] for i, raster in enumerate(raster_list, 1): basename = os.path.basename(raster) name, _ = os.path.splitext(basename) out_path = os.path.join(output_dir, name + "_masked.tif") try: # 设置环境参数 arcpy.env.extent = raster arcpy.env.snapRaster = raster arcpy.env.overwriteOutput = OVERWRITE # 执行掩膜提取 out_raster = arcpy.sa.ExtractByMask(raster, mask) out_raster.save(out_path) success += 1 print("[{}/{}] 成功: {}".format(i, total, basename)) except Exception as e: failed.append((basename, str(e))) print("[{}/{}] 失败: {} - {}".format(i, total, basename, str(e))) return success, failed if __name__ == "__main__": start_time = time.time() check_license() if not os.path.exists(OUTPUT_DIR): os.makedirs(OUTPUT_DIR) rasters = get_raster_list(INPUT_DIR, RASTER_EXT) print("共找到 {} 景影像".format(len(rasters))) success, failed = batch_extract_by_mask(rasters, MASK_VECTOR, OUTPUT_DIR) elapsed = time.time() - start_time print("处理完成: 成功 {} 景, 失败 {} 景, 耗时 {:.1f} 秒".format( success, len(failed), elapsed)) if failed: print("失败列表:") for name, err in failed: print(" {}: {}".format(name, err)) arcpy.CheckInExtension("Spatial")

这个框架看着简单,但每一段都有讲究。参数配置区用全大写变量名,一眼就能找到。check_license函数把许可检查单独拎出来,避免在主逻辑里混着写。get_raster_list用glob递归匹配,支持子文件夹。batch_extract_by_mask是核心处理函数,用try-except包住每一景的处理,单景失败不影响整体,最后统一报告失败列表。

4.2 环境参数设置的三个关键项

arcpy.env下面有几十个环境变量,但批处理掩膜的时候最关键的只有三个:extent、snapRaster、overwriteOutput。

arcpy.env.extent控制输出范围。如果不设,ExtractByMask默认用输入栅格的范围,但有时候掩膜矢量的范围比栅格大,输出就会按矢量范围来,导致结果比预期大一圈。显式设为输入栅格的范围,保证输出和输入范围一致。

arcpy.env.snapRaster控制像元对齐。如果掩膜矢量的坐标系和栅格不一致,arcpy在转换矢量掩膜的时候可能会产生半个像元的偏移,导致输出栅格的像元中心和输入不对齐。设了snapRaster之后,输出像元会严格对齐到snapRaster的像元网格上。这个参数在做多景影像镶嵌或者时序分析的时候是必须的。

arcpy.env.overwriteOutput控制是否覆盖已有文件。批处理的时候一般设为True,避免每次弹窗确认。但要注意,如果输出路径和输入路径相同,这个设置会让你直接覆盖原始数据。所以脚本里最好加一个路径检查:

if os.path.abspath(OUTPUT_DIR) == os.path.abspath(INPUT_DIR): raise ValueError("输出路径不能和输入路径相同")

4.3 掩膜提取的完整执行流程

把上面的框架串起来,完整的执行流程是这样的:

第一步,检查许可。arcpy.CheckExtension("Spatial")返回"Available"就借用,返回"Unavailable"就报错退出。这一步不能省,尤其是在ArcGIS Desktop环境下,有时候许可被其他程序占用了,不检查的话跑到一半才报错,浪费时间。

第二步,获取文件列表。用glob.glob递归匹配所有TIF文件,返回一个列表。如果文件数量是零,直接提示退出,不用往下跑。

第三步,创建输出文件夹。用os.makedirs创建,加一个exist_ok=True参数避免文件夹已存在时报错。

第四步,循环处理。每一景影像做四件事:拼输出路径、设环境参数、调ExtractByMask、保存结果。输出文件名在原始文件名后面加_masked后缀,方便区分。

第五步,统计结果。记录成功和失败的数量,失败的原因存到列表里,最后统一打印。这样跑完之后一眼就能看出哪些景出了问题,不用去翻日志。

第六步,归还许可。arcpy.CheckInExtension("Spatial")把许可还回去,养成好习惯。

整个流程跑一遍,100景影像大概需要10到20分钟,具体取决于影像大小和机器性能。比手动处理快了几十倍不止。

4.4 裁剪的批处理实现与参数差异

裁剪的批处理逻辑和掩膜基本一样,区别在于调用的函数和参数。用arcpy.Clip_management的话:

def batch_clip(raster_list, clip_vector, output_dir): for i, raster in enumerate(raster_list, 1): basename = os.path.basename(raster) name, _ = os.path.splitext(basename) out_path = os.path.join(output_dir, name + "_clipped.tif") try: arcpy.Clip_management( in_raster=raster, rectangle="", # 用矢量范围 out_raster=out_path, in_template_dataset=clip_vector, nodata_value="0", clipping_geometry="ClippingGeometry", maintain_clipping_extent="NO_MAINTAIN_EXTENT" ) print("[{}/{}] 成功: {}".format(i, len(raster_list), basename)) except Exception as e: print("[{}/{}] 失败: {} - {}".format(i, len(raster_list), basename, e))

Clip_management的参数比ExtractByMask多,但核心是in_template_dataset传矢量,clipping_geometry设为"ClippingGeometry"表示按矢量形状裁剪,maintain_clipping_extent设为"NO_MAINTAIN_EXTENT"表示输出范围按实际裁剪结果来,不保持原范围。

和ExtractByMask的区别在于,Clip_management输出的是矩形范围,矢量边界外的像元保留但值可能被设为NoData(取决于nodata_value设置),而ExtractByMask输出的是严格按矢量形状,边界外全部NoData。实际用的时候,如果只需要矩形范围,用Clip_management更快;如果需要精确形状,用ExtractByMask。

5. 性能优化与大批量处理的实战技巧

5.1 内存管理与栅格对象释放

处理大量影像的时候,内存管理是个大问题。ExtractByMask返回的是一个栅格对象,如果不及时释放,内存会一直涨,跑到几十景之后就可能报内存错误。解决办法是在每次循环结束时显式删除对象:

out_raster = arcpy.sa.ExtractByMask(raster, mask) out_raster.save(out_path) del out_raster # 显式释放

如果还是不够,可以用arcpy.Delete_management删除中间文件,或者用gc.collect()强制垃圾回收。不过gc.collect()比较耗时,不建议每次循环都调,可以每处理10景调一次。

还有一个技巧是用with语句管理栅格对象,但arcpy的栅格对象不直接支持上下文管理器,需要自己封装。简单起见,用del就够了。

5.2 并行处理的可能性与限制

Python的多线程因为GIL的存在,对CPU密集型任务效果不好,但arcpy的处理大部分时间花在IO和底层C++计算上,GIL在调用底层函数的时候会释放,所以多线程还是有一定效果的。可以用concurrent.futures.ThreadPoolExecutor开4到8个线程并行处理:

from concurrent.futures import ThreadPoolExecutor def process_one(raster): # 单景处理逻辑 pass with ThreadPoolExecutor(max_workers=4) as executor: executor.map(process_one, raster_list)

但要注意,arcpy的许可管理不是线程安全的,多个线程同时调CheckOutExtension可能会出问题。稳妥的做法是在主线程借用许可,子线程只做处理不碰许可。另外,多个线程同时写同一个输出文件夹一般没问题,但如果输出文件名有冲突就会互相覆盖,所以文件名要保证唯一。

如果机器核多内存大,用多进程ProcessPoolExecutor效果更好,但arcpy在多进程下的初始化比较麻烦,每个进程都要重新导入arcpy和借用许可,开销不小。我一般用4线程,再多了收益递减。

5.3 处理进度监控与日志记录

批处理跑起来之后,最怕的就是不知道跑到哪了。加一个进度打印是最基本的:

print("[{}/{}] 处理: {}".format(i, total, basename))

但光打印到控制台不够,如果脚本在后台跑,控制台输出看不到。更好的做法是同时写日志文件:

import logging logging.basicConfig( filename=os.path.join(OUTPUT_DIR, "process.log"), level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" ) logging.info("开始处理: {}".format(basename))

日志文件的好处是可以事后查,哪一景失败了、失败原因是什么,一目了然。我习惯在日志里记录每景的处理时间,方便估算总体耗时和发现异常慢的影像。

还有一个技巧是用tqdm库显示进度条,比纯文本打印直观得多:

from tqdm import tqdm for raster in tqdm(raster_list, desc="处理进度"): # 处理逻辑 pass

tqdm不是标准库,需要pip install tqdm。如果环境不允许装第三方库,那就用纯文本的百分比打印,效果也不差。

6. 常见报错与排查技巧实录

6.1 坐标系不匹配导致的掩膜错位

这是最常见的问题。表现是掩膜结果和预期位置偏移,或者输出全是NoData。原因是矢量掩膜的坐标系和输入栅格不一致,arcpy在转换的时候按默认规则处理,导致位置对不上。

排查方法:用arcpy.Describe读取两者的坐标系,对比spatialReference.name是否一致。

raster_sr = arcpy.Describe(raster).spatialReference mask_sr = arcpy.Describe(mask).spatialReference print("栅格坐标系: {}".format(raster_sr.name)) print("矢量坐标系: {}".format(mask_sr.name))

如果不一致,解决办法是用arcpy.Project_management把矢量投影到栅格坐标系,输出一个临时文件,用临时文件做掩膜。不要直接改原始矢量,保留原始数据是个好习惯。

6.2 输出全为NoData的几种原因

掩膜结果全是NoData,除了坐标系问题,还有几种可能:

一是矢量范围完全不覆盖栅格范围。检查两者的extent是否相交。用arcpy.Describe(raster).extent和arcpy.Describe(mask).extent对比。

二是矢量的几何有问题,比如自相交、空几何。用arcpy.CheckGeometry_management检查并修复。

三是arcpy.env.extent设错了,设成了和栅格不相交的范围。检查环境设置。

四是栅格的NoData值设置有问题,导致有效像元被当成NoData。检查栅格的noDataValue属性。

排查的时候按这个顺序来:先看范围是否相交,再看几何是否有效,再看环境设置,最后看NoData值。大部分问题在前两步就能定位。

6.3 大批量处理中途中断的恢复策略

跑了几百景,跑到一半脚本崩了,重新跑一遍太浪费时间。解决办法是在脚本里加断点续跑逻辑:处理之前先检查输出文件是否已存在,存在就跳过。

if os.path.exists(out_path) and not OVERWRITE: print("[{}/{}] 跳过已存在: {}".format(i, total, basename)) continue

这样重新跑的时候只处理没处理过的,已经完成的直接跳过。配合日志文件,还能知道上次跑到哪了。

如果输出文件存在但不确定是否完整,可以检查文件大小,小于某个阈值就认为不完整,重新处理。或者用arcpy.Describe读取栅格的行列数,和输入对比,不一致就重新处理。

6.4 常见报错速查表

报错信息可能原因解决办法
ERROR 000732: 输入不存在或不受支持路径错误或文件被占用检查路径拼写,关闭占用文件的程序
ERROR 999999: 执行函数时出错通用错误,原因多样查看详细错误信息,通常是坐标系或许可问题
RuntimeError: 无法借用许可Spatial Analyst许可被占用关闭其他ArcGIS程序,重启脚本
MemoryError内存不足减少并行数,及时释放栅格对象
输出全为NoData坐标系不匹配或范围不相交检查坐标系和范围
输出像元偏移未设snapRaster设置arcpy.env.snapRaster
文件名乱码中文路径编码问题用英文路径,或设置sys.setdefaultencoding

这张表是我这些年踩坑攒下来的,大部分报错都能在里面找到对应。遇到没见过的报错,先把错误信息完整读一遍,ArcGIS的报错信息虽然有时候很笼统,但仔细看还是能找到线索。

7. 从单机脚本到工程化处理的进阶思路

7.1 参数外部化与配置文件管理

脚本写多了之后,每次改参数都要打开脚本改代码,很麻烦。更好的做法是把参数抽到配置文件里,用JSON或者YAML管理:

{ "input_dir": "D:/Project/input_rasters", "mask_vector": "D:/Project/mask/study_area.shp", "output_dir": "D:/Project/output/masked", "raster_ext": "*.tif", "overwrite": true, "max_workers": 4 }

脚本里读配置文件:

import json with open("config.json", "r", encoding="utf-8") as f: config = json.load(f)

这样换项目的时候只需要改配置文件,脚本本身不用动。配置文件还可以用版本控制管理,记录每次处理的参数,方便回溯。

7.2 处理结果的质量检查自动化

批处理跑完之后,结果质量检查也很重要。可以写一个检查脚本,自动统计每景输出的有效像元比例、范围是否正确、像元大小是否一致:

def check_result(out_raster, expected_extent): desc = arcpy.Describe(out_raster) # 检查范围 if desc.extent != expected_extent: return False, "范围不一致" # 检查像元大小 if desc.meanCellWidth != expected_cell_size: return False, "像元大小不一致" # 检查有效像元比例 # ... return True, "OK"

检查结果输出成CSV,方便在Excel里筛选和统计。这一步能发现很多肉眼看不出来的问题,比如某几景影像因为云覆盖导致有效像元比例极低,或者某几景的范围偏了一点。

7.3 与其他处理步骤的串联

裁剪和掩膜往往只是整个处理流程中的一步。前面可能有辐射定标、大气校正,后面可能有镶嵌、重采样、时序分析。把每一步都写成独立的脚本,用统一的配置文件管理参数,用文件命名规范串联,就能形成一条完整的处理链。

比如:

01_radiometric_calibration.py 02_atmospheric_correction.py 03_batch_clip.py 04_batch_mask.py 05_mosaic.py 06_resample.py

每个脚本的输出是下一个脚本的输入,中间用统一的命名规范衔接。这样整个流程模块化,哪一步出问题就单独重跑那一步,不用从头来。

我在实际项目里用这套流程处理过上千景影像,从原始数据到最终分析结果,全程脚本自动化,中间只需要人工检查关键节点的输出质量。这套方法的核心思路就是:把重复劳动交给脚本,把判断和决策留给人。脚本负责稳定、一致、快速地执行,人负责检查结果、调整参数、处理异常。两者配合,效率和质量都能兼顾。

最后分享一个小技巧:脚本写完之后,先拿3到5景影像做测试,确认结果正确再跑全量。测试的时候把日志级别调到DEBUG,把每一步的中间结果都打印出来,方便定位问题。全量跑的时候把日志级别调到INFO,只记录关键信息,避免日志文件太大。这个习惯帮我省了很多时间,尤其是处理新类型数据的时候,先小批量验证再大批量执行,基本不会出大问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询