04 - 核心概念¶
本章介绍 LESS 的核心概念和工作流程。理解这些概念后,您将能够灵活组合各种功能来完成复杂的遥感模拟任务。
场景生命周期:build、simulate 与 rebuild¶
LESS 的 Scene 是一个可持续复用的数字场景,并采用 结构—光谱分离 架构。几何和加速结构可以跨多次模拟复用;传感器波段、光照和属性则按当前模拟更新。
import less
# 1. 创建场景
scene = less.Scene()
scene.size = 50.0
# 2. 配置场景元素
scene.terrain = less.Terrain(property=less.Lambertian(reflectance=0.15))
scene.illumination = less.Illumination(source=less.Sun(zenith=30, azimuth=150), atmosphere=less.NoAtmosphere())
tree = less.Object("ash", mesh=less.examples.asset_path("FREX.obj"))
tree.set_property("leaves", less.Prospect(cab=40))
tree.set_property("stem_branch", less.Lambertian(reflectance=0.1))
scene.add(tree, positions=[[25, 25, 0]])
# 3. 模拟 → 必要时自动构建场景并获得产品
image = scene.simulate(less.OpticalImager(
less.Orthographic(image_size=512),
bands=[650, 550, 450],
))
# 4. 修改参数后再次模拟(不需要 rebuild)
scene.illumination = less.Illumination(source=less.Sun(zenith=50, azimuth=200), atmosphere=less.NoAtmosphere())
image2 = scene.simulate(less.OpticalImager(
less.Orthographic(image_size=512),
bands=[650, 550, 450],
))
# 5. 图像可视化
image.show() # 原始辐亮度可视化
image.to_brf().show() # 转为BRF再可视化
simulate():普通用户的默认入口¶
scene.simulate(...) 可以自动构建场景。如果场景尚未构建,LESS 会在第一次模拟前自动构建;后续模拟会尽可能复用已有原生场景和加速结构。
传感器波段会按本次模拟重新准备,因此不需要为了更换波段而提前调用 build()。
build():可选的显式准备步骤¶
build() 是可选的显式准备步骤。它用于提前验证场景、加载几何、上传资源并构建加速结构,例如需要把构建耗时与模拟耗时分开测量时:
build() 具有幂等性:对于已经构建且没有变化的场景,再次调用通常复用已有原生场景,而不是无条件重新构建。动态实例接口 Scene.update_instances() 也要求场景已经显式构建。
rebuild():强制完整重建¶
rebuild() 用于强制完整重建:它会刷新原生场景表示,重新读取或上传几何,并重建加速结构。
典型用途是外部程序修改了同一路径下的 OBJ 文件,但 LESS 无法从路径变化判断文件内容已经改变。
修改属性或光照通常不需要调用 rebuild():
scene.terrain.set_property(
less.Lambertian(reflectance=0.2)
)
scene.illumination = less.Illumination(
source=less.Sun(
zenith=45,
azimuth=scene.illumination.source.azimuth,
spectrum=scene.illumination.source.spectrum,
wavelengths=scene.illumination.source.wavelengths,
irradiance=scene.illumination.source.irradiance,
),
atmosphere=scene.illumination.atmosphere,
)
image = scene.simulate(sensor)
LESS 会更新相关参数,并尽可能复用已有几何和 BVH。通过公开 API 修改几何时,LESS 会维护场景 dirty 状态;通常直接再次调用 simulate() 即可。只有必须无条件刷新几何时才使用 rebuild()。
| 操作 | 含义 | 推荐使用场景 |
|---|---|---|
simulate() |
必要时自动构建,然后执行模拟 | 普通用户的默认入口 |
build() |
按需提前构建,未变化时复用 | 提前验证、区分构建耗时与模拟耗时、准备动态实例更新 |
rebuild() |
强制完整刷新几何和加速结构 | 同路径几何文件被外部替换等情况 |
推荐原则:普通模拟直接使用 simulate();需要提前准备场景时使用 build();确定必须完整刷新几何时使用 rebuild()。
重复场景 (scene.repetitive)¶
遥感中经常需要模拟均质植被冠层(如大面积农田、森林),此时场景应该向四周无限延伸。LESS 通过 scene.repetitive 参数实现这一效果:光线在到达场景边界时会自动"折回",等效于将同一场景平铺成无限大的镶嵌阵列。
scene = less.Scene()
scene.size = 50.0 # 场景边长 50m(周期性单元)
# 默认:孤立场景(有限场景,自动启用 AABB silhouette 发射以消除边缘暗化)
scene.repetitive = False
# 当前公共模拟默认有限场景;周期 modifier 已发布(仅限平坦均质地形)
# scene.repetitive = True # 无限平铺(wrap 上限 100)
两种模式怎么选¶
有限场景和周期场景的物理含义不同,选择取决于你要模拟的对象:
| 设置 | 适用 | 光子发射策略 |
|---|---|---|
scene.repetitive = False(默认) |
单木、孤立地块、城市街区等真实有边界的目标 | AABB silhouette:顶面 + 迎光侧面均匀发射,场景迎光的侧面也有入射光线 |
scene.repetitive = True |
水平无限场景 | 已发布:射线跨边界折回(wrap 上限 100,也可显式设置整数) |
True对应光线最多折返 100 次;整数 1–4 被拒绝(原生实现会产生黑色接缝伪影)。DEM/网格地形不能重复,也不能与terrain_following组合。
老版本(≤0.3.1)在 repetitive=0 时只从场景顶面撒光子。斜射太阳下,原本应该从场景外"越过上空再进入冠层侧面"的那部分光被丢掉了,场景迎光边缘会出现系统性偏暗。过去我们用 repetitive=100 绕过这个问题——代价是把本来有限的场景当无限周期处理,物理上不准确。
0.3.2 起,有限场景(repetitive=False)会自动在 AABB 的 5 个朝天面(顶 + 4 侧)上采样光子:
- 太阳光:只用 3 个迎光面(顶 + 迎光 X 侧 + 迎光 Y 侧),按
A_face · |d·n|加权 - 天空光:5 个朝天面,按面积加权 + 各面本地半球 cos 采样方向
对平地或场景天然扁平(D ≈ 0)时,侧面权重自动退化为 0,与老版本完全等价;有 3D 结构时,侧面发射把过去丢掉的那部分斜射光补回来。
启用 repetitive = True 后:
- 射出场景边界的光线会从对面边界重新进入(周期性边界条件)
- 等价于场景被无限复制并拼接
- 所选后端只存储一份场景几何,不会因周期复制而额外占用内存
当前公共规则:默认
False。均质冠层的周期基准使用True(wrap 100)或>=5整数;ALS/LiDAR 飞行覆盖靠传感器布置而不是场景复制。
场景元素¶
Object —— 三维物体¶
Object 是场景中的三维物体。一个 Object 由一个或多个 组件
(component)组成,每个 component 包含一种几何表示和若干属性:
组件目前可以使用两种几何表示:
| 几何表示 | 描述 | 常见用途 |
|---|---|---|
Mesh |
保留 OBJ 中的真实三角面片 | 树干、枝条、显式叶片、建筑物 |
TurbidBoundary |
OBJ 只定义封闭空间,空间内用统计参数(叶面积体密度、叶倾角分布等)描述大量小叶片 | 大型树冠、连续冠层 |
两者都是普通 component,可以放在同一个 Object 中,也使用相同的
set_property()、scene.add() 和 scene.simulate()。
使用 Mesh 组件¶
当整个 OBJ 都作为一个 Mesh 使用时,可以采用简写:
# 加载内置的欧洲白蜡树模型
tree = less.Object("ash_tree", mesh=less.examples.asset_path("FREX.obj"))
# 查看组件名称(自动从 OBJ 文件中扫描)
print(tree.components) # ['leaves', 'stem_branch']
# 为不同组件设置不同属性
tree.set_property("leaves", less.Prospect(cab=40, car=10))
tree.set_property("stem_branch", less.Lambertian(reflectance=0.1))
也可以显式添加指定分组:
tree = less.Object("ash_tree")
tree.add_component(
"leaves",
less.Mesh("tree.obj", group="leaves"),
)
tree.add_component(
"stem_branch",
less.Mesh("tree.obj", group="stem_branch"),
)
一个 OBJ 文件可以包含多个 group。指定 group 时只读取该分组,不会重复添加
整个文件。
使用 TurbidBoundary 组件¶
当树冠包含大量叶片而不需要保留每片叶子的形状时,可以让 OBJ 只描述树冠的 封闭范围:
tree = less.Object("ash_tree")
tree.add_component(
"crown",
less.TurbidBoundary("tree.obj", group="crown"),
)
tree.set_property(
"crown",
less.OpticalVegetation(
leaf=less.Prospect(cab=40, car=10),
leaf_area_density=1.5, # m²/m³
leaf_angle_distribution="spherical",
leaf_size=(0.08, 0.04), # 长、宽,单位 m
leaf_shape="ellipse",
),
)
TurbidBoundary 不是单独的模拟模式。创建场景后,仍然使用普通
OpticalImager、BRFSensor、PhotosynthesisProcess 或
EnergyBalanceProcess。LESS 会根据 component 的几何表示完成相应计算。
在一个 Object 中混合使用¶
tree = less.Object("tree")
tree.add_component(
"crown",
less.TurbidBoundary("tree.obj", group="crown"),
)
tree.add_component(
"trunk",
less.Mesh("tree.obj", group="trunk"),
)
tree.set_property(
"crown",
less.OpticalVegetation(
leaf=less.Prospect(cab=40),
leaf_area_density=1.5,
leaf_angle_distribution="spherical",
),
)
tree.set_property("trunk", less.Lambertian(reflectance=0.12))
树冠与树干会在同一次模拟中相互遮挡,并共同参与多次散射。统计植被的边界要求、 叶片尺寸、阴阳叶和精度设置参见 统计植被的进阶设置。
将物体添加到场景时,需要指定位置,还可以选择性地指定缩放和旋转:
import numpy as np
# 添加多棵树
positions = np.array([
[10, 10, 0],
[20, 15, 0],
[30, 25, 0],
])
scales = [0.8, 1.0, 1.2] # 每棵树的均匀缩放因子(XYZ 等比)
rotations = [0, 90, 180] # 绕 Z 轴旋转角度(度)
handle = scene.add(tree, positions=positions, scales=scales, rotations=rotations)
scales 参数支持多种形式:
| 形式 | 含义 | 示例 |
|---|---|---|
float |
所有实例统一均匀缩放 | scales=1.5 |
[N] 一维数组,长度 = 实例数 |
每个实例各自的均匀缩放(XYZ 等比) | scales=[0.8, 1.0, 1.2](3 棵树) |
[N, 3] 二维数组 |
每个实例各自的非均匀缩放 | scales=[[1,1,2],[1,1,1.5],[1,1,3]] |
如果需要对所有实例统一做非均匀缩放,也用二维数组:
scene.add() 返回一个 InstanceHandle,可以在 build 之后用它来修改实例的变换。
Terrain —— 地表¶
Terrain 表示场景的地面。默认为平坦地面(z = 0),也可以加载 DEM 数据。
# 简单的平坦地面
terrain = less.Terrain()
terrain.set_property(less.Lambertian(reflectance=0.15))
scene.terrain = terrain
# 或者在创建时直接设置
scene.terrain = less.Terrain(property=less.Lambertian(reflectance=0.15))
Illumination —— 光照¶
Illumination 定义场景的光照条件,包括太阳直射光和天空散射光。
# Hosek–Wilkie 物理天空模型(可见光波段推荐)
scene.illumination = less.Illumination(
source=less.Sun(zenith=30, azimuth=150),
atmosphere=less.HosekWilkieAtmosphere(turbidity=2.5),
)
# 简化光谱大气 + 各向同性散射(宽波段快速模拟)
scene.illumination = less.Illumination(
source=less.Sun(zenith=30, azimuth=150),
atmosphere=less.SimpleSpectralAtmosphere(turbidity=2.5),
)
# 真空:TOA 太阳直射不衰减,也不产生天空散射
scene.illumination = less.Illumination(
source=less.Sun(zenith=30, azimuth=150),
atmosphere=less.NoAtmosphere(),
)
# 可选的 6S 大气模型
scene.illumination = less.Illumination(
source=less.Sun(zenith=30, azimuth=150),
atmosphere=less.SixSAtmosphere(
atmosphere_profile="midlatitude_summer",
aerosol_type="continental",
aot550=0.2,
),
)
LESS 将外部辐射环境表示为“大气层顶入射源 + 大气模型”。less.Sun 只描述
太阳方向和大气层顶(TOA)法向光谱辐照度;大气模型负责计算到达地表的太阳
直射、天空散射,以及模型支持的上行传播。二者统一设置在
scene.illumination 中:
scene.illumination = less.Illumination(
source=less.Sun(
zenith=30,
azimuth=150,
),
atmosphere=less.Atmosphere.standard(
"midlatitude_summer",
aerosol="continental",
aot550=0.10,
ground_altitude_km=0.0,
streams=8,
sky_mode="anisotropic",
),
)
# 无需手工准备地表直射和散射光谱;simulate() 会按传感器波段求解。
image = scene.simulate(sensor)
也可以独立求解大气边界条件,以便检查光谱:
atmosphere = scene.illumination.atmosphere
result = atmosphere.solve(
[450, 550, 650, 850],
sun_zenith=30,
)
direct = result.direct_normal_irradiance
diffuse = result.diffuse_horizontal_irradiance
其中 direct_normal_irradiance 是太阳法向直射光谱辐照度,
diffuse_horizontal_irradiance 是水平面天空散射光谱辐照度,单位均为
W m⁻² nm⁻¹。less.Atmosphere 还可用于地表至观测高度的大气透过率和路径
辐亮度计算;完整参数和输出见大气模型与地气传输。
| 模型 | 适用场景 | 说明 |
|---|---|---|
Atmosphere |
原生分层大气和地气传输 | 短波 300–2500 nm;热红外 3000–14000 nm |
SixSAtmosphere |
6S 参数体系 | 需要可选依赖 less3d[atmosphere] |
HosekWilkieAtmosphere |
RGB 可视化、可见光研究 | 320–720 nm 的方向天空模型 |
SimpleSpectralAtmosphere |
快速宽光谱照明 | 简化直射衰减和各向同性天空光 |
NoAtmosphere |
真空或无大气实验 | TOA 直射原样到达场景,漫射为零 |
PrescribedAtmosphere |
已知大气传输系数 | 用户给定直射与水平漫射传输系数 |
用户指定的大气传输系数¶
已知大气传输系数时,可以使用 PrescribedAtmosphere:
scene.illumination = less.Illumination(
source=less.Sun(
zenith=30,
azimuth=150,
wavelengths=[450, 550, 650, 850],
irradiance=[1.85, 1.88, 1.55, 1.10], # TOA 法向光谱,W m⁻² nm⁻¹
),
atmosphere=less.PrescribedAtmosphere(
wavelengths=[450, 550, 650, 850],
direct_beam_transmittance=[0.62, 0.70, 0.76, 0.82],
diffuse_horizontal_transmittance=[0.18, 0.14, 0.10, 0.06],
),
)
两个参数的参考量不同,不能混用:
direct_beam_transmittance是通常意义上的太阳直射束透过率:地表 束法向直射光谱辐照度除以 TOA 束法向光谱辐照度。diffuse_horizontal_transmittance是水平散射传输系数:地表水平面 散射光谱辐照度除以 TOA 太阳光谱在同一水平面上的投影辐照度。它不是 “地表光照中的散射占比”。
对于某个波长,若 TOA 束法向光谱辐照度为
E_toa_normal、太阳天顶角为 θ,则:
E_direct_normal_surface = E_toa_normal × direct_beam_transmittance
E_diffuse_horizontal_surface = E_toa_normal × cos(θ)
× diffuse_horizontal_transmittance
二者均为无量纲系数,可以是标量;使用光谱数组时同时提供参考
wavelengths,LESS 会插值到实际求解波长。对无内部短波源的
被动大气中,每个波段的两个系数之和不能大于 1。Sun.irradiance 始终表示
TOA 束法向光谱辐照度,不接受地表总辐照度语义。
属性系统 (Property)¶
属性描述 component 在不同物理过程中的行为。
geometry描述地物在哪里以及采用哪种空间表示,例如Mesh或TurbidBoundary。property描述地物如何反射光、发射热辐射、交换能量或进行光合作用。
属性设置在 component 上。同一个 component 可以同时具有多个物理域的属性:
tree.set_property("leaves", optical_property)
tree.set_property("leaves", thermal_property)
tree.set_property("leaves", biophysical_property)
tree.set_property("leaves", physiological_property)
LESS 根据属性的 domain 自动区分它们,因此不需要在
set_property() 中重复填写属性域名称。
属性域¶
| 属性域 | 描述 | 常用属性 |
|---|---|---|
| optical | 短波辐射的反射、透射、吸收和散射 | Lambertian、Prospect、Fluspect、OpticalVegetation |
| thermal | 地物温度和长波发射率 | ThermalProperty |
| microwave | 微波介电、消光和散射特性 | MicrowaveSoil、MicrowaveVegetation、MicrowaveTrunk |
| biophysical | 显热、潜热和地表热通量交换参数 | BiophysicalProperty |
| physiological | 光合、气孔导度等植物生理参数 | Farquhar |
地物不需要同时设置全部属性。模拟开始前,LESS 会检查参与该过程的 component 是否具有所需属性。
波段自动插值¶
LESS 中所有 Property 均支持任意波段组合,无需在定义属性时预先指定波段数量。模拟时,属性会自动插值到传感器实际使用的波长,因此同一套属性可以直接用于 RGB、多光谱、高光谱等不同传感器。
光学属性¶
光学属性描述地物对短波辐射的反射、透射、吸收和散射。只有参与光学模拟的 component 才需要设置光学属性。
# Lambertian:标量(全波段统一反射率)
soil = less.Lambertian(reflectance=0.15)
# Lambertian:光谱曲线(自动插值到模拟波段)
import numpy as np
soil_spectral = less.Lambertian(
reflectance=[0.10, 0.14, 0.18, 0.22, 0.30],
wavelengths=[400, 550, 670, 800, 1600], # nm
)
# Lambertian:半透明叶片(朗伯近似)
leaf_simple = less.Lambertian(
reflectance=0.08,
transmittance=0.05,
)
# Prospect:叶片光学模型(由生化参数计算光谱,覆盖 400-2500 nm)
leaf = less.Prospect(
cab=40, # 叶绿素含量 (μg/cm²)
car=10, # 类胡萝卜素 (μg/cm²)
cw=0.012, # 等效水厚度 (cm)
cm=0.008, # 干物质含量 (g/cm²)
N=1.5, # 叶片结构参数
)
# RPV:非朗伯 BRDF 模型
brdf_surface = less.RPV(rho0=0.1, k=0.7, theta=-0.15)
# 从内置光谱库加载
lib = less.SpectrumLibrary()
lib.list_spectra() # 查看可用光谱
green_leaf = lib.as_property("birch_leaf_green")
对于 Mesh,光学属性作用于三角面片:
tree.add_component(
"leaves",
less.Mesh("tree.obj", group="leaves"),
)
tree.set_property("leaves", less.Prospect(cab=40))
对于 TurbidBoundary,封闭边界定义统计植被所在的范围。叶片光学、叶面积密度
和叶倾角分布由 OpticalVegetation 描述:
tree.add_component(
"crown",
less.TurbidBoundary("tree.obj", group="crown"),
)
tree.set_property(
"crown",
less.OpticalVegetation(
leaf=less.Prospect(cab=40),
leaf_area_density=1.5, # m² leaf / m³ canopy
leaf_angle_distribution="spherical",
),
)
完整的边界要求、叶片尺寸和精度设置参见 统计植被的进阶设置。
热属性¶
热属性描述地物的温度和长波发射率,供热红外、能量平衡以及其他依赖温度的 过程使用。温度单位为 K。
# 固定温度
leaf_thermal = less.ThermalProperty(
emissivity=0.98,
temperature=303.15,
)
# 运行时使用空气温度
air_coupled_thermal = less.ThermalProperty(
emissivity=0.98,
temperature="air",
)
# 显式设置阳叶和阴叶温度;两项必须同时提供
split_leaf_thermal = less.ThermalProperty(
emissivity=0.98,
temperature_sunlit=308.0,
temperature_shaded=299.0,
)
# 同一个组件可以同时设置光学和热属性
tree.set_property("leaves", leaf) # 光学
tree.set_property("leaves", leaf_thermal) # 热
ThermalProperty.temperature 是该 component 的温度状态,也是能量平衡的
初始迭代温度。固定温度过程直接使用它;设置为 "air" 时,运行时从
Microclimate 读取空气温度。能量平衡收敛后,求解结果成为当前面片温度,供
后续光合或热红外模拟使用。温度和发射率只在 ThermalProperty 中设置,
BiophysicalProperty 不重复保存这两个量。
temperature_sunlit 和 temperature_shaded 用于已知阴、阳叶温度的热红外
模拟,必须成对设置,单位同样为 K。对 Mesh 叶片,原生传输在每个命中点判断
其是否被太阳直接照亮,再选择相应温度;对 TurbidBoundary,模型使用阳叶
概率对两个条件温度进行处理。temperature 仍作为组件的基础温度和能量平衡
初值;显式阴、阳叶温度不会替代这个基础状态。
如果温度需要由环境条件和叶片生理过程动态决定,则不必预先猜测两个温度, 而是运行能量平衡:
microclimate = less.Microclimate(
air_temperature=25.0, # °C
humidity=60.0, # %
wind_speed=1.5,
)
energy = scene.simulate(
less.EnergyBalanceProcess(),
microclimate=microclimate,
)
sunlit_temperature = energy.result.temperature_sunlit
shaded_temperature = energy.result.temperature_shaded
sunlit_fraction = energy.result.sunlit_fraction
能量平衡结果中的 temperature_sunlit 和 temperature_shaded 是分别在阳叶与
阴叶辐射条件下求得的条件温度;sunlit_fraction 描述两者在当前空间单元中的
权重。热红外和后续耦合过程可直接使用该求解结果。
微波属性¶
微波属性描述地物在微波波段的介电和散射特性。只有微波模拟需要设置该属性。 同一个 component 可以同时具有光学、热学和微波属性。
tree.set_property(
"crown",
less.MicrowaveVegetation(
components=[
less.MicrowaveLeaf(
radius=0.03,
thickness=0.0002,
density=100.0,
moisture=0.5,
),
],
lad="spherical",
),
)
微波属性描述散射和介电行为;需要物理温度的微波过程同时读取
ThermalProperty。如果 component 设置了 ThermalProperty,它的当前温度
会用于微波植被和树干;未设置时使用微波散射体中的温度。土壤仍可在
MicrowaveSoil 中保留表层—深层温度梯度。
被动微波的 polarization 可以是 "H"、"V"、"HV" 或 "full"。
"full" 返回 I、Q、U、V 四个 Stokes 分量,同时通过 Tb_H 和 Tb_V
提供线偏振亮温。主动微波的 "full" 返回 HH、HV、VH、VV,两者不要混淆。
生物物理属性¶
BiophysicalProperty 描述地物与环境交换能量的方式,例如叶片宽度、换热面、
气孔所在面和地表热通量。它主要用于能量平衡和动态叶温计算。
tree.set_property(
"leaves",
less.BiophysicalProperty(
two_sided=True,
stomata_side="bottom",
leaf_width=0.05,
),
)
树干、土壤和墙面可以使用不同的配置:
生理属性¶
生理属性描述植物如何利用吸收的光合有效辐射。Farquhar 提供 C3 植物光合和
Ball–Berry 气孔导度参数。
tree.set_property(
"leaves",
less.Farquhar(
Vcmax25=60,
Jmax25=120,
Rd25_ratio=0.015,
BallBerrySlope=9.0,
BallBerryIntercept=0.01,
),
)
没有生理属性的 component 不参与光合计算。例如,树干可以具有光学属性和热
属性,但通常不设置 Farquhar。
不同模拟过程需要哪些属性¶
下表中的“必须”只针对参与相应过程的 component。例如,计算树冠光合时,
树冠必须具有生理属性,但不参与光合的建筑物不需要设置 Farquhar。
| 模拟过程 | optical | thermal | microwave | biophysical | physiological |
|---|---|---|---|---|---|
| 光学成像、BRF | 必须 | — | — | — | — |
| LiDAR 点云与波形 | 必须 | — | — | — | — |
| 短波辐射场、APAR | 必须 | — | — | — | — |
| 静态光合 | 必须 | 必须 | — | 可选 | 必须 |
| 动态光合 | 必须 | 必须 | — | 必须 | 必须 |
| 能量平衡 | 必须 | 必须 | — | 必须 | 可选 |
| 热红外成像 | — | 必须 | — | — | — |
| SIF | 必须 | 按温度模式 | — | 按温度模式 | 必须 |
| 被动微波 | — | 必须 | 必须 | — | — |
| 主动微波 | — | 可选 | 必须 | — | — |
“可选”表示过程可以使用该属性提供的附加参数,但没有该属性时仍可采用简化 计算。“按温度模式”表示固定温度计算只需要热属性,动态温度计算还需要生物 物理属性。
完整的叶片 component¶
下面的叶片 component 可以用于光学成像、静态光合和动态能量平衡:
tree.add_component(
"leaves",
less.Mesh("tree.obj", group="leaves"),
)
tree.set_property(
"leaves",
less.Prospect(cab=40, car=10, cw=0.012, cm=0.008),
)
tree.set_property(
"leaves",
less.ThermalProperty(emissivity=0.98, temperature=303.15),
)
tree.set_property(
"leaves",
less.BiophysicalProperty(
two_sided=True,
stomata_side="bottom",
leaf_width=0.05,
),
)
tree.set_property(
"leaves",
less.Farquhar(Vcmax25=60, Jmax25=120),
)
属性按物理域独立设置,因此可以只修改温度而不改变光学参数,也可以只修改 叶绿素含量而不改变能量交换参数。
传感器 (Sensor / Imager)¶
传感器由两部分组成:投影方式 和 光谱波段。
投影方式¶
# 正射投影(默认垂直观测)
ortho = less.Orthographic(image_size=512)
# 透视投影(相机)
persp = less.Perspective(
resolution=1024, fov=35,
position=(25, 60, 20), # 相机位置
target=(25, 25, 5), # 观察目标点
)
# 鱼眼投影(半球观测)
fisheye = less.Fisheye(image_size=512, fov=180)
光谱波段¶
# 自定义离散波段
bands = [475, 560, 668, 842]
# 预定义卫星传感器及其 SRF 内部采样间隔
sentinel2 = less.OpticalImager(
less.Orthographic(image_size=512),
bands=less.Sentinel2A(),
spectral_resolution=5,
)
landsat8 = less.OpticalImager(
less.Orthographic(image_size=512),
bands=less.Landsat8_OLI(),
spectral_resolution=5,
)
# 高光谱
bands = less.Hyperspectral(start=400, stop=2500, step=10)
组合为 Imager¶
# 光学成像仪 = 投影 + 波段
sensor = less.OpticalImager(
less.Orthographic(image_size=512),
bands=[650, 550, 450], # RGB
quality=256, # 光子数(越大越准,越慢)
)
# 热红外成像仪
sensor = less.ThermalImager(
less.Orthographic(image_size=512),
bands=[10600], # 10.6 μm
)
输出产品 (Product)¶
所有 simulate() 调用都返回一个 Product 对象:
image = scene.simulate(sensor)
# 保存为文件
image.save("output.png") # PNG(RGB 自动色调映射)
image.save("output.tif") # GeoTIFF(保留原始辐射值)
# 获取原始数据(NumPy 数组)
data = image.data # shape: (height, width, bands)
print(data.shape, data.dtype)
不同传感器返回不同类型的产品:
| 传感器 | 产品类型 | 数据内容 |
|---|---|---|
| OpticalImager | ImageProduct | 各波段辐亮度/反射率 |
| ThermalImager | ImageProduct | 热辐射亮温 |
| BRFSensor | BRFProduct | 多角度反射率 |
| LiDARSurvey | PointCloud | xyz 坐标 + 强度 |
| RadiationFieldSensor | RadiationFieldProduct | 逐叶片辐照度 |
LAI 测量¶
LESS 可以直接从三维场景计算叶面积指数 (LAI):
lai = scene.measure(less.LAIMeasurement())
print(f"场景 LAI = {lai:.2f}")
# 按组件分组
lai_detail = scene.measure(less.LAIMeasurement(group_by='component'))
print(lai_detail) # {'leaves': 3.2, 'stem_branch': 0.8}
同一个场景可以同时包含 Mesh 和 TurbidBoundary。显式面元按三角形的单面面积计算;
浑浊介质按“场景范围内的有效体积 × leaf_area_density”计算。边界壳本身不属于叶面积。
如果浑浊介质跨出有限场景,只统计裁剪后留在场景内的部分;周期场景则先把跨边界部分
折回基础单元。多个介质发生空间重叠时,各自代表的叶面积相加。
property_filter 对两种表示使用相同语义。例如 property_filter=less.Prospect
既匹配显式的 PROSPECT 叶片,也匹配 OpticalVegetation(leaf=less.Prospect(...))
中的代表叶片。
概念总结¶
下一步¶
掌握了这些核心概念,让我们开始实际模拟:
相关 API¶
less.Scene.simulate():按需自动构建并执行模拟less.Scene.build():可选的提前准备步骤less.Scene.rebuild():强制刷新几何和加速结构less.Scene.update_instances():已构建场景中的动态实例更新less.Scene、less.Object、less.Terrainless.Mesh、less.TurbidBoundaryless.Lambertian、less.Prospect、less.RPVless.Illumination、less.Sun、less.NoAtmosphereless.Atmosphere、less.AtmosphereResult、less.SixSAtmosphereless.PrescribedAtmosphere、less.SimpleSpectralAtmosphere、less.HosekWilkieAtmosphereless.ThermalProperty、less.Microclimate、less.EnergyBalanceProcessless.OpticalImager、less.ThermalImager、less.LAIMeasurement