08 - 属性系统¶
几何决定物体在哪里、长什么样,属性决定它如何与辐射和环境相互作用。本章介绍属性的物理域、设置方法,以及光谱属性如何对应到传感器的波段。
属性的物理域¶
每个属性属于一个物理域:
| 物理域 | 描述 | 属性 | 详细介绍 |
|---|---|---|---|
| 光学 | 短波辐射的反射、透射和吸收 | Lambertian、RPV、WaterSurface、Prospect、Fluspect、MeasuredSpectrum、SpectrumDB、OpticalVegetation |
第 9、10、12 章 |
| 热 | 温度和长波发射率 | ThermalProperty |
第 26 章 |
| 微波 | 介电常数和散射体 | MicrowaveSoil、MicrowaveVegetation、MicrowaveTrunk 等 |
第 28 章 |
| 生物物理 | 与空气交换感热、潜热,土壤热通量 | BiophysicalProperty |
第 32 章 |
| 生理 | 光合作用和气孔导度 | Farquhar |
第 33 章 |
一个组件可以同时具有多个物理域的属性,用 set_property() 逐个设置。LESS 根据属性类型判断它属于哪个物理域,设置新属性时只替换同一物理域原有的属性:
import less
tree = less.Object("ash", mesh=less.asset_path("FREX.obj"))
tree.set_property("leaves", less.Prospect(cab=40, car=10)) # 光学
tree.set_property("leaves", less.ThermalProperty(temperature=303.15, emissivity=0.98)) # 热
tree.set_property("leaves", less.BiophysicalProperty(leaf_width=0.05)) # 生物物理
tree.set_property("leaves", less.Farquhar(Vcmax25=60)) # 生理
tree.set_property("stem_branch", less.Lambertian(reflectance=0.1))
tree.set_property("leaves", less.Prospect(cab=20)) # 只替换光学属性
物体只有一个组件,或希望所有组件使用同一属性时,可以省略组件名:
maize = less.Object("maize", mesh=less.asset_path("maize.obj"))
maize.set_property(less.Prospect(cab=45))
tree.components 列出 OBJ 文件中的组件名(即 OBJ 的 group 名):
不需要为每个组件设置所有物理域的属性,只要参与当前模拟的组件具有所需的属性即可(见第 4 章的表格)。
光谱属性与波段¶
光学属性可以是一个数值(所有波长相同),也可以是一条光谱曲线:
soil = less.Lambertian(reflectance=0.15)
soil_spectrum = less.Lambertian(
wavelengths=[400, 550, 670, 800, 1600], # nm
reflectance=[0.10, 0.14, 0.18, 0.22, 0.30],
)
模拟时,光谱曲线按传感器的实际波长线性插值。同一个属性因此可以直接用于 RGB、多光谱和高光谱传感器,不需要事先确定波段。
以 Lambertian 的数值输入为例,三种写法的含义不同:
| 输入 | 模拟时如何使用 |
|---|---|
单个数值,如 reflectance=0.15 |
所有模拟波长使用相同值。 |
数值数组,同时提供 wavelengths |
按波长坐标线性插值到传感器请求的模拟波长。 |
数值数组,不提供 wavelengths |
数组长度必须等于模拟波长数,按顺序一一对应;不会推断波长坐标或自动插值。 |
例如,光谱在 500、600、700 nm 的反射率分别为 0.1、0.3、0.2,传感器请求 550 nm 时,插值得到的反射率为 0.2:
soil = less.Lambertian(
wavelengths=[500, 600, 700],
reflectance=[0.1, 0.3, 0.2],
)
print(soil.evaluate([550])["reflectance"]) # [0.2]
只有一个元素的数值数组也按常数处理。需要在不同传感器之间复用多点数组光谱时,应提供 wavelengths。插值只是在已有光谱采样点之间估计数值,不能恢复原始数据中未被采到的窄吸收特征。
有效波长范围¶
“可以指定任意波长”不等于所有波长都有可靠的物性数据。应让全部模拟波长落在属性数据及所用物理模型的有效范围内。当前 Lambertian 的带波长数值数组,以及 Prospect.evaluate(),在范围外使用最近的端点值;这不是有物理依据的范围外预测。其他属性可能直接拒绝范围外请求,不应把端点延伸视为通用规则。
PROSPECT 等叶片模型直接由生化参数计算 400–2500 nm 的光谱。可以用 evaluate() 查看属性在给定波长上的值:
leaf = less.Prospect(cab=40, car=10)
values = leaf.evaluate([450, 550, 650, 850])
print(values["reflectance"], values["transmittance"])
离散波长与宽波段¶
传感器设置 bands=[550, 850] 时,计算的是 550 nm 和 850 nm 两个离散波长,不是以它们为中心的宽波段平均值。Hyperspectral 同样生成一组离散模拟波长,其 step 是波长采样间隔,不代表仪器的光谱响应宽度。
模拟真实仪器的宽波段响应时,应使用 SRFBands 或相应的传感器波段定义。LESS 在光谱响应函数(SRF)覆盖的范围内生成模拟波长,将属性光谱插值到这些波长,逐波长模拟后再对结果按 SRF 加权积分;不是先平均反射率再只模拟中心波长。此时,物性光谱应覆盖整个 SRF 范围,而不仅是中心波长。
spectral_resolution 控制 SRF 积分使用的波长采样间隔(nm)。定量分析时逐步减小该间隔,检查结果是否稳定;增加光子采样数不能补偿光谱采样过粗造成的误差。传感器设置示例见第 18 章和第 19 章。
反射率与透射率之和不能超过 1,否则在创建属性时报错。
修改场景中的属性¶
属性可以随时修改,下一次模拟会使用新的属性,不需要重建几何:
scene = less.Scene()
scene.size = 20.0
scene.terrain = less.Terrain(property=less.Lambertian(reflectance=0.15))
scene.illumination = less.Illumination(
source=less.Sun(zenith=30, azimuth=150), atmosphere=less.NoAtmosphere())
handle = scene.add(tree, positions=[[5, 5, 0], [10, 10, 0], [15, 15, 0]])
sensor = less.OpticalImager(less.Orthographic(image_size=64),
bands=[650, 550, 450], quality=16)
summer = scene.simulate(sensor)
tree.set_property("leaves", less.Prospect(cab=8, car=12, cbrown=0.3))
scene.terrain.set_property(less.Lambertian(reflectance=0.25))
autumn = scene.simulate(sensor)
也可以用“物体名.组件名”的路径直接修改:
为单个实例设置属性¶
同一物体的所有实例默认共享属性。scene.add() 返回的句柄可以为指定实例替换某个组件的属性,其他实例保持不变:
# 每个实例一个属性,None 表示沿用物体的属性
handle.set_property("leaves", [None,
less.Prospect(cab=20),
less.Prospect(cab=10, cbrown=0.2)])
# 用实例编号选择
handle.set_property("stem_branch", less.Lambertian(reflectance=0.2), instances=[0, 2])
# 用函数按实例编号生成
handle.set_property("leaves", lambda i: less.Prospect(cab=20 + 10 * i))
# 恢复为物体的属性
handle.clear_property("leaves", "optical")
mixed = scene.simulate(sensor)
逐实例属性适用于三角网格组件;统计植被组件每个组件使用一个属性。
内置光谱库¶
LESS 自带一组实测光谱,覆盖常见的叶片、土壤和人工材料:
library = less.SpectrumLibrary()
names = library.list_spectra()
print(len(names), names[:5])
data = library.get("birch_leaf_green")
print(data["wavelength"][:3], data["reflectance"][:3])
scene.terrain.set_property(less.SpectrumDB("loam_brown"))
less.SpectrumDB(name) 直接把光谱库中的一条光谱作为光学属性。