spacr.crop_source

Where a classifier’s training images come from.

TWO NAMES FOR THE ONE CHOICE, and they are the same two every other panel in spaCR asks it with – LOAD IMAGES and STREAM IMAGES. Training used to ask the same question in a private vocabulary (pre_generated / on_demand), which is one idea in two spellings: a user reading the annotation panel and the training panel could not tell they were being asked the same thing, and the two halves of the code could not tell either.

png — load pre-generated images (default)

Read crops previously written by the measurement workflow. path_string filters paths by substring and file_type filters by image extension. This source performs no cropping.

merged — stream images

Extract crops during training from merged/*.npy arrays. extract_channels selects the intensity planes and object_array selects the labelled mask plane that defines each object’s extent. When coordinate_columns are configured, database coordinates instead define fixed bounding-box crops.

generate — generate images, then load them

Materialize a complete crop set on disk before training, then read it through the same file-backed path as png. This is a preprocessing action rather than a streaming source.

The stored values did not change. png and merged are what spacr.crops.resolve_crop_source has always read, so a settings file written under either vocabulary means what it always meant: pre_generated, load_images and auto all arrive as LOAD IMAGES, on_demand and stream_images as STREAM IMAGES. CROP_SOURCE_ALIASES is that migration, in one place, and it is what stops a panel that has been renamed from handing this module a word it refuses.

Why streaming exists. Pre-cutting every crop writes a copy of the dataset to disk before a single epoch runs, and every change of crop size or channel selection writes another. Cutting as training runs costs a slice per object and no disk at all.

Bounding box versus object. A bounding box is the smallest rectangle containing the object; an object crop masks everything outside it away. Both are useful – the background around a cell is sometimes signal and sometimes contamination – so it is a setting. Database-sourced objects can only ever be bounding boxes: a coordinate has no outline to mask against.

Exceptions

CropSourceError

A crop source that cannot produce images, and why.

Functions

crop_at(→ Optional[numpy.ndarray])

Cut a fixed box centred on a coordinate.

crop_object(→ Optional[numpy.ndarray])

Cut one object out of a merged array.

crops_from_merged(→ List[Tuple[int, numpy.ndarray]])

Every object's crop from one merged array.

inapplicable_settings(→ Tuple[str, ...])

Settings belonging to the OTHER sources -- what the panel greys out.

mask_plane_for(→ int)

Which plane of the merged array holds object_array's masks.

matches_path(→ bool)

Whether one crop belongs in the dataset.

normalise_extension(→ str)

The extension file_type names, without its dot and lower-cased.

object_bounds(→ Optional[Tuple[int, int, int, int]])

(row0, row1, col0, col1) of one labelled object, or None if absent.

resolve_source(→ str)

Which crop source a settings dict asks for, in the two names.

select_crops(→ List[str])

The crops a settings dict selects, in the order given.

stream_planes(→ List[int])

Which planes of a merged array become image channels, by either name.

validate(→ str)

Check a settings dict can actually produce crops. Returns the source.

Module Contents

exception spacr.crop_source.CropSourceError[source]

Bases: ValueError

A crop source that cannot produce images, and why.

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

spacr.crop_source.crop_at(array: numpy.ndarray, row: float, column: float, *, channels: Sequence[int], size: int) → numpy.ndarray | None[source]

Cut a fixed box centred on a coordinate.

The database path. Only a bounding box is possible here and that is not a limitation to be worked around: a coordinate has no outline, so there is nothing to mask against, and a crop that claimed to be object-shaped would be a rectangle wearing the wrong name.

Parameters:
  • array – the merged stack, (H, W, planes). Its rank is not checked as crop_object() checks it, so a 2-D image raises IndexError from the slice rather than a CropSourceError.

  • row – centre on the FIRST axis – an image row, not y. Floats are rounded half-to-even, Python’s rule, so a centroid of 2.5 lands on row 2 while 3.5 lands on row 4.

  • column – centre on the second axis; rounded the same way.

  • channels – which planes become image channels, in the order given, so [2, 0] returns them swapped. A bare int counts as a one-plane list. NOT bounds-checked here as it is in crop_object(): a plane the array does not have raises IndexError, and a negative one silently counts back from the last plane. An empty list yields a zero-channel crop rather than None.

  • size – the positive side length returned. Odd sizes keep the rounded coordinate at index size // 2. A box crossing an array edge is zero-padded rather than clipped or rescaled, preserving its centre and pixel scale.

Returns:

(size, size, len(channels)) as float32, or None when the box falls entirely off the array.

Raises:

CropSourceError – channels is None or is not planes, or size is not positive.

spacr.crop_source.crop_object(array: numpy.ndarray, mask: numpy.ndarray, label: int, *, channels: Sequence[int], shape: str = 'bounding_box', size: int | None = None, padding: int = 0) → numpy.ndarray | None[source]

Cut one object out of a merged array.

Parameters:
  • array – the merged stack, (H, W, planes).

  • mask – the plane holding this object’s labels.

  • label – which object.

  • channels – which planes become image channels, in order.

  • shape – bounding_box keeps the rectangle’s contents; object zeroes everything outside the object itself. The background around a cell is sometimes signal and sometimes contamination, which is why this is a choice rather than a default.

  • size – resize the result to size × size when given. It must be positive; None preserves the object’s natural bounding box.

Returns:

(h, w, len(channels)), or None if the object is not there.

Raises:

CropSourceError – a plane the array does not have, or an explicit size that is not positive.

spacr.crop_source.crops_from_merged(array: numpy.ndarray, settings: Mapping[str, Any], *, labels: Sequence[int] | None = None) → List[Tuple[int, numpy.ndarray]][source]

Every object’s crop from one merged array.

Parameters:
  • array – merged (height, width, planes) image whose configured object-mask plane supplies the labels and whose other planes supply the crop pixels.

  • settings – crop settings used to choose the object mask, extracted channels, crop shape and optional fixed image size.

  • labels – only these objects; by default every label in the mask.

Returns:

(label, image) pairs, skipping objects that are not present.

Raises:

CropSourceError – a setting that makes cutting impossible.

spacr.crop_source.inapplicable_settings(source: str) → Tuple[str, ...][source]

Settings belonging to the OTHER sources – what the panel greys out.

Greyed, never removed (INVARIANTS 6): a key absent from the dict makes the pipeline fall back to its own default, which can differ from the value the module needs and says nothing when it does.

Any spelling CROP_SOURCE_ALIASES knows is accepted, because what a panel has in hand is the value stored in the settings file, not the name this module resolved it to.

Parameters:

source – the crop source whose settings stay active; any spelling in CROP_SOURCE_ALIASES, matched case-insensitively. An unknown source raises CropSourceError.

spacr.crop_source.mask_plane_for(object_array: str, settings: Mapping[str, Any]) → int[source]

Which plane of the merged array holds object_array’s masks.

Read from the *_mask_dim settings the mask step already writes, so the two cannot disagree about which plane is which.

Parameters:
  • object_array – the object name (e.g. cell, nucleus); lower-cased and used to build the <name>_mask_dim setting key.

  • settings – the settings mapping the <name>_mask_dim plane index is read from.

Raises:

CropSourceError – the object has no mask plane, naming the setting that would give it one.

spacr.crop_source.matches_path(path: str, *, path_string: str = '', file_type: Any = '') → bool[source]

Whether one crop belongs in the dataset.

Two independent tests, which is the whole point of splitting them: WHICH OBJECT the crop is of (a substring of its path, e.g. cell_png) and WHAT FORMAT it is in (its extension). One setting could never express “every nucleus crop, whatever format” or “every TIFF, whatever object”.

Parameters:

path – the crop’s file path, tested as a string for the path_string substring and for its extension.

spacr.crop_source.normalise_extension(file_type: Any) → str[source]

The extension file_type names, without its dot and lower-cased.

It used to hold 'cell_png' – a path substring wearing a file type’s name, duplicating png_type. Now it is an extension and only that, so '.TIF', 'tif' and 'tiff' all mean what they look like.

Parameters:

file_type – an extension such as '.TIF', 'tif' or 'cell_png' (only the part after the last underscore is kept); empty or None gives ''.

Raises:

CropSourceError – an extension spaCR cannot read, named alongside the ones it can.

spacr.crop_source.object_bounds(mask: numpy.ndarray, label: int) → Tuple[int, int, int, int] | None[source]

(row0, row1, col0, col1) of one labelled object, or None if absent.

Half-open on the far edge, like every other slice in Python, so the caller can index with it directly rather than remembering to add one.

Parameters:
  • mask – 2-D label image holding the object.

  • label – the label value of the object to bound.

spacr.crop_source.resolve_source(settings: Mapping[str, Any]) → str[source]

Which crop source a settings dict asks for, in the two names.

Every spelling in CROP_SOURCE_ALIASES resolves, so a settings file from any panel spaCR has shipped answers this question. Unset means LOAD IMAGES, which is the default everywhere the question is asked.

Parameters:

settings – the settings mapping; its crop_source value is looked up in CROP_SOURCE_ALIASES, and an empty value means png.

Raises:

CropSourceError – an unrecognised source. Guessing would train on a different set of images than was asked for and report success.

spacr.crop_source.select_crops(paths: Iterable[str], settings: Mapping[str, Any]) → List[str][source]

The crops a settings dict selects, in the order given.

Parameters:
  • paths – candidate crop file paths.

  • settings – the settings mapping; path_string (or, failing that, png_type) and file_type are passed to matches_path().

spacr.crop_source.stream_planes(settings: Mapping[str, Any]) → List[int][source]

Which planes of a merged array become image channels, by either name.

channel_arrays is the current spelling and extract_channels the older one; both are a list of plane indices and both are still written to settings files, so both are read here. The current spelling wins when a file carries both, because that is the one the panel is editing.

Parameters:

settings – the settings mapping; channel_arrays is read first, then extract_channels.

Raises:

CropSourceError – neither is set, naming the one to set.

spacr.crop_source.validate(settings: Mapping[str, Any]) → str[source]

Check a settings dict can actually produce crops. Returns the source.

Run before training rather than during it: discovering that the planes were never named after an hour of dataset building is a worse failure than refusing at the start, and the message here names the setting to fix.

Parameters:

settings – the training settings mapping to check: the crop source, plus file_type for png or the plane, crop-shape, coordinate and mask settings for a streamed source.

Raises:

CropSourceError – with what to change.