Skip to content

Commit 5d537d6

Browse files
Merge pull request #224 from berombau/macsima-reader
add macsima reader
2 parents db91898 + c4f6cf7 commit 5d537d6

File tree

9 files changed

+777
-5
lines changed

9 files changed

+777
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning][].
1111
## incoming release
1212

1313
- (Visium/Visium HD) lowres and hires images now mapped also to the 'global' coordinate system #230
14+
- (Macsima) added support @berombau #224
1415

1516
## [0.1.6] - 2024-11-26
1617

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@ This package contains reader functions to load common spatial omics formats into
2424
- Steinbock (output data)
2525
- STOmics Stereo-seq®
2626
- Vizgen MERSCOPE® (MERFISH)
27+
- MACSima® (MACS® iQ View output)
2728

2829
Note: all mentioned technologies are registered trademarks of their respective companies.
2930

3031
## Known limitations
3132

32-
Contributions for addressing the below limitations are very welcomed.
33+
Contributions for addressing the below limitations are very welcomed.
3334

34-
- Only Stereo-seq 7.x is supported, 8.x is not currently supported. https://github.com/scverse/spatialdata-io/issues/161
35+
- Only Stereo-seq 7.x is supported, 8.x is not currently supported. https://github.com/scverse/spatialdata-io/issues/161
3536

3637
### How to Contribute
3738

@@ -41,7 +42,6 @@ Contributions for addressing the below limitations are very welcomed.
4142

4243
3. **Optional Enhancements**: To facilitate reproducibility and ease of data access, consider adding a folder in the [spatialdata-sandbox](https://github.com/giovp/spatialdata-sandbox) repository. Include a `download.py` and `to_zarr.py` script (refer to examples in the repository) to enable others to reproduce your reader by simply running these scripts sequentially.
4344

44-
4545
## Getting started
4646

4747
Please refer to the [documentation][link-docs]. In particular, the

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ dependencies = [
3434
"pyarrow",
3535
"readfcs",
3636
"tifffile>=2023.8.12",
37+
"ome-types",
3738
]
3839

3940
[project.optional-dependencies]

src/spatialdata_io/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from spatialdata_io.readers.cosmx import cosmx
55
from spatialdata_io.readers.curio import curio
66
from spatialdata_io.readers.dbit import dbit
7+
from spatialdata_io.readers.macsima import macsima
78
from spatialdata_io.readers.mcmicro import mcmicro
89
from spatialdata_io.readers.merscope import merscope
910
from spatialdata_io.readers.seqfish import seqfish
@@ -32,6 +33,7 @@
3233
"xenium_explorer_selection",
3334
"dbit",
3435
"visium_hd",
36+
"macsima",
3537
]
3638

3739
__version__ = version("spatialdata-io")

src/spatialdata_io/readers/_utils/_utils.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
from anndata import AnnData, read_text
99
from h5py import File
10+
from ome_types import from_tiff
11+
from ome_types.model import Pixels, UnitsLength
12+
from spatialdata._logging import logger
1013

1114
from spatialdata_io.readers._utils._read_10x_h5 import _read_10x_h5
1215

@@ -75,3 +78,48 @@ def _initialize_raster_models_kwargs(
7578
if "scale_factors" not in labels_models_kwargs:
7679
labels_models_kwargs["scale_factors"] = [2, 2, 2, 2]
7780
return image_models_kwargs, labels_models_kwargs
81+
82+
83+
def calc_scale_factors(lower_scale_limit: float, min_size: int = 1000, default_scale_factor: int = 2) -> list[int]:
84+
"""Calculate scale factors based on image size to get lowest resolution under min_size pixels."""
85+
# get lowest dimension, ignoring channels
86+
scale_factor: int = default_scale_factor
87+
scale_factors = [scale_factor]
88+
lower_scale_limit /= scale_factor
89+
while lower_scale_limit >= min_size:
90+
# scale_factors are cumulative, so we don't need to do e.g. scale_factor *= 2
91+
scale_factors.append(scale_factor)
92+
lower_scale_limit /= scale_factor
93+
return scale_factors
94+
95+
96+
def parse_channels(path: Path) -> list[str]:
97+
"""Parse channel names from an OME-TIFF file."""
98+
images = from_tiff(path).images
99+
if len(images) > 1:
100+
logger.warning("Found multiple images in OME-TIFF file. Only the first one will be used.")
101+
channels = images[0].pixels.channels
102+
logger.debug(channels)
103+
names = [c.name for c in channels if c.name is not None]
104+
return names
105+
106+
107+
def parse_physical_size(path: Path | None = None, ome_pixels: Pixels | None = None) -> float:
108+
"""Parse physical size from OME-TIFF to micrometer."""
109+
pixels = ome_pixels or from_tiff(path).images[0].pixels
110+
logger.debug(pixels)
111+
if pixels.physical_size_x_unit != pixels.physical_size_y_unit:
112+
logger.error("Physical units for x and y dimensions are not the same.")
113+
raise NotImplementedError
114+
if pixels.physical_size_x != pixels.physical_size_y:
115+
logger.error("Physical sizes for x and y dimensions are not the same.")
116+
raise NotImplementedError
117+
# convert to micrometer if needed
118+
if pixels.physical_size_x_unit == UnitsLength.NANOMETER:
119+
physical_size = pixels.physical_size_x / 1000
120+
elif pixels.physical_size_x_unit == UnitsLength.MICROMETER:
121+
physical_size = pixels.physical_size_x
122+
else:
123+
logger.error(f"Physical unit not recognized: '{pixels.physical_size_x_unit}'.")
124+
raise NotImplementedError
125+
return float(physical_size)

0 commit comments

Comments
 (0)