Skip to content

Commit 2c8b560

Browse files
committed
Pad images with 0 on right and bottom, if images differ in dimensions
1 parent 28eacc9 commit 2c8b560

3 files changed

Lines changed: 94 additions & 7 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,4 @@ data/
4444
tests/data
4545
uv.lock
4646
.asv/
47+
.venv/

src/spatialdata_io/readers/macsima.py

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -125,12 +125,11 @@ def from_paths(
125125
),
126126
)
127127
imgs = [imread(img, **imread_kwargs) for img in valid_files]
128-
for img, path in zip(imgs, valid_files, strict=True):
129-
if img.shape[1:] != imgs[0].shape[1:]:
130-
raise ValueError(
131-
f"Images are not all the same size. Image {path} has shape {img.shape[1:]} while the first image "
132-
f"{valid_files[0]} has shape {imgs[0].shape[1:]}"
133-
)
128+
129+
# Pad images to same dimensions if necessary
130+
if cls._check_for_differing_xy_dimensions(imgs):
131+
imgs = cls._pad_images(imgs)
132+
134133
# create MultiChannelImage object with imgs and metadata
135134
output = cls(data=imgs, metadata=channel_metadata)
136135
return output
@@ -220,6 +219,56 @@ def calc_scale_factors(self, default_scale_factor: int = 2) -> list[int]:
220219
def get_stack(self) -> da.Array:
221220
return da.stack(self.data, axis=0).squeeze(axis=1)
222221

222+
@staticmethod
223+
def _check_for_differing_xy_dimensions(imgs: list[da.Array]) -> bool:
224+
"""Checks whether any of the images have differing extent in dimensions X and Y."""
225+
# Shape has order CYX
226+
dims_x = [x.shape[2] for x in imgs]
227+
dims_y = [x.shape[1] for x in imgs]
228+
229+
dims_x_different = False if len(set(dims_x)) == 1 else True
230+
dims_y_different = False if len(set(dims_y)) == 1 else True
231+
232+
different_dimensions = any([dims_x_different, dims_y_different])
233+
234+
warnings.warn(
235+
"Supplied images have different dimensions!",
236+
UserWarning,
237+
stacklevel=2,
238+
)
239+
240+
return different_dimensions
241+
242+
@staticmethod
243+
def _pad_images(imgs: list[da.Array]) -> list[da.Array]:
244+
"""Pad all images to the same dimensions in X and Y with 0s."""
245+
dims_x_max = max([x.shape[2] for x in imgs])
246+
dims_y_max = max([x.shape[1] for x in imgs])
247+
248+
warnings.warn(
249+
f"Padding images with 0s to same size of ({dims_y_max}, {dims_x_max})",
250+
UserWarning,
251+
stacklevel=2,
252+
)
253+
254+
padded_imgs = []
255+
for img in imgs:
256+
pad_y = dims_y_max - img.shape[1]
257+
pad_x = dims_x_max - img.shape[2]
258+
# Only pad if necessary
259+
if (pad_y, pad_y) != (0, 0):
260+
# Always pad to the right/bottom
261+
pad_width = (
262+
(0, 0),
263+
(0, pad_y),
264+
(0, pad_x),
265+
)
266+
267+
img = da.pad(img, pad_width, mode="constant", constant_values=0)
268+
padded_imgs.append(img)
269+
270+
return padded_imgs
271+
223272

224273
def macsima(
225274
path: str | Path,

tests/test_macsima.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,51 @@ def test_exception_on_no_valid_files(tmp_path: Path) -> None:
9393
# Write a tiff file without metadata
9494
height = 10
9595
width = 10
96-
arr = np.zeros((height, width, 1), dtype=np.uint16)
96+
arr = np.zeros((1, height, width), dtype=np.uint16)
9797
path_no_metadata = Path(tmp_path) / "tiff_no_metadata.tiff"
9898
imwrite(path_no_metadata, arr, metadata=None, description=None, software=None, datetime=None)
9999

100100
with pytest.raises(ValueError, match="No valid files were found"):
101101
macsima(tmp_path)
102102

103103

104+
@pytest.mark.parametrize(
105+
"dimensions,expected",
106+
[
107+
(((10, 10), (10, 10)), False),
108+
(((10, 10), (15, 10)), True),
109+
(((10, 10), (10, 15)), True),
110+
(((15, 10), (10, 15)), True),
111+
],
112+
)
113+
def test_check_differing_dimensions_works(dimensions: tuple[tuple[int, int], tuple[int, int]], expected: bool) -> None:
114+
imgs = []
115+
for img_dim in dimensions:
116+
arr = da.from_array(np.ones((1, img_dim[0], img_dim[1]), dtype=np.uint16))
117+
imgs.append(arr)
118+
119+
if expected:
120+
with pytest.warns(UserWarning, match="Supplied images have different dimensions!"):
121+
assert MultiChannelImage._check_for_differing_xy_dimensions(imgs) == expected
122+
else:
123+
assert MultiChannelImage._check_for_differing_xy_dimensions(imgs) == expected
124+
125+
126+
def test_padding_on_differing_dimensions() -> None:
127+
heights = [10, 10, 15, 20]
128+
widths = [10, 15, 10, 20]
129+
130+
imgs = []
131+
for height, width in zip(heights, widths, strict=True):
132+
arr = da.from_array(np.ones((1, height, width), dtype=np.uint16))
133+
imgs.append(arr)
134+
135+
with pytest.warns(UserWarning, match="Padding images with 0s to same size of \\(20, 20\\)"):
136+
imgs_padded = MultiChannelImage._pad_images(imgs)
137+
for img in imgs_padded:
138+
assert img.shape == (1, 20, 20)
139+
140+
104141
@skip_if_below_python_version()
105142
@pytest.mark.parametrize(
106143
"dataset,expected",

0 commit comments

Comments
 (0)