spacr.image_import¶
Workflow inputs and outputs¶
Import Images¶
Review field identities, image channels and optional external masks before writing a separate project. Image-only imports still need segmentation before measurement.
Open: Import → Import Images.
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.
Images and label masks — merged/*.npy in the project; channels and integer label planes share each field array.
Label masks — masks/ when retained, or explicitly saved image/mask pairs. Intermediate masks may be removed by cleanup.
After this module
Mask: Use imported image planes and identities; image-only imports still need segmentation.
Infer what the filenames in a folder of images encode, and import the folder.
A fixed list of filename conventions – cellvoyager, cq1, custom
(a user-written regular expression), auto – covers few real layouts: of
the ten acquisition layouts in tests/import_corpus.py, two parse with it
and eight recover nothing. Opera Phenix and ImageXpress are among the eight.
All ten layouts encode the same six facts – plate, well, field, channel, z, t – and differ only in how. So rather than matching a whole filename against one of N templates, this module reads the parts of the name that vary across the folder and infers what each varying part means.
Variation across the folder identifies the axes. A token that is the same
in every file carries no information: L01 in
plate1_A01_T0001F001L01A01Z01C01.tif is a constant for the whole plate
and identifies nothing. A token that takes two values across eight files is
an axis with two positions. Only the folder, not a single filename, shows
which tokens are axes.
Markers identify which axis a varying token is. F001 and C01 both
vary and are not interchangeable; the letter in front names the axis, and
every convention in the corpus uses one. The marker table below is that
vocabulary, and it is short because the conventions mostly share their
letters.
No role is assigned without evidence. A token that varies and carries no
recognisable marker is reported as unplaced, with its values, so the caller
can ask the user. A partly unparseable tree imports what it can and reports
the rest, rather than returning a plausible but wrong answer; the older
consolidate path instead lost images without an error.
Classes¶
A proposed import, shown to the user for checking before anything is written. |
|
Counts and per-file reasons describing what |
|
Axes inferred from a folder's filenames, and the tokens left unresolved. |
|
What one file's own metadata says about the axes it holds. |
|
One position in a filename, and what was seen there across the folder. |
Functions¶
|
Write the spaCR project |
|
The spaCR filename for one resolved image. |
|
Infer which axes the filenames under |
|
Rebuild a plan from a file written by |
|
Propose an import of |
|
Read the page count and the C, Z and T axis sizes declared in |
|
Write |
|
Split a name into |
Module Contents¶
- class spacr.image_import.ImportPlan[source]¶
A proposed import, shown to the user for checking before anything is written.
With
metadata_type, the user cannot see how filenames were parsed until masks come out wrong; a plan makes the proposal visible and correctable before it is acted on. This mirrorsspacr.foreign, whose column mapping is shown on its screen and is editable in place.Nothing here touches the destination.
problems()lists what would stop an import;with_mapping()returns a new plan, resolved in memory, so an edit is applied immediately and can be discarded.- Parameters:
layout – what the names gave.
inside – per relative path, what the file’s own metadata gave.
mapping – user answers for axes inference could not name, as
{token position: {value: index}}– e.g.{0: {"DAPI": 1, "GFP": 2}}for a tree with dye folders.
- columns() List[str][source]¶
filethen every axis anything resolved, counts last.Only axes present in at least one file get a column. A column of empty cells for an axis no file carries would read as “spaCR looked for a Z and found none”, which differs from “this acquisition has no Z”. The tiled tree gains a
tilecolumn and the flat OME one gainsc_count; neither shows the other’s.
- counts() Dict[str, int][source]¶
Return the number of distinct values per axis.
A plate with the wrong number of wells or channels shows up here before anything is imported.
- problems() List[str][source]¶
Return every problem that would make this import wrong, as sentences.
Problems are returned, not raised, and all of them at once, so the user can fix them in one pass rather than one at a time.
- rows(limit: int | None = None) List[List[str]][source]¶
One row of strings per file, cells aligned to
columns().The Qt model draws these rows into a widget and
table()pads them into a monospace block, so the GUI and the text output show the same columns and values.
- table(limit: int = 8) str[source]¶
Return the proposal as a plain-text table, followed by axis counts and problems.
Example filenames are shown beside the fields they were parsed into, because a wrong assignment (field and channel swapped, say) is only visible next to the name it came from.
- with_mapping(mapping: Dict[int, Dict[str, int]]) ImportPlan[source]¶
Return a new plan with
mappingmerged in. Nothing on disk is touched.- Parameters:
mapping – further answers as
{token position: {value: index}}; for each position they are added to, and override, the answers this plan already holds.
- property files: Dict[str, Dict[str, object]][source]¶
Relative path -> every axis known, from all three sources.
Names first, then the file’s own metadata (as
<axis>_countentries), then the user’s mapping, which setschannel– in that order because each is more specific than the last.
- property root: pathlib.Path[source]¶
The folder every path in this plan is relative to.
- Returns:
the root path.
- class spacr.image_import.ImportResult[source]¶
Counts and per-file reasons describing what
apply_import()wrote.- Parameters:
destination – the plate folder written.
written – how many images the project now names.
linked – how many were symlinked rather than copied.
bytes_saved – source bytes not duplicated by linking.
skipped – source paths that were not written, with the reason.
stitched – fields assembled from several tiles.
unverified – written name -> why its tile placement could not be verified. Such a field is still written, and listed here so the user can check it.
- summary() str[source]¶
Return a text report of what was written, what was not, and why.
Each skipped file is listed by name with its reason (the first 20, then a count of the rest), because the fix depends on the reason – a tiled tree needs
tiles_as_fields, an unwritable destination needs a different folder.
- class spacr.image_import.InferredLayout[source]¶
Axes inferred from a folder’s filenames, and the tokens left unresolved.
- Parameters:
root – the folder examined.
per_file – relative path ->
{axis: value}for what was resolved.slots – the token slots, resolved and unresolved alike.
unplaced – axes-shaped tokens that vary but carry no known marker, as
{position: [values]}. The caller shows these to the user instead of guessing their axis.skipped – files whose token shape did not match the majority, so nothing was claimed about them.
sampled – how many files were read to decide.
- class spacr.image_import.InsideFile[source]¶
What one file’s own metadata says about the axes it holds.
- Parameters:
pages – how many pages the file has.
1means a plain 2-D image and nothing below matters.axes – the axis letters the file declares, e.g.
"CYX","ZYX","TYX". Empty when the file declares none.sizes –
{axis: length}for the non-spatial axes only, so{"c": 2}or{"z": 5}.declared – whether the axes came from the file’s metadata.
Falsewithpages > 1means the axis is unknown – a multi-page TIFF with no metadata could be Z, T or C.
- class spacr.image_import.TokenSlot[source]¶
One position in a filename, and what was seen there across the folder.
- Parameters:
index – position among the tokens of a name.
marker – the alphabetic run immediately before it, lower-cased.
values – every value seen at this position, in first-seen order.
axis – the axis it was resolved to, or
""when unplaced.
- spacr.image_import.apply_import(plan: ImportPlan, destination, *, link: bool = True, plate: str = 'plate1', tiles_as_fields: bool = False, stitch_tiles: bool = True) ImportResult[source]¶
Write the spaCR project
plandescribes.Images are symlinked by default.
consolidatecopies every image to rename it, so a 300 GB plate needs 600 GB to import; renaming does not require duplicating bytes. Where a symlink cannot be created the file is copied, and the result reports how many were linked and how many bytes linking saved.A plan with problems is refused. Every problem the plan reports can make the result wrong – an unnamed axis means images that cannot be told apart – so the plan must be fixed or its questions answered first.
- Parameters:
plan – a plan whose
ImportPlan.problems()is empty.destination – the plate folder to create.
link – symlink rather than copy. Falls back to copying per file.
plate – the plate name to write into the filenames.
stitch_tiles –
stitch each field’s tiles into one image for that field. On by default.
The filename convention the core modules read is plate/well/T/field/L/A/Z/channel and has no tile slot, so four tiles of one field would produce four images with one name. A stitched field is one image with one name, so downstream modules do not need a tile axis. See
spacr.image_stitchfor how the placement is computed, and for what it does when the tiles cannot be aligned.tiles_as_fields –
give each tile its own field number instead.
This takes precedence over
stitch_tiles, as the more specific request. Tiles are numbered as fields per well, so anything measured per field then describes a part of one acquired field; for that reason it is off by default.With both off, the first tile written under a field’s name is kept and every later tile with the same name is skipped with a reason, rather than written over it.
- Raises:
ValueError – when the plan still has problems.
- spacr.image_import.canonical_name(entry: Dict[str, object], *, plate: str = 'plate1') str[source]¶
The spaCR filename for one resolved image.
Missing axes take 1 rather than 0: spaCR’s convention is one-based, and a plate whose only timepoint is
T0000reads as a bug in the acquisition rather than as an absence.- Parameters:
entry – one resolved file’s axes, read by the keys
plate,well,t,field,zandchannel; a missing well becomesA01and a missing or zero index becomes 1.plate – the plate name used when
entryhas noplatekey.
- spacr.image_import.infer_layout(root, *, sample: int = 400, extensions: Sequence[str] = ('.tif', '.tiff', '.png', '.jpg', '.jpeg', '.bmp')) InferredLayout[source]¶
Infer which axes the filenames under
rootencode.- Parameters:
root – the folder to inspect.
sample – how many files to read before deciding. Only this many files are read, in sorted order, not the whole tree: a naming convention is visible in a few hundred names, so inspecting a 400-plate archive takes about as long as inspecting one plate.
extensions – which files count as images.
- Returns:
an
InferredLayout. Does not raise for an unrecognised tree; the result then has an emptyper_fileand a populatedunplaced, which the caller is expected to show.
- spacr.image_import.load_plan(path) ImportPlan[source]¶
Rebuild a plan from a file written by
save_plan().A saved plan lets a repeated acquisition be imported without answering the same mapping questions again, and lets an import run from a script or a cluster job without the GUI.
The plan is re-derived from the folder and the saved mapping rather than from the saved per-file table: the folder may have gained images since, and a stale table would import only the files listed when it was saved.
- Parameters:
path – the JSON file written by
save_plan(); itsrootandmappingkeys are read and the rest is ignored.
- spacr.image_import.plan_import(root, *, sample: int = 400, mapping: Dict[int, Dict[str, int]] | None = None, read_files: bool = True) ImportPlan[source]¶
Propose an import of
root. Writes nothing.- Parameters:
root – the folder of images.
sample – how many files to inspect; see
infer_layout().mapping – answers for axes the names cannot name.
read_files – also open each file for its axis metadata. On by default because channels stored inside a file are not visible in its name; turn it off for a faster first look at a large archive.
- spacr.image_import.read_axes_inside(path) InsideFile[source]¶
Read the page count and the C, Z and T axis sizes declared in
path’s own metadata.infer_layoutreads names, and a name does not carry what an acquisition put inside the file: an OME-TIFF holding two channels is one filename, and a Z-stack and a timelapse of the same field can have identical names. Only the file’s metadata distinguishes them.No axis is guessed. A multi-page TIFF with no axis metadata could be Z, T or C, and this returns
declared=Falsewith the page count rather than picking one, so the caller can ask the user.- Parameters:
path – an image file.
- Returns:
an
InsideFile. A file that cannot be opened at all comes back asInsideFile(pages=0)rather than raising, so one truncated file does not stop the inspection of the rest of a folder.
- spacr.image_import.save_plan(plan: ImportPlan, path) pathlib.Path[source]¶
Write
planas JSON thatload_plan()can read back.The root, the mapping and the per-file table are written. The table is there so a person can review the saved plan;
load_plan()re-derives the files from the folder and uses only the root and the mapping – see its docstring for why.- Parameters:
plan – the plan to save.
path – the JSON file to write; missing parent folders are created and an existing file is overwritten.
- Returns:
the path written, so a caller can report it.
- spacr.image_import.tokenise(name: str) List[Tuple[str, str]][source]¶
Split a name into
(kind, text)runs, kind"alpha"or"digit"."plate1_A01_F001"becomes alpha/digit pairs. Separators are dropped: conventions disagree about_versus-versus nothing, and the disagreement carries no information.- Parameters:
name – the file or folder name to split; every character that is not an ASCII letter or a digit is dropped.