spacr.illumination¶
Workflow inputs and outputs¶
Illumination¶
Estimate and apply flat-field correction at configured stages. Keep corrected display values distinct from raw measurements and record the applied policy.
Open: Measure → Illumination.
Inputs and outputs below include conditional alternatives. The guidance and handoff notes say which route applies.
Inputs
Microscope images — Source image folder; original files, supported vendor files or imported TIFFs.
Outputs
Microscope images — Source image folder; original files, supported vendor files or imported TIFFs.
Illumination / flat-field correction for the measurement path.
The problem¶
No microscope lights a field of view evenly. A lamp profile, vignetting in
the objective, a tilted condenser and dirt on the optics together make the
same cell measure brighter at the centre of the field than at its edge –
routinely 10-40 % between the middle and a corner on a widefield screen.
Every intensity feature spaCR writes (*_mean_intensity,
*_percentile_*, the radial distribution, the texture channels that are
computed on intensity) therefore carries a position-dependent bias, and
because objects are not distributed identically over every well, that bias
does not average out: it survives into the per-well aggregate, and from there
into classification and regression as an effect that looks entirely real and
is entirely an artefact of the optics.
It is also the root cause of the plate-scale edge effects
spacr.plate_qc already detects and reports. Detecting them is useful;
removing them is what changes the answer.
The model¶
Per channel, per plate:
observed(y, x) = dark + flat(y, x) * true(y, x)
flat is the multiplicative illumination field, normalised so its mean is
1 – the plate’s overall intensity level is preserved, so a corrected number
stays on the same scale as an uncorrected one and only its position
dependence is removed. dark is the additive camera offset. The
correction the hook applies is:
corrected = (observed - dark) / flat
What is estimated, and why¶
Retrospective, from the data itself: a per-pixel median across many fields of the plate, followed by a fit of a smooth low-order surface.
Why the median across fields. A pixel is covered by a cell in only a minority of the fields on a plate, so the across-field median at that pixel sees background almost every time and the objects drop out. Each field is first divided by its own median, so a densely-seeded field does not pull the estimate up because it has more cells in it – what is being averaged is the relative profile, not the brightness.
Why the surface fit on top. Illumination is a physically smooth,
low-frequency function of position: a lamp profile plus a vignette. Fitting a
low-order 2-D polynomial (default degree 4, 15 terms) to the per-pixel median
imposes exactly that prior, so residual object structure and photon noise
cannot leak into the gain map and be baked into every measurement on the
plate. The fit is trimmed twice against a MAD threshold, so a persistent
bright artefact – a fluorescent speck in the same place on every field –
is rejected rather than smeared into the surface. For illumination that is
genuinely not polynomial (a dust shadow, a sharply structured lamp) pass
estimator='smooth': the same per-pixel median, Gaussian-smoothed at
1/16 of the short side and interpolated back to full resolution.
Why not BaSiC. BaSiC’s low-rank + sparse decomposition is the better
estimator when you have hundreds of fields and a genuine dark-field to
recover, but it is an iterative optimisation with its own convergence
failure modes, and its dark-field term is only identifiable because of those
extra assumptions. From a single acquisition, dark and flat are not
separately identifiable at all: multiplying flat by a constant and
absorbing it into the per-field brightness leaves every observation
unchanged. Estimating a dark-field anyway – for instance from the per-pixel
minimum across fields, which is the usual shortcut – returns dark plus the
dimmest background, and subtracting it removes real signal. So spaCR does
not guess: dark is 0 unless you supply the camera offset you measured
from a dark frame, via illumination_dark.
Reaching the worker processes¶
spacr.measure.measure_crop() measures fields in a
multiprocessing.Pool. Under spawn / forkserver each worker is
a fresh interpreter with an empty hook registry, so a correction registered
only in the parent applies to nothing while the run looks perfectly
normal – the single worst outcome for this feature, because the user then
believes their numbers are corrected. enable_illumination_correction()
therefore does not merely register the hook: it writes the model path to
MODEL_ENV_VAR and appends spacr.illumination:install to
SPACR_MEASURE_HOOKS, which every start method inherits, so each worker
installs the correction for itself. worker_delivery_status() reports
whether that is actually in place, and enable_illumination_correction()
prints it.
Using it¶
Off by default. From a settings dict (the keys are registered through the
spacr.settings.register_defaults() seam, see
illumination_settings()):
settings = get_measure_crop_settings(settings={...})
settings['illumination_correction'] = True
prepare_illumination_correction(settings) # estimate, save, enable, QC
measure_crop(settings)
or explicitly:
model = estimate_illumination(src, channels=[0, 1, 2])
model.save('/data/plate1/illumination/illumination_model.npz')
illumination_qc(model, src, save_dir='/data/plate1/illumination')
enable_illumination_correction('/data/plate1/illumination/illumination_model.npz')
Nothing in this module runs unless one of those calls is made, and
disable_illumination_correction() returns the process to a state where
measure_crop measures exactly what it measured before.
In the GUI the same two routes exist and end here: the “Illumination Correction” category on the Measure panel, which is the switch thrown on the run whose numbers it changes, and the Illumination button on that screen’s masthead, which opens this module’s own settings form and Run button so the field can be estimated and QC’d without measuring the plate. Neither is a tile: the module folded into Measure and left the app registry with it.
Exceptions¶
Illumination correction was asked for and could not be delivered. |
Classes¶
The preprocessing hook that applies an |
|
The illumination estimate for one plate, for one or more channels. |
|
Estimated illumination fields for every plate in a source folder. |
|
One fitted/loaded model and the stage-neutral objects derived from it. |
|
Apply one illumination model exactly once per segmentation field. |
Functions¶
|
Turn the correction off, here and for any worker started afterwards. |
Turn the correction on, here and in every worker process. |
|
|
Estimate the illumination field from the merged fields in |
|
Show that the correction worked, and say by how much. |
|
Defaults for illumination correction. Registered through the seam. |
|
Install the correction in this process from the environment. |
|
Read a saved model. Thin alias for |
Read and validate prior segmentation illumination without side effects. |
|
|
The plate a merged field belongs to, from its file name. |
|
Least-squares slope of intensity against distance from the field centre. |
|
Estimate, save, enable and QC the Measure correction. |
Prepare one reusable optical model without installing a Measure hook. |
|
Prepare correction for segmentation inputs without changing raw data. |
|
|
Register the illumination settings through the defaults seam. |
|
Fail closed before Measure corrects pixels a second time. |
|
Validate a |
|
Whether the correction will actually reach |
Module Contents¶
- exception spacr.illumination.IlluminationError[source]¶
Bases:
spacr.errors.ConfigurationErrorIllumination correction was asked for and could not be delivered.
A
spacr.errors.ConfigurationError, not a per-field data error: every failure this class reports (no fields to estimate from, a model that does not cover the plate being measured, a channel the model was never estimated for) is wrong for the whole run, and the alternative – measuring on quietly uncorrected pixels – is the outcome this module exists to prevent.Initialize self. See help(type(self)) for accurate signature.
- class spacr.illumination.IlluminationCorrector(model: IlluminationModel, *, on_missing: str = 'error', verbose: bool = True)[source]¶
The preprocessing hook that applies an
IlluminationModel.Registered through
spacr.measure_hooks.register_preprocessing_hook(), so it is handed exactly the array the intensity measurements see – the channels named bysettings['channels'], selected out of the merged stack, before a single feature is computed.The dtype round trip is this class’s decision, and it is made here rather than in the hook machinery on purpose (see
spacr.measure_hooks.apply_preprocessing_hooks()). Integer input is corrected in float32 and returned by rounding to nearest and then clipping to the dtype’s range:round, not truncate. Truncation would shave a mean of 0.5 counts off every corrected pixel. Averaged over a 500-pixel object that does not wash out – it is a systematic, one-directional shift of exactly the kind this feature exists to remove. Rounding is unbiased.
clip, and count. A gain above 1 at the edge of the field can push a near-full-scale pixel past the top of a uint16. Clipping is the only option that keeps the dtype the hook contract requires, but silently clipping real signal is a lie about the data, so every pixel that was below full scale before the correction and lands at full scale after it is counted and reported. Pixels that were already saturated are not counted: they were destroyed by the microscope, not by this class.
Float input is returned in its own float dtype with no rounding and no clipping at all – there is nothing to round to and no range to leave.
- Parameters:
model – the estimated
IlluminationModel.on_missing –
'error'(default) or'skip'for a field whose plate the model does not cover. The default is to fail the field: a half-corrected table is worse than a failed one.verbose – print the clipping reports.
Arm a corrector over a fitted illumination model.
- Parameters:
model – the fitted model to divide fields by.
on_missing – what to do with a field the model has no profile for –
'error'fails the field,'skip'measures it uncorrected and counts it.verbose – report the first few clipping events.
- Raises:
IlluminationError – if
on_missingis neither of the two.
- __call__(channel_arrays: numpy.ndarray, context) numpy.ndarray[source]¶
Return
channel_arrayscorrected, in the same shape and dtype.- Parameters:
channel_arrays –
(Y, X, C)or(Z, Y, X, C)intensities.context – the
spacr.measure_hooks.PreprocessingContext.
- class spacr.illumination.IlluminationField[source]¶
The illumination estimate for one plate, for one or more channels.
- Parameters:
plate – the plate key, or
ALL_PLATESwhen the model was estimated across every plate at once.channels – source channel indices, in the order they index
flatfield’s first axis. These are indices into the merged stack, i.e. exactly the values insettings['channels'].flatfield –
(C, Y, X)float32 multiplicative field, normalised so each channel’s mean is 1.0.corrected = (observed - dark) / flatfield.dark – per-channel additive offset subtracted before dividing. Zero unless the user supplied a measured camera offset – see the module docstring for why it is not estimated.
n_fields – how many fields the estimate was made from.
estimator –
'polynomial'or'smooth'.degree – polynomial degree, or 0 for the smooth estimator.
bin_size – the binning factor the per-pixel median was computed at. Illumination is low-frequency, so binning costs nothing and buys both the memory to hold many fields at once and a quieter statistic.
floored – pixels the fitted surface had to be floored at (see
FLAT_FLOOR_FRACTION). Non-zero means the fit went negative somewhere and the estimate should be looked at before it is trusted.darkfield – optional
(C, Y, X)spatial background subtracted on top ofdark. Only a vendor profile that ships its own background surface carries one; an estimated field never does.
- dark_stack(channels: Sequence[int]) numpy.ndarray[source]¶
(C,)additive offsets forchannels, ready to broadcast.- Parameters:
channels – source channel indices in the order required by the array being corrected.
- gain_stack(channels: Sequence[int]) numpy.ndarray[source]¶
(Y, X, C)multiplicative gains1 / flatfieldforchannels.Shaped for the array a preprocessing hook is handed, so applying the correction is one broadcast multiply.
- Parameters:
channels – source channel indices, in the order they appear along the last axis of the array being corrected.
- index_of(channel: int) int[source]¶
Position of source
channelalongflatfield’s first axis.- Parameters:
channel – a merged-stack channel index.
- Raises:
IlluminationError – if the model was never estimated for it. Correcting the channels that happen to be present and leaving the rest alone would put corrected and uncorrected numbers in the same table.
- nonuniformity() Dict[int, float][source]¶
Per channel,
(p98 - p2) / meanof the field, as a fraction.The headline “how uneven is this microscope” number: 0.30 means the bright and dim ends of the field of view differ by 30 % of the mean, and therefore so does the same cell measured in those two places.
- class spacr.illumination.IlluminationModel[source]¶
Estimated illumination fields for every plate in a source folder.
- Parameters:
fields – plate key ->
IlluminationField. A model estimated withper_plate=Falseholds the single keyALL_PLATES, which matches every plate.meta – provenance – source folders, channels, when it was estimated, the settings it was estimated with. Written into the
.npzand read back, so a model on disk can always say what produced it.
- describe() str[source]¶
Every field’s
IlluminationField.describe(), one per line.
- field_for(plate: str) IlluminationField[source]¶
The
IlluminationFieldthat applies toplate.- Parameters:
plate – plate key whose estimated illumination field is needed.
- Raises:
IlluminationError – when nothing in the model covers it. This is deliberately not a fall back to “some other plate’s field”: illumination differs between acquisition sessions, which is the whole reason the default is one field per plate.
- classmethod load(path: str) IlluminationModel[source]¶
Read a model written by
save().- Parameters:
path – the
.npzfile.- Raises:
IlluminationError – when the file is missing or is not a spaCR illumination model. A worker that cannot load the model must say so rather than measure uncorrected pixels.
- save(path: str) str[source]¶
Write the model to
pathas a compressed.npz.The path is what
enable_illumination_correction()puts in the environment, and what each worker process loads: the model has to be on disk for aspawnworker to be able to see it at all.- Parameters:
path – destination file. Parent folders are created.
- Returns:
the absolute path written.
- class spacr.illumination.PreparedIllumination[source]¶
One fitted/loaded model and the stage-neutral objects derived from it.
The saved model is deliberately not tagged
measurementorsegmentation: both stages may reuse the same optical estimate. The stage that applies it owns that provenance separately.- Parameters:
model – loaded or newly estimated illumination model.
corrector – corrector configured with the requested missing-plate policy, but not registered as a Measure preprocessing hook.
model_path – absolute path of the saved model.
model_sha256 – digest of the exact saved bytes at
model_path.qc_artifacts – QC figure paths written while preparing the model.
- class spacr.illumination.SegmentationIlluminationSession(prepared: PreparedIllumination, *, provenance_path: str, pipeline_style: str, resume: bool = False)[source]¶
Apply one illumination model exactly once per segmentation field.
Correction always receives a private copy.
correct()records that an in-memory input was corrected;mark_completed()is deliberately separate and is the only operation that persists a field id. A pipeline therefore marks a field only after its durable NPZ/mask output exists.- Parameters:
prepared – model/corrector returned by
prepare_illumination_model().provenance_path – destination
segmentation_application.json.pipeline_style –
'v1'or'v2'for the audit record.resume – restore explicitly completed fields from an existing compatible record without rewriting it; absence is an error. False starts a fresh regenerated-output session and atomically replaces any old completion claim with an explicit
preparedrecord.
Open a segmentation-input correction session and stamp its provenance.
- Parameters:
prepared – the validated illumination model and its QC artefacts.
provenance_path – where the application record is written.
pipeline_style – which segmentation pipeline this corrects for.
resume – continue a previous session, reading back which fields it already completed. Without it a fresh record is written stating that no field has yet been corrected – a new preprocessing run invalidates any earlier completion claim, and saying so explicitly is what stops a half-finished run being read as a finished one.
- correct(field_id: str, channel_arrays: numpy.ndarray, context) numpy.ndarray[source]¶
Correct a private copy of one raw field, refusing a second pass.
- Parameters:
field_id – identifier of the raw field, compared as a string; a field already corrected or marked complete raises
IlluminationError.channel_arrays – raw field intensities,
(Y, X, C)or(Z, Y, X, C); a copy is corrected and the input is left unchanged.context – the
spacr.measure_hooks.PreprocessingContextpassed to the prepared corrector; its file name selects the plate’s model.
- finish(expected_fields: Iterable[str]) None[source]¶
Mark the journal stage done after every expected field is durable.
- Parameters:
expected_fields – identifiers of every field the run should have completed; the set, compared as strings, must equal the completed set exactly or
IlluminationErroris raised.
- mark_completed(field_id: str) bool[source]¶
Persist
field_idafter its corrected pipeline output is durable.- Parameters:
field_id – identifier of a field already passed to
correct(), compared as a string; an uncorrected field raisesIlluminationError.- Returns:
Truewhen the record changed,Falsewhen the same completed field was marked again.
- spacr.illumination.disable_illumination_correction() bool[source]¶
Turn the correction off, here and for any worker started afterwards.
Unregisters the hook and removes this module’s entry from
SPACR_MEASURE_HOOKS– leaving any other extension’s entries alone – plus the two model variables.- Returns:
True if a correction was registered and has been removed.
- spacr.illumination.enable_illumination_correction(model, *, path: str | None = None, on_missing: str = 'error', verbose: bool = True) str[source]¶
Turn the correction on, here and in every worker process.
Three things happen, and the third is the one that matters:
the model is saved to disk if it is not already there – a
spawnworker can only reach it through the file system;MODEL_ENV_VARandON_MISSING_ENV_VARare set;spacr.illumination:installis appended toSPACR_MEASURE_HOOKS(appended, not assigned – another extension may already be in there), and the registry is then consulted so that this process installs the hook through that same environment route.
- Parameters:
model – an
IlluminationModel, or the path to a saved one.path – where to save
modelwhen it is not already a path. Defaults to<first src>/../illumination/illumination_model.npz.on_missing –
'error'or'skip'; seeIlluminationCorrector.verbose – print what was enabled and whether workers will see it.
- Returns:
the registry key the hook is registered under.
- Raises:
IlluminationError – when the model cannot be saved or loaded.
- spacr.illumination.estimate_illumination(src, channels: Sequence[int], *, per_plate: bool = True, estimator: str = 'polynomial', degree: int = 4, max_fields: int = 50, grid: int = 256, dark: float = 0.0, verbose: bool = True) IlluminationModel[source]¶
Estimate the illumination field from the merged fields in
src.Retrospective: the data corrects itself. See the module docstring for what is estimated and why.
- Parameters:
src – the merged field folder
measure_cropreads, or a list of them (settings['src']accepts both).channels – merged-stack channel indices to estimate, i.e.
settings['channels']. One field is estimated per channel: two fluorophores go through different filters and vignette differently.per_plate – one field per plate (default) or one for everything. Per plate is the default because illumination differs between acquisition sessions – lamp age, a re-seated filter cube, a different objective – and pooling two sessions estimates neither.
estimator –
'polynomial'(default) or'smooth'.degree – polynomial degree. 4 gives 15 terms: enough for a lamp profile plus a vignette and a tilt, far too few to fit a cell.
max_fields – fields per plate to estimate from, sampled evenly across the sorted file list. 50 is well past the point where the across-field median stops moving, and bounds the memory.
grid – the per-pixel median is computed on a binned grid whose long side is at most this. Illumination is low-frequency, so binning loses nothing, quiets the photon noise and is what makes 50 fields fit in memory. The fitted surface is returned at full resolution.
dark – additive camera offset subtracted before dividing, in raw counts. Not estimated – see the module docstring.
verbose – print one line per estimated field.
- Returns:
- Raises:
IlluminationError – when
srcholds no merged fields, or a field is unreadable in a way that would make the estimate meaningless.
- spacr.illumination.illumination_qc(model: IlluminationModel, src, *, channels: Sequence[int] | None = None, save_dir: str | None = None, max_fields: int = 25, verbose: bool = True, stage: str | None = None) Dict[str, Any][source]¶
Show that the correction worked, and say by how much.
Three things, per plate and per channel:
the estimated field as an image, so a lamp profile that is really a dirty objective is visible rather than inferred;
the position-versus-intensity trend before and after, measured on the same fields the estimate came from – the curve that should be flat after correction and is not before it;
a number: the residual slope of intensity against distance from the centre of the field, before and after, and the percentage of that bias the correction removed.
- Parameters:
model – the estimated model.
src – the merged field folder(s) to measure the trend on.
channels – channels to report; defaults to the model’s own.
save_dir – where the figure goes. Defaults to
<first src>/../illumination. Pass''to skip the figure and compute only the numbers.max_fields – fields per plate to measure the trend over.
verbose – print the per-channel summary.
stage – optional consumer label such as
'segmentation_input'. When supplied it appears in the figure title and filename, preventing segmentation and measurement QC artifacts from being confused.
- Returns:
{plate: {channel: {...metrics...}}}with, per channel,slope_before,slope_after,bias_removed_pct,nonuniformity_pct,gain_min,gain_maxandn_fields; plus, when a figure was written, one extra key'_figures'mapping each plate to the path of its PNG.
- spacr.illumination.illumination_settings(settings=None)[source]¶
Defaults for illumination correction. Registered through the seam.
- Parameters:
settings – values to seed, exactly like every
set_default_*inspacr.settings.- Returns:
the settings dict with the illumination defaults applied.
- spacr.illumination.install() str[source]¶
Install the correction in this process from the environment.
This is the zero-argument installer
SPACR_MEASURE_HOOKSnames, and the only route that survives aspawn/forkserverworker: the worker is a fresh interpreter, so it imports this module and calls this function for itself, reading the model fromMODEL_ENV_VAR.- Returns:
the registry key the hook was registered under.
- Raises:
IlluminationError – if the environment does not name a readable model. Refusing loudly is the point – a worker that cannot load the model must not go on to measure uncorrected pixels.
- spacr.illumination.load_illumination_model(path: str) IlluminationModel[source]¶
Read a saved model. Thin alias for
IlluminationModel.load().- Parameters:
path – the
.npzwritten byIlluminationModel.save().
- spacr.illumination.load_segmentation_illumination_resume(settings: Mapping[str, Any], *, provenance_path: str, pipeline_style: str, expected_fields: Iterable[str], verbose: bool | None = None) PreparedIllumination[source]¶
Read and validate prior segmentation illumination without side effects.
This is the
preprocess=Falseentry point. The application record is authoritative: its exact model path is loaded and its digest, metadata, pipeline style, and completed-field set are checked before the caller may trust existing normalised mask NPZ files. No model is fitted, no QC or application record is written, no pixels are corrected, and no Measure hook is installed.- Parameters:
settings – run settings;
illumination_correctionmust be true, andillumination_model(when set),illumination_on_missingandverboseare read.provenance_path – path of the prior segmentation application record (JSON); its
model_pathis resolved relative to this file’s folder when not absolute.pipeline_style – segmentation pipeline style,
'v1'or'v2'(case-insensitive); any other value raisesIlluminationError.expected_fields – identifiers of the mask fields already on disk; the record’s completed-field set must equal them exactly.
- spacr.illumination.plate_of_field(file_name: str) str[source]¶
The plate a merged field belongs to, from its file name.
spaCR names merged fields
<plateID>_<wellID>_<fieldID>.npy(seespacr.io), so the plate is the first underscore-separated token. A name with no underscore is its own plate, which keeps a hand-assembled folder working instead of silently pooling it.- Parameters:
file_name – file name or stem, with or without directories.
- spacr.illumination.position_intensity_slope(intensities: Sequence[float], coordinates: numpy.ndarray, shape: Tuple[int, int]) float[source]¶
Least-squares slope of intensity against distance from the field centre.
This is the number illumination correction exists to drive to zero, and the same statistic is used on pixels (the QC images) and on objects (the scientific claim: the same cell must not measure brighter in the middle of the field).
Intensities are divided by their own mean and the radius is normalised so that 0 is the centre of the field and 1 is a corner, so the slope reads as the fraction of the mean intensity gained or lost between the centre of the field and its corner. -0.30 means a corner object measures 30 % of the mean below a central one.
- Parameters:
intensities – one value per position.
coordinates –
(N, 2)array of(row, col)positions in pixels.shape –
(Y, X)shape of the field the positions live in.
- Returns:
the slope, or 0.0 when there is nothing to fit.
- spacr.illumination.prepare_illumination_correction(settings: Mapping[str, Any], *, verbose: bool | None = None)[source]¶
Estimate, save, enable and QC the Measure correction.
The one call a pipeline makes before
measure_crop, and the one the Illumination button on the Measure masthead runs on its own – the model and its QC figures in minutes, before committing hours to the measure run that will reuse them throughillumination_model.It does nothing at all – and returns None – unless
settings['illumination_correction']is True, which is NOT the shipped default: the correction is opt-in, so a run that never mentions it is measured uncorrected. Being asked to run with the switch off is a no-op worth hearing about rather than a silent one, because from a settings form it looks exactly like a Run button that does nothing, so a verbose call says which switch was not thrown.- Parameters:
settings – a
measure_cropsettings dict. Readsillumination_correction,illumination_model,illumination_estimator,illumination_degree,illumination_per_plate,illumination_max_fields,illumination_dark,illumination_on_missing,illumination_qc,illumination_vendor_profile,illumination_vendor_channel_map, plussrcandchannels.verbose – overrides
settings['verbose'].
- Returns:
the
IlluminationModelthat was enabled, or None.
- spacr.illumination.prepare_illumination_model(settings: Mapping[str, Any], *, src=None, channels: Sequence[int] | None = None, qc_stage: str | None = None, verbose: bool | None = None) PreparedIllumination | None[source]¶
Prepare one reusable optical model without installing a Measure hook.
This is the direct consumer of the
illumination_*settings. It estimates, loads or reads a vendor flat-field profile into the model once, ensures a fitted model is saved, hashes the exact saved bytes, optionally writes stage-labelled QC, and builds a corrector. Applying that corrector belongs to the caller’s stage.- Parameters:
settings – settings carrying the illumination controls. A non-empty
illumination_vendor_profilereplaces the estimate with the vendor’s own flat field;illumination_vendor_channel_mapoptionally assigns persisted channels to vendor channels/planes.illumination_modelstill wins over both.src – optional raw field folder override. Defaults to
settings['src'].channels – optional persisted intensity-axis positions. Defaults to
settings['channels'].qc_stage – optional stage label included in QC filenames/titles.
verbose – override
settings['verbose'].
- Returns:
a prepared model/corrector, or
Nonewhen correction is off.
- spacr.illumination.prepare_segmentation_illumination(settings: Mapping[str, Any], *, src=None, channels: Sequence[int] | None = None, pipeline_style: str, verbose: bool | None = None) SegmentationIlluminationSession | None[source]¶
Prepare correction for segmentation inputs without changing raw data.
The returned session is not a Measure hook. V1/V2 adapters hand it raw field copies, then explicitly mark fields complete after their durable segmentation output exists. The application record is stored beside the current run’s source folder, even when the optical model is shared from an external path, so two runs cannot overwrite one another’s completion set.
- Parameters:
settings – run settings carrying the illumination controls read by
prepare_illumination_model(), andsrcwhensrcis not given.pipeline_style – segmentation pipeline style,
'v1'or'v2'(case-insensitive); any other value raisesIlluminationError.
- spacr.illumination.register_illumination_settings(replace: bool = False) bool[source]¶
Register the illumination settings through the defaults seam.
Called once at import. Uses
spacr.settings.register_defaults()rather than appending tospacr/settings.py, so this module owns its own knobs.Types, tooltips and the module description are contributed; categories deliberately are not.
spacr.settings.categoriesis one shared, ordered map that every settings panel walks, and its growth is guarded by an exact-equality test against a hand-kept list. A key contributed at import time is in that map only in a session that imported this module, so contributing categories would make that test’s result depend on which files pytest was pointed at.The illumination keys ARE filed under a heading – “Illumination Correction” in
spacr.settings.categories– and Measure’s panel offers every one of them, becausemeasure_cropcallsprepare_illumination_correction()itself and these are the keys that call reads. The heading is written in that map by hand for the reason above: it has to exist for every process that groups the Measure settings, not only for one that happened to import this module.- Parameters:
replace – re-register over an existing registration.
- Returns:
True if it registered, False if it was already registered.
- spacr.illumination.validate_measurement_illumination_inputs(settings: Mapping[str, Any], *, src=None) Dict[str, Dict[str, Any]][source]¶
Fail closed before Measure corrects pixels a second time.
Segmentation is allowed to correct only its private model input. If a mask run ever records that it instead changed the persisted intensities, Measure must not install another gain over those pixels: doing so squares the optical field while producing entirely plausible numbers. A missing application record is the legacy/raw case and remains valid; a present record must prove the current
segmentation-input-onlycontract.This check is deliberately read-only. It neither creates an illumination folder nor repairs a malformed record, and it does nothing when Measure’s own illumination correction is off.
- Parameters:
settings – resolved Measure settings.
src – optional merged-folder override; defaults to
settings['src']and accepts the same folder-or-list shape.
- Returns:
absolute application-record paths mapped to their validated JSON objects; an empty dict means no segmentation record was present.
- Raises:
IlluminationError – when a present record is unreadable or cannot prove that persisted intensity pixels remain raw.
- spacr.illumination.validate_segmentation_illumination_resume(prepared: PreparedIllumination, *, provenance_path: str, pipeline_style: str, expected_fields: Iterable[str]) Dict[str, Any][source]¶
Validate a
preprocess=Falsemask resume without writing anything.Normalised mask NPZ files cannot prove which intensity state Cellpose saw. A bypassed preprocessing stage therefore proceeds only when a prior application record names the same model bytes and pipeline style and covers exactly the fields already on disk. This function never fits a model, creates a record, corrects pixels, or updates the run journal.
- Parameters:
prepared – the loaded model and corrector whose model path and digest the record must name.
provenance_path – path of the prior segmentation application record (JSON) to read.
pipeline_style – segmentation pipeline style the record must match,
'v1'or'v2'(case-insensitive).expected_fields – identifiers of the mask fields already on disk; the record’s completed-field set, compared as strings, must equal them exactly.
- Returns:
the validated existing application record.
- Raises:
IlluminationError – for an absent or incompatible record, model, pipeline style, or completed-field set.
- spacr.illumination.worker_delivery_status(start_method: str | None = None) Tuple[bool, str][source]¶
Whether the correction will actually reach
measure_crop’s workers.The failure this answers is silent by construction: a hook registered only in the parent process is a no-op in every
spawnworker, the run completes, and the numbers are uncorrected while the user believes they are not.- Parameters:
start_method – the pool start method to judge against. Defaults to whatever
SPACR_START_METHODselects, falling back to the platform default – i.e. whatspacr.measure.measure_crop()will use.- Returns:
(ok, message).okis False whenever a field could be measured uncorrected without anything saying so.
Nested helpers¶
- _lenient_profile_json._value(match)¶
Quote a bare profile value while preserving JSON booleans and null.
spacr/illumination.py:884