spacr.normalization

Control crop storage precision and model-input normalization.

.npy crops keep whatever the merged stack held, PNG crops narrow once through spacr.crops.narrow_to_uint8() – the high byte of a uint16, a linear rescale rather than a clip – and measurements are taken from the full-precision array. So the structure the request asks for is the structure: original precision wherever it can be, one declared narrowing at the only boundary that requires one.

Storage dtype and model input scaling are separate choices. transforms.ToTensor() divides by 255 and hands the model a float in [0, 1] whatever the file held, so “scale to 0-1” is already what happens at the point it matters. The dtype on disk decides file size and what other tools can open the crop – a storage decision.

The subsequent normalization setting changes the model input. spaCR has historically normalized with

mean = std = (0.5, 0.5, 0.5)

which maps [0, 1] onto [-1, 1]. Every ImageNet-pretrained torchvision model was fitted on

mean = (0.485, 0.456, 0.406) std = (0.229, 0.224, 0.225)

so a finetune starts by handing pretrained weights inputs distributed differently from the ones they learned. A long finetune adapts; a short one, or a frozen backbone, pays for it. That was a literal in two places and is now a choice.

The default remains symmetric for compatibility. This is what spaCR has always done, and switching it silently would move every existing model’s scores with nothing in the artifact to say why.

Functions

apply_crop_dtype(→ numpy.ndarray)

Return array in the dtype a crop file should hold.

dataset_statistics(→ Tuple[Tuple[float, ...], ...)

Return the dataset's per-channel mean and standard deviation.

describe_normalization(→ str)

One line for the log, so a model card records what it was trained on.

normalization_stats(→ Optional[Tuple[Tuple[float, ...)

(mean, std) for mode, or None when nothing should be applied.

Module Contents

spacr.normalization.apply_crop_dtype(array: numpy.ndarray, dtype: Any = 'original') → numpy.ndarray[source]

Return array in the dtype a crop file should hold.

Parameters:
  • array – the crop as the pipeline produced it.

  • dtype – one of CROP_DTYPES.

Returns:

the array, unchanged for original.

Conversion to uint8 delegates to spacr.crops.narrow_to_uint8(), which applies the project’s declared 16-to-8-bit linear mapping. Conversion to uint16 is a cast rather than an intensity stretch; values from an 8-bit input remain unchanged.

spacr.normalization.dataset_statistics(loader: Any, *, max_batches: int | None = None) → Tuple[Tuple[float, ...], Tuple[float, ...]][source]

Return the dataset’s per-channel mean and standard deviation.

Dataset-specific statistics are appropriate when fluorescence channels differ substantially from the natural-image distribution represented by ImageNet statistics. Fluorescence channels may contain independently exposed stains and a large background fraction.

Computed in one streaming pass with the sum-of-squares identity, so a dataset that does not fit in memory still yields exact statistics rather than statistics of whatever fitted.

Parameters:
  • loader – anything iterable yielding (images, ...) batches, or bare image tensors, shaped (N, C, H, W) on [0, 1].

  • max_batches – stop after this many. The mean of a plate converges in far fewer batches than the plate has, and a full pass over a million crops to compute two numbers per channel is a cost with no matching gain. None reads everything.

Returns:

(mean, std), per channel.

Raises:

ValueError – the loader yielded nothing, so the answer would be the statistics of an empty set rather than of this dataset.

spacr.normalization.describe_normalization(mode: Any, **kwargs) → str[source]

One line for the log, so a model card records what it was trained on.

Parameters:

mode – normalization preset or explicit normalization mode.

spacr.normalization.normalization_stats(mode: Any, *, mean: Sequence[float] | None = None, std: Sequence[float] | None = None, channels: int = 3) → Tuple[Tuple[float, ...], Tuple[float, ...]] | None[source]

(mean, std) for mode, or None when nothing should be applied.

Parameters:
  • mode – one of NORMALIZATIONS. Anything unrecognised falls back to symmetric with a log line – a typo must not silently train a model on statistics nobody chose, but it must not stop a run either, and symmetric is what the run would have used before this setting existed.

  • mean – for custom and dataset, the per-channel means. One value is broadcast to every channel, which is what a single-stain dataset wants.

  • std – as mean. A zero is replaced by 1.0 rather than dividing by it – a channel with no variance is a constant channel, and dividing it by its own zero spread produces inf and then a loss of nan, several minutes into training, with nothing saying why.

  • channels – how many planes the model will see. Only used to broadcast a single supplied value.