Skip to content

Commit fe13dd3

Browse files
Merge pull request #276 from scverse/fix/xenium-cleanup
Cleanup Xenium and CosMx readers
2 parents 6afb839 + 4c8c4b0 commit fe13dd3

File tree

2 files changed

+18
-17
lines changed

2 files changed

+18
-17
lines changed

src/spatialdata_io/readers/cosmx.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,17 @@
1515
from dask.dataframe import DataFrame as DaskDataFrame
1616
from dask_image.imread import imread
1717
from scipy.sparse import csr_matrix
18-
19-
# from spatialdata._core.core_utils import xy_cs
2018
from skimage.transform import estimate_transform
2119
from spatialdata import SpatialData
2220
from spatialdata._logging import logger
2321
from spatialdata.models import Image2DModel, Labels2DModel, PointsModel, TableModel
24-
25-
# from spatialdata._core.ngff.ngff_coordinate_system import NgffAxis # , CoordinateSystem
2622
from spatialdata.transformations.transformations import Affine, Identity
2723

2824
from spatialdata_io._constants._constants import CosmxKeys
2925
from spatialdata_io._docs import inject_docs
3026

3127
__all__ = ["cosmx"]
3228

33-
# x_axis = NgffAxis(name="x", type="space", unit="discrete")
34-
# y_axis = NgffAxis(name="y", type="space", unit="discrete")
35-
# c_axis = NgffAxis(name="c", type="channel", unit="index")
36-
3729

3830
@inject_docs(cx=CosmxKeys)
3931
def cosmx(

src/spatialdata_io/readers/xenium.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,6 @@ def xenium(
298298
f"{XeniumKeys.MORPHOLOGY_FOCUS_CHANNEL_IMAGE.value.format(0)} to "
299299
f"{XeniumKeys.MORPHOLOGY_FOCUS_CHANNEL_IMAGE.value.format(len(files) - 1)}, found {files}"
300300
)
301-
# the 'dummy' channel is a temporary workaround, see _get_images() for more details
302301
if len(files) == 1:
303302
channel_names = {
304303
0: XeniumKeys.MORPHOLOGY_FOCUS_CHANNEL_0.value,
@@ -309,7 +308,6 @@ def xenium(
309308
1: XeniumKeys.MORPHOLOGY_FOCUS_CHANNEL_1.value,
310309
2: XeniumKeys.MORPHOLOGY_FOCUS_CHANNEL_2.value,
311310
3: XeniumKeys.MORPHOLOGY_FOCUS_CHANNEL_3.value,
312-
4: "dummy",
313311
}
314312
# this reads the scale 0 for all the 1 or 4 channels (the other files are parsed automatically)
315313
# dask.image.imread will call tifffile.imread which will give a warning saying that reading multi-file
@@ -601,6 +599,8 @@ def xenium_aligned_image(
601599
imread_kwargs: Mapping[str, Any] = MappingProxyType({}),
602600
image_models_kwargs: Mapping[str, Any] = MappingProxyType({}),
603601
dims: tuple[str, ...] | None = None,
602+
rgba: bool = False,
603+
c_coords: list[str] | None = None,
604604
) -> DataTree:
605605
"""
606606
Read an image aligned to a Xenium dataset, with an optional alignment file.
@@ -619,6 +619,13 @@ def xenium_aligned_image(
619619
Example: for an image with shape (1, y, 1, x, 3), use dims=("anystring", "y", "dummy", "x", "c"). Values that
620620
are not "c", "x" or "y" are considered dummy dimensions and will be squeezed (the data must have len 1 for
621621
those axes).
622+
rgba
623+
Interprets the `c` channel as RGBA, by setting the channel names to `r`, `g`, `b` (`a`). When `c_coords` is not
624+
`None`, this argument is ignored.
625+
c_coords
626+
Channel names for the image. By default, the function will try to infer the channel names from the image
627+
shape and name (by detecting if the name suggests that the image is a H&E image). Example: for an RGB image with
628+
shape (3, y, x), use c_coords=["r", "g", "b"].
622629
623630
Returns
624631
-------
@@ -645,10 +652,6 @@ def xenium_aligned_image(
645652
else:
646653
assert len(image.shape) == 3
647654
assert image.shape[0] in [3, 4]
648-
if image.shape[0] == 4:
649-
# as explained before in _get_images(), we need to add a dummy channel until we support 4-channel images as
650-
# non-RGBA images in napari
651-
image = da.concatenate([image, da.zeros_like(image[0:1])], axis=0)
652655
dims = ("c", "y", "x")
653656
else:
654657
logging.info(f"Image has shape {image.shape}, parsing with dims={dims}.")
@@ -667,10 +670,19 @@ def xenium_aligned_image(
667670
alignment = pd.read_csv(alignment_file, header=None).values
668671
transformation = Affine(alignment, input_axes=("x", "y"), output_axes=("x", "y"))
669672

673+
if c_coords is None and (rgba or image_path.name.endswith(XeniumKeys.ALIGNED_HE_IMAGE_SUFFIX)):
674+
c_index = dims.index("c")
675+
n_channels = image.shape[c_index]
676+
if n_channels == 3:
677+
c_coords = ["r", "g", "b"]
678+
elif n_channels == 4:
679+
c_coords = ["r", "g", "b", "a"]
680+
670681
return Image2DModel.parse(
671682
image,
672683
dims=dims,
673684
transformations={"global": transformation},
685+
c_coords=c_coords,
674686
**image_models_kwargs,
675687
)
676688

@@ -790,6 +802,3 @@ def prefix_suffix_uint32_from_cell_id_str(cell_id_str: ArrayLike) -> tuple[Array
790802
cell_id_prefix = [int(x, 16) for x in cell_id_prefix_hex]
791803

792804
return np.array(cell_id_prefix, dtype=np.uint32), np.array(dataset_suffix_int)
793-
794-
795-
##

0 commit comments

Comments
 (0)