msi_processor.computing.radiometric package#

Radiometric correction stage (C-PU-RAD; DPM-M-RAD; ALG-RAD-*).

Dark/offset (DSNU) subtraction, NUC/flat-field (PRNU) equalisation, bad-pixel detection & replacement, and saturation/no-data flagging. The stage is a pure core plus a thin RadiometricUnit wrapper.

Submodules#

msi_processor.computing.radiometric.core module#

Pure radiometric-correction core (C-PU-RAD; ALG-RAD-NUC/DARK/BPR/SAT).

CPM-free, I/O-free numpy functions implementing the radiometric algorithms of ATBD <5.2>, ported faithfully from the heritage level_1.py NUC class (compute_nuc / apply_nuc_and_bpr; RD-9). All arrays are processed in float32 with a fixed operation order for reproducibility (REQ-F-DEP-02, REQ-D-05).

Geometry convention: arrays are 2-D (line, detector) in focal-plane geometry; per-detector gain/offset/dark are 1-D (detector,) vectors broadcast across lines (ATBD <4.2>, SDD <5.5>).

Trace: REQ-F-RAD-01..05; DPM-M-RAD; ALG-RAD-*.

class msi_processor.computing.radiometric.core.RadiometricParams(bit_depth=12, g_min=None, g_max=None, saturation=None, fill_value=None, remove_dark_fft=False)#

Bases: object

Tunable radiometric parameters (SDD <5.4.3>; DPM-PRM-RAD-01..04).

Parameters:
bit_depth:

Sensor radiometric depth; valid DN range [0, 2**bit_depth - 1] (DPM-PRM-GEN-01, default 12).

g_min, g_max:

Bad-pixel gain bounds (DPM-PRM-RAD-02). None disables that bound.

saturation:

Saturation threshold DN; defaults to 2**bit_depth - 1 when None.

fill_value:

No-data sentinel DN; flagged NO_DATA when present.

remove_dark_fft:

Enable the optional FFT dark-noise pre-removal (DPM-PRM-RAD-03).

Attributes:
fill_value
g_max
g_min
max_dn

Upper bound of the valid DN range, 2**bit_depth - 1.

saturation
bit_depth: int = 12#
fill_value: Optional[int] = None#
g_max: Optional[float] = None#
g_min: Optional[float] = None#
property max_dn: int#

Upper bound of the valid DN range, 2**bit_depth - 1.

remove_dark_fft: bool = False#
saturation: Optional[int] = None#
msi_processor.computing.radiometric.core.apply_nuc(dn, gain, offset, dark_offset)#

ALG-RAD-DARK — apply the per-detector affine plus dark-offset. :rtype: ndarray[tuple[Any, ...], dtype[float32]]

\[X^{(b)}_{l,d} = \mathrm{DN}^{(b)}_{l,d}\,g^{(b)}_d + o^{(b)}_d - k^{(b)}\]

(heritage apply_nuc_and_bpr). gain/offset are 1-D (detector,) vectors broadcast across lines; dark_offset is the per-band scalar \(k\) (or a per-detector vector). Returns float32.

msi_processor.computing.radiometric.core.detect_bad_pixels(gain, params, bpm)#

ALG-RAD-BPR (detection) — flag bad detectors.

A detector is bad when its gain falls outside the admissible bounds, when its gain is non-finite (NUC singularity), or when it is listed in the bad-pixel-map ADF: :rtype: ndarray[tuple[Any, ...], dtype[bool]]

\[\text{bad}(d) \iff g_d \ge g_{\max} \;\vee\; g_d \le g_{\min} \;\vee\; \neg\,\mathrm{finite}(g_d) \;\vee\; \mathrm{bpm}(d).\]

Returns a boolean array of shape (detector,).

msi_processor.computing.radiometric.core.estimate_nuc(dark, flat, cut_dark=0, cut_flat=0)#

ALG-RAD-NUC — estimate per-detector gain/offset (heritage compute_nuc).

From a dark frame \(D\) and a flat-field frame \(F\) (both (line, detector)), reduce to per-detector column means and derive the normalising affine that maps every detector’s flat response to the array mean \(\mu_F\) and its dark to \(\mu_D\): :rtype: tuple[ndarray[tuple[Any, ...], dtype[float32]], ndarray[tuple[Any, ...], dtype[float32]]]

\[g_d = \frac{\mu_F - \mu_D}{\bar F_d - \bar D_d}, \qquad o_d = \mu_F - g_d\,\bar F_d .\]
Parameters:
dark, flat:

Dark / flat-field reference frames, dims (line, detector).

cut_dark, cut_flat:

Number of leading along-track rows to discard before reduction (edge-cut alignment; default 0).

Returns:
tuple of ndarray

(gain[detector], offset[detector]) as float32.

Notes

Detectors with \(\bar F_d = \bar D_d\) yield a non-finite gain (NUC singularity); these are not raised here but are caught downstream by detect_bad_pixels() and repaired (SDD <5.4.3>).

msi_processor.computing.radiometric.core.flag_saturation(corrected, params)#

ALG-RAD-SAT — clip to the valid range and flag saturation / no-data.

Values at or above the saturation level (params.saturation or 2**bit_depth - 1) are flagged QAFlag.SATURATED; non-finite values and any equal to params.fill_value are flagged QAFlag.NO_DATA. The data is then clipped to [0, 2**bit_depth - 1] (REQ-F-RAD-04, REQ-D-05).

Return type:

tuple[ndarray[tuple[Any, ...], dtype[float32]], ndarray[tuple[Any, ...], dtype[uint16]]]

Returns:
tuple of ndarray

(clipped[float32], qa[uint16]) of the same shape as corrected.

msi_processor.computing.radiometric.core.remove_dark_fft(dn, dark)#

Optional FFT dark-noise removal (heritage dark_noise_removal). :rtype: ndarray[tuple[Any, ...], dtype[float32]]

\[\hat I = \Re\big\{\mathcal F^{-1}(\mathcal F(I) - \mathcal F(D))\big\}\]

By linearity of the Fourier transform this equals spatial dark subtraction \(I - D\); the FFT route is retained where a frequency-selective dark notch is desired (ATBD <5.4>, ALG-ENH-FFTDARK). The dark frame is cropped to the image extent and negative results are floored at 0 (upper clipping is deferred to flag_saturation()). Returns float32.

msi_processor.computing.radiometric.core.replace_bad_pixels(corrected, bad)#

ALG-RAD-BPR (replacement) — across-track neighbour interpolation.

Repairs bad detectors column-wise (heritage apply_nuc_and_bpr): :rtype: ndarray[tuple[Any, ...], dtype[float32]]

\[\begin{split}X_{:,d} \leftarrow \begin{cases} X_{:,d_{\text{next valid}}}, & d = 0,\\ X_{:,d-1}, & d \text{ and } d{+}1 \text{ both bad},\\ \tfrac12\big(X_{:,d-1}+X_{:,d+1}\big), & d \text{ bad}, d{+}1 \text{ valid},\\ X_{:,d-1}, & d = N_d - 1. \end{cases}\end{split}\]

The pass is sequential left-to-right and operates on a copy. If every detector is bad (degenerate input) the array is returned unchanged. Returns float32.

msi_processor.computing.radiometric.unit module#

Thin EOProcessingUnit wrapper for radiometric correction (C-PU-RAD).

Adapts the pure core to the EOPF CPM runtime following the wrapper template of SDD <5.4.1>: read parameters, extract input bands, load/validate ADFs, call the core per band, attach QA flags, and build the output EOProduct. No algorithm lives here.

Mandatory inputs/ADFs are declared by the CPM computing-model JSON (models/msi_radiometric_1.0.0.json), not by overriding the list methods.

ADF data convention (this increment). The unit reads ADF content from the AuxiliaryDataFile.data_ptr mapping (the CPM data_ptr is documented as holding the opened data):

  • dark -> {"dark_offset": {band: float}, "frame": {band: ndarray2d}} (both keys optional; dark_offset defaults to 0.0, frame is only needed for FFT dark removal or calibration-mode NUC derivation)

  • nuc -> {"gain": {band: ndarray1d}, "offset": {band: ndarray1d}} (nominal mode)

  • flatfield -> {band: ndarray2d} (calibration mode)

  • badpixel -> {band: ndarray1d[bool]} (optional)

Trace: REQ-F-RAD-01..05; DPM-M-RAD; ALG-RAD-*; ICD <5.3.4>A.

class msi_processor.computing.radiometric.unit.RadiometricUnit(identifier='')#

Bases: EOProcessingUnit

Radiometric correction processing unit (C-PU-RAD; SDD <5.4.3>).

Attributes:
identifier

Identifier of the processing step

Methods

run:

Execute dark/NUC/BPR/saturation correction over the input bands.

PROCESSOR_LEVEL = 'L1A'#
PROCESSOR_MODEL = True#
PROCESSOR_NAME = 'msi_radiometric'#
PROCESSOR_VERSION = '1.0.0'#
run(inputs, adfs=None, mode=None, **kwargs)#

Run the radiometric correction.

Return type:

Mapping[str, Union[EOProduct, EOContainer, DataTree, Iterable[Union[EOProduct, EOContainer, DataTree]]]]

Parameters:
inputs:

{"l1a": EOProduct} with bands under measurements/detector.

adfs:

dark (mandatory); nuc (nominal mode) or flatfield (calibration mode); badpixel (optional).

mode:

"nominal" (read NUC from ADF) or "calibration" (derive NUC from dark+flat and emit a nuc product, REQ-F-RAD-05).

**kwargs:

RadiometricParams fields plus optional name for the output product.

Returns:
Mapping[str, DataType]

{"rad": EOProduct} (corrected DN + QA); additionally {"nuc": EOProduct} in calibration mode.