09 - 多光谱与高光谱模拟¶
本章介绍如何使用 LESS 模拟多光谱和高光谱遥感数据,包括自定义波段、预定义卫星传感器、以及不同大气模型的选择。
光谱波段类型¶
LESS 提供了多种方式来指定模拟的光谱波段:
离散波段¶
最简单的方式,直接指定中心波长列表:
import less
# 自定义波段(单位:nm)
bands = [475, 560, 668, 717, 842]
sensor = less.OpticalImager(
less.Orthographic(image_size=512),
bands=bands,
)
预定义卫星传感器¶
LESS 内置了常用卫星传感器的光谱响应函数 (SRF):
# Sentinel-2A MSI(13 个波段)
sensor_s2 = less.OpticalImager(
less.Orthographic(image_size=512),
bands=less.Sentinel2A(),
spectral_resolution=5,
quality=128,
name="Sentinel-2A",
)
# Landsat 8 OLI(11 个波段)
sensor_l8 = less.OpticalImager(
less.Orthographic(image_size=512),
bands=less.Landsat8_OLI(),
spectral_resolution=5,
quality=128,
name="Landsat-8",
)
使用 SRF 波段时,模拟会考虑光谱响应函数的形状,而不是简单的中心波长。这对宽波段传感器(如 Landsat)更为精确。
高光谱¶
连续光谱,指定起始波长、终止波长和步长:
# 400-2500 nm,每 10 nm 一个波段(共 211 个波段)
bands_hyper = less.Hyperspectral(start=400, stop=2500, step=10)
sensor_hyper = less.OpticalImager(
less.Orthographic(image_size=256), # 高光谱用较小图像加快速度
bands=bands_hyper,
quality=64,
name="Hyperspectral",
)
高光谱模拟的计算量 = 波段数 × 单波段计算量。211 个波段会比 3 个波段慢约 70 倍(LESS 内部有批处理优化,实际比例更小)。
大气模型选择¶
光照模型决定了入射到场景的太阳辐射光谱。不同的光照模型适用于不同的波段范围:
HosekWilkieAtmosphere —— 可见光天空模型¶
# 适合 320-720 nm 范围,物理天空辐亮度分布
scene.illumination = less.Illumination(source=less.Sun(zenith=30, azimuth=150), atmosphere=less.HosekWilkieAtmosphere(turbidity=2.5))
特点:天空辐亮度具有真实的角分布(地平线附近更亮、太阳周围有光晕)。但只覆盖可见光波段。
SimpleSpectralAtmosphere —— 宽波段大气模型¶
# 适合 300-2500 nm 全波段
scene.illumination = less.Illumination(source=less.Sun(zenith=30, azimuth=150), atmosphere=less.SimpleSpectralAtmosphere(turbidity=2.5))
特点:内置 Rayleigh 散射、气溶胶消光、臭氧和水汽吸收的大气模型。天空散射光为各向同性。覆盖全短波范围,适合多光谱和高光谱模拟。
NoAtmosphere —— 真空¶
# TOA 直射不衰减,也不产生天空散射
scene.illumination = less.Illumination(
source=less.Sun(zenith=30, azimuth=150),
atmosphere=less.NoAtmosphere(),
)
特点:真空边界不会衰减 TOA 直射,也不会生成漫射。若已知大气直射和漫射传输系数,使用 PrescribedAtmosphere。
Atmosphere —— 原生大气传输¶
# 太阳几何与大气状态分别设置
scene.illumination = less.Illumination(
source=less.Sun(zenith=30, azimuth=150),
atmosphere=less.Atmosphere.standard(
"midlatitude_summer",
aerosol="continental",
aot550=0.2,
ground_altitude_km=0.0,
),
)
原生模型计算 300–2500 nm 的气体吸收、Rayleigh 散射、气溶胶散射以及直射/漫射多次散射。普通安装已经包含模型和参数数据库。大气层顶成像、参数含义和适用范围见原生大气与大气层顶成像。
大气剖面选项:
| 参数值 | 含义 |
|--------|------|
| "tropical" | 热带 |
| "midlatitude_summer" | 中纬度夏季 |
| "midlatitude_winter" | 中纬度冬季 |
| "subarctic_summer" | 亚北极夏季 |
| "subarctic_winter" | 亚北极冬季 |
| "us_standard" | 美国标准大气 |
气溶胶类型:
| 参数值 | 含义 |
|--------|------|
| "continental" | 大陆型 |
| "maritime" | 海洋型 |
| "urban" | 城市型 |
| "desert" | 沙漠型 |
| "no_aerosols" | 无气溶胶 |
less.SixSAtmosphere 使用 6S,需要另行安装 less3d[atmosphere]。
完整示例:多传感器模拟¶
import less
import numpy as np
# ── 构建玉米田场景 ───────────────────────────────────────────
scene = less.Scene()
scene.size = 10.0
scene.repetitive = False # REPETITIVE_SCENE 尚未公共发布
scene.terrain = less.Terrain(property=less.Lambertian(reflectance=0.15))
scene.illumination = less.Illumination(source=less.Sun(zenith=30, azimuth=150), atmosphere=less.NoAtmosphere())
maize = less.Object("maize", mesh=less.examples.asset_path("maize.obj"))
maize.set_property(less.Prospect(cab=45, car=10, cw=0.012, cm=0.006, N=1.55))
# 行列种植
rng = np.random.RandomState(42)
row_sp, plant_sp, margin = 0.75, 0.40, 0.5
n_rows = int((10.0 - 2 * margin) / row_sp) + 1
n_plant = int((10.0 - 2 * margin) / plant_sp) + 1
xs = margin + np.arange(n_rows) * row_sp
ys = margin + np.arange(n_plant) * plant_sp
gx, gy = np.meshgrid(xs, ys, indexing='ij')
positions = np.column_stack([
gx.ravel() + rng.uniform(-0.04, 0.04, gx.size),
gy.ravel() + rng.uniform(-0.04, 0.04, gy.size),
np.zeros(gx.size),
])
scene.add(maize, positions=positions,
scales=rng.uniform(0.9, 1.1, len(positions)),
rotations=rng.uniform(0, 360, len(positions)))
sensor_rgb = less.OpticalImager(
less.Orthographic(image_size=512),
bands=[650, 550, 450],
quality=128, name="RGB",
)
img_rgb = scene.simulate(sensor_rgb)
img_rgb.save("09_rgb.png")
# ── 2. Sentinel-2A 模拟 ─────────────────────────────────────
sensor_s2 = less.OpticalImager(
less.Orthographic(image_size=512),
bands=less.Sentinel2A(),
spectral_resolution=5,
quality=128, name="Sentinel-2A",
)
img_s2 = scene.simulate(sensor_s2)
img_s2.save("09_sentinel2a.tif")
# 计算 NDVI(Sentinel-2A: B4=红 index 3, B8=NIR index 7)
data_s2 = img_s2.data
red_s2 = data_s2[:, :, 3] # B4 (665 nm)
nir_s2 = data_s2[:, :, 7] # B8 (842 nm)
ndvi_s2 = (nir_s2 - red_s2) / (nir_s2 + red_s2 + 1e-10)
print(f"Sentinel-2A NDVI: {np.nanmean(ndvi_s2):.3f} (mean)")
# ── 3. 高光谱 ────────────────────────────────────────────────
sensor_hyper = less.OpticalImager(
less.Orthographic(image_size=256),
bands=less.Hyperspectral(start=400, stop=1000, step=5),
quality=64, name="Hyperspectral",
)
img_hyper = scene.simulate(sensor_hyper)
img_hyper.save("09_hyperspectral.tif")
# 提取场景平均光谱
data_hyper = img_hyper.data
mean_spectrum = np.mean(data_hyper, axis=(0, 1))
print(f"高光谱波段数: {mean_spectrum.shape[0]}")
print(f"平均辐亮度 (500 nm): {mean_spectrum[20]:.4f} W/m²/sr/nm")
print("Tutorial 09 完成!")
NDVI 计算示例¶
归一化植被指数 (NDVI) 是最常用的植被指数,利用红光和近红外波段的差异:
# 使用自定义波段
sensor = less.OpticalImager(
less.Orthographic(image_size=512),
bands=[650, 842], # Red, NIR
quality=128,
)
image = scene.simulate(sensor)
data = image.data
red = data[:, :, 0]
nir = data[:, :, 1]
ndvi = (nir - red) / (nir + red + 1e-10)
# NDVI 典型范围
# 裸土: 0.05 ~ 0.15
# 稀疏植被:0.15 ~ 0.40
# 密集植被:0.40 ~ 0.90
光谱分析:植被红边¶
红边(Red Edge, ~700-750 nm)是植被光谱最显著的特征之一——从红光的强吸收到近红外的高反射之间的急剧跃变。
使用高光谱模拟可以精确捕捉红边特征:
# 红边区域细致采样
sensor_rededge = less.OpticalImager(
less.Orthographic(image_size=512),
bands=less.Hyperspectral(start=650, stop=800, step=2), # 2 nm 分辨率
quality=128,
)
image_re = scene.simulate(sensor_rededge)
data_re = image_re.data
# 提取某个像元的光谱
pixel_spectrum = data_re[256, 256, :]
wavelengths = np.arange(650, 800, 2)
# 红边位置 = 一阶导数最大值对应的波长
deriv = np.gradient(pixel_spectrum, 2) # 2 nm 步长
red_edge_pos = wavelengths[np.argmax(deriv)]
print(f"红边位置: {red_edge_pos} nm")
下一步¶
相关 API¶
less.SpectralBands、less.Hyperspectralless.Sentinel2A、less.Landsat8_OLIless.OpticalImager、less.Orthographicless.Atmosphere、less.HosekWilkieAtmosphereless.SimpleSpectralAtmosphere、less.PrescribedAtmosphere、less.NoAtmosphere