07 - Scenes and projects¶
This chapter shows how to save and reopen scenes, and how a project (less.Project) organises a complete experiment with several sensors.
Scene or project¶
less.Scene is what gets simulated. less.Project adds named sensors, file resources and experiment information on top of a scene.
| Need | Use |
|---|---|
| Interactive work, one-off calculations | Scene |
| Saving or sharing a scene | scene.save() |
| Saving a scene together with several sensors | Project |
| Managing model files, keeping an experiment long term | Project |
| Packaging an experiment to run on another machine | Project.export_snapshot() |
A project holds an ordinary Scene; all scene operations still go through project.scene.
Saving and opening scenes¶
First a small 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]]))
There are three ways to save it:
scene.save("forest-scene.less") # one compressed file including the model files
scene.save("forest-scene-folder.less", pack=False) # an uncompressed folder, good for large models
scene.save("forest-scene.json") # only records the model file paths
A .less file packs the OBJ, DEM and spatial property files together and can be copied to another computer as is. A .json file only records paths, so the other computer needs the same files.
To open it again:
scene = less.Scene.load("forest-scene.less")
image = scene.simulate(less.OpticalImager(
less.Orthographic(image_size=64), bands=[650, 550, 450], quality=16))
A scene file stores the scene's settings: terrain, objects, properties, illumination and microclimate. Temperatures solved by processes such as the energy balance are not included; solve again after reopening.
Creating a project¶
project = less.Project(
scene=scene,
title="Ash canopy experiment",
author="LESS Lab",
description="Canopy reflectance at nadir and oblique views",
)
You can also start from an empty project and set up the scene through project.scene:
Adding sensors¶
Sensors in a project have names and are run by name:
project.add_sensor(
less.OpticalImager(less.Orthographic(image_size=64),
bands=[450, 550, 650, 850], quality=16),
name="Nadir multispectral")
project.add_sensor(
less.OpticalImager(less.Orthographic(image_size=64, view_zenith=30, view_azimuth=90),
bands=[550, 650, 850], quality=16),
name="30 degrees from east")
print([sensor.name for sensor in project.sensors])
Running simulations¶
Without a name, all sensors run in turn and a dictionary keyed by sensor name is returned:
from pathlib import Path
Path("outputs").mkdir(exist_ok=True)
for name, product in project.simulate().items():
product.save(f"outputs/{name}.tif")
All sensors share one scene, so the geometry is built only once.
After changing the illumination or properties, simply run again; nothing needs rebuilding:
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("Nadir multispectral")
After adding or removing objects, replacing models or changing the terrain, call project.rebuild(). project.session_status shows the current state:
| Status | Meaning |
|---|---|
unbuilt |
Not built yet |
ready |
Ready to simulate |
parameters_dirty |
Illumination or properties changed; updated automatically at the next run |
geometry_dirty |
Geometry changed; a rebuild is needed |
Managing model files¶
Files in a project come in two kinds:
- Managed files are copied into the project and saved and shared with it;
- External files are only referenced by path, which suits very large or shared data.
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")
Saving a project¶
project.save_directory("ash-experiment.less") # an editable folder
project.pack("ash-experiment-portable.less") # an additional single-file copy
A folder project suits day-to-day editing because saving does not recompress the whole scene; the single-file copy suits archiving and sharing. project.save("xxx.less") also saves a single file and makes it the current project path.
A folder project looks like this:
ash-experiment.less/
├── project.json
├── scene.json
├── sensors.json
├── resources.json
└── resources/
Opening and checking a project¶
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("Nadir multispectral")
validate() checks sensor names, whether model files exist and whether their content is intact.
Running on another machine¶
export_snapshot() exports the current project with selected sensors as a self-contained job:
Path("jobs").mkdir(exist_ok=True)
snapshot = project.export_snapshot("jobs/nadir-job.less", sensors=["Nadir multispectral"])
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()
A project opened from a single file is unpacked into a temporary folder; call close() when you are done to clean it up. External files are still referenced by path inside a job, so the machine that runs it needs access to them.
Importing LESS 2 projects¶
A LESS 2 project folder can be read into a scene with less.Scene.from_less(path). Observation settings are not converted; create the sensors again:
project = less.Project(scene=scene, title="Experiment imported from LESS 2")
project.add_sensor(
less.OpticalImager(less.Orthographic(image_size=64), bands=[550, 650, 850]),
name="Nadir")
project.save_directory("imported-experiment.less")