
简介国产卫星定标系数与光谱响应函数资料包面向卫星遥感数据处理、GIS与地球观测应用场景聚焦高分GF和资源ZY系列卫星为开发者和科研人员提供辐射定标、大气校正及地表参数反演所需的关键参数。压缩包共93个文件包含txt、json、doc、xls、xlsx、pdf等类型其中txt和json文件对应各传感器光谱响应函数及定标数据表格文件便于批量查阅和对比doc/pdf收录历年国产陆地观测卫星绝对辐射定标系数文档整体大小仅1.85MB。内容丰富且结构清晰覆盖GF-1/2/4/6、ZY-02C/ZY-3、HJ-1A/B等型号时间跨度从2011年到2019年可配合6S辐射传输模型使用有效提升遥感数据处理精度。目前已有2949人学习适合遥感算法研究、生态环境监测及卫星数据应用开发等场景。1. 拿到国产卫星定标系数和光谱响应函数.zip先别急着解压就跑做定量遥感的人第一次打开这个 zip 时通常想的是赶紧把定标系数读出来算辐亮度。但真正让项目翻车的往往不是系数本身而是参数表里的单位、公式版本和光谱归一化方式。定标系数解决的是 DN 值到物理辐亮度的换算光谱响应函数SRF解决的是波段等效计算和传感器间光谱匹配两个文件配合才能算出可靠的反射率产品。无论你是做地表参数反演、大气校正还是交叉定标拿到包后先花十分钟搞清楚数据组织方式和有效期比直接跑通一条命令更有价值。2. 先搞懂这两类参数在矫正什么从 DN 到辐亮度的链路和波段等效计算2.1 绝对辐射定标为什么 Gain 和 Offset 不能照搬别的卫星定标系数是传感器把入瞳辐亮度转换成 DN 计数的桥梁。绝大多数国产卫星 L1 级产品给出的是线性定标关系L Gain × DN Offset其中 L 为表观辐亮度DN 为影像像元值。看起来很简单但每个数据集的写法差异很大。有的给Gain和Offset有的给Scale和Bias还有的给C0和C1甚至给的是辐亮度到 DN 的反向公式用的时候要先把公式倒过来。单位也经常不一致常见的有W/(m²·sr·μm)和mW/(m²·sr·μm)两者相差 1000 倍如果不统一算出的反射率不是零点几而是几百。我拿到参数表后第一件事是整理一份单位对照把包里的参数全部归一到国际单位制。下面是一个典型的核对表格整理完再进代码参数名常见写法单位使用说明辐射增益Gain / Scale / C1W/(m²·sr·μm)/DN乘以 DN 得到辐亮度辐射偏移Offset / Bias / C0W/(m²·sr·μm)加到乘积结果上中心波长Center_WLnm 或 μm用于波段标记不参与加权计算波段宽度FWHMnm辅助判断 SRF 覆盖范围定标日期Cal_Dateyyyymmdd确认参数有效期另外要注意部分 CMOS 探测器或宽幅相机的定标关系不是线性的包里可能给出的是二次多项式L C0 C1 × DN C2 × DN²这种公式常见于探测器响应存在压缩或非线性段的情况。如果文档里写了二次项就别拿线性公式硬套高亮目标雪、云、盐碱地计算值的偏差会比暗目标明显得多。我见过有人用线性公式处理 16 位影像的雪像元反射率超过 1.6后来核对发现包里的 C2 项根本没有进代码。2.2 光谱响应函数不是一条曲线那么简单光谱响应函数描述的是传感器每个波段对不同波长能量的响应权重横轴是波长纵轴是归一化响应值。它解决两类问题一是把地物光谱仪测到的高光谱数据换算到卫星波段二是把不同传感器的波段做光谱匹配。计算公式是典型的加权平均ρ_band ∫ ρ(λ) × SRF(λ) dλ / ∫ SRF(λ) dλ关键在于分母的处理。很多 SRF 文件出厂时是按峰值归一化的响应最大值等于 1但这不是严格的归一化。如果做传感器间的等效计算要让响应函数在波段范围内的积分等于有效带宽也就是面积归一化。两者差多少取决于波段形状矩形波段差距小高斯型波段差距能到 5% 以上对反演 Leaf Area Index 这类敏感参数影响很大。判断包里 SRF 用的是哪种归一化直接看文件末尾或说明文档中是否写了normalized to peak或area normalized。都不写的话把整条曲线加起来看和带宽的关系基本能猜出来。这一步很关键因为后面做大气校正或模型模拟时很多辐射传输模型要求特定格式的响应文件归一化方式错了模型不会报错只会给你偏小的等效反射率。2.3 典型的 zip 包文件清单与命名规律国产卫星的定标包没有统一标准但组织方式大同小异。常见做法是按传感器分目录目录下是每个波段的定标系数文本和光谱响应数据附带一个 README 或 XML 描述文件。拿到包先不要散落解压到桌面列一下文件结构文件类型常见扩展名内容说明定标系数表.csv / .txt / .dat每波段一行包含 Gain、Offset、有效期光谱响应函数.csv / .txt / .srf每波段一列波长和响应值或分文件存放波段参数描述.xml / .json / .md中心波长、带宽、定标日期、公式版本使用说明README / ReadMe公式约定、单位、更新记录文件名通常有规律可循比如WFV1_B1_Calibration.csv中的 WFV1 是传感器标识B1 是波段号。先unzip -l看一眼清单再解压能避免解压出一堆B1.txt之后分不清属于哪个载荷。把文件清单和 README 过一遍对包内的公式版本、单位和有效期有数才开始写解析脚本这是省时间的做法不是洁癖。3. 用 Python 把 zip 里的参数吃到内存解压、解析与定标计算3.1 解压 zip 的三个前置动作伪加密识别、GBK 文件名转码、目录规划zip 文件看似普通但遥感数据包经常有幺蛾子。先检查文件头是不是PK\x03\x04有的下载渠道会把 zip 包做一层传输编码拿到手是 base64 文本或者扩展名对不上的文件。还有一类叫 zip 伪加密文件头标记了加密标志但其实没有加密内容Windows 自带解压会直接要求输密码而 7-Zip 有时能直接打开。下面是检查文件头和伪加密位的代码from zipfile import ZipFile import base64 import os def inspect_zip(path): with open(path, rb) as f: header f.read(4) if header bPK\x03\x04: print(正常 ZIP 文件头) else: content open(path, rb).read() try: decoded base64.b64decode(content) if decoded[:4] bPK\x03\x04: print(检测到 base64 包裹的 ZIP尝试还原...) with open(path.replace(.zip, _decoded.zip), wb) as f: f.write(decoded) except Exception as e: print(不是标准 ZIP也不是 base64 包裹注意核对下载来源) with ZipFile(path if header bPK\x03\x04 else path.replace(.zip, _decoded.zip)) as zf: for info in zf.infolist(): flag_encrypted info.flag_bits 0x1 if flag_encrypted and not info.flag_bits 0x2: print(f伪加密嫌疑: {info.filename})代码先读文件前四个字节判断真实格式再遍历 zip 内的文件条目检查通用标志位。伪加密的特征是第 0 位加密标志置 1但第 1 位压缩标志为 0这种文件用 Python 的 ZipFile 有时能直接读出内容用 WinRAR 解压却要密码。参数说明flag_bits 0x1是加密位 0x2是压缩方式位两者组合起来能区分真加密和伪加密。这段脚本不修改原始文件只输出诊断信息解压动作留给自己决定。3.2 解析定标系数表读 CSV 并把 Gain/Offset 组织成字典定标系数表通常是 CSV 或空格分隔的文本带注释行和表头。解析时要注意分隔符可能是逗号、制表符或连续空格还有文件编码可能是 GBK 而不是 UTF-8在 Linux 上直接用open()读会出现乱码。下面写一个兼容分隔符和编码的解析函数import csv import chardet def load_calibration(path): # 先检测编码避免 GBK 文件被当作 UTF-8 读取 with open(path, rb) as f: raw f.read(4096) encoding chardet.detect(raw)[encoding] or utf-8 print(f检测到编码: {encoding}) calib {} with open(path, r, encodingencoding) as f: reader csv.reader(f, delimiterNone) for row in reader: row [r.strip() for r in row if r.strip()] if not row or row[0].startswith(#): continue # 期望行格式: band, gain, offset, cal_date try: band, gain, offset row[0], float(row[1]), float(row[2]) calib[band] {gain: gain, offset: offset} except ValueError as e: print(f跳过无法解析的行: {row}, 原因: {e}) return calib # 使用示例 cal load_calibration(WFV1_B1_Calibration.csv) print(cal)delimiterNone让 csv 模块自动识别逗号、Tab 或空格分隔减少手工处理。编码检测用chardet它在中文遥感数据文件上准确率不错。参数说明函数把波段名作为字典键方便后续按波段索引注释行统一跳过。如果文件里 Gain 和 Offset 的单位不是国际单位制在这个函数里顺手除 1000 换算掉后续计算就不用处处提心吊胆。3.3 从 DN 到表观反射率一条命令算完整链路有了定标系数下一步是把原始 DN 影像换算成表观反射率TOA Reflectance。辐亮度到反射率的公式是ρ_TOA π × L × d² / (ESUN × cos(θ_s))其中 d 是日地距离因子天文单位θ_s 是太阳天顶角ESUN 是波段平均太阳辐照度。ESUN 一般由太阳光谱曲线与 SRF 加权积分得到有的定标包里直接给了每个波段的 ESUN 值。下面是从辐亮度到反射率的批处理import numpy as np from osgeo import gdal def dn_to_reflectance(dn_path, calib_band, esun, sza_deg, d_au): ds gdal.Open(dn_path) dn ds.ReadAsArray().astype(np.float32) gain calib_band[gain] offset calib_band[offset] radiance dn * gain offset # 太阳天顶角转弧度计算余弦 cos_theta np.cos(np.deg2rad(sza_deg)) # 日地距离因子 d_au 由成像日期决定可查表或天文公式计算 reflectance (np.pi * radiance * d_au**2) / (esun * cos_theta) return np.clip(reflectance, 0, None) # 示例参数ESUN 以 W/(m²·sr·μm) 为单位 rho dn_to_reflectance(scene_B1.tif, cal[B1], esun1823.5, sza_deg42.3, d_au0.983)参数说明sza_deg从影像头文件或元数据里读d_au可以按儒略日近似计算1 月初约 0.9837 月初约 1.016。np.clip把负值清零避免大气校正前出现物理上不可能的结果。如果算出来的反射率普遍大于 1不要急着怀疑代码先回头检查定标系数的单位和 ESUN 是否对应同一套单位制。这一步是定性遥感的第一道坎错了后面所有产品都不可信。4. 把光谱响应函数重采样成波段模拟器插值、归一化与模型输入4.1 先做数据体检波长单位、采样间隔、响应范围SRF 文件的格式比定标系数更乱。有的波长列用纳米有的用微米有的曲线等间隔采样有的在带肩处加密采样有的响应范围正好覆盖半高宽附近有的拖尾到很远。直接把原始曲线喂给插值函数很可能得到错误结果。先写一个体检脚本把每个波段的波长范围和采样点数打出来import pandas as pd def inspect_srf(path): # 读取 SRF 文件假设列格式为: wavelength, response df pd.read_csv(path, comment#, sepNone, enginepython) wl df.iloc[:, 0].values resp df.iloc[:, 1].values # 判断波长单位若最大值小于 100 则视为微米转成纳米 if wl.max() 100: wl wl * 1000 print(波长已从微米转换为纳米) sr pd.Series(resp, indexwl).sort_index() print(f波长范围: {sr.index.min():.1f} - {sr.index.max():.1f} nm) print(f采样点数: {len(sr)}) print(f响应峰值: {sr.max():.4f}) print(f响应积分: {sr.sum():.4f} (采样间隔 {sr.index.to_series().diff().median():.3f} nm)) return sr srf inspect_srf(WFV1_B2_SRF.csv)参数说明seqNone会触发自动分隔识别对 CSV 够用。判断波长单位用的是经验规则纳米制波长一般到 2500微米制一般不超过 30。响应积分值除以采样间隔可以估算有效带宽如果响应最大值明显大于 1说明文件可能不是归一化曲线而是原始量子效率需要先除以最大值。这一步把后续插值、归一化的数据质量问题提前暴露掉。4.2 重采样到 1nm 网格并做归一化不同来源的 SRF 采样间隔不同做波段模拟前统一到 1nm 网格最省事。1nm 网格量级适中既能保留曲线形状又不会让积分计算太慢。重采样用线性插值即可不需要高阶拟合因为 SRF 本身是测量曲线高频抖动多半是噪声。归一化要区分场合峰值归一化用于展示曲线形状面积归一化用于辐射计算。下面代码同时输出两种结果import numpy as np def resample_srf(srf, wl_gridnp.arange(400, 2501, 1)): # srf 是 pandas Series, index 为波长值为响应 # 去掉重复波长并排序避免插值报错 srf srf[~srf.index.duplicated()].sort_index() # 线性插值到标准网格超出原范围的填 0 resp_interp np.interp(wl_grid, srf.index.values, srf.values, left0, right0) # 峰值归一化: 除以最大值 resp_peak_norm resp_interp / resp_interp.max() # 面积归一化: 使曲线积分等于 1即等效带宽为 1nm resp_area_norm resp_interp / np.sum(resp_interp) return wl_grid, resp_peak_norm, resp_area_norm wl, srf_peak, srf_area resample_srf(srf) print(f峰值归一化积分: {srf_peak.sum():.2f} nm) print(f面积归一化积分: {srf_area.sum():.6f} nm^-1)参数说明np.interp的left0, right0参数让插值范围外补零避免外推带来的虚假响应。峰值归一化适合做曲线对比图面积归一化适合做波段等效计算。积分结果可以自己验证峰值归一化后曲线求和约等于有效带宽面积归一化后求和等于 1。如果两个结果差异超过 5%说明曲线形状偏离矩形较多波段模拟时必须用面积归一化。4.3 用 SRF 计算波段等效值把高光谱数据折到传感器波段有了归一化后的 SRF就可以把野外地物光谱、机载高光谱影像或辐射传输模型输出换算到传感器波段。原理就是前面公式的离散实现关键是保证波长网格对齐。这里写一个通用函数def band_equivalent(wl_spec, spec_values, wl_srf, srf_resp): 将高光谱数据与 SRF 卷积得到传感器波段等效值 wl_spec: 高光谱数据的波长数组 (nm) spec_values: 对应波长的光谱值 wl_srf: 重采样后的 SRF 波长 srf_resp: 面积归一化的 SRF 响应 # 将高光谱数据插值到 SRF 网格保证逐点相乘 spec_interp np.interp(wl_srf, wl_spec, spec_values, leftnp.nan, rightnp.nan) valid ~np.isnan(spec_interp) numerator np.sum(spec_interp[valid] * srf_resp[valid]) denominator np.sum(srf_resp[valid]) return numerator / denominator # 示例假设野外实测光谱 reflectance 是 1nm 间隔数组 band_ref band_equivalent(np.arange(400, 901, 1), field_spec, wl, srf_area) print(f波段等效反射率: {band_ref:.4f})参数说明函数把高光谱数据插值到 SRF 的波长网格再做加权平均保证两条曲线逐点对应。valid掩码处理高光谱范围不覆盖 SRF 的情况避免乘积出现 NaN。面积归一化后的 SRF 在这里做分母归一等价于公式里的积分归一。这个函数可以用在多个场景验证光谱指数、模拟不同传感器的波段差异、把地面光谱仪数据与卫星影像对比。唯一的坑是波长单位必须一致传参前统一转成纳米。4.4 给 6S/FLAASH 生成 SRF 响应数据文件大气校正模型比如 6S、MODTRAN 和 FLAASH都要求输入传感器的光谱响应函数。各家格式略有不同但共同点是接受两列的波长-响应文本。生成这类文件其实就是格式化输出注意波长单位与模型要求一致。下面是输出 6S 响应的典型写法def write_srf_for_model(wl, srf_resp, output_path, unitnm): if unit um: wl_out wl / 1000.0 else: wl_out wl with open(output_path, w) as f: f.write(* Wave_Length Response\n) for w, r in zip(wl_out, srf_resp): f.write(f{w:.4f} {r:.6f}\n) print(fSRF 文件写入: {output_path}, 共 {len(wl)} 行) write_srf_for_model(wl, srf_area, WFV1_B2_6S.srf, unitnm)参数说明unitnm和unitum控制是否做微米换算6S 的太阳光谱文件通常以微米为单位FLAASH 则偏好纳米。输出格式不做过多修饰保持空格分隔的纯文本。写完后人工抽查头尾几行确认波长从低到高排列响应值都在 0 到 1 之间。文件生成后可以先跑一次 6S 的测试模式看看输出透过率曲线是否平滑如果出现振荡多半是 SRF 尾部有毛刺需要对原始曲线做平滑或截断处理。5. 用定标包最容易翻车的 5 个坑现象、原因与排查办法5.1 zip 伪加密双击能打开但解压就报错要求输密码现象在 Windows 上双击 zip 文件能看到里面的文件列表但右键解压时提示需要密码或者用 WinRAR 打开后文件带星号锁。用 7-Zip 有时却能正常解压行为表现不一致。原因这类文件多半是 zip 伪加密即文件头的通用位标志里加密位被置 1但数据区并没有真正加密。产生原因可能是数据打包工具写错了标志位也可能是人为设置防止直接预览。Python 的 zipfile 模块对伪加密容忍度较高能直接读出内容。解决优先用 7-Zip 尝试解压它默认忽略部分伪加密标志。如果 7-Zip 也不行用前面代码里的flag_bits检查脚本确认加密标志和压缩标志的组合确认是伪加密后用 PythonZipFile直接extractall()一步到位。记住不要到处找密码方向基本是错的。5.2 Linux 下中文文件名乱码与解压路径失控现象在 Ubuntu 上用unzip解压后文件名变成之类的乱码进入目录后找不到对应文件。更糟的情况是解压出带绝对路径的条目文件散落到/tmp或其他目录。原因zip 里的文件名是 GBK 编码而 Linux 的unzip默认按 UTF-8 解码两者冲突。部分打包工具还把路径写成了绝对路径比如/home/user/data/xxx解压时直接按照绝对路径落地脱离了当前目录。解决检查 zip 内容用什么编码zipinfo -v看不了编码但可以用 Python 打印info.filename.encode(cp437).decode(gbk)验证。解压时用unzip -O gbk指定编码如果系统unzip版本不支持-O参数写个 Python 脚本遍历infolist()先转码再提取同时过滤掉以/开头的条目把路径重新映射到当前目录。这一步能同时解决乱码和路径失控两个问题。5.3 把二次多项式定标公式当线性用高亮区反射率失真现象同一景影像上暗像元反演结果正常但云和雪像元表观反射率超过 1.2且与相邻传感器同期观测偏差明显。检查代码Gain 和 Offset 代入了线性公式一切看起来都对。原因定标系数包更新后采用了非线性定标模型文档里写的是 L C0 C1 × DN C2 × DN²其中 C2 是负值在大信号区压缩辐亮度。代码只取了 C0 和 C1相当于忽略了 C2 × DN² 项DN 越大偏差越大1 万以上的 DN 值可差 10% 以上。解决解析定标表时不要硬编码两个字段先读 README 确认公式形式。如果是多项式把 C2 加进去并让解析函数返回公式类型字段。另一个自检手段是画 DN-辐亮度散点图如果中高 DN 段出现明显曲率说明还有二次项没进代码。不要觉得多项式少见就不写近两年新发射的传感器不少采用这种模型。5.4 SRF 归一化方式用错模拟反射率整体偏低现象用国产卫星 SRF 模拟 MODIS 波段反射率结果比 MODIS 官方波段反射率系统性低 3%~6%又不是明显的云或气溶胶影响找不出原因。原因包里 SRF 是峰值归一化最大值等于 1但直接用它做波段等效计算相当于默认把曲线面积积分当成 1。实际带宽是 60nm峰值归一化后的曲线积分也是 60nm但正确的面积归一化要把每个响应值除以积分宽度。两种归一化下对平坦光谱差别不大对植被这类陡坡光谱差别明显。解决在重采样函数里同时输出峰值归一化和面积归一化两种版本。做波段模拟、辐射传输、交叉定标时全部用面积归一化版本只有画曲线对比图时用峰值归一化。代码里加一段断言检查np.sum(srf_area) 1不满足就报错提醒。5.5 用过期的定标系数处理跨年影像把增益漂移当成地表变化现象同一地区 2022 年和 2023 年的同一传感器影像反演地表反射率出现系统性偏移裸地反射率年际差超过 0.04而且偏移方向与定标系数更新方向一致不是随机噪声。原因星上定标器会逐渐衰减传感器的增益不是恒定的所以遥感数据集会定期重新定标发布新的系数表。如果拿旧的系数算新影像等于把传感器的物理衰减直接混入地表信号。国产卫星定标系数的更新频率通常以季度或年度为单位。解决从包或数据集的元数据里提取定标系数的生效日期按影像成像时间匹配对应版本。处理时间序列影像时不要把系数当成常数每次都检查目标影像的日期落在哪个系数有效期内。归档时把系数文件按日期命名不要只存最后一个版本。这个习惯能省掉后续很多重算的时间和尴尬。6. 最后一道工序用参考传感器交叉比对验证定标参数参数包好不好用跑通流程只算完成了 80%剩下 20% 是验证。我现在处理新拿到的定标系数习惯性找一个同期过境的参考传感器做交叉比对。Landsat-8/9 OLI 光谱分辨率高定标稳定常被当作参考基准。做法是选一景晴空影像找一块大面积均匀目标区清洁水体或裸地都可以分别用各自定标参数计算 TOA 反射率再做 SRF 光谱匹配看差值是否在合理范围内。一个最小验证脚本是取两景影像同名点的 TOA 反射率用第 4.3 节的波段等效函数把参考传感器模拟成目标传感器波段然后计算差值def validate_toa(rho_target, rho_ref_simulated, tolerance0.02): diff rho_target - rho_ref_simulated print(f目标波段反射率: {rho_target:.4f}) print(f参考模拟反射率: {rho_ref_simulated:.4f}) print(f差值: {diff:.4f}) if abs(diff) tolerance: print(通过光谱匹配后差值在合理范围) else: print(注意差值超出预期检查定标系数有效期和 SRF 归一化)如果差值是随机正负波动多半是 BRDF 或气溶胶差异如果所有波段系统性偏亮或偏暗就要回到定标系数的单位和公式版本上。我吃过一次亏拿到包后没看公式版本直接用线性模型算了一整景影像发现水体反射率 0.11比实测高了 0.03排查半天才发现是二次项缺失。从那以后我每次解压后先写三行脚本把 README 里的公式、单位、有效期打印出来贴在处理脚本头部当作参数包的使用声明。这个习惯让我少走了很多弯路也希望帮到你。本文还有配套的精品资源点击获取