Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #34

Merged
merged 8 commits into from
Mar 13, 2024
Merged

Dev #34

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
## [1.0.6] - 2024-03-13

### Added
- Spatial join between shapes (`from sdata.spatial import sjoin`)
- H&E tutorial (basic usage)
- New backend for the MERSCOPE reader (requires `rioxarray`, currently experimental, should use less RAM)

## Changed
- Using `MultiscaleSpatialImage` by default for multiplex imaging technologies

## Fixed
- Issue in report creation when channel names are integers

## [1.0.5] - 2024-03-01

### Changed
Expand Down
4 changes: 4 additions & 0 deletions docs/api/spatial.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
::: sopa.spatial.sjoin
options:
show_root_heading: true

::: sopa.spatial.mean_distance
options:
show_root_heading: true
Expand Down
511 changes: 511 additions & 0 deletions docs/tutorials/he.ipynb

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ nav:
- API usage: tutorials/api_usage.ipynb
- Spatial statistics: tutorials/spatial.ipynb
- Align images (e.g. H&E): tutorials/align.md
- H&E usage: tutorials/he.ipynb
- Advanced segmentation: tutorials/advanced_segmentation.md
- CLI: cli.md
- API:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "sopa"
version = "1.0.5"
version = "1.0.6"
description = "Spatial-omics pipeline and analysis"
documentation = "https://gustaveroussy.github.io/sopa"
homepage = "https://gustaveroussy.github.io/sopa"
Expand Down
2 changes: 1 addition & 1 deletion sopa/embedding/patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def _get_extraction_parameters(
level = 0

if magnification is None:
return level, 1, patch_width, True
return level, 1, patch_width * 2**level, True # TODO: what if scaling != 2?

if tiff_metadata["properties"].get("tiffslide.objective-power"):
objective_power = int(tiff_metadata["properties"].get("tiffslide.objective-power"))
Expand Down
2 changes: 1 addition & 1 deletion sopa/io/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from .imaging import macsima, phenocycler, hyperion, ome_tif
from .explorer import write
from .explorer import write, align
from .standardize import write_standardized
from .transcriptomics import merscope, xenium, cosmx
from .histopathology import wsi, wsi_autoscale
Expand Down
15 changes: 2 additions & 13 deletions sopa/io/explorer/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@
from multiscale_spatial_image import MultiscaleSpatialImage, to_multiscale
from spatial_image import SpatialImage
from spatialdata import SpatialData
from spatialdata.models import Image2DModel
from spatialdata.transformations import Affine
from spatialdata.transformations import Affine, set_transformation
from tqdm import tqdm

from ..._sdata import get_intrinsic_cs, get_spatial_image
from ...utils.image import resize_numpy, scale_dtype
from ..imaging import _default_image_models_kwargs
from ._constants import ExplorerConstants, FileNames, image_metadata
from .utils import explorer_file_path

Expand Down Expand Up @@ -201,7 +199,6 @@ def align(
image: SpatialImage,
transformation_matrix_path: str,
image_key: str = None,
image_models_kwargs: dict | None = None,
overwrite: bool = False,
):
"""Add an image to the `SpatialData` object after alignment with the Xenium Explorer.
Expand All @@ -211,11 +208,9 @@ def align(
image: A `SpatialImage` object. Note that `image.name` is used as the key for the aligned image.
transformation_matrix_path: Path to the `.csv` transformation matrix exported from the Xenium Explorer
image_key: Optional name of the image on which it has been aligned. Required if multiple images in the `SpatialData` object.
image_models_kwargs: Kwargs to the `Image2DModel` model.
overwrite: Whether to overwrite the image, if already existing.
"""
image_name = image.name
image_models_kwargs = _default_image_models_kwargs(image_models_kwargs)

to_pixel = Affine(
np.genfromtxt(transformation_matrix_path, delimiter=","),
Expand All @@ -226,13 +221,7 @@ def align(
default_image = get_spatial_image(sdata, image_key)
pixel_cs = get_intrinsic_cs(sdata, default_image)

image = Image2DModel.parse(
image,
dims=("c", "y", "x"),
transformations={pixel_cs: to_pixel},
c_coords=image.coords["c"].values,
**image_models_kwargs,
)
set_transformation(image, to_pixel, pixel_cs)

log.info(f"Adding image {image_name}:\n{image}")
sdata.add_image(image_name, image, overwrite=overwrite)
9 changes: 8 additions & 1 deletion sopa/io/histopathology.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@
from spatialdata.transformations import Identity, Scale


def wsi(path: str | Path, chunks: tuple[int, int, int] = (3, 256, 256)) -> SpatialData:
def wsi(
path: str | Path, chunks: tuple[int, int, int] = (3, 256, 256), as_image: bool = False
) -> SpatialData:
"""Read a WSI into a `SpatialData` object

Args:
path: Path to the WSI
chunks: Tuple representing the chunksize for the dimensions `(C, Y, X)`.
as_image: If `True`, returns a, image instead of a `SpatialData` object

Returns:
A `SpatialData` object with a multiscale 2D-image of shape `(C, Y, X)`
Expand Down Expand Up @@ -46,6 +49,10 @@ def wsi(path: str | Path, chunks: tuple[int, int, int] = (3, 256, 256)) -> Spati

multiscale_image = MultiscaleSpatialImage.from_dict(images)
multiscale_image.attrs["metadata"] = tiff_metadata
multiscale_image.name = image_name

if as_image:
return multiscale_image

return SpatialData(images={image_name: multiscale_image})

Expand Down
7 changes: 5 additions & 2 deletions sopa/io/imaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,15 @@ def _get_channel_names_macsima(files):
return _deduplicate_names(df_antibodies)


def _default_image_models_kwargs(image_models_kwargs: dict | None):
def _default_image_models_kwargs(image_models_kwargs: dict | None = None):
image_models_kwargs = {} if image_models_kwargs is None else image_models_kwargs

if "chunks" not in image_models_kwargs:
image_models_kwargs["chunks"] = (1, 1024, 1024)

if "scale_factors" not in image_models_kwargs:
image_models_kwargs["scale_factors"] = [2, 2, 2, 2]

return image_models_kwargs


Expand Down Expand Up @@ -268,7 +271,7 @@ def ome_tif(path: Path, as_image: bool = False) -> SpatialImage | SpatialData:
Returns:
A `SpatialImage` or a `SpatialData` object
"""
image_models_kwargs = _default_image_models_kwargs(None)
image_models_kwargs = _default_image_models_kwargs()
image_name = Path(path).absolute().name.split(".")[0]
image: da.Array = imread(path)

Expand Down
2 changes: 1 addition & 1 deletion sopa/io/report/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def channel_section(self):
SubSection(
"Names",
Paragraph(
f"Channels names:<br>{Message(', '.join(list(image.coords['c'].values)))}"
f"Channels names:<br>{Message(', '.join(map(str, list(image.coords['c'].values))))}"
),
)
]
Expand Down
Loading
Loading