12 - Thermal Infrared Simulation¶
This chapter describes how to use LESS to simulate thermal infrared remote sensing images.
Thermal infrared remote sensing principle¶
All objects with a temperature above absolute zero emit thermal radiation. In the thermal infrared band (8-14 μm), the thermal radiation of surface objects is related to its temperature and emissivity:
in:
- L: Thermal radiance (W/m²/sr/μm)
- ε: emissivity (0-1)
- B(T, λ): Planck function (blackbody radiation)
- T: Object temperature (K)
In the vegetation canopy, different components (sunny leaves, shady leaves, soil, tree trunks) have different temperatures, which results in rich spatial differentiation information of temperature in thermal infrared images.
ThermalProperty - Thermal properties¶
Each scene element can have thermal properties set, independent of optical properties:
import less
# Leaves: different temperatures on the sunny side and the shaded side
leaf_thermal = less.ThermalProperty(
emissivity=0.98,
temperature_sunlit=305.0, # Sunny surface temperature (K) ≈ 32°C
temperature_shaded=300.0, # Shadow temperature (K) ≈ 27°C
)
# Soil: greater temperature difference
soil_thermal = less.ThermalProperty(
emissivity=0.95,
temperature_sunlit=312.0, # Sunny side ≈ 39°C
temperature_shaded=303.0, # Shadow side ≈ 30°C
)
# Trunk: Use a single temperature
trunk_thermal = less.ThermalProperty(
emissivity=0.97,
temperature=300.0, # Uniform temperature ≈ 27°C
)
Sunny side/shady side temperature¶
LESS automatically determines whether the patch is on the sun side or the shade side based on the relationship between the normal direction of each triangular patch and the sun direction:
- Sunlit: Patch normal faces the sun and is not occluded → use
temperature_sunlit - shaded: normal faces away from the sun or is occluded → use
temperature_shaded
This is more realistic than using a single temperature.
ThermalImager —— Thermal infrared imager¶
sensor_thermal = less.ThermalImager(
less.Orthographic(image_size=512),
bands=[10600], # 10.6 μm = 10600 nm(Landsat TIRS B10)
quality=128,
)
Commonly used thermal infrared bands:
| Sensor | Band | Center wavelength |
|---|---|---|
| Landsat TIRS B10 | 10.6 μm | 10600 nm |
| Landsat TIRS B11 | 12.0 μm | 12000 nm |
| ASTER B13 | 10.7 μm | 10700 nm |
| MODIS B31 | 11.0 μm | 11000 nm |
Complete Example: Forest Thermal Infrared Simulation¶
import less
import numpy as np
# ── Create a scene ───────────────────────────────────────────────
scene = less.Scene()
scene.size = 50.0
# Soil (optical + thermal)
terrain = less.Terrain()
terrain.set_property(less.Lambertian(reflectance=0.15))
terrain.set_property(less.ThermalProperty(
emissivity=0.95,
temperature_sunlit=312.0,
temperature_shaded=303.0,
))
scene.terrain = terrain
# Lighting (needs to set sky_temperature for long wave radiation)
scene.illumination = less.Illumination(source=less.Sun(zenith=30, azimuth=150), atmosphere=less.NoAtmosphere())
# ──Trees (optical + thermal)─────────────────────────────────────
tree = less.Object("ash", mesh=less.examples.asset_path("FREX.obj"))
# Optical properties
tree.set_property("leaves", less.Prospect(cab=40, car=10, cw=0.012, cm=0.008, N=1.5))
tree.set_property("stem_branch", less.Lambertian(reflectance=0.08))
# thermal properties
tree.set_property("leaves", less.ThermalProperty(
emissivity=0.98,
temperature_sunlit=305.0,
temperature_shaded=300.0,
))
tree.set_property("stem_branch", less.ThermalProperty(
emissivity=0.97,
temperature=300.0,
))
# place
rng = np.random.RandomState(42)
positions = less.place.random(scene, n=40, min_dist=4.0, seed=42)
n = len(positions)
scene.add(tree, positions=positions,
scales=less.place.uniform_scale(n, 0.7, 1.3, seed=42),
rotations=less.place.random_rotation(n, seed=42))
sensor_thermal = less.ThermalImager(
less.Orthographic(image_size=512),
bands=[10600],
quality=128,
name="Thermal IR",
)
image_thermal = scene.simulate(sensor_thermal)
image_thermal.save("12_thermal.png")
image_thermal.save("12_thermal.tif") # Keep original radiance values
# ── Get RGB at the same time for comparison ─────────────────────────────────
sensor_rgb = less.OpticalImager(
less.Orthographic(image_size=512),
bands=[650, 550, 450],
quality=128,
)
image_rgb = scene.simulate(sensor_rgb)
image_rgb.save("12_rgb.png")
# ── Analyzing thermal images ───────────────────────────────────────────
data = image_thermal.data
print(f"Thermal radiance range: {data.min():.2f} ~ {data.max():.2f} W/m²/sr/μm")
print("Tutorial 12 completed!")
Supporting script:
scripts/12_thermal.py
The role of sky_temperature¶
sky_temperature defines the equivalent radiant temperature of the sky in the thermal infrared band. It affects:
- Sky Long Wave Radiation: Contribution of downward thermal radiation from the sky
- Reflection component: The sky thermal radiation reflected by the surface of the object (especially the low emissivity surface)
Typical values: - Sunny day: 250-270 K - Cloudy: 270-290 K - Completely cloudy: ≈ ground temperature
Suggestions for setting temperature difference parameters¶
| Component | Sunny side temperature (K) | Yin side temperature (K) | Temperature difference |
|---|---|---|---|
| Positive leaves | 303-310 | 298-303 | 3-7 K |
| Negative leaves | 300-305 | 297-301 | 2-4 K |
| Bare soil | 310-320 | 300-308 | 8-15 K |
| Grass | 303-308 | 298-302 | 3-6 K |
| Trunk | Single temperature 298-303 | - | - |
These are manually specified temperature values. If you require physics-driven temperature simulation, please refer to Chapter 13: Energy Balance .
Multi-temporal thermal infrared simulation¶
Using the digital twin architecture, thermal infrared images at different times can be quickly simulated:
# Early morning (small temperature difference)
terrain.set_property(less.ThermalProperty(
emissivity=0.95, temperature_sunlit=295.0, temperature_shaded=293.0))
tree.set_property("leaves", less.ThermalProperty(
emissivity=0.98, temperature_sunlit=294.0, temperature_shaded=293.0))
scene.illumination = less.Illumination(source=less.Sun(zenith=70, azimuth=90), atmosphere=less.NoAtmosphere())
image_morning = scene.simulate(sensor_thermal)
image_morning.save("12_thermal_morning.png")
# Noon (big temperature difference)
terrain.set_property(less.ThermalProperty(
emissivity=0.95, temperature_sunlit=318.0, temperature_shaded=305.0))
tree.set_property("leaves", less.ThermalProperty(
emissivity=0.98, temperature_sunlit=308.0, temperature_shaded=301.0))
scene.illumination = less.Illumination(source=less.Sun(zenith=10, azimuth=180), atmosphere=less.NoAtmosphere())
image_noon = scene.simulate(sensor_thermal)
image_noon.save("12_thermal_noon.png")
Note: Only the attributes and lighting are modified here, not the geometry, so no rebuild is required.
Next step¶
- 13 - Energy balance and leaf temperature solution \1 Turbid Thermal Infrared
The thermal imaging of TurbidBoundary directly consumes the resident temperature state and adaptive spatial field. use
ThermalImager(mode="property") observable attribute temperature, use mode="eb" or
mode="precomputed" can observe the energy balance results. Python no longer generates blade or upload sample-by-sample temperatures.
Related API¶
less.ThermalPropertyless.ThermalImager、less.Orthographicless.Illumination、less.Sun、less.Atmosphereless.EnergyBalanceProcess、less.Microclimateless.Product.save()