跳转至

04 - 核心概念

本章介绍 LESS 的核心概念和工作流程。理解这些概念后,您将能够灵活组合各种功能来完成复杂的遥感模拟任务。

场景生命周期:build、simulate 与 rebuild

LESS 的 Scene 是一个可持续复用的数字场景,并采用 结构—光谱分离 架构。几何和加速结构可以跨多次模拟复用;传感器波段、光照和属性则按当前模拟更新。

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.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 会在第一次模拟前自动构建;后续模拟会尽可能复用已有原生场景和加速结构。

image = scene.simulate(sensor)

传感器波段会按本次模拟重新准备,因此不需要为了更换波段而提前调用 build()。

build():可选的显式准备步骤

build() 是可选的显式准备步骤。它用于提前验证场景、加载几何、上传资源并构建加速结构,例如需要把构建耗时与模拟耗时分开测量时:

scene.build()
image = scene.simulate(sensor)

build() 具有幂等性:对于已经构建且没有变化的场景,再次调用通常复用已有原生场景,而不是无条件重新构建。动态实例接口 Scene.update_instances() 也要求场景已经显式构建。

rebuild():强制完整重建

rebuild() 用于强制完整重建:它会刷新原生场景表示,重新读取或上传几何,并重建加速结构。

scene.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 包含一种几何表示和若干属性:

component = geometry + properties

组件目前可以使用两种几何表示:

几何表示 描述 常见用途
Mesh 保留 OBJ 中的真实三角面片 树干、枝条、显式叶片、建筑物
TurbidBoundary OBJ 只定义封闭空间,空间内用统计参数(叶面积体密度、叶倾角分布等)描述大量小叶片 大型树冠、连续冠层

两者都是普通 component,可以放在同一个 Object 中,也使用相同的 set_property()、scene.add() 和 scene.simulate()。

使用 Mesh 组件

当整个 OBJ 都作为一个 Mesh 使用时,可以采用简写:

# 加载内置的欧洲白蜡树模型
tree = less.Object("ash_tree", mesh=less.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]]

如果需要对所有实例统一做非均匀缩放,也用二维数组:

# 所有树 Z 方向拉伸两倍
scales = [[1.0, 1.0, 2.0]] * len(positions)

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,
    ),
)

树干、土壤和墙面可以使用不同的配置:

tree.set_property(
    "trunk",
    less.BiophysicalProperty(
        two_sided=False,
        stomata_side="none",
    ),
)

生理属性

生理属性描述植物如何利用吸收的光合有效辐射。Farquhar 提供 C3 植物光合和 Ball–Berry 气孔导度参数。

tree.set_property(
    "leaves",
    less.Farquhar(
        formulation="jmax",
        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(formulation="jmax", 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(...)) 中的代表叶片。

概念总结

LESS 核心类关系总览

下一步

掌握了这些核心概念,让我们开始实际模拟:

相关 API

  • less.Scene.simulate():按需自动构建并执行模拟
  • less.Scene.build():可选的提前准备步骤
  • less.Scene.rebuild():强制刷新几何和加速结构
  • less.Scene.update_instances():已构建场景中的动态实例更新
  • less.Scene、less.Object、less.Terrain
  • less.Mesh、less.TurbidBoundary
  • less.Lambertian、less.Prospect、less.RPV
  • less.Illumination、less.Sun、less.NoAtmosphere
  • less.Atmosphere、less.AtmosphereResult、less.SixSAtmosphere
  • less.PrescribedAtmosphere、less.SimpleSpectralAtmosphere、less.HosekWilkieAtmosphere
  • less.ThermalProperty、less.Microclimate、less.EnergyBalanceProcess
  • less.OpticalImager、less.ThermalImager、less.LAIMeasurement