spacr.image_stitch

Assemble the tiles of one field into the one image the field really is.

spaCR’s filename convention – plate_well_T####F###L##A##Z##C## – has no tile slot, so a field split into four tiles produces four images with ONE canonical name and three of them would be overwritten. Three answers were possible: grow the convention a tile slot, give each tile its own field number, or put the field back together. spaCR stitches at import, with the option to turn it off, and that is the right answer: a stitched field IS one image with one name, so nothing downstream has to learn what a tile is.

WHAT MAKES THIS HARD IS THAT A TILE INDEX IS NOT A POSITION. tile03 says an image is the third of some number; it does not say where it goes, how the tiles are ordered, or how far they overlap. Every one of those has to come from evidence, and this module is arranged around where the evidence is:

  1. THE FILE’S OWN STAGE COORDINATES, when it has them. An OME-TIFF records PositionX/PositionY in microns and the pixel size beside them, so the layout is not inferred at all – it is read. Nothing else is as good.

  2. THE PIXELS, otherwise. Adjacent tiles overlap, and the overlap is the same picture twice, so the right displacement is the one whose implied overlap correlates – scored for EVERY admissible displacement rather than picked from a phase-correlation peak, which a small overlap buries. This also settles the ORDER – row-major and serpentine put different tiles beside each other, so the arrangement that correlates is the arrangement the microscope used.

  3. NOTHING, in which case it says so. Blank tiles, single-pixel overlaps and images with no shared content produce no correlation to speak of. The tiles are then butt-joined in a square-ish grid and the mosaic is marked assumed with its confidence, because a seam in an image a user was told about is a different thing from one they were not.

THE FAILURE THIS EXISTS TO AVOID is the one the whole import module was written against: an answer that looks plausible and is wrong. A mosaic assembled at the wrong overlap is a field with a duplicated band through it, every measurement in that band counted twice, and nothing on screen saying so. So the confidence travels with the result, and a caller that cares can refuse it.

Classes

Mosaic

A plan for one field: where every tile goes, and how that was decided.

Placement

Where one tile goes in the mosaic.

Functions

arrangement_of(→ Tuple[int, int])

(row, col) for the index-th tile under one arrangement.

grid_shape(→ Tuple[int, int])

(rows, cols) for count tiles, as square as it can be.

plan_mosaic(→ Optional[Mosaic])

Work out where each tile of one field goes. Reads; writes nothing.

read_stage_positions(→ Optional[List[Tuple[float, float]]])

(y, x) in PIXELS for each path, or None when any file lacks them.

stitch_tiles(paths[, tiles, mosaic])

The tiles of one field, assembled into one array.

Module Contents

class spacr.image_stitch.Mosaic[source]

A plan for one field: where every tile goes, and how that was decided.

Parameters:
  • placements – one per tile, in tile order.

  • height – mosaic height in pixels.

  • width – mosaic width in pixels.

  • rows – rows in the grid.

  • cols – columns in the grid.

  • arrangement – which key of ARRANGEMENTS was used.

  • how – "single" (one tile, nothing to place), "stage", "correlated" or "assumed" – the evidence the placement rests on, in descending order of trust.

  • confidence – mean normalised cross-correlation over the seams, 0 when nothing was correlated.

  • overlap – (y, x) overlap in pixels between neighbours.

describe() → str[source]

One sentence a user can act on.

property is_believed: bool[source]

Whether the placement rests on evidence rather than on a guess.

class spacr.image_stitch.Placement[source]

Where one tile goes in the mosaic.

Parameters:
  • tile – the tile’s own index, as the filename gave it.

  • row – its row in the grid.

  • col – its column in the grid.

  • y – top edge in mosaic pixels.

  • x – left edge in mosaic pixels.

spacr.image_stitch.arrangement_of(index: int, rows: int, cols: int, arrangement: str) → Tuple[int, int][source]

(row, col) for the index-th tile under one arrangement.

Parameters:
  • index – zero-based position of the tile in acquisition order.

  • rows – number of rows in the grid; used by the column-wise arrangements.

  • cols – number of columns in the grid; used by the row-wise arrangements.

  • arrangement – tile order, one of "row_major", "serpentine_rows", "column_major" or "serpentine_columns"; anything else raises ValueError.

spacr.image_stitch.grid_shape(count: int) → Tuple[int, int][source]

(rows, cols) for count tiles, as square as it can be.

THE SHAPE IS A GUESS AND THE ORDER IS NOT. A 6-tile field is 2x3 or 3x2 and nothing in a tile index says which, so the squarest grid is taken and both arrangements are then scored against the pixels – a 3x2 read as 2x3 correlates badly, which is how the wrong one is found.

Parameters:

count – number of tiles in the field; zero or less gives (0, 0), a prime count gives a single row.

spacr.image_stitch.plan_mosaic(paths: Sequence, tiles: Sequence | None = None) → Mosaic | None[source]

Work out where each tile of one field goes. Reads; writes nothing.

Parameters:
  • paths – the tile images, in tile order.

  • tiles – the tile indices, for the record. Defaults to 1..n.

Returns:

the mosaic, or None when the tiles cannot be read at all.

spacr.image_stitch.read_stage_positions(paths: Sequence) → List[Tuple[float, float]] | None[source]

(y, x) in PIXELS for each path, or None when any file lacks them.

ALL OR NOTHING, deliberately. Half a mosaic placed from stage coordinates and half from correlation is two coordinate systems in one image, and the join between them is exactly where nothing checks.

Returns None rather than raising for a file that cannot be read: a missing position is the ordinary case, not an error, and the caller has a second method to fall back to.

Parameters:

paths – tile TIFF paths (str or path-like), each opened with tifffile and read for its OME stage position and pixel size.

spacr.image_stitch.stitch_tiles(paths: Sequence, tiles: Sequence | None = None, mosaic: Mosaic | None = None)[source]

The tiles of one field, assembled into one array.

OVERLAPS ARE AVERAGED, not overwritten. Where two tiles cover the same pixel they are two measurements of it, and taking the last one writes a visible step at every seam that a segmenter reads as an edge. Averaging also keeps the join honest when the placement is slightly off: the seam blurs rather than doubling a structure.

Parameters:
  • paths – the tile images, in tile order.

  • tiles – the tile indices, for the record.

  • mosaic – a plan from plan_mosaic(); computed when omitted.

Returns:

(array, mosaic), or (None, None) when the tiles cannot be read.

Nested helpers

_position_from._first(pattern: str) → float | None

The first number pattern captures in the OME block, or None.

The XML is read with a regular expression rather than parsed because one attribute is wanted from a document whose schema version varies, and a parser that must know the namespace fails on the versions it has not been told about.

spacr/image_stitch.py:246

_sliding_ncc._prefix(values)

Running totals with a leading zero, so p[b] - p[a] is the sum over [a, b) for every window without a loop over windows.

spacr/image_stitch.py:300