spacr.point_spread

Calibrated point-spread kernels and reproducible CPU image processing.

Calculated kernels are sampled Gaussian approximations with explicitly supplied FWHM and pixel/voxel spacing, not estimates of a microscope’s optical PSF. Measured kernels retain their supplied centre pixel as the optical origin. Processing uses half-sample symmetric boundaries, independently per channel. Richardson–Lucy uses the transpose of that same boundary operator, including its sensitivity normalization for asymmetric kernels. More iterations may amplify noise; this is not a claim of recovered biological structure.

The original image is never modified. Results are float32 in the input’s intensity units, with no percentile stretch, integer rounding or clipping to 0..1. Kernels, sampling and settings are included in a JSON-safe receipt.

When the calibration is not known, infer_optics() supplies it from the image’s own metadata, a chosen objective from OBJECTIVES or common defaults, recording where each value came from: pixel size is the camera pixel divided by the magnification and the lateral FWHM is 0.51 λ/NA.

References: https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.fftconvolve.html and https://scikit-image.org/docs/stable/api/skimage.restoration.html#skimage.restoration.richardson_lucy

Exceptions

ProcessingCancelled

A caller cancelled processing; no partial image should be applied.

Classes

Objective

One microscope objective: nominal magnification, numerical aperture and immersion.

OpticalValue

An inferred optical quantity and where it came from.

PSF

An immutable, normalized kernel whose bytes identify the actual PSF.

PSFResult

Processed float32 image and a JSON-safe processing receipt.

Functions

apply_psf(image, kernel, *, operation, image_sampling_um)

Convolve or Richardson–Lucy deconvolve each channel with a known PSF.

describe_optics(values)

One English line per inferred value: name = value (source: detail).

fill_psf_settings(settings[, source])

Fill unset Gaussian PSF calibration in Mask settings, in place.

gaussian_psf(*, fwhm_um, sampling_um[, ndim, truncate])

Calculate a sampled Gaussian approximation from declared physical widths.

image_optics_metadata(path)

Calibration an image file states about itself, each value with its source.

infer_optics([source, objective_name, ...])

Infer PSF calibration from image metadata, a chosen objective and defaults.

lateral_fwhm_um(emission_nm, numerical_aperture[, ...])

Lateral FWHM of a diffraction-limited widefield PSF, 0.51 λ/NA.

load_psf(path, *, sampling_um[, cancel])

Read a calibrated measured kernel from NPY or a single TIFF series.

measured_psf(data, *, sampling_um[, source_name])

Normalize a measured 2-D/3-D kernel, preserving its centre as origin.

objective(name)

Return the Objective table row called name.

pixel_size_um(camera_pixel_um, magnification)

Sample spacing at the specimen: camera pixel pitch divided by total magnification.

Module Contents

exception spacr.point_spread.ProcessingCancelled[source]

Bases: RuntimeError

A caller cancelled processing; no partial image should be applied.

Initialize self. See help(type(self)) for accurate signature.

class spacr.point_spread.Objective[source]

Bases: NamedTuple

One microscope objective: nominal magnification, numerical aperture and immersion.

Parameters:
  • name – the row’s label, e.g. '40x/1.30 oil'.

  • magnification – nominal magnification.

  • numerical_aperture – the objective’s NA.

  • immersion – air, water or oil (a key of IMMERSION_INDEX).

class spacr.point_spread.OpticalValue[source]

Bases: NamedTuple

An inferred optical quantity and where it came from.

source is one of metadata (OME-XML), imagej (ImageJ TIFF calibration), tiff_resolution (TIFF resolution tags in centimetres), file_name, image (the file’s own dimensions), chosen (supplied by the caller), objective (the objective table), default or calculated. detail names the file, table row or formula.

Parameters:
  • value – the quantity itself.

  • source – where it came from, one of the words above.

  • detail – the file, table row or formula; empty when there is none.

class spacr.point_spread.PSF[source]

An immutable, normalized kernel whose bytes identify the actual PSF.

Parameters:
  • shape – odd spatial dimensions, YX or ZYX.

  • sampling_um – pixel/voxel spacing in the same axis order, in µm.

  • values – C-ordered little-endian float32 normalized kernel bytes.

  • source – measured or gaussian approximation.

  • details_json – JSON object holding acquisition/file or calculation provenance. Use measured_psf(), gaussian_psf() or load_psf() to construct a kernel from ordinary arrays/files.

__post_init__()[source]

Validate serialized kernel geometry, calibration and immutable float32 values.

array()[source]

Return a read-only float32 view of the normalized kernel.

provenance()[source]

Return a fresh JSON-safe record of the kernel and its identity.

class spacr.point_spread.PSFResult[source]

Bases: NamedTuple

Processed float32 image and a JSON-safe processing receipt.

Parameters:
  • image – processed image with the input’s spatial shape, as float32.

  • provenance – JSON-safe kernel and processing settings for this result.

spacr.point_spread.apply_psf(image, kernel, *, operation, image_sampling_um, iterations=20, channel_axis=None, cancel=None, progress: Callable | None = None)[source]

Convolve or Richardson–Lucy deconvolve each channel with a known PSF.

Parameters:
  • image – finite nonnegative real YX/ZYX data, optionally with one explicitly identified channel axis. Input pixels are never changed.

  • kernel – calibrated immutable PSF.

  • operation – convolve (blur) or deconvolve (Richardson–Lucy).

  • image_sampling_um – image spacing matching kernel spacing in spatial axis order. Mismatches raise; no implicit kernel resampling is done.

  • iterations – deconvolution iterations, integer 1..200, default20.

  • channel_axis – None for a spatial image, otherwise the channel axis; channels are processed independently and returned in their original order.

  • cancel – callable or Event; checked between channels, convolutions and iterations. Cancellation raises ProcessingCancelled.

  • progress – optional worker-thread callback(channel, completed, total), with zero-based channel and one-based completed iteration.

Returns:

PSFResult, float32 image in original intensity units and provenance. Half-sample symmetric boundaries do not wrap opposite edges. Richardson–Lucy assumes nonnegative Poisson-like intensities; it is unregularized and can amplify noise. Output is not clipped to the input range. Dimensionality/channel identity are preserved.

spacr.point_spread.describe_optics(values)[source]

One English line per inferred value: name = value (source: detail).

Parameters:

values – the mapping infer_optics() returns.

Returns:

list of strings, in the mapping’s order.

spacr.point_spread.fill_psf_settings(settings, source=None)[source]

Fill unset Gaussian PSF calibration in Mask settings, in place.

Acts only when psf_operation is convolve or deconvolve, psf_source is gaussian and psf_image_sampling_um or psf_fwhm_um is None; values already set are never replaced. psf_objective ('auto' or an OBJECTIVES row name) picks the objective and infer_optics() supplies the rest.

Parameters:
  • settings – Mask or timelapse settings dict.

  • source – images to read metadata from; defaults to settings['src'].

Returns:

the infer_optics() values used, or None when nothing was filled.

spacr.point_spread.gaussian_psf(*, fwhm_um, sampling_um, ndim=2, truncate=4.0)[source]

Calculate a sampled Gaussian approximation from declared physical widths.

Parameters:
  • fwhm_um – full width at half maximum per spatial axis, or one value.

  • sampling_um – pixel/voxel spacing per axis in µm, or one value.

  • ndim – two (YX) or three (ZYX) spatial dimensions.

  • truncate – radius in standard deviations, between two and eight.

Returns:

normalized PSF. Sigma is FWHM/sqrt(8*ln(2)); radius is ceil(truncate*sigma/sampling). No optical parameters are inferred.

spacr.point_spread.image_optics_metadata(path)[source]

Calibration an image file states about itself, each value with its source.

Reads only the TIFF header: OME-XML PhysicalSizeX/Y, Objective LensNA/NominalMagnification/Immersion, ObjectiveSettings RefractiveIndex and Channel EmissionWavelength; an ImageJ calibration in microns; or TIFF resolution tags in centimetres. A bare dots-per-inch tag says nothing about the specimen and is ignored. A magnification token such as _40x_ in the file name is used when no header states one.

Parameters:

path – an image file; formats other than TIFF contribute only the file-name token.

Returns:

mapping of field name to OpticalValue; empty when the file states nothing or cannot be read.

spacr.point_spread.infer_optics(source=None, *, objective_name=None, camera_pixel_um=None, emission_nm=None, magnification=None, numerical_aperture=None, refractive_index=None, image_pixel_um=None)[source]

Infer PSF calibration from image metadata, a chosen objective and defaults.

Every returned value carries its source. Precedence, highest first: a value passed here (chosen), the image’s own metadata (ignored for the objective’s own magnification, NA and immersion once an objective is chosen), the objective table (the chosen row, else the row nearest a stated magnification), then the defaults: a 20x/0.75 air objective, a 6.5 µm sCMOS pixel and 520 nm (GFP) emission. Immersion refractive indices are air 1.0, water 1.33 and oil 1.515.

Formulae: pixel size = camera pixel / magnification (pixel_size_um()); lateral Gaussian FWHM = 0.51 λ / NA (lateral_fwhm_um(); Born and Wolf §8.5.2; Zhang, Zerubia and Olivo-Marin 2007). A pixel size stated in metadata wins over the calculated one because it already includes binning and adapters.

Parameters:
  • source – an image file, a folder (its first TIFF, or orig/’s) or an iterable of paths; None uses the table and defaults only.

  • objective_name – an OBJECTIVES row name, or None/'auto'.

  • camera_pixel_um – camera pixel pitch in micrometers.

  • emission_nm – emission wavelength in nanometers.

  • magnification – total magnification.

  • numerical_aperture – objective NA.

  • refractive_index – immersion refractive index.

  • image_pixel_um – image pixel size in micrometers, one value or (Y, X).

Returns:

dict of OpticalValue for objective, magnification, numerical_aperture, refractive_index, emission_nm, camera_pixel_um, pixel_size_um (Y, X), fwhm_um (Y, X) and, when an image was read, image and image_shape (Y, X).

Raises:

ValueError – for an unknown objective or implausible optics.

spacr.point_spread.lateral_fwhm_um(emission_nm, numerical_aperture, refractive_index=None)[source]

Lateral FWHM of a diffraction-limited widefield PSF, 0.51 λ/NA.

The intensity full width at half maximum of the Airy pattern is 0.514 λ / NA (Born and Wolf, Principles of Optics, 7th ed., §8.5.2, whose Rayleigh radius is 0.61 λ / NA). A Gaussian fitted to the widefield PSF has σ ≈ 0.21 λ / NA, a FWHM of ≈ 0.49 λ / NA (Zhang, Zerubia and Olivo-Marin 2007, Appl. Opt. 46:1819–1829), so a Gaussian of this width approximates the ideal emission PSF. Aberrations, confocal pinholes, thick specimens and camera binning are not modelled.

Parameters:
  • emission_nm – emission wavelength in nanometers.

  • numerical_aperture – objective NA.

  • refractive_index – immersion index; when given, NA must not exceed it.

Returns:

FWHM in micrometers.

Raises:

ValueError – for an implausible wavelength or aperture.

spacr.point_spread.load_psf(path, *, sampling_um, cancel=None)[source]

Read a calibrated measured kernel from NPY or a single TIFF series.

Parameters:
  • path – .npy, .tif or .tiff file, at most 64 MiB. Pickled/object arrays, RGB/channel axes, multiple TIFF series and even axes are rejected. TIFF YX/ZYX (or a plane stack) is supported.

  • sampling_um – explicitly supplied measured kernel spacing in µm; TIFF metadata is not silently assumed to describe the target image.

  • cancel – optional callable or threading.Event checked around reads.

Returns:

immutable PSF, including SHA-256 of the exact file bytes decoded. Subsequent edits to the source file do not change this PSF.

spacr.point_spread.measured_psf(data, *, sampling_um, source_name='array')[source]

Normalize a measured 2-D/3-D kernel, preserving its centre as origin.

Parameters:
  • data – real nonnegative kernel with positive signal and odd axes. Background must already be removed; negative values are rejected.

  • sampling_um – calibrated YX or ZYX spacing in µm, or one isotropic spacing. Sampling must match the image when processing it.

  • source_name – acquisition identifier retained in provenance.

Returns:

immutable normalized PSF; input data stay unchanged.

spacr.point_spread.objective(name)[source]

Return the Objective table row called name.

Parameters:

name – a row name such as '60x/1.40 oil'; case and surrounding spaces are ignored.

Returns:

the matching Objective.

Raises:

ValueError – when the table has no such objective.

spacr.point_spread.pixel_size_um(camera_pixel_um, magnification)[source]

Sample spacing at the specimen: camera pixel pitch divided by total magnification.

Parameters:
  • camera_pixel_um – physical camera pixel pitch in micrometers.

  • magnification – total magnification between specimen and camera, including any tube-lens or camera-adapter factor (binning multiplies the camera pixel instead).

Returns:

pixel size in micrometers.

Raises:

ValueError – for a nonpositive or nonfinite input.

Nested helpers

infer_optics.pick(key, given, table_value)

Resolve one value: the argument, then metadata, then the table row.

spacr/point_spread.py:743