Skip to content

Commit 395609a

Browse files
committed
Handle some edge cases for files with invalid metadata
Truly skip invalid files. Iterate over all files to parse physical size, until one works.
1 parent a6dc25f commit 395609a

2 files changed

Lines changed: 57 additions & 13 deletions

File tree

src/spatialdata_io/readers/macsima.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ def from_paths(
7878
imread_kwargs: Mapping[str, Any],
7979
skip_rounds: list[int] | None = None,
8080
) -> MultiChannelImage:
81+
valid_files: list[Path] = []
8182
channel_metadata: list[ChannelMetadata] = []
8283
for p in path_files:
8384
try:
@@ -90,6 +91,7 @@ def from_paths(
9091
)
9192
continue
9293

94+
valid_files.append(p)
9395
channel_metadata.append(
9496
ChannelMetadata(
9597
name=metadata["name"],
@@ -103,34 +105,33 @@ def from_paths(
103105
)
104106
)
105107

106-
if len(path_files) != len(channel_metadata):
107-
raise ValueError("Length of path_files and metadata must be the same.")
108-
# if any of round_channels is in skip_rounds, remove that round from the list and from path_files
108+
if not valid_files:
109+
raise ValueError("No valid files were found.")
110+
if len(valid_files) != len(channel_metadata):
111+
raise ValueError("Length of valid files and metadata must be the same.")
112+
# if any of round_channels is in skip_rounds, remove that round from the list and from valid_files
109113
if skip_rounds:
110114
logger.info(f"Skipping cycles: {skip_rounds}")
111-
path_files, channel_metadata = map(
115+
valid_files, channel_metadata = map(
112116
list,
113117
zip(
114118
*[
115119
(p, ch_meta)
116-
for p, ch_meta in zip(path_files, channel_metadata, strict=True)
120+
for p, ch_meta in zip(valid_files, channel_metadata, strict=True)
117121
if ch_meta.cycle not in skip_rounds
118122
],
119123
strict=True,
120124
),
121125
)
122-
imgs = [imread(img, **imread_kwargs) for img in path_files]
123-
for img, path in zip(imgs, path_files, strict=True):
126+
imgs = [imread(img, **imread_kwargs) for img in valid_files]
127+
for img, path in zip(imgs, valid_files, strict=True):
124128
if img.shape[1:] != imgs[0].shape[1:]:
125129
raise ValueError(
126130
f"Images are not all the same size. Image {path} has shape {img.shape[1:]} while the first image "
127-
f"{path_files[0]} has shape {imgs[0].shape[1:]}"
131+
f"{valid_files[0]} has shape {imgs[0].shape[1:]}"
128132
)
129133
# create MultiChannelImage object with imgs and metadata
130-
output = cls(
131-
data=imgs,
132-
metadata=channel_metadata,
133-
)
134+
output = cls(data=imgs, metadata=channel_metadata)
134135
return output
135136

136137
@classmethod
@@ -695,7 +696,16 @@ def create_sdata(
695696

696697
with warnings.catch_warnings():
697698
warnings.simplefilter("ignore")
698-
pixels_to_microns = parse_physical_size(path_files[0])
699+
# Iterate over path files, as it may still contain invalid files
700+
pixels_to_microns = None
701+
for p in path_files:
702+
try:
703+
pixels_to_microns = parse_physical_size(p)
704+
except Exception:
705+
logger.debug(f"Could not parse physical size from {p}. Trying next file.")
706+
continue
707+
if pixels_to_microns is None:
708+
raise ValueError("Could not parse physical size from any file")
699709

700710
image_element = create_image_element(
701711
mci,

tests/test_macsima.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import math
2+
import shutil
23
from copy import deepcopy
34
from pathlib import Path
45
from tempfile import TemporaryDirectory
56
from typing import Any
67

78
import dask.array as da
9+
import numpy as np
810
import pandas as pd
911
import pytest
1012
from click.testing import CliRunner
@@ -19,6 +21,7 @@
1921
)
2022
from spatialdata import read_zarr
2123
from spatialdata.models import get_channel_names
24+
from tifffile import imwrite
2225

2326
from spatialdata_io.__main__ import macsima_wrapper
2427
from spatialdata_io.readers.macsima import (
@@ -67,6 +70,37 @@ def make_ChannelMetadata(
6770
)
6871

6972

73+
def test_images_with_invalid_ome_metadata_are_excluded(tmp_path: Path) -> None:
74+
# Write a tiff file without metadata
75+
# Use same dimensions as OMAP10_small, which we will use as a positive example
76+
height = 77
77+
width = 94
78+
arr = np.zeros((height, width, 1), dtype=np.uint16)
79+
path_no_metadata = Path(tmp_path) / "tiff_no_metadata.tiff"
80+
imwrite(path_no_metadata, arr, metadata=None, description=None, software=None, datetime=None)
81+
82+
# Copy 1 image from OMAP10 small
83+
omap_10_image_path = Path("./data") / "OMAP10_small" / "C-001_S-000_S_APC_R-01_W-C-1_ROI-01_A-CD15_C-VIMC6.tif"
84+
shutil.copy(omap_10_image_path, Path(tmp_path))
85+
86+
sdata = macsima(tmp_path)
87+
el = sdata[list(sdata.images.keys())[0]]
88+
channels = get_channel_names(el)
89+
assert channels == ["CD15"]
90+
91+
92+
def test_exception_on_no_valid_files(tmp_path: Path) -> None:
93+
# Write a tiff file without metadata
94+
height = 10
95+
width = 10
96+
arr = np.zeros((height, width, 1), dtype=np.uint16)
97+
path_no_metadata = Path(tmp_path) / "tiff_no_metadata.tiff"
98+
imwrite(path_no_metadata, arr, metadata=None, description=None, software=None, datetime=None)
99+
100+
with pytest.raises(ValueError, match="No valid files were found"):
101+
macsima(tmp_path)
102+
103+
70104
@skip_if_below_python_version()
71105
@pytest.mark.parametrize(
72106
"dataset,expected",

0 commit comments

Comments
 (0)