
做遥感处理的同行大概都有过这样的经历硬盘里躺着一批高分系列卫星影像GF1、GF2、GF6、GF7混在一起每景都要在ENVI里打开、检查、辐射定标、大气校正、正射校正、图像融合。一步一点一景三十分钟打底十景就是大半天而且参数选错一个整批全废。我被这件事磨了两年最后彻底忍不了直接拿IDL写了一套自动化批量预处理程序从GF1起步后来陆续兼容GF2、GF6、GF7跑通了从原始L1产品到融合反射率影像的完整链路。今天把这套程序的设计思路、核心原理、关键实现和踩坑经历完整写出来给同样被高分数据处理折腾的同行一份能直接抄的作业。无论你是刚接触高分数据的初学者还是已经在ENVI里点鼠标点到手酸的“老手工党”这篇文章都值得花十分钟看完。1. 手工预处理一景高分影像到底有多慢1.1 看起来简单的六步背后全是重复劳动“一景高分影像预处理”听起来就是几步操作实际每一步都要带脑子。辐射定标要填定标参数大气校正要选大气模型和气溶胶模型正射校正要准备DEM和RPC融合前还要确认全色和多光谱是否对齐。GF1、GF2、GF6、GF7每颗星参数不同同一颗星不同时期下发数据的定标系数也在变靠人肉记录几乎一定会出错。我统计过自己手动处理一景GF2 PMS数据的耗时打开和检查约5分钟辐射定标加参数检查约5分钟大气校正参数设置约10分钟正射校正加等待约10分钟配准融合约10分钟。一景下来40到50分钟属于正常发挥赶上数据质量不好一个上午搭进去也出不了两景。最难受的不是慢而是重复。参数就那几个流程就那几步但你必须一遍遍盯屏幕。批量数据一来就是几十景手动处理意味着连续高强度操作好几个工作日中间不能走神、不能漏步骤否则后面发现的错误可能已经覆盖掉前面的产品。这种场景下写程序是唯一的出路。1.2 选IDL而不是Python/GDAL的理由有人可能问现在Python遥感生态也不差为什么守着IDL我的回答是ENVI本身就是IDL写的IDL和ENVI是原生的父子关系调用ENVI的算法任务只需要几行代码不需要自己实现或者绕一层接口。具体到高分预处理链路辐射定标、正射校正、图像融合这些都是ENVI已经打磨得很成熟的算法模块。在IDL脚本里创建任务、设参数、执行远比用GDAL从底层实现一遍稳得多。IDL自身又是数组语言处理栅格数据时代码非常短循环和索引操作对影像友好调试体验也不算差。还有一个现实原因大量前辈代码、实验室脚本都是IDL写的接手过来改改就能用。Python方案当然也能跑通但我用下来发现额外要处理的库版本冲突、坐标系转换、内存管理问题更多。工具没有绝对好坏在“有ENVI使用基础”这个前提下IDL是切入批处理最高效的路径。顺便提醒一句搜索IDL资料时很容易被带偏。IDL这个名字在别的领域也有比如工控或汽车总线工具链里的接口定义语言Interface Definition Language网上甚至有人讨论IDL转VCDL之类的话题那跟遥感编程里的IDLInteractive Data Language完全是两码事。搜资料时记得带上ENVI或者遥感关键词准确率会高很多。1.3 程序的边界处理什么不处理什么写程序之前先划清边界这比写代码更重要。我这套程序的目标输入是中国资源卫星应用中心下发的GF1/2/6/7 L1级标准产品输出是经过辐射定标、大气校正、正射校正、全色与多光谱融合的反射率产品带准确的地理坐标可以直接进后续的分类、解译或定量反演流程。程序不做的事从一开始就明确不做云检测和去云不做影像镶嵌不做信息提取。一开始我也脑子发热想全塞进去后来发现每个模块都是无底洞云检测精度、镶嵌匀色策略都会消耗大量开发时间。把核心链路做扎实让程序稳定、可靠、能断点续跑这比追求大而全实在得多。这个边界感是程序能真正落地而不是烂尾的关键。2. GF1/2/6/7的数据差异与统一处理思路2.1 四颗高分卫星的影像特点速查高分系列卫星不是同一颗星的复制品GF1、GF2、GF6、GF7四颗星在传感器配置上差异不小拿同一套代码去套必定翻车。先把四颗星的基本差异摆清楚卫星主要载荷全色分辨率多光谱分辨率处理侧重点GF1PMS、WFV2m8mPMS、16mWFV幅宽较大中分辨率广域覆盖GF2PMS0.8m3.2m亚米级城市细节几何精度要求高GF6PMS、WFV2m8mPMS、16mWFV含红边波段WFV红边波段适合农业遥感波段数更多GF7立体测绘相机优于1m约0.8m级约3.2m级同轨立体像对兼顾测绘制图注意表格里的数值是标称参数实际使用以官方发布为准。GF6的WFV载荷是一个重点它比GF1多出红边相关波段波段数量不一样处理程序如果写死了四波段逻辑遇到GF6必然出错。GF7又是另一个特例它是立体测绘卫星数据包里经常是前视、后视两套全色影像加上多光谱影像不再像GF2那样一景只有一个全色文件。2.2 用元数据驱动抹平卫星差异四颗星参数差异这么大统一处理的关键在哪答案就四个字元数据驱动。GF系列下发产品都会带一个XML格式的元数据文件里面记录了卫星编号、传感器编号、成像时间、定标系数、RPC参数等关键信息。程序所有关键步骤的参数都从XML里动态读取而不是写死在代码里。比如辐射定标GF1和GF2的绝对定标系数数值不同GF6的红边波段定标系数还多个通道这些差异都不需要代码感知只要能从XML里正确取出来算法对所有数据一视同仁。再比如RPC参数GF7和GF2的RPC组织方式可能略有不同但正射校正任务认的是统一格式的RPC集合程序的任务就是把不同位置的字段统一映射到同一个数据结构里。这个思路说起来简单做起来最花时间的恰恰是“从XML里把字段正确取出来”。不同时期的GF数据包XML字段名可能从RadiometricCalibration变成AbsCalib也可能多嵌一层节点解析代码必须写得足够容错。我踩过的坑是用固定的字段路径解析换一批数据就报错。后来改成先遍历整个XML树、按关键词模糊匹配再对取到的值做合法性校验兼容性立刻上去了。2.3 预处理链路统一后的顺序取舍预处理流程的顺序我最终方案是辐射定标 → 大气校正 → 正射校正 → 配准 → 融合。这个顺序有几层考虑。辐射定标必须放最前面因为它是从DN值到物理量的基础换算后续所有辐射相关操作都依赖它。大气校正放在正射之前是为了在原始传感器几何下做辐射计算避免几何重采样对像元光谱造成额外插值影响。正射校正放在大气校正之后意味着几何变换只做一次输出产品的地理精度由RPC加DEM保证。最后做融合因为融合要求全色和多光谱在同一个几何基准下、空间分辨率匹配正射之后再做融合位置对齐的问题会少很多。当然实际工程里有人会把正射放在大气校正之前理由是先用原始几何做正射可以避免大气校输出被重采样两次。这个取舍没有绝对对错只要保证流程自洽、每一步输入输出单位明确即可。关键是中间文件都保留下来万一某步参数不合适还能从断点重新跑而不是整条链路推倒重来。3. 预处理链路的核心原理与实现要点3.1 辐射定标从DN值到地物辐亮度辐射定标要解决的是把传感器记录的DN值转换为物理辐亮度。卫星传感器在轨运行时间长了光电响应会衰减所以定标系数不是永久不变的。中国资源卫星应用中心会定期更新绝对定标系数处理高分影像时如果还用老旧的定标参数反射率计算就会有系统偏差。辐射定标公式并不复杂L Gain × DN Bias其中Gain和Bias从GF的元数据文件读取L的单位是W·m⁻²·sr⁻¹·μm⁻¹。实际实现里需要注意两点。一是单位换算有的元数据给的是辐亮度有的给的是反射率定标系数用错了单位后面的FLAASH输入就是废数据。二是影像量化等级GF系列多数数据是16位整数存储定标输出应该保存为浮点型避免再次量化引入误差。我在IDL里的做法是先解析XML拿到每个波段的Gain和Bias数组再用IDL数组语法一次完成整景转换radiance float(dn) * gain[*, *, band] bias[band]IDL的数组写法在这类场景非常舒服一行代码就把某个波段转换完了。但要注意内存GF2多光谱3.2m分辨率一景影像宽高都很大float转换后内存占用明显上升批量循环里要提前考虑释放机制。3.2 大气校正去掉影像上的那层“灰纱”大气校正的目的很直接消除大气散射和吸收对地表反射率的影响。高分影像如果只做辐射定标很多地方看起来会蒙一层灰纱水体发亮、植被发白直接做定量反演不可避免会偏。ENVI里常用的大气校正模块有FLAASH和QUAC我的程序里两者都接了。FLAASH精度高但参数多需要选择大气模型、气溶胶模型、水汽含量、能见度等。这些参数在IDL脚本里可以设置也可以从场景信息推算。我的做法是先按影像纬度和时相自动匹配大气模型比如中纬度夏季数据选Mid-Latitude Summer再允许用户通过配置文件覆盖。QUAC则适合快速处理不需要过多地表和大气参数直接从影像自身统计反推速度比FLAASH快不少。批量处理时如果只是做基础解译QUAC够用如果要反演植被参数这类定量产品建议FLAASH。脚本里把两个模块都做成可选通过配置文件切换灵活度会高很多。大气校正输出经常会出现负值尤其是水体、地形阴影这些低反射区域。ENVI输出选项里有反射率缩放因子scale factor我一般用10000倍缩放把反射率变成0到10000左右的整型范围这样既能保留精度又能控制文件体积后面导入其他软件也不会因为负值报警。3.3 正射校正用RPC把影像摆到正确位置上正射校正解决的是地形起伏和传感器视角引起的几何变形。高分影像侧摆角度大的时候高层建筑和山地会明显“倾倒”不校正的地图产品没法用。GF数据包一般会带RPC有理多项式参数正射校正的核心就是拿着RPC加DEM把影像上的每个像元重新投影到正确的地理位置。IDL里调用ENVI的RPCOrthorectification任务代码本身不长task ENVITask(RPCOrthorectification) task.INPUT_RASTER atmCorrectedRaster task.DEM_RASTER demRaster task.OUTPUT_RASTER_URI outPath task.Execute关键细节在DEM选型和重采样方法。DEM我用过SRTM 30米、ASTER GDEM也用过局部地区的机载LiDAR DEM精度差异直接反映到正射后的配准效果。GF2这种亚米级影像30米DEM在很多陡峭山区还是不够如果项目区高差大建议补更高精度的局部DEM。重采样方法按用途区分最邻近法保留原始光谱值适合后续做分类和辐射分析双线性内插平滑适合目视解译三次卷积更锐利但会产生轻微过冲。程序默认用双线性然后允许用户改。正射的输入输出建议都用ENVI标准栅格格式避免常见图像格式在重投影时丢元数据。3.4 配准与融合把全色纹理和多光谱色彩合成一张图高分影像的全色波段分辨率高多光谱波段分辨率低融合就是把两者优势合到一起输出高分辨率多光谱影像。但融合有个前提全色影像和多光谱影像必须精确对齐否则相当于把两张错位的图叠在一起结果全是重影。GF卫星的PMS传感器在设计上全色和多光谱同源一般情况下偏差不大但不同批次数据、不同处理级别差距也可能有几个像元。程序在融合前会增加一个可选配准步骤以全色为参考影像用自动配准任务查找控制点并校正多光谱影像。对于GF1/2/6/7这种同源影像自动配准成功率很高不需要人工选点程序里给失败数据单独打标记人工检查。融合算法主力用NNDiffuse也就是ENVI的NNDiffusePanSharpening。这个算法对高分辨率影像的保真度明显好于传统Brovey、Gram-Schmidt和PC色彩不容易被冲淡边缘细节也更利落。融合任务在IDL里的调用方式和前面类似注意设置好输入全色波段路径和输出路径融合前几何基准必须一致。融合完成后建议做一个快速质检输出一张缩小预览图检查河流、道路、建筑边缘有没有异常色差然后把不合格的影像踢回到配准环节重新处理。这个质检步骤看起来不起眼实际批量处理时能救回一大批废产品。4. 批量自动化架构目录、状态与日志4.1 目录组织别让中间产物乱成一锅粥批量预处理最怕的不是算法报错而是跑到一半找不到文件。我的经验是第一件事就把目录结构定死。程序运行前会自动创建一套固定目录输入输出严格分离每一大步一个子目录中间产物按“步骤号_文件名”的规则保留。目录结构大致是这样的input/ GF1_PMS1_20200101/ GF2_PMS2_20200102/ output/ step1_rad/ step2_atm/ step3_ortho/ step4_fused/ logs/ run_20250101.log status.csv输入目录放下载好的原始数据包输出目录按处理步骤分成四层。好处是每一步中间结果都在哪一步参数不合适只重跑对应步骤不用整条链路推倒。代价是硬盘占用会翻几倍我已经不止一次因为磁盘写满导致程序中断所以现在脚本开头和每个大步骤结束都会检查剩余磁盘空间低于阈值直接停。中间产品要不要保留很多人会犹豫。我的建议是程序调通之前全保留程序稳定后可以定期清理step1和step2只保留最终融合产品和关键中间文件。磁盘这玩意看着大GF2融合产品一景能到几百MB批量跑下来一个TB都是小意思。4.2 批处理状态机与断点续跑批量程序跑了几百景之后大概率会遇到网络断线、磁盘满了、某个文件损坏等意外。如果程序没有断点续跑机制重头再来非常痛苦。我的做法是在日志目录下维护一个状态文件把每景影像的处理进度按状态记录状态分queued、running、done、failed四种。每次程序启动时先扫描状态文件done的跳过failed的重新入队running的说明上次异常中断重置为failed后重新入队。每处理完一个步骤立即更新状态并写一行日志。有了这个机制批量任务哪怕是半夜崩了第二天重启脚本它自己知道该从哪景、哪一步继续不会重复劳动。状态文件用简单文本或CSV都可以未必上数据库。我最初用文本表格后来数据量大了改成CSV方便Excel打开检查。关键是更新逻辑要原子化先写临时文件再改名替换避免程序被强制终止时状态文件写了一半。4.3 日志系统与失败重跑日志是批量程序的第二生命。一开始我觉得IDL的print输出够了结果程序一跑就是几小时屏幕滚过去的报错根本找不到。后来把日志全部落盘每个步骤都带时间戳、影像名、步骤名和结果状态排查问题效率立刻成倍上升。IDL里可以用CATCH语句捕获异常把出错信息和调用栈写入日志文件。核心逻辑是CATCH, errStatus IF errStatus NE 0 THEN BEGIN logError, !ERROR_STATE.msg CATCH, /CANCEL ENDIF这里要注意并不是所有错误都会触发异常比如某些ENVI任务执行后只是输出为空不主动抛错。所以我还会在关键步骤后加结果校验检查输出文件是否存在、大小是否合理、是否包含有效值。只有通过校验才把状态标成done否则一律算failed进入等待重跑队列。日志里除了错误信息还记录每个步骤耗时和磁盘占用。积累一段时间后这些数据能帮你判断瓶颈在哪个环节是大气校正太慢还是正射太慢再有针对性地优化而不是瞎猜。5. 关键代码与参数配置实操5.1 环境准备IDL安装与ENVI版本选择IDL安装这事看起来简单实际很多人都卡在第一关。IDL是商业软件需要license要么单机许可要么局域网license server。安装时注意三点选择64位版本路径不要有中文环境变量里的IDL_DIR指向正确安装目录。ENVI版本建议5.6以上ENVITask框架在这几个版本里已经很稳定四颗卫星的数据格式支持也更新得比较勤。如果手里还是老版本ENVI 5.3以下部分新任务接口可能不支持程序需要做兼容分支工作量会变大。有条件的话把IDL和ENVI更新到较新版本能让脚本省掉很多适配麻烦。再次提醒搜索IDL资料的时候网上一大堆结果是其他领域的IDL甚至“IDL转VCDL”这种完全不相干的讨论。遥感程序里说的IDL全称Interactive Data Language互动数据语言跟接口定义、模型转换那些完全是两码事。搜“ENVI IDL 批量处理”这类组合关键词找资料准确率会高很多。5.2 读取GF元数据XML解析的两种实用套路GF影像的元数据是一个XML文件里面藏着定标系数、成像时间、RPC等关键参数。IDL解析XML有两种常用做法一是用IDL自带的XML DOM对象二是直接把XML当字符串读进来用正则或字符串搜索提取字段。XML DOM方法适合字段结构稳定的情况代码规范但对命名空间和节点层级敏感遇到结构变化容易报错。字符串搜索方法虽然土但极其抗造。实际程序里用混合策略先用DOM解析失败后退回字符串搜索。比如读定标系数可以先把XML所有带Gain和Bias字样的节点找出来再按波段顺序匹配。元数据解析是整个程序兼容性的命门。我遇到过几次情况同一个卫星不同时期的数据定标系数字段从RadiometricCalibration挪到了Calibration节点下RPC参数从内嵌XML变成了独立.rpb文件。处理这种变化没有捷径只能在解析层做模糊匹配和补丁同时把“未知字段名”打进日志方便后续补齐。5.3 调用ENVI任务完成辐射定标与正射校正IDL脚本调用ENVI功能的现代姿势是用ENVITask思路是创建ENVI对象打开栅格创建任务设置参数执行。以辐射定标为例核心代码大概是这样的e ENVI(/HEADLESS) raster e.OpenRaster(GF2_PMS2_20200101.xml) task ENVITask(RadiometricCalibration) task.INPUT_RASTER raster task.OUTPUT_RASTER_URI output/step1_rad.dat task.Execute注意几点。ENVI(/HEADLESS)是无头模式不弹界面适合批量跑。OpenRaster直接指向GF的XML文件ENVI能自动识别数据格式。RadiometricCalibration任务默认输出辐亮度如果要做反射率需要额外设置参数与大气校正流程的输入要求相匹配。正射校正也是同样套路唯一区别是额外传DEM栅格。DEM用ENVI标准栅格最省事其他格式也行只要ENVI能读。这里强调一个配置细节RPCOrthorectification任务对DEM的投影和像元大小有要求最好提前把DEM重投影到输出目标坐标系否则任务会用默认方式重采样位置对不齐。5.4 批处理调度循环、状态判断与内存释放批处理主循环的骨架不复杂遍历输入目录里所有数据包对每个数据包执行预处理链路最后更新状态。但细节决定了程序能不能长时间稳定运行。最重要的就是内存。IDL里数组变量和ENVI任务对象都会占内存跑一景大影像可能吃掉好几个GB。每处理完一景要把打开的栅格对象、任务输出引用全部清理。IDL没有强制的垃圾回收我一般用显式置空加HEAP_GC在每景循环结束时强制释放内存。如果连续跑几十景不释放哪怕64GB内存的机器也可能被拖死。另一个实用技巧是并行。ENVI任务本身支持多核但一景数据一个任务很难吃满整机。批处理程序支持两种并行方式一是开多个IDL进程每个进程处理不同目录、不同磁盘数据二是用IDL_IDLbridge在单进程里调度多个工作线程。实践下来最稳的还是多个独立进程各自维护独立日志和状态文件互不干扰。进程数按CPU核心数和内存量决定我一般每景预留8GB内存8核32GB的机器开3个进程最稳妥。6. 常见问题排查与高分数据避坑实录6.1 辐射定标结果出现负值或不合理数值定标后影像出现大量负值最直接原因是定标系数读取错误或单位理解偏差。GF的元数据里有些字段给的是定标增益和偏移有些给的是转换后的辐亮度公式系数没看清就代入公式很容易出现系统性偏差。排查思路是取一块已知地物比如清洁水体、均匀植被检查辐亮度值量级水体在近红外的辐亮度应接近0植被在近红外相对较高。另一个常见原因是数据本身存在饱和像元或坏像元。GF影像在云顶、高反射建筑物等地方可能饱和DN值被截断定标后显得异常。我的做法是定标后再做一步“有效值掩膜”把小于0或大于传感器量子化上限转换出的辐亮度像元标记为无效后续大气校正和融合步骤只处理有效像元避免异常值传导。6.2 正射校正后影像偏移严重正射校正后影像与参考底图偏移最常见原因是DEM精度不够或RPC参数没被正确读取。GF2亚米级影像对DEM非常敏感30米SRTM在平地还可以地形起伏大的区域会看到明显沿山脊线偏移。解决办法是换更精细的DEM或者用影像自带的控制点做二次几何精校正。如果RPC没读对正射结果甚至会出现“影像被拉伸到一个离谱位置”的情况。这时候先别急着调参数回来看日志里RPC读取是否成功以及RPC经纬度范围和影像元数据里的经纬度范围是否一致。我遇到过一次GF7数据RPC字段在XML里的顺序和GF2不一致解析程序默认按GF2顺序读取结果前后视两套影像全错位。后来在解析RPC时加入范围一致性校验读出来的RPC给出的中心经纬度要与元数据一致不一致就报警并停止该景处理。6.3 内存不足与IDL卡死内存不足是批处理程序最常见的死法。很多人以为机器配置不行其实多半是代码编写问题。IDL里一个整型影像GB级阵列很正常但如果同时保存原始数据、定标结果、大气校正结果、正射结果四个对象内存就是好几倍翻。我的经验是大步骤之间及时清理中间变量能直接写盘就不要全张保存在内存里能用文件传递就不用数组传递。IDL还有一个特殊坑程序长时间运行后对象可能因为句柄冲突卡死界面无响应。无头模式跑批量任务时如果任务内部弹了对话框整个进程就会挂住。处理办法是设置环境变量禁止ENVI弹窗同时在关键节点加超时检测。真遇到卡死日志能告诉你卡在哪一景、哪一步单独把那景数据重跑就行。6.4 高分数据常见坑点速查表我把实际处理GF1/2/6/7时遇到的坑整理成速查表方便对照排查数据常见问题原因处理建议GF1WFV拼接条带感明显多台相机拼接不均匀先匀色再融合分景处理时避免边缘切割GF2正射后高层建筑倾斜明显DEM缺失造成投影误差结合局部DSM城区数据慎重使用30m DEMGF6程序报波段数量错误WFV含红边波段波段数与GF1不一致代码按XML动态识别波段不写死四波段GF7前视后视文件混淆立体测绘图带两套全色按文件标识区分正视与侧视按需选择全色源通用融合后色彩异常多光谱与全色未配准融合前强制自动配准并检查控制点精度这张表里每个问题我都真实遇到过尤其是GF6的红边波段和GF7的前后视区分属于“数据一来代码就崩”的典型。程序里专门为每个卫星保留独立参数映射表把已知坑转化成配置项即使官方更新数据格式改配置比改代码快得多。最后再分享一个个人体会写这套批量预处理程序最花时间的不是实现某个算法而是让程序在几百景数据上稳定地从头跑到尾。断点续跑、日志记录、磁盘空间检查这些看似不性感的功能才是批量程序能长期服役的关键。如果你也要做类似的高分影像批量处理建议先做深元数据解析再做核心算法链路最后补批处理骨架顺序反了很容易烂尾。这套程序我还在继续加云检测和镶嵌匀色模块等有阶段性成果了再回来补充一篇。