- Home
- Documentation
- API reference
- Corrections
- hyperproc.correct.brdf
hyperproc.correct.brdf¶
Source-derived reference
Generated from the current hyperproc 0.1.2 checkout.
Implementation: hyperproc/correct/brdf.py. Signatures, defaults, docstrings, and expandable source are extracted statically; the module is not imported or executed. Names beginning with _ are implementation details, not a stable public API.
Use the function signature as the authority for individual parameter defaults and return annotations. Original docstrings sometimes group parameter names or wrap return descriptions across lines; these descriptions are preserved rather than inferred or rewritten.
FlexBRDF: kernel-driven BRDF normalisation, stratified by NDVI.
From Queally et al. (2022), FlexBRDF: A flexible BRDF correction for grouped processing of airborne imaging spectroscopy flightlines, JGR Biogeosciences, The model per band and per NDVI class is the Ross-Li linear kernel model of Lucht et al. (2000):
rho(sza, vza, raa) = f_iso + f_vol K_vol(sza, vza, raa) + f_geo K_geo(sza, vza, raa)
and the correction is a multiplicative normalisation of each pixel to a reference geometry - nadir view under a chosen solar zenith:
rho_ref = rho * (f_iso + f_vol K_vol_ref + f_geo K_geo_ref)
/ (f_iso + f_vol K_vol + f_geo K_geo)
Three things make it "flex":
- NDVI stratification. Coefficients are fitted separately for NDVI bins,
because the anisotropy of bare soil, sparse and dense canopies differs.
Bin edges are dynamic - percentiles of the pooled NDVI between
perc_minandperc_max- so each bin holds a comparable number of pixels (:func:dynamic_bins). - Grouped fitting. The samples are pooled across every flightline in a
group before fitting, so one coefficient set applies to all of them and
seams between adjacent lines close. Grouping is the caller's job (the
pipeline pools
samplesfrom many images); this module fits what it is given. - Interpolation across bins. At apply time the per-bin coefficients are interpolated linearly in NDVI (with extrapolation at the ends), so the correction is continuous rather than stepped at bin boundaries.
Scale invariance, worth knowing: the correction is a ratio of the same linear model, so coefficients fitted on reflectance x 10 000 (as EnSpec's NEON coefficient files were) apply unchanged to reflectance in 0-1.
FlexFit
dataclass
¶
Fitted FlexBRDF coefficients for one group of images.
Source code in hyperproc/correct/brdf.py
bins: list
instance-attribute
¶
coeffs: np.ndarray
instance-attribute
¶
volume: str
instance-attribute
¶
geometric: str
instance-attribute
¶
b_r: float
instance-attribute
¶
h_b: float
instance-attribute
¶
sza_ref: float
instance-attribute
¶
n_per_bin: np.ndarray
instance-attribute
¶
r2: np.ndarray
instance-attribute
¶
wavelength: np.ndarray | None = None
class-attribute
instance-attribute
¶
meta: dict = field(default_factory=dict)
class-attribute
instance-attribute
¶
__init__(bins: list, coeffs: np.ndarray, volume: str, geometric: str, b_r: float, h_b: float, sza_ref: float, n_per_bin: np.ndarray, r2: np.ndarray, wavelength: np.ndarray | None = None, meta: dict = dict()) -> None
¶
to_dict()
¶
JSON-ready, wavelength-keyed where a wavelength axis is known.
Source code in hyperproc/correct/brdf.py
from_dict(d)
classmethod
¶
Inverse of :meth:to_dict (wavelength-keyed or index-keyed).
Source code in hyperproc/correct/brdf.py
dynamic_bins(ndvi, num_bins=18, ndvi_min=0.05, ndvi_max=1.0, perc_min=10, perc_max=95, second_split=True)
¶
NDVI bin edges as percentiles of the pooled sample (Queally et al. 2022).
num_bins - 1 percentiles are taken evenly between perc_min and
perc_max of the NDVI values above zero, then ndvi_min and
ndvi_max are added as the outer edges. second_split additionally
splits any bin wider than a threshold that shrinks with the bin count
(0.43125 - 0.015625 * (n - 1)) at that bin's median, as the reference
implementation of the method does.
| RETURNS | DESCRIPTION |
|---|---|
|
list of |
Source code in hyperproc/correct/brdf.py
_second_split(edges, v)
¶
Source code in hyperproc/correct/brdf.py
assign_bins(ndvi, bins)
¶
Bin number 1..n per pixel (ndvi in (lo, hi]), 0 outside every bin.
Source code in hyperproc/correct/brdf.py
bin_centres(bins)
¶
fit_flex(rho, k_vol, k_geo, ndvi, bins, min_per_bin=30)
¶
Fit f_vol, f_geo, f_iso per band and NDVI bin by least squares.
| PARAMETER | DESCRIPTION |
|---|---|
rho
|
|
k_vol, k_geo
|
|
ndvi
|
|
bins
|
from :func:
|
min_per_bin
|
bins with fewer samples get NaN coefficients (the apply step then falls back to interpolation from neighbours) rather than a fit through a handful of points.
DEFAULT:
|
| RETURNS | DESCRIPTION |
|---|---|
|
|
Source code in hyperproc/correct/brdf.py
interpolate_coeffs(coeffs, bins, ndvi)
¶
Per-pixel (f_vol, f_geo, f_iso) by linear interpolation in NDVI.
Interpolates each band's coefficients between bin centres, extrapolating linearly beyond the outer centres. Bins with NaN coefficients are skipped, so a sparse bin borrows from its neighbours.
| RETURNS | DESCRIPTION |
|---|---|
|
|
Source code in hyperproc/correct/brdf.py
apply_flex(rho, k_vol, k_geo, ndvi, fit: FlexFit, mask=None, slab=48, ratio_max=5.0)
¶
Normalise rho (..., band) to nadir view at fit.sza_ref.
Pixels where mask is False, NDVI is outside every bin, or the modelled
observed reflectance is not positive are returned unchanged - the last
because a non-positive denominator means the model does not describe that
pixel and a ratio would be nonsense. The same goes for a ratio outside
[1/ratio_max, ratio_max]: a kernel model fitted on a bin describes the
bin's average anisotropy, and a factor of 5 or more only arises where the
modelled reflectance sits at the noise floor (seen at 427-479 nm in a
high-NDVI bin, where -0.0009 became -0.064 without the bound). Such a
pixel/band is left as it is rather than amplified. Bands are processed
slab at a time so the per-pixel interpolated coefficients never
exceed a few hundred MB for a full-swath block.
Source code in hyperproc/correct/brdf.py
fit_group(rho, sza, vza, raa, ndvi, wavelength=None, volume='ross_thick', geometric='li_dense_r', b_r=1.0, h_b=2.0, sza_ref=None, bins=None, bin_ndvi=None, **bin_kwargs)
¶
Fit a :class:FlexFit from pooled samples of one or many images.
sza_ref defaults to the mean solar zenith of the samples, which for a
group is the group mean.
bin_ndvi is the population the dynamic bin edges are cut from when
bins is not given. FlexBRDF cuts them from the NDVI of every valid
pixel of every image in the group, and only then restricts the fit to the
masked, subsampled pixels; passing the fit sample instead (the default
when bin_ndvi is None) shifts the edges upward because the vegetation
mask has already removed the low-NDVI tail.