Skip to content

Commit

Permalink
Merge pull request #34 from gustaveroussy/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
quentinblampey authored Mar 13, 2024
2 parents 54e5999 + 7797bad commit c4c6797
Show file tree
Hide file tree
Showing 15 changed files with 689 additions and 149 deletions.
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

0 comments on commit c4c6797

Please sign in to comment.