Fractional cover¶
Per-pixel spectral unmixing of Sentinel-2 surface reflectance into three ground-cover fractions:
| Band | Meaning |
|---|---|
bg |
bare ground / soil |
pv |
green (photosynthetic) vegetation |
npv |
non-green (non-photosynthetic) vegetation — senescent crop, stubble, woody material |
The model is a small TFLite MLP adapted from
fractionalcover3 by
Robert Denham (MIT-licensed; see
PaddockTS/LICENSES/fractionalcover3.LICENSE). Four model variants
ship inside the package as bundled .tflite files at
PaddockTS/FractionalCover/_models/, indexed n=1..4 from least to
most complex. n=4 is the default and most accurate.
Output is per-pixel per-timestep, persisted to
troi.fractional_cover_path as Zarr v2 (guarded by a _SUCCESS
marker).
Example: compute and inspect¶
from datetime import date
from troi.troi import Troi
from PaddockTS.FractionalCover import compute_fractional_cover
q = Troi(
bbox=[148.36265, -33.52606, 148.38265, -33.50606],
start=date(2024, 1, 1),
end=date(2024, 3, 31),
stub="fc_demo",
)
# Will download + clean Sentinel-2 on demand if needed
fc = compute_fractional_cover(q)
print(fc)
# <xarray.Dataset>
# Dimensions: (time: 19, y: 257, x: 197)
# Data variables:
# bg (time, y, x) float64 ...
# pv (time, y, x) float64 ...
# npv (time, y, x) float64 ...
# Mean green-vegetation fraction across the AOI over time
fc.pv.mean(dim=("y", "x")).plot()
Example: render as a false-colour RGB¶
The fractional-cover video stage maps (bg, pv, npv) → (R, G, B) for
intuitive at-a-glance interpretation: red = bare, green = growing,
blue = stubble / dry. To reproduce that scheme on a single timestep:
import matplotlib.pyplot as plt
import numpy as np
fc_t = fc.isel(time=0)
total = np.maximum(fc_t.bg + fc_t.pv + fc_t.npv, 1e-6)
rgb = np.stack([
(fc_t.bg / total).values,
(fc_t.pv / total).values,
(fc_t.npv / total).values,
], axis=-1)
rgb = np.clip(np.nan_to_num(rgb), 0, 1)
plt.imshow(rgb)
plt.axis("off")
plt.title(f"Fractional cover — {str(fc.time.values[0])[:10]}")
Choosing a model variant¶
Use model_n=4 (default) unless you have a specific need to trade
accuracy for runtime; the model is small enough that the difference is
typically negligible for AOIs under a few thousand pixels per side.
Calibration correction¶
The correction=True flag applies per-band sensor calibration
factors (gains and offsets fitted in the upstream fractionalcover3
work) instead of the simple × 0.0001 DN-to-reflectance scaling:
Use this only when your inputs match the calibration assumptions of the original model — i.e. raw Landsat-like DN. For ARD-corrected DEA Sentinel-2 the default scaling is correct.
Reference¶
PaddockTS.FractionalCover.compute_fractional_cover.compute_fractional_cover ¶
Run the TFLite unmixing model over every Sentinel-2 timestep.
Stacks the six Sentinel-2 SR bands into a (time, band, y, x)
tensor, scales reflectance, and invokes the chosen model variant
once per timestep. The result is written to
Paths(troi).fractional_cover and returned as an xarray Dataset
with bg, pv, npv data variables on dims
(time, y, x).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
troi
|
Troi
|
The :class: |
required |
ds_sentinel2
|
Optional in-memory Sentinel-2 dataset. If |
None
|
|
model_n
|
int
|
Which bundled model variant to use ( |
4
|
correction
|
bool
|
If |
False
|
Returns:
| Type | Description |
|---|---|
|
xarray.Dataset: Dataset with variables |
|
|
on dims |
|
|
|
BANDS¶
The six Sentinel-2 SR bands stacked into the model input, in this order: