API Reference¶
Thyra's Python API centres on two functions: convert_msi, which does the
work, and preview_msi, which tells you what an input is without converting
it. For most use cases those are all you need. The remaining sections document
configuration types, metadata objects, and base classes for advanced users who
want to inspect results or extend Thyra with new formats.
Converting Data¶
The primary entry point. Detects the input format, reads metadata, and writes a SpatialData/Zarr directory.
Basic usage¶
from thyra import convert_msi
# Minimal -- auto-detects format, pixel size, and streaming
success = convert_msi("input.imzML", "output.zarr")
# With explicit parameters
success = convert_msi(
"data/experiment.d",
"output/experiment.zarr",
dataset_id="hippocampus",
pixel_size_um=10.0,
)
The Python API does not resample by default
resampling_config defaults to None, and nothing builds one for you, so
the call above keeps the original mass axis. The thyra command-line tool
is the opposite -- it resamples unless you pass --no-resample, because
the default is applied in the CLI layer rather than in convert_msi. Pass
a resampling_config explicitly to get the CLI's behaviour from Python.
With resampling configuration¶
success = convert_msi(
"input.imzML",
"output.zarr",
resampling_config={
"method": "nearest_neighbor",
"axis_type": "orbitrap",
"target_bins": 50000,
},
)
Multi-region dataset (select one region)¶
region takes either a .mis Area Name or a DB RegionNumber. A string is
matched against the area names first and only parsed as a number if no name
matches, so the two forms below are not interchangeable -- area '03' need not
be RegionNumber 3.
# By the name flexImaging gives the area
success = convert_msi(
"data/slide.d",
"output/tissue_only.zarr",
region="03",
)
# By the database's own region number, which starts at 0
success = convert_msi(
"data/slide.d",
"output/tissue_only.zarr",
region=0,
)
Thyra logs the RegionNumber-to-Area-Name mapping at INFO when it opens a
multi-region dataset. See --region for the detail.
Force streaming for large datasets¶
Full signature¶
convert_msi(input_path: Union[str, Path], output_path: Union[str, Path], format_type: str = 'spatialdata', dataset_id: str = 'msi_dataset', pixel_size_um: Optional[float] = None, handle_3d: bool = False, z_spacing_um: Optional[float] = None, resampling_config: Optional[Dict[str, Any]] = None, reader_options: Optional[Dict[str, Any]] = None, sparse_format: str = 'csc', include_optical: bool = True, apply_optical_alignment: bool = True, streaming: Union[bool, Literal['auto']] = 'auto', region: Optional[Union[int, str]] = None, **kwargs: Any) -> bool
¶
Convert MSI data to the specified format.
Provides automatic pixel size detection from metadata or accepts user-specified values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_path
|
Union[str, Path]
|
Path to input MSI data file or directory |
required |
output_path
|
Union[str, Path]
|
Path for output file |
required |
format_type
|
str
|
Output format type (default: "spatialdata") |
'spatialdata'
|
dataset_id
|
str
|
Identifier for the dataset |
'msi_dataset'
|
pixel_size_um
|
Optional[float]
|
In-plane pixel size in micrometers (None for auto) |
None
|
handle_3d
|
bool
|
Whether to process as 3D data (default: False) |
False
|
z_spacing_um
|
Optional[float]
|
Distance between consecutive slices in micrometers.
Only meaningful together with |
None
|
resampling_config
|
Optional[Dict[str, Any]]
|
Optional resampling configuration |
None
|
reader_options
|
Optional[Dict[str, Any]]
|
Optional format-specific reader options: - intensity_threshold: float - Minimum intensity to include. Default: None (no filtering). - use_recalibrated_state: bool - For Bruker data, use active/recalibrated calibration (default True). - max_mass_axis_length: int - For processed-mode imzML converted with --no-resample, give up once the raw mass axis exceeds this many unique m/z values. This matters when the peak lists share no m/z values, where the raw axis grows to roughly one column per peak in the whole dataset. Default: 10,000,000, which is the limit SCiLS Lab places on the same quantity (2026b User Guide, p.76). Pass None for no limit. - spectrum_type: str - For imzML, declare the spectrum representation explicitly: 'profile' or 'centroid' (the full CV names are accepted too). Outranks the file's own MS:1000127/MS:1000128, so it can correct a file that declares the wrong thing; contradicting a declaration is logged as a warning. SCiLS Lab spells this --rep_type (2026b User Guide, p.81). Default: None, meaning detect. Changes stored values for files where it disagrees with what detection would have chosen, because the representation feeds instrument and axis-type selection. |
None
|
sparse_format
|
str
|
Sparse matrix format ('csc' or 'csr') |
'csc'
|
include_optical
|
bool
|
Include optical images (default: True) |
True
|
apply_optical_alignment
|
bool
|
If True (default) and the MSI source
carries FlexImaging Area metadata, MSI elements are placed
in optical-image pixel space at |
True
|
streaming
|
Union[bool, Literal['auto']]
|
Use streaming converter for large datasets. - "auto": Auto-detect based on dataset size >10GB (default) - True: Force streaming converter - False: Force standard converter |
'auto'
|
region
|
Optional[Union[int, str]]
|
For multi-region datasets (e.g. Bruker timsTOF), select a specific region. Accepts an int (DB RegionNumber) or a str (matched against .mis Area Name, falling back to integer parse). None (default) converts all regions. Passed to the reader as reader_options["region"]. |
None
|
**kwargs
|
Any
|
Additional keyword arguments |
{}
|
Returns:
| Type | Description |
|---|---|
bool
|
True if conversion was successful, False otherwise |
Previewing an Input¶
preview_msi answers "what is this file?" without converting it. It detects
the format, builds the reader, and reads metadata only -- no spectra are
decoded and no store is written -- so it is cheap enough to call while a user
waits. It is the entry point behind the Ousia import wizard's per-sample
preview card.
Two properties make it usable directly from UI code:
- It never raises. Any failure -- a path that does not exist, an
unrecognised format, a truncated file -- comes back as an
MsiPreviewwithreadable=Falseanderrorset to the message. Checkreadablebefore reading the numeric fields; they hold zeroes when it isFalse. - It is fast. The design budget is under 500 ms for inputs up to about 50 GB, because it never touches the spectra.
from pathlib import Path
from thyra import preview_msi
p = preview_msi(Path("example_data/synthetic_brain.imzML"))
if p.readable:
print(p.grid_dims) # (48, 36)
print(p.n_pixels) # 1728
print(p.mz_range) # (250.0, 1200.0)
print(p.pixel_size_um) # 25.0
print(p.instrument_type) # AxisType.CONSTANT
else:
print("cannot read:", p.error)
Failure looks the same shape, which is the point:
p = preview_msi(Path("no/such/file.imzML"))
print(p.readable, p.error)
# False Path does not exist: no\such\file.imzML
instrument_type is the AxisType the resampling decision tree would pick for
this input, so a caller can show the default before the user commits to it.
has_escdat_folder reports whether <path>/EscDat/ exists, which is how a
downstream tool decides whether EscDat-derived registration is available.
preview_msi(path: Path) -> MsiPreview
¶
Return a metadata-only snapshot of an MSI input.
This is the entry point used by the Ousia Import Wizard to drive
the per-sample preview card in step 2. It detects the format,
constructs the appropriate reader, and calls
:meth:BaseMSIReader.get_essential_metadata (plus
:meth:get_comprehensive_metadata for the instrument-type guess).
No spectra are decoded.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Filesystem path to an |
required |
Returns:
| Type | Description |
|---|---|
MsiPreview
|
class: |
MsiPreview
|
attr: |
Example
from thyra import preview_msi p = preview_msi(Path("slice_01.imzML")) if p.readable: ... print(p.grid_dims, p.mz_range)
MsiPreview(mz_range: Tuple[float, float], n_pixels: int, grid_dims: Tuple[int, int], instrument_type: Optional[AxisType], pixel_size_um: Optional[float], has_escdat_folder: bool, readable: bool, error: Optional[str] = None, resampling_method: Optional[ResamplingMethod] = None)
dataclass
¶
Metadata-only snapshot of an MSI input.
Attributes:
| Name | Type | Description |
|---|---|---|
mz_range |
Tuple[float, float]
|
|
n_pixels |
int
|
Total number of spectra (pixels) in the dataset.
|
grid_dims |
Tuple[int, int]
|
Grid dimensions as |
instrument_type |
Optional[AxisType]
|
The :class: |
pixel_size_um |
Optional[float]
|
Pixel pitch in micrometres. Thyra stores a
|
has_escdat_folder |
bool
|
|
readable |
bool
|
|
error |
Optional[str]
|
The exception's |
resampling_method |
Optional[ResamplingMethod]
|
The :class: |
Resampling Configuration¶
When you pass resampling_config to convert_msi, the dictionary keys map
to the fields of ResamplingConfig. You can pass a plain dict (as shown in
the examples above) or construct the dataclass directly:
from thyra.resampling.types import ResamplingConfig, ResamplingMethod, AxisType
config = ResamplingConfig(
method=ResamplingMethod.TIC_PRESERVING,
axis_type=AxisType.ORBITRAP,
target_bins=50000,
)
success = convert_msi("input.imzML", "output.zarr", resampling_config=config)
ResamplingConfig(method: Optional[ResamplingMethod] = None, axis_type: Optional[AxisType] = None, target_bins: Optional[int] = None, mass_width_da: Optional[float] = None, reference_mz: float = DEFAULT_REFERENCE_MZ, min_mz: Optional[float] = None, max_mz: Optional[float] = None, gap_tolerance_da: Optional[float] = None)
dataclass
¶
Configuration for resampling operations.
All fields default to None (auto-detect from instrument metadata).
You can override individual fields while leaving the rest automatic.
Attributes:
| Name | Type | Description |
|---|---|---|
method |
Optional[ResamplingMethod]
|
Resampling algorithm. |
axis_type |
Optional[AxisType]
|
Mass axis spacing model. |
target_bins |
Optional[int]
|
Number of bins in the resampled axis. |
mass_width_da |
Optional[float]
|
Bin width in Daltons at |
reference_mz |
float
|
Reference m/z for |
min_mz |
Optional[float]
|
Override the lower bound of the mass range. |
max_mz |
Optional[float]
|
Override the upper bound of the mass range. |
gap_tolerance_da |
Optional[float]
|
How far, in Daltons, a target bin may sit from the
nearest source m/z before |
ResamplingMethod
¶
Bases: Enum
Available resampling methods.
Attributes:
| Name | Type | Description |
|---|---|---|
NONE |
No resampling -- keep the original mass axis. |
|
NEAREST_NEIGHBOR |
Snap each peak to the nearest target bin. |
|
TIC_PRESERVING |
Redistribute intensity so the total ion count is preserved after rebinning (recommended for quantitative work). |
AxisType
¶
Bases: Enum
Mass axis spacing model, determined by the analyser physics.
The axis type controls how target bins are distributed across the
mass range. When set to None in :class:ResamplingConfig, the
type is auto-detected from instrument metadata.
Attributes:
| Name | Type | Description |
|---|---|---|
CONSTANT |
Equidistant spacing (constant Da per bin). |
|
LINEAR_TOF |
Linear TOF -- spacing proportional to
|
|
REFLECTOR_TOF |
Reflector TOF -- spacing proportional to |
|
ORBITRAP |
Orbitrap -- spacing proportional to |
|
FTICR |
FTICR -- spacing proportional to |
|
UNKNOWN |
Unknown analyser; falls back to constant spacing. |
CONSTANT = 'constant'
class-attribute
instance-attribute
¶
FTICR = 'fticr'
class-attribute
instance-attribute
¶
LINEAR_TOF = 'linear_tof'
class-attribute
instance-attribute
¶
ORBITRAP = 'orbitrap'
class-attribute
instance-attribute
¶
REFLECTOR_TOF = 'reflector_tof'
class-attribute
instance-attribute
¶
UNKNOWN = 'unknown'
class-attribute
instance-attribute
¶
Metadata Types¶
Readers expose metadata through two dataclasses. EssentialMetadata contains
everything needed for conversion decisions (grid size, mass range, memory
estimate). ComprehensiveMetadata wraps essential metadata and adds
vendor-specific details for provenance and QC.
from thyra.readers.imzml import ImzMLReader
with ImzMLReader("sample.imzML") as reader:
meta = reader.get_essential_metadata()
print(f"Grid: {meta.dimensions}")
print(f"m/z range: {meta.mass_range}")
print(f"Spectra: {meta.n_spectra}")
print(f"Est. memory: {meta.estimated_memory_gb:.1f} GB")
EssentialMetadata(dimensions: Tuple[int, int, int], coordinate_bounds: Tuple[float, float, float, float], mass_range: Tuple[float, float], pixel_size: Optional[Tuple[float, float]], n_spectra: int, total_peaks: int, estimated_memory_gb: float, source_path: str, coordinate_offsets: Optional[Tuple[int, int, int]] = None, spectrum_type: Optional[str] = None, peak_counts_per_pixel: Optional[NDArray[np.int32]] = None, z_spacing_um: Optional[float] = None)
dataclass
¶
Critical metadata for processing decisions and interpolation setup.
Attributes:
| Name | Type | Description |
|---|---|---|
dimensions |
Tuple[int, int, int]
|
Grid dimensions as |
coordinate_bounds |
Tuple[float, float, float, float]
|
Spatial extent as |
mass_range |
Tuple[float, float]
|
Mass-to-charge range as |
pixel_size |
Optional[Tuple[float, float]]
|
In-plane pixel dimensions as |
n_spectra |
int
|
Total number of spectra in the dataset. |
total_peaks |
int
|
Total number of peaks across all spectra (used for sparse matrix pre-allocation). |
estimated_memory_gb |
float
|
Estimated dense memory footprint in GB. |
source_path |
str
|
Absolute path to the source data. |
coordinate_offsets |
Optional[Tuple[int, int, int]]
|
Raw coordinate offsets |
spectrum_type |
Optional[str]
|
Spectrum type string (e.g. |
peak_counts_per_pixel |
Optional[NDArray[int32]]
|
Per-pixel peak counts for CSR |
z_spacing_um |
Optional[float]
|
Distance between consecutive slices in micrometres,
or |
ComprehensiveMetadata(essential: EssentialMetadata, format_specific: Dict[str, Any], acquisition_params: Dict[str, Any], instrument_info: Dict[str, Any], raw_metadata: Dict[str, Any])
dataclass
¶
Complete metadata including format-specific details.
Wraps :class:EssentialMetadata and adds vendor-specific information
that is not needed for conversion but useful for provenance and QC.
Attributes:
| Name | Type | Description |
|---|---|---|
essential |
EssentialMetadata
|
Core metadata required for conversion. |
format_specific |
Dict[str, Any]
|
Vendor-specific metadata (e.g. ImzML CV params, Bruker property tables). |
acquisition_params |
Dict[str, Any]
|
Acquisition parameters such as polarity, scan range, and laser settings. |
instrument_info |
Dict[str, Any]
|
Instrument model, serial number, and software version. |
raw_metadata |
Dict[str, Any]
|
Unprocessed metadata exactly as read from the source file, preserved for round-trip fidelity. |
coordinate_bounds: Tuple[float, float, float, float]
property
¶
Convenience access to coordinate bounds from essential metadata.
dimensions: Tuple[int, int, int]
property
¶
Convenience access to dimensions from essential metadata.
pixel_size: Optional[Tuple[float, float]]
property
¶
Convenience access to pixel size from essential metadata.
Metadata Schema¶
The versioned, ontology-mapped uns["msi_metadata"] block every store
carries. See Metadata Schema for the storage
contract, the CLI (thyra validate, thyra export-metaspace), and the
versioning rules.
from thyra.metadata.schema import (
read_msi_metadata_blocks,
to_metaspace,
validate_document,
)
blocks = read_msi_metadata_blocks("output.zarr")
meta, issues = validate_document(blocks["msi_dataset_z0"])
submission, warnings = to_metaspace(meta)
MSIMetadata
¶
Bases: _SchemaModel
The versioned MSI metadata document.
sample and preparation cannot be auto-populated from raw
files and default to empty; ms_analysis and provenance are
written by the converter for every store.
to_uns_dict() -> Dict[str, Any]
¶
Serialise for storage in table.uns.
None fields are dropped, and the sample / preparation
/ processing sections are omitted entirely when empty --
following the store convention that a section the source has
nothing for is omitted rather than written empty, so consumers
can tell "not available" from "available and empty".
processing is stored as a JSON string: it is a list of
objects, which AnnData/zarr cannot round-trip (the same reason
uns["regions"] is JSON). read_msi_metadata_blocks and
validate_document both decode it transparently.
validate_document(doc: Any) -> Tuple[Optional[MSIMetadata], List[ValidationIssue]]
¶
Validate one metadata document.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
doc
|
Any
|
The parsed document (normally a dict read from a store's
|
required |
Returns:
| Type | Description |
|---|---|
Optional[MSIMetadata]
|
|
List[ValidationIssue]
|
validation failed; issues carry everything found, errors first |
Tuple[Optional[MSIMetadata], List[ValidationIssue]]
|
is not guaranteed -- filter on |
read_msi_metadata_blocks(store_path: Union[str, Path]) -> Dict[str, Dict[str, Any]]
¶
Read every table's msi_metadata block from a SpatialData store.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
store_path
|
Union[str, Path]
|
Path to a converted |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Dict[str, Any]]
|
Mapping of table name to the block as a plain dict. Tables |
Dict[str, Dict[str, Any]]
|
without a block are skipped; a store written by a Thyra version |
Dict[str, Dict[str, Any]]
|
that predates the schema therefore returns an empty mapping. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the path is not a SpatialData store (no
|
to_metaspace(meta: MSIMetadata) -> Tuple[Dict[str, Any], List[str]]
¶
Render the METASPACE submission metadata document.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
meta
|
MSIMetadata
|
A validated MSI metadata document. |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
|
List[str]
|
one warning per required field that had to be left empty. |
build_msi_metadata(comprehensive: Optional[ComprehensiveMetadata], *, pixel_size_um: Tuple[float, float], pixel_size_source: Optional[str] = None, source_format: Optional[str] = None, processing: Optional[List[ProcessingStep]] = None) -> MSIMetadata
¶
Build an :class:MSIMetadata document from extracted metadata.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
comprehensive
|
Optional[ComprehensiveMetadata]
|
The reader's comprehensive metadata, or |
required |
pixel_size_um
|
Tuple[float, float]
|
Resolved in-plane pixel pitch |
required |
pixel_size_source
|
Optional[str]
|
How the pixel size was determined
( |
None
|
source_format
|
Optional[str]
|
Detected input format name ( |
None
|
processing
|
Optional[List[ProcessingStep]]
|
Ordered processing steps performed so far, oldest
first (see :class: |
None
|
Returns:
| Type | Description |
|---|---|
MSIMetadata
|
The populated document. Fields the source does not report are |
MSIMetadata
|
left unset. |
Reader Base Class¶
All format readers (ImzML, Bruker, Waters, PHI) inherit from this base class.
If you are writing a custom reader for a new format, subclass BaseMSIReader
and implement the abstract methods below. See
Supported Formats for which optional methods are worth
implementing and what each one buys you.
BaseMSIReader(data_path: Path, intensity_threshold: Optional[float] = None, **kwargs: object)
¶
Bases: ABC
Abstract base class for reading MSI data formats.
Initialize the reader with the path to the data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data_path
|
Path
|
Path to the data file or directory |
required |
intensity_threshold
|
Optional[float]
|
Minimum intensity value to include. Values below this threshold are filtered out during iteration. Useful for removing detector noise in continuous mode data. Default: None (no filtering, include all values). |
None
|
**kwargs
|
object
|
Additional reader-specific parameters |
{}
|
has_shared_mass_axis: bool
property
¶
Check if all spectra share the same m/z axis.
For continuous ImzML data, all spectra have identical m/z values, so get_common_mass_axis() only needs to read the first spectrum. For processed/centroid data, each spectrum may have different m/z values, requiring iteration through all spectra.
Returns:
| Type | Description |
|---|---|
bool
|
True if all spectra share the same m/z axis (continuous mode), |
bool
|
False if each spectrum has different m/z values (processed mode). |
get_essential_metadata() -> EssentialMetadata
¶
Get essential metadata for processing.
get_comprehensive_metadata() -> ComprehensiveMetadata
¶
Get complete metadata.
get_common_mass_axis() -> NDArray[np.float64]
abstractmethod
¶
Return the common mass axis for all spectra.
This method must always return a valid array. If no common mass axis can be created, implementations should raise an exception.
get_mass_axis_annotations() -> Optional[dict]
¶
Get extra per-channel columns to store alongside the m/z axis.
Returns a mapping of column name to an array with one entry per
entry of :meth:get_common_mass_axis. These are written into the
table's var next to mz.
This exists so a format whose native axis is not m/z can keep that axis in the output. Time-of-flight instruments measure flight time and derive m/z from a calibration, so storing the flight time makes the conversion reversible without the reader: a later recalibration can be applied to the stored times directly.
Annotations are dropped if their length does not match the axis the converter actually writes, which is what happens when resampling is enabled and the axis is rebuilt.
Returns:
| Type | Description |
|---|---|
Optional[dict]
|
Mapping of column name to per-channel values, or None. |
get_optical_image_paths() -> List[Path]
¶
Get paths to optical/microscopy images associated with this data.
Returns list of TIFF file paths that contain optical images of the sample. These images can be stored alongside MSI data in SpatialData output for multimodal analysis.
Default implementation returns empty list. Subclasses should override to return paths to optical images specific to their format.
Returns:
| Type | Description |
|---|---|
List[Path]
|
List of paths to TIFF files, empty if no optical images available. |
get_peak_counts_per_pixel() -> Optional[NDArray[np.int32]]
¶
Get per-pixel peak counts for CSR indptr construction.
This method enables optimized streaming conversion by providing pre-computed peak counts, avoiding the need for a separate counting pass.
Returns:
| Type | Description |
|---|---|
Optional[NDArray[int32]]
|
Array of size n_pixels where arr[pixel_idx] = peak_count. |
Optional[NDArray[int32]]
|
pixel_idx = z * (n_x * n_y) + y * n_x + x |
Optional[NDArray[int32]]
|
Returns None if not supported/available for this reader. |
Note
Override in subclass to enable optimized streaming conversion. The default implementation returns None, which causes the streaming converter to fall back to a two-pass approach.
Warning
When intensity_threshold is set, the actual peak counts after filtering may be lower than the values returned here, since this method typically returns pre-computed counts from metadata that don't account for intensity filtering. The streaming converter handles this gracefully by using a two-pass approach.
iter_spectra(batch_size: Optional[int] = None) -> Generator[Tuple[Tuple[int, int, int], NDArray[np.float64], NDArray[np.float64]], None, None]
abstractmethod
¶
Iterate through spectra with optional batch processing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch_size
|
Optional[int]
|
Optional batch size for spectrum iteration |
None
|
Yields:
| Type | Description |
|---|---|
Tuple[Tuple[int, int, int], NDArray[float64], NDArray[float64]]
|
Tuple containing:
|
Note
Subclasses should apply intensity threshold filtering by calling _apply_intensity_filter() on the intensities before yielding.
get_region_map() -> Optional[dict]
¶
Get per-pixel region mapping for multi-region datasets.
Returns a dictionary mapping normalized (0-based) (x, y) coordinate tuples to integer region numbers. This enables the converter to annotate each pixel with its acquisition region in obs["region_number"].
Default implementation returns None (single-region or no region info). Subclasses should override when region information is available.
Returns:
| Type | Description |
|---|---|
Optional[dict]
|
Dict mapping (x, y) tuples to region numbers, or None if |
Optional[dict]
|
region information is not available. |
get_region_info() -> Optional[list]
¶
Get summary information about acquisition regions.
Returns a list of dictionaries, each describing one region with at minimum: {"region_number": int, "n_spectra": int}. Additional keys (e.g. "name") are format-specific and optional.
Default implementation returns None (single-region or no region info). Subclasses should override when region information is available.
Returns:
| Type | Description |
|---|---|
Optional[list]
|
List of region summary dicts, or None if region information |
Optional[list]
|
is not available. |
close() -> None
abstractmethod
¶
Close all open file handles.
Converter Base Class¶
All output converters inherit from this base class. Currently only
SpatialData output is supported, but the architecture allows adding new output
formats by subclassing BaseMSIConverter.
BaseMSIConverter(reader: BaseMSIReader, output_path: Union[str, Path, PathLike[str]], dataset_id: str = 'msi_dataset', pixel_size_um: float = 1.0, pixel_size_source: PixelSizeSource = PixelSizeSource.DEFAULT, compression_level: int = 5, handle_3d: bool = False, z_spacing_um: Optional[float] = None, **kwargs: Any)
¶
Bases: ABC
Base class for MSI data converters with shared functionality.
Implements common processing steps while allowing format-specific customization.
Initialize the MSI converter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
reader
|
BaseMSIReader
|
MSI data reader instance |
required |
output_path
|
Union[str, Path, PathLike[str]]
|
Path for output file |
required |
dataset_id
|
str
|
Identifier for the dataset |
'msi_dataset'
|
pixel_size_um
|
float
|
In-plane pixel pitch in micrometers |
1.0
|
pixel_size_source
|
PixelSizeSource
|
How pixel size was determined |
DEFAULT
|
compression_level
|
int
|
Compression level for output |
5
|
handle_3d
|
bool
|
Whether to process as 3D data |
False
|
z_spacing_um
|
Optional[float]
|
Distance between consecutive slices in
micrometers. Only meaningful with |
None
|
**kwargs
|
Any
|
Additional keyword arguments |
{}
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Format Detection and Plugin Registry¶
Thyra uses a registry to map file extensions and directory structures to the correct reader and converter classes. The public functions below let you detect formats programmatically or register your own reader/converter.
Detecting a format¶
from pathlib import Path
from thyra.core.registry import detect_format
fmt = detect_format(Path("experiment.imzML")) # "imzml"
fmt = detect_format(Path("data.d")) # "bruker" or "rapiflex"
fmt = detect_format(Path("data.raw")) # "waters"
Registering a custom reader¶
from thyra.core.registry import register_reader
from thyra.core.base_reader import BaseMSIReader
@register_reader("my_format")
class MyFormatReader(BaseMSIReader):
...
detect_format(input_path: Path) -> str
¶
Detect MSI format from input path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_path
|
Path
|
Path to MSI data file or directory |
required |
Returns:
| Type | Description |
|---|---|
str
|
Format name ('imzml', 'bruker', 'rapiflex', 'waters', or 'phi') |
register_reader(format_name: str)
¶
Decorator for reader registration.
register_converter(format_name: str)
¶
Decorator for converter registration.