pyfli.reconstruction.decay_reconstruction#

Reconstruct per-pixel modeled decay curves from fitted FLI parameter maps.

This module belongs to pyfli.reconstruction and sits downstream of pyfli.solver: it reuses the solver’s mono-/bi-exponential forward-model kernels and fit-quality metrics to turn a dictionary of fitted parameter maps (shaped like pyfli.solver.FLICPUProcessor’s output) back into decay cubes. Public API includes class ParamToDecay.

Classes

ParamToDecay(model_type, freq[, irf, num_gates])

Reconstruct per-pixel modeled decay curves from fitted parameter maps.

class ParamToDecay(model_type, freq, irf=None, num_gates=None)[source]#

Bases: object

Reconstruct per-pixel modeled decay curves from fitted parameter maps.

Rebuilds the forward-model decay for every pixel from a dictionary of parameter maps shaped like pyfli.solver.FLICPUProcessor’s output, and optionally derives fit-quality maps (TR_maps and per-pixel compute_fli_stats() summaries) when the measured decay is also supplied. When an IRF is available, pixels are rebuilt via pyfli.solver.forward_model.model_numpy() (kernel convolved with the IRF); when no IRF is given, pixels are rebuilt directly from pyfli.solver.forward_model.decay_kernel() (the un-convolved model).

"photon_count_map", "v_shift_map", and "h_shift_map" are optional — missing ones default to 1.0, 0.0, and 0.0 respectively (see PARAM_MAP_DEFAULTS), except "photon_count_map": if it’s omitted and decay is supplied to reconstruct(), it is instead solved for as the amplitude that makes the model’s own convolved-kernel discrete sum match decay’s discrete sum at each pixel (not simply decay.sum(axis=-1) — see _fill_photon_count_from_decay()), rather than falling back to 1.0. The lifetime map(s) ("tau_map" for mono-exponential; "alpha1_map", "tau1_map", "tau2_map" for bi-exponential) are always required.

A 2-D boolean bool_mask can be passed to reconstruct() to only reconstruct pixels where it is True; every output map is NaN elsewhere. Single-pixel reconstruction is also supported: pass scalar parameter values (and a 1-D IRF/decay) and every output collapses to a plain 1-D trace (or scalar, for the fit-quality numbers) instead of an (H, W, ...) map — see reconstruct().

New model families can be added by extending PARAM_MAP_KEYS (and the matching kernel in pyfli.solver.forward_model); new post-reconstruction map products can be added by overriding _compute_tr_maps() in a subclass.

Parameters:
  • model_type (str) – FLI model family; one of the keys in PARAM_MAP_KEYS ("mono-exponential" or "bi-exponential").

  • freq (float) – Acquisition frequency in MHz — i.e. freq[1] in pyfli.solver.BaseFLIFitter’s/pyfli.solver.FLIGPUProcessor’s (laser_freq_mhz, acq_freq_mhz) convention. Only the acquisition frequency is ever needed here, to derive the time axis as T_acq = 1000.0 / freq.

  • irf (np.ndarray | None) – Instrument response function; either a shared 1-D trace or a per-pixel (H, W, T) cube. The number of time gates is inferred from its last axis (or its length, if 1-D). If omitted, pixels are reconstructed without IRF convolution (see pyfli.solver.forward_model.decay_kernel()) and num_gates must be supplied instead.

  • num_gates (int | None) – Number of time gates/bins. Required only when irf is omitted; otherwise it is inferred from irf and this argument is ignored.

PARAM_MAP_KEYS: ClassVar[dict[str, tuple[str, ...]]] = {'bi-exponential': ('photon_count_map', 'alpha1_map', 'tau1_map', 'tau2_map', 'v_shift_map', 'h_shift_map'), 'mono-exponential': ('photon_count_map', 'tau_map', 'v_shift_map', 'h_shift_map')}#

required parameter-map keys}, in the exact order expected by forward_model.model_numpy/decay_kernel — the last key is always the temporal shift consumed as h_shift. Extend this (plus a matching kernel branch in forward_model.decay_kernel) to support new model families.

Type:

Registry of {model_type

PARAM_MAP_DEFAULTS: ClassVar[dict[str, float]] = {'h_shift_map': 0.0, 'photon_count_map': 1.0, 'v_shift_map': 0.0}#

Default value used for a parameter map when it’s omitted from params. Keys absent from this dict are required (no sensible physical default).

property param_keys: tuple[str, ...]#

All parameter-map keys for model_type, in kernel order.

property required_keys: tuple[str, ...]#

Parameter-map keys for model_type that have no default.

reconstruct_unit_amplitude(params, bool_mask=None)[source]#

Build the un-convolved kernel and IRF-convolved model at whatever literal amplitude params carries (a constant "photon_count_map" of 1.0 gives a pure shape/PDF result), without adding v_shift or computing any fit-quality maps.

Pairs with rescale_fit_to_measured_totals() for callers whose parameter maps don’t carry a meaningful literal amplitude (e.g. lifetime-only estimator output) and need the final scale pinned to a measured photon count instead — see pyfli.reconstruction.DetailedRecon.reconstruct().

Returns:

{"kernel_map": <H, W, T>, "convolved_map": <H, W, T>} (or (T,) each, for single-pixel input). convolved_map equals kernel_map when the instance has no IRF.

Return type:

dict[str, np.ndarray]

Parameters:
rescale_fit_to_measured_totals(fit_map, decay, eps=_EPS)[source]#

Rescale fit_map per pixel so its sum along the time axis matches decay’s exactly, instead of using whatever literal amplitude ("photon_count_map") went into building it.

Useful when the parameter maps don’t carry a meaningful absolute amplitude (e.g. lifetime-only estimator output) and the fit’s total should instead be pinned to the measured photon count — this also compensates for any convolution-truncation loss the way pyfli.reconstruction.DetailedRecon.reconstruct() requires.

Parameters:
Return type:

ndarray

reconstruct_vectorized(params, decay=None, bool_mask=None, verbose=True)[source]#

Vectorized equivalent of reconstruct() — same contract and (up to floating-point noise) identical numeric output, but built with batched numpy/fftconvolve instead of a Python per-pixel loop. Prefer this for whole-image reconstruction; reconstruct remains available for its progress bar and for subclasses that override the per-pixel hooks.

Parameters:
Return type:

dict[str, Any]

reconstruct(params, decay=None, bool_mask=None, verbose=True)[source]#

Rebuild the per-pixel modeled decay from fitted parameter maps.

Parameters:
  • params (dict[str, Any]) – Parameter maps keyed as in pyfli.solver.FLICPUProcessor’s output for model_type (see param_keys). Each value is normally an (H, W) array; "photon_count_map", "v_shift_map", and "h_shift_map" may be omitted (see PARAM_MAP_DEFAULTS and, for "photon_count_map", the decay-derived fallback described in the class docstring). For single-pixel reconstruction, pass plain scalars instead.

  • decay (np.ndarray | None) – Measured decay, (H, W, T) (or (T,) for a single pixel). When supplied, TR_maps and fit_stats_maps are also computed and returned.

  • bool_mask (np.ndarray | None) – Optional 2-D boolean mask; only True pixels are reconstructed, everything else is NaN in every output map. Ignored for single-pixel reconstruction.

  • verbose (bool) – Show a progress bar while reconstructing.

Returns:

{"fit_map": <H, W, T>}, plus "TR_maps" and "fit_stats_maps" when decay is supplied. For single-pixel input, every value collapses to a (T,) trace or, for fit_stats_maps, a plain float.

Return type:

dict[str, Any]