03 - First simulation¶
In this chapter we simulate a patch of flat bare soil, produce an RGB image looking straight down, and then put a maize plant on the ground.
The complete code¶
import less
# 1. Scene: 10 m × 10 m
scene = less.Scene()
scene.size = 10.0
# 2. Ground: Lambertian reflection with reflectance 0.3
scene.terrain = less.Terrain(property=less.Lambertian(reflectance=0.3))
# 3. Illumination: sun at 30° zenith and 150° azimuth (south-east), simple spectral atmosphere
scene.illumination = less.Illumination(
source=less.Sun(zenith=30, azimuth=150),
atmosphere=less.SimpleSpectralAtmosphere(turbidity=2.0),
)
# 4. Sensor: orthographic view, 256 × 256 pixels, red, green and blue bands
sensor = less.OpticalImager(
less.Orthographic(image_size=256),
bands=[650, 550, 450],
quality=64,
)
# 5. Simulate and save
image = scene.simulate(sensor)
image.save("bare_soil_rgb.png")
print(image.data.shape)
Companion script: scripts/03_first_simulation.py
Step by step¶
Scene and ground¶
less.Scene() creates an empty scene and scene.size = 10.0 makes the ground 10 m × 10 m. less.Terrain is the ground; here it uses the simplest reflection model, Lambertian, which reflects equally in all directions. A reflectance of 0.3 means 30% of the incoming light is reflected.
Illumination¶
Illumination combines the sun and the atmosphere. Sun gives the sun's direction and the solar spectrum at the top of the atmosphere; SimpleSpectralAtmosphere computes the direct sunlight and the diffuse skylight reaching the ground, and a larger turbidity means a hazier sky. The other atmosphere models are described in Illumination and atmosphere.
Sensor¶
An OpticalImager combines a projection and a set of bands:
Orthographic(image_size=256): parallel rays looking straight down, 256 × 256 pixels, covering the whole scene by default;bands=[650, 550, 450]: the band centres in nm; the bands in the result keep this order;quality: the number of samples per pixel. Higher values give less noise and take longer.
| quality | Use |
|---|---|
| 16–64 | Quick previews |
| 128–256 | General analysis |
| 512 and above | Quantitative work, publication figures |
In an orthographic projection all viewing rays are parallel, as if looking down from a very great height:
Simulating¶
The first call to scene.simulate(sensor) builds the scene and then traces rays. When you change the illumination, a property or the sensor and simulate again, the geometry that is already built is reused.
Looking at the result¶
image.data is a NumPy array of shape (rows, columns, bands) holding radiance in W m⁻² sr⁻¹ nm⁻¹:
The first row of the image is north and the first column is west. To save it:
image.save("bare_soil.png") # 8-bit PNG for viewing
image.save("bare_soil.tif") # keeps the radiance values
To convert radiance to bidirectional reflectance factor (BRF):
Changing parameters¶
To change the sun position or the ground reflectance, just simulate again:
scene.illumination = less.Illumination(
source=less.Sun(zenith=60, azimuth=90),
atmosphere=less.SimpleSpectralAtmosphere(turbidity=2.0),
)
image_low_sun = scene.simulate(sensor)
scene.terrain.set_property(less.Lambertian(reflectance=0.1))
image_dark = scene.simulate(sensor)
Adding a maize plant¶
LESS includes a maize plant model, maize.obj. Give its leaves optical properties with the PROSPECT leaf model and place it in the centre of the scene:
maize = less.Object("maize", mesh=less.asset_path("maize.obj"))
maize.set_property(less.Prospect(cab=40, car=8, cw=0.012, cm=0.006, N=1.5))
scene.add(maize, positions=[[5.0, 5.0, 0.0]])
image_maize = scene.simulate(sensor)
image_maize.save("one_maize.png")
After an object is added, the next simulation rebuilds the geometry automatically.