spacr.qt.folder_metadata

Folder-structure metadata + auto-generated field ids.

Some datasets don’t carry metadata in the filename — instead the folder tree encodes it (plate1/A01/field_01/ch1.tif). This module handles both:

  • detect_folder_metadata() — walk the tree, propose a folder template that captures plate / well / field / channel.

  • assign_missing_fields() — for datasets that have no filename metadata AND no folder metadata, mint fresh wellID / fieldID / chanID values in a stable order and emit a filename_map.csv linking the original file paths to the canonical spaCR names (<plate>_<well>_F<field>_C<channel>.tif).

The mapping CSV format is intentionally compatible with the one spacr.pipeline_v2 writes so the two flows share downstream tooling.

Classes

FolderTemplate

One inferred folder metadata layout.

NameMapping

One row of the generated filename_map.csv.

Functions

assign_missing_fields(→ List[NameMapping])

Mint synthetic wellID / fieldID / chanID values for

detect_folder_metadata(→ Optional[FolderTemplate])

Walk root, try to recognise a folder-structured layout.

iter_image_files(root[, cap])

Yield the image files under root, from a single recursive walk.

save_filename_map(→ pathlib.Path)

Write mappings to dst as a CSV Excel opens cleanly.

Module Contents

class spacr.qt.folder_metadata.FolderTemplate[source]

One inferred folder metadata layout.

Variables:
  • depth_labels – e.g. ("plate", "well", "field") — describes which subfolder level maps to which spaCR field.

  • sample_paths – a few original files that fit the layout.

  • chan_from_filename – True if the LEAF filename carries the channel id (e.g. ch1.tif).

class spacr.qt.folder_metadata.NameMapping[source]

One row of the generated filename_map.csv.

Parameters:
  • original_path – path of the source image file, as a string.

  • canonical – canonical file name minted for it, <plate>_<well>_F<field>_C<channel>.tif.

  • plate – plate id used in the canonical name.

  • well – well name such as A01.

  • field – field index within the well, starting at 1.

  • channel – channel index, starting at 1.

  • time – time-point index written to the time column.

spacr.qt.folder_metadata.assign_missing_fields(filenames: Sequence[pathlib.Path], plate: str = 'plate1', have_well: bool = False, have_field: bool = False, have_channel: bool = True) → List[NameMapping][source]

Mint synthetic wellID / fieldID / chanID values for filenames that lack them.

Filenames are grouped into “sets” of (well × field) — one entry per file. Ordering is stable (sorted alphabetically) so re-running on the same folder yields the same canonical names.

Parameters:
  • filenames – absolute paths to the source images.

  • plate – plate id used in the canonical name.

  • have_well – True if the caller already knows well ids from elsewhere (folder structure); False to auto-assign A01, A02, …

  • have_field – True if the caller knows field ids.

  • have_channel – True when we can extract channels from the filename or the folder — otherwise every file is treated as channel 1.

Returns:

list of NameMapping, in the same order as filenames after sort.

spacr.qt.folder_metadata.detect_folder_metadata(root: pathlib.Path, max_probe: int = 30, files: Iterable[pathlib.Path] | None = None) → FolderTemplate | None[source]

Walk root, try to recognise a folder-structured layout.

Parameters:
  • root – dropped folder.

  • max_probe – cap on files inspected — the layout should repeat, so no need to walk millions.

  • files – image paths already collected from root, used instead of walking. This is how a caller that needs both a template and a full file list gets them out of ONE traversal: it pulls a probe off iter_image_files(), hands it here, and keeps draining the same generator afterwards. Dropping a 100 000 file plate folder used to walk the tree three times.

Returns:

a FolderTemplate describing the detected layout, or None if we can’t infer one.

spacr.qt.folder_metadata.iter_image_files(root: pathlib.Path, cap: int | None = None)[source]

Yield the image files under root, from a single recursive walk.

Lazy, and that is the whole point. The drop handlers need two things from a dropped folder — a small probe to guess the layout from, and (only if the guess succeeds) the full file list to plan an extraction. Pulling both off one generator means the tree is traversed once; abandoning the generator after the probe means a folder with no layout to detect is never fully walked at all. The previous shape returned lists, and the same tree was walked three times per drop.

Parameters:
  • root – folder to walk.

  • cap – stop after this many image files. None for no cap.

spacr.qt.folder_metadata.save_filename_map(dst: pathlib.Path, mappings: Sequence[NameMapping]) → pathlib.Path[source]

Write mappings to dst as a CSV Excel opens cleanly.

Columns: original_path, canonical, plate, well, field, channel, time.

Parameters:
  • dst – CSV file to write; missing parent folders are created and an existing file is overwritten.

  • mappings – rows to write, one per NameMapping, in the given order after a header row.