- Home
- Documentation
- API reference
- Spectral
- hyperproc.spectral.smoothing
hyperproc.spectral.smoothing¶
Source-derived reference
Generated from the current hyperproc 0.1.2 checkout.
Implementation: hyperproc/spectral/smoothing.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.
Exported entry points¶
| Importable name | Definition |
|---|---|
hyperproc.spectral.smoothing.smooth_spectra |
hyperproc.spectral.smoothing.smooth_spectra |
hyperproc.spectral.smoothing.find_spikes |
hyperproc.spectral.smoothing.find_spikes |
hyperproc.spectral.smoothing.spline_gapfill |
hyperproc.spectral.smoothing.spline_gapfill |
hyperproc.spectral.smoothing.PRISMA_ARTEFACT_RANGES |
hyperproc.spectral.smoothing.PRISMA_ARTEFACT_RANGES |
hyperproc.spectral.smoothing.PRISMA_MASK_AFTER |
hyperproc.spectral.smoothing.PRISMA_MASK_AFTER |
Cosmetic spectral smoothing.
A per-pixel optimal-estimation retrieval leaves real band-to-band structure: on the EMIT test granule its size matches JPL's own product exactly, and on Tanager it sits well below the retrieval's posterior uncertainty. Some providers publish spectra with that structure removed (Planet's Tanager reflectance is smooth to four decimals in the near infrared, which no per-pixel retrieval produces), so a product compared against theirs can look noisy without being wrong.
:func:smooth_spectra makes the same cosmetic change, explicitly and
reversibly. It never runs by itself: correction and export leave the
retrieval as it is, and the result records what was done in
attrs["spectral_smoothing"].
It is cosmetic. Smoothing correlates neighbouring bands, so it invalidates the posterior uncertainty and biases anything that measures narrow features: fit absorption depths, continuum removal and spectral indices on the unsmoothed cube.
PRISMA_ARTEFACT_RANGES = ((535, 550), (755, 780), (755, 775), (810, 855), (885, 970), (1015, 1050), (1080, 1165), (1225, 1285), (1330, 1490), (1685, 1700), (1725, 1750), (1780, 1960), (1990, 2030))
module-attribute
¶
PRISMA_MASK_AFTER = ((1350, 1510), (1795, 2000), (2320, 2500))
module-attribute
¶
_smooth_block(a: np.ndarray, runs, window: int, order: int, method: str) -> np.ndarray
¶
Source code in hyperproc/spectral/smoothing.py
smooth_spectra(ds: xr.Dataset, var: str | None = None, window: int = 5, order: int = 2, method: str = 'savgol', good_only: bool = True) -> xr.Dataset
¶
A copy of ds whose spectra are smoothed along wavelength.
| PARAMETER | DESCRIPTION |
|---|---|
ds
|
dataset with a
TYPE:
|
var
|
which variable to smooth; the cube variable by default.
TYPE:
|
window
|
filter length in bands, odd, at least
TYPE:
|
order
|
polynomial order for
TYPE:
|
method
|
TYPE:
|
good_only
|
smooth only runs of bands flagged usable by
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Dataset
|
A new dataset; the smoothed variable carries |
Dataset
|
attrs and the dataset carries |
Dataset
|
layers are dropped, because smoothing makes them wrong. |
Source code in hyperproc/spectral/smoothing.py
find_spikes(spectrum, threshold: float = 0.018, nups: int = 1, ndowns: int | None = None, min_height: float = -np.inf) -> np.ndarray
¶
Bands belonging to an upward spike, as R's pracma::findpeaks marks them.
A faithful port, because reproducing a published pipeline means reproducing
its peak finder. The sign of the first difference becomes a string of +
and -, runs matching [+]{nups,}[-]{ndowns,} are peaks, and a peak
survives when it exceeds the higher of its two ends by threshold.
For every surviving peak exactly three bands are marked: the peak, the band where its rise began and the band where its fall ended. Not the span between them. On a real PRISMA spectrum that is 11 to 26 bands of 230.
Note that this finds maxima only. Downward spikes are left for the spline.
| PARAMETER | DESCRIPTION |
|---|---|
spectrum
|
one spectrum, in the order the bands are stored.
|
threshold
|
minimum height above the higher end of the peak.
TYPE:
|
nups, ndowns
|
how many rises and falls make a peak;
|
min_height
|
peaks below this absolute value are ignored.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ndarray
|
Boolean mask, True where a band belongs to a peak. |
Source code in hyperproc/spectral/smoothing.py
_in_ranges(wl: np.ndarray, ranges) -> np.ndarray
¶
_one_blas_thread()
¶
Fit spectra with BLAS held to a single thread.
The spline solves one spectrum at a time, and each fit is a handful of dense solves on a matrix the size of the knot count - a couple of hundred rows. That is far below the size where threading a solve pays for itself, so the BLAS threads only contend for cores. On a busy machine the tax is not small: one Tanager spectrum that fits in 57 ms single-threaded took 38 s with 32 OpenBLAS threads fighting over it, a factor of 665.
Falls through quietly when threadpoolctl is not installed.
Source code in hyperproc/spectral/smoothing.py
_gapfill_block(a: np.ndarray, wl, exclude, mask_after, df, threshold, despike, min_points)
¶
Source code in hyperproc/spectral/smoothing.py
spline_gapfill(ds: xr.Dataset, var: str | None = None, df: float = 60.0, threshold: float = 0.018, despike: bool = True, exclude=PRISMA_ARTEFACT_RANGES, mask_after=PRISMA_MASK_AFTER, min_points: int = 20, keep_fill_flag: bool = True) -> xr.Dataset
¶
Despike, mask, spline-smooth with gap filling, then mask again.
The published PRISMA route, in four steps per spectrum:
- mark upward spikes with :func:
find_spikesand blank them, - blank
exclude, the regions where the instrument's spectral shift leaves artefacts around the gaseous absorptions, - fit a smoothing spline with
dfdegrees of freedom through what survives and evaluate it at every wavelength, which smooths and fills the blanked bands in one step, - blank
mask_after, the deep water absorptions and the SWIR tail.
This is not what :func:smooth_spectra does, and the difference is the
point. That one filters inside runs of usable bands and never crosses a
gap, so it cannot invent a value. This one crosses them deliberately. On a
230-band PRISMA scene about 120 bands come out with values, and some of
those are spline fill rather than measurement, which is what
keep_fill_flag records.
The spline is R's smooth.spline, ported in
:mod:hyperproc.spectral._smoothspline and verified against it.
| PARAMETER | DESCRIPTION |
|---|---|
ds
|
dataset with a
TYPE:
|
var
|
variable to smooth; the main cube by default.
TYPE:
|
df
|
degrees of freedom for the spline.
TYPE:
|
threshold
|
spike height for step 1, in reflectance units.
TYPE:
|
despike
|
run step 1 at all.
TYPE:
|
exclude
|
ranges blanked before fitting (nm).
DEFAULT:
|
mask_after
|
ranges blanked after fitting (nm).
DEFAULT:
|
min_points
|
a spectrum with fewer surviving bands is left as NaN.
TYPE:
|
keep_fill_flag
|
attach
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Dataset
|
A copy of |
Dataset
|
|
Dataset
|
correlates bands and invents some of them. |