07 - 场景与工程¶
本章介绍如何保存和重新打开场景,以及如何用工程(less.Project)管理一个包含多个传感器的完整实验。
场景还是工程¶
less.Scene 是模拟的主体,less.Project 在场景之外再记录命名的传感器、文件资源和实验信息。
| 需求 | 使用 |
|---|---|
| 交互式实验、一次性计算 | Scene |
| 保存或分享一个场景 | scene.save() |
| 保存场景和多个传感器 | Project |
| 管理模型文件,长期保存实验 | Project |
| 把实验打包到另一台机器上运行 | Project.export_snapshot() |
工程内部持有一个普通的 Scene,所有场景操作仍然通过 project.scene 完成。
保存和打开场景¶
先准备一个小场景:
import numpy as np
import less
scene = less.Scene()
scene.size = (100.0, 100.0)
scene.terrain = less.Terrain(property=less.Lambertian(reflectance=0.18))
scene.illumination = less.Illumination(
source=less.Sun(zenith=32, azimuth=145),
atmosphere=less.SimpleSpectralAtmosphere(turbidity=2.2))
tree = less.Object("ash", mesh=less.asset_path("FREX.obj"))
tree.set_property("leaves", less.Prospect(cab=42))
tree.set_property("stem_branch", less.Lambertian(reflectance=0.1))
scene.add(tree, positions=np.array([[20, 20, 0], [40, 30, 0], [65, 25, 0],
[30, 70, 0], [70, 65, 0]]))
保存场景有三种方式:
scene.save("forest-scene.less") # 单个压缩文件,包含模型文件
scene.save("forest-scene-folder.less", pack=False) # 不压缩的目录,适合大型模型
scene.save("forest-scene.json") # 只记录模型文件路径
.less 文件把 OBJ、DEM 和空间属性文件一起打包,可以直接复制到其他电脑。.json 只记录路径,换一台电脑时需要同样的文件。
重新打开:
scene = less.Scene.load("forest-scene.less")
image = scene.simulate(less.OpticalImager(
less.Orthographic(image_size=64), bands=[650, 550, 450], quality=16))
场景文件保存的是场景的设置,包括地形、物体、属性、光照和微气象。能量平衡等过程求得的温度不在其中,重新打开后需要再次求解。
创建工程¶
project = less.Project(
scene=scene,
title="白蜡林冠实验",
author="LESS Lab",
description="比较正射与斜视的冠层反射率",
)
也可以先创建空工程,再通过 project.scene 设置场景:
添加传感器¶
工程中的传感器有名字,之后按名字运行:
project.add_sensor(
less.OpticalImager(less.Orthographic(image_size=64),
bands=[450, 550, 650, 850], quality=16),
name="正射多光谱")
project.add_sensor(
less.OpticalImager(less.Orthographic(image_size=64, view_zenith=30, view_azimuth=90),
bands=[550, 650, 850], quality=16),
name="东侧30度斜视")
print([sensor.name for sensor in project.sensors])
运行模拟¶
不写名字时依次运行所有传感器,返回一个以传感器名为键的字典:
from pathlib import Path
Path("outputs").mkdir(exist_ok=True)
for name, product in project.simulate().items():
product.save(f"outputs/{name}.tif")
所有传感器共用同一个场景,几何只构建一次。
修改光照或属性后直接再运行,不需要重建:
project.set_illumination(less.Illumination(
source=less.Sun(zenith=50, azimuth=210),
atmosphere=less.SimpleSpectralAtmosphere(turbidity=2.5)))
project.scene.objects["ash"].set_property("leaves", less.Prospect(cab=25))
autumn = project.simulate("正射多光谱")
增删物体、更换模型或修改地形后,调用 project.rebuild()。project.session_status 显示当前状态:
| 状态 | 含义 |
|---|---|
unbuilt |
尚未构建 |
ready |
可以直接模拟 |
parameters_dirty |
光照或属性已修改,下次模拟时自动更新 |
geometry_dirty |
几何已修改,需要重建 |
管理模型文件¶
工程中的文件分为两类:
- 托管文件:复制到工程目录中,随工程一起保存和分享;
- 外部文件:只记录路径,适合很大的或多人共享的数据。
mesh = project.resources.import_file(less.asset_path("FREX.obj"), resource_type="mesh")
birch = less.Object("birch", mesh=project.resources.resolve(mesh.id))
terrain = project.resources.register_external(
less.asset_path("tutorial_terrain.obj"), resource_type="terrain")
for resource in project.resources:
print(resource.name, project.resources.verify(resource.id, checksum=True) or "OK")
保存工程¶
project.save_directory("ash-experiment.less") # 可编辑的目录
project.pack("ash-experiment-portable.less") # 另存一个单文件副本
目录工程适合日常编辑,每次保存不必压缩整个场景;单文件副本适合归档和分享。project.save("xxx.less") 也会保存为单文件,并把它作为当前工程路径。
目录工程的结构:
ash-experiment.less/
├── project.json
├── scene.json
├── sensors.json
├── resources.json
└── resources/
打开和检查工程¶
project = less.Project.open("ash-experiment-portable.less")
report = project.validate(verify_checksums=True)
for issue in report.issues:
print(issue.severity, issue.path, issue.message)
report.raise_for_errors()
result = project.simulate("正射多光谱")
validate() 会检查传感器名称、模型文件是否存在以及文件内容是否完整。
打包到其他机器运行¶
export_snapshot() 把当前工程和选定的传感器导出为一个独立的任务包:
Path("jobs").mkdir(exist_ok=True)
snapshot = project.export_snapshot("jobs/nadir-job.less", sensors=["正射多光谱"])
print(snapshot.revision, snapshot.sensors)
job = less.Project.open("jobs/nadir-job.less")
job.validate().raise_for_errors()
results = job.simulate()
job.close()
project.close()
从单文件打开的工程会解压到临时目录,用完后调用 close() 清理。
外部文件在任务包中仍然只记录路径,运行任务的机器需要能访问这些文件。
导入 LESS 2 工程¶
LESS 2 的工程目录可以用 less.Scene.from_less(path) 读入为场景。观测设置不会自动转换,需要重新创建传感器:
project = less.Project(scene=scene, title="从 LESS 2 导入的实验")
project.add_sensor(
less.OpticalImager(less.Orthographic(image_size=64), bands=[550, 650, 850]),
name="正射")
project.save_directory("imported-experiment.less")