spatialdata-io 0.1.4__tar.gz → 0.1.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/.flake8 +2 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/.github/workflows/test.yaml +3 -3
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/.mypy.ini +1 -1
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/.pre-commit-config.yaml +3 -3
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/.readthedocs.yaml +1 -1
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/CHANGELOG.md +18 -1
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/PKG-INFO +3 -4
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/_version.py +2 -2
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/docs/conf.py +1 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/pyproject.toml +3 -3
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/_constants/_constants.py +4 -3
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/_constants/_enum.py +2 -1
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/_docs.py +2 -1
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/_utils.py +2 -1
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/_utils/_read_10x_h5.py +4 -4
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/_utils/_utils.py +2 -11
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/codex.py +1 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/cosmx.py +3 -2
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/dbit.py +7 -8
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/mcmicro.py +6 -4
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/merscope.py +5 -3
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/steinbock.py +4 -4
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/stereoseq.py +2 -2
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/visium.py +16 -9
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/visium_hd.py +55 -20
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/xenium.py +42 -21
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/tests/test_xenium.py +1 -1
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/.bumpversion.cfg +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/.codecov.yaml +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/.editorconfig +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/.github/codecov.yml +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/.github/workflows/build.yaml +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/.github/workflows/prepare_test_data.yaml +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/.github/workflows/release.yaml +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/.gitignore +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/LICENSE +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/README.md +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/docs/Makefile +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/docs/_static/.gitkeep +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/docs/_templates/.gitkeep +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/docs/_templates/autosummary/class.rst +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/docs/api.md +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/docs/changelog.md +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/docs/contributing.md +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/docs/extensions/.gitkeep +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/docs/extensions/typed_returns.py +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/docs/index.md +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/docs/make.bat +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/docs/references.bib +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/docs/references.md +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/docs/template_usage.md +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/__init__.py +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/_constants/__init__.py +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/converters/__init__.py +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/converters/legacy_anndata.py +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/experimental/__init__.py +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/__init__.py +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/_utils/__init__.py +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/curio.py +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/iss.py +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/seqfish.py +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/tests/__init__.py +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/tests/converters/__init__.py +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/tests/converters/test_legacy_anndata.py +0 -0
- {spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/tests/test_basic.py +0 -0
|
@@ -18,7 +18,7 @@ jobs:
|
|
|
18
18
|
strategy:
|
|
19
19
|
fail-fast: false
|
|
20
20
|
matrix:
|
|
21
|
-
python: ["3.
|
|
21
|
+
python: ["3.10", "3.12"]
|
|
22
22
|
os: [ubuntu-latest]
|
|
23
23
|
|
|
24
24
|
env:
|
|
@@ -52,7 +52,7 @@ jobs:
|
|
|
52
52
|
pip install --pre -e ".[dev,test]"
|
|
53
53
|
|
|
54
54
|
- name: Download artifact of test data
|
|
55
|
-
if: matrix.python == '3.
|
|
55
|
+
if: matrix.python == '3.12'
|
|
56
56
|
uses: dawidd6/action-download-artifact@v2
|
|
57
57
|
with:
|
|
58
58
|
workflow: prepare_test_data.yaml
|
|
@@ -60,7 +60,7 @@ jobs:
|
|
|
60
60
|
path: ./data
|
|
61
61
|
|
|
62
62
|
- name: List the data directory
|
|
63
|
-
if: matrix.python == '3.
|
|
63
|
+
if: matrix.python == '3.12'
|
|
64
64
|
run: |
|
|
65
65
|
ls -l ./data
|
|
66
66
|
pwd
|
|
@@ -2,8 +2,8 @@ fail_fast: false
|
|
|
2
2
|
default_language_version:
|
|
3
3
|
python: python3
|
|
4
4
|
default_stages:
|
|
5
|
-
- commit
|
|
6
|
-
- push
|
|
5
|
+
- pre-commit
|
|
6
|
+
- pre-push
|
|
7
7
|
minimum_pre_commit_version: 2.16.0
|
|
8
8
|
repos:
|
|
9
9
|
- repo: https://github.com/psf/black
|
|
@@ -73,7 +73,7 @@ repos:
|
|
|
73
73
|
rev: v3.17.0
|
|
74
74
|
hooks:
|
|
75
75
|
- id: pyupgrade
|
|
76
|
-
args: [--py3-plus, --
|
|
76
|
+
args: [--py3-plus, --py310-plus, --keep-runtime-typing]
|
|
77
77
|
- repo: local
|
|
78
78
|
hooks:
|
|
79
79
|
- id: forbid-to-commit
|
|
@@ -8,7 +8,24 @@ and this project adheres to [Semantic Versioning][].
|
|
|
8
8
|
[keep a changelog]: https://keepachangelog.com/en/1.0.0/
|
|
9
9
|
[semantic versioning]: https://semver.org/spec/v2.0.0.html
|
|
10
10
|
|
|
11
|
-
## [0.1.
|
|
11
|
+
## [0.1.6] - 2024-11-26
|
|
12
|
+
|
|
13
|
+
- (MERSCOPE) added `feature_key` attribute for points (i.e., the `'gene'` column) #210
|
|
14
|
+
- (Visium HD) get transformation matrices even when only images are parsed #215
|
|
15
|
+
- Support for `xarray.DataTree` (which was moved from `datatree.DataTree`) #232
|
|
16
|
+
|
|
17
|
+
## [0.1.5] - 2024-09-25
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- (Xenium) added `dims` parameter for more control in `xenium_aligned_image()`
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- Passing `rgb=None` to image model parser for both visium and visiumhd, leading to 3-4 channel images being
|
|
26
|
+
interpreted as RGB(A)
|
|
27
|
+
- Fix header bug Visium data #200
|
|
28
|
+
- (Visium HD) Fix path parsing when images are missing #204 #206
|
|
12
29
|
|
|
13
30
|
## [0.1.4] - 2024-08-07
|
|
14
31
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: spatialdata-io
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: SpatialData IO for common techs
|
|
5
5
|
Project-URL: Documentation, https://spatialdata-io.readthedocs.io/
|
|
6
6
|
Project-URL: Source, https://github.com/scverse/spatialdata-io
|
|
@@ -36,8 +36,7 @@ License: BSD 3-Clause License
|
|
|
36
36
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
37
37
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
38
38
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
39
|
-
|
|
40
|
-
Requires-Python: >=3.9
|
|
39
|
+
Requires-Python: >=3.10
|
|
41
40
|
Requires-Dist: anndata
|
|
42
41
|
Requires-Dist: dask-image
|
|
43
42
|
Requires-Dist: h5py
|
|
@@ -48,7 +47,7 @@ Requires-Dist: pyarrow
|
|
|
48
47
|
Requires-Dist: readfcs
|
|
49
48
|
Requires-Dist: scanpy
|
|
50
49
|
Requires-Dist: scikit-image
|
|
51
|
-
Requires-Dist: spatialdata>=0.2.
|
|
50
|
+
Requires-Dist: spatialdata>=0.2.6
|
|
52
51
|
Requires-Dist: tifffile>=2023.8.12
|
|
53
52
|
Provides-Extra: dev
|
|
54
53
|
Requires-Dist: bump2version; extra == 'dev'
|
|
@@ -90,6 +90,7 @@ source_suffix = {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
intersphinx_mapping = {
|
|
93
|
+
"python": ("https://docs.python.org/3", None),
|
|
93
94
|
"anndata": ("https://anndata.readthedocs.io/en/stable/", None),
|
|
94
95
|
"spatialdata": ("https://scverse-spatialdata.readthedocs.io/en/latest/", None),
|
|
95
96
|
"numpy": ("https://numpy.org/doc/stable/", None),
|
|
@@ -10,7 +10,7 @@ dynamic= [
|
|
|
10
10
|
]
|
|
11
11
|
description = "SpatialData IO for common techs"
|
|
12
12
|
readme = "README.md"
|
|
13
|
-
requires-python = ">=3.
|
|
13
|
+
requires-python = ">=3.10"
|
|
14
14
|
license = {file = "LICENSE"}
|
|
15
15
|
authors = [
|
|
16
16
|
{name = "scverse"},
|
|
@@ -25,7 +25,7 @@ dependencies = [
|
|
|
25
25
|
"anndata",
|
|
26
26
|
"numpy",
|
|
27
27
|
"scanpy",
|
|
28
|
-
"spatialdata>=0.2.
|
|
28
|
+
"spatialdata>=0.2.6",
|
|
29
29
|
"scikit-image",
|
|
30
30
|
"h5py",
|
|
31
31
|
"joblib",
|
|
@@ -83,7 +83,7 @@ skip_glob = ["docs/*"]
|
|
|
83
83
|
|
|
84
84
|
[tool.black]
|
|
85
85
|
line-length = 120
|
|
86
|
-
target-version = ['
|
|
86
|
+
target-version = ['py310']
|
|
87
87
|
include = '\.pyi?$'
|
|
88
88
|
exclude = '''
|
|
89
89
|
(
|
|
@@ -322,6 +322,7 @@ class MerscopeKeys(ModeEnum):
|
|
|
322
322
|
GLOBAL_Z = "global_z"
|
|
323
323
|
Z_INDEX = "ZIndex"
|
|
324
324
|
REGION_KEY = "cells_region"
|
|
325
|
+
GENE_KEY = "gene"
|
|
325
326
|
|
|
326
327
|
|
|
327
328
|
@unique
|
|
@@ -353,9 +354,9 @@ class VisiumHDKeys(ModeEnum):
|
|
|
353
354
|
TISSUE_POSITIONS_FILE = "tissue_positions.parquet"
|
|
354
355
|
|
|
355
356
|
# images
|
|
356
|
-
IMAGE_HIRES_FILE = "
|
|
357
|
-
IMAGE_LOWRES_FILE = "
|
|
358
|
-
IMAGE_CYTASSIST = "
|
|
357
|
+
IMAGE_HIRES_FILE = "tissue_hires_image.png"
|
|
358
|
+
IMAGE_LOWRES_FILE = "tissue_lowres_image.png"
|
|
359
|
+
IMAGE_CYTASSIST = "cytassist_image.tiff"
|
|
359
360
|
|
|
360
361
|
# scalefactors
|
|
361
362
|
SCALEFACTORS_FILE = "scalefactors_json.json"
|
{spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/readers/_utils/_read_10x_h5.py
RENAMED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
# code below taken from https://github.com/scverse/scanpy/blob/master/scanpy/readwrite.py
|
|
32
32
|
|
|
33
33
|
from pathlib import Path
|
|
34
|
-
from typing import Any
|
|
34
|
+
from typing import Any
|
|
35
35
|
|
|
36
36
|
import h5py
|
|
37
37
|
import numpy as np
|
|
@@ -40,8 +40,8 @@ from spatialdata._logging import logger
|
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
def _read_10x_h5(
|
|
43
|
-
filename:
|
|
44
|
-
genome:
|
|
43
|
+
filename: str | Path,
|
|
44
|
+
genome: str | None = None,
|
|
45
45
|
gex_only: bool = True,
|
|
46
46
|
) -> AnnData:
|
|
47
47
|
"""
|
|
@@ -96,7 +96,7 @@ def _read_10x_h5(
|
|
|
96
96
|
return adata
|
|
97
97
|
|
|
98
98
|
|
|
99
|
-
def _read_v3_10x_h5(filename:
|
|
99
|
+
def _read_v3_10x_h5(filename: str | Path, *, start: Any | None = None) -> AnnData:
|
|
100
100
|
"""Read hdf5 file from Cell Ranger v3 or later versions."""
|
|
101
101
|
with h5py.File(str(filename), "r") as f:
|
|
102
102
|
try:
|
|
@@ -3,9 +3,8 @@ from __future__ import annotations
|
|
|
3
3
|
import os
|
|
4
4
|
from collections.abc import Mapping
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Any,
|
|
6
|
+
from typing import Any, Union
|
|
7
7
|
|
|
8
|
-
import numpy as np
|
|
9
8
|
from anndata import AnnData, read_text
|
|
10
9
|
from h5py import File
|
|
11
10
|
|
|
@@ -13,19 +12,11 @@ from spatialdata_io.readers._utils._read_10x_h5 import _read_10x_h5
|
|
|
13
12
|
|
|
14
13
|
PathLike = Union[os.PathLike, str] # type:ignore[type-arg]
|
|
15
14
|
|
|
16
|
-
try:
|
|
17
|
-
from numpy.typing import NDArray
|
|
18
|
-
|
|
19
|
-
NDArrayA = NDArray[Any]
|
|
20
|
-
except (ImportError, TypeError):
|
|
21
|
-
NDArray = np.ndarray # type: ignore[misc]
|
|
22
|
-
NDArrayA = np.ndarray # type: ignore[misc]
|
|
23
|
-
|
|
24
15
|
|
|
25
16
|
def _read_counts(
|
|
26
17
|
path: str | Path,
|
|
27
18
|
counts_file: str,
|
|
28
|
-
library_id:
|
|
19
|
+
library_id: str | None = None,
|
|
29
20
|
**kwargs: Any,
|
|
30
21
|
) -> tuple[AnnData, str]:
|
|
31
22
|
path = Path(path)
|
|
@@ -5,7 +5,7 @@ import re
|
|
|
5
5
|
from collections.abc import Mapping
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from types import MappingProxyType
|
|
8
|
-
from typing import Any
|
|
8
|
+
from typing import Any
|
|
9
9
|
|
|
10
10
|
import dask.array as da
|
|
11
11
|
import numpy as np
|
|
@@ -38,7 +38,7 @@ __all__ = ["cosmx"]
|
|
|
38
38
|
@inject_docs(cx=CosmxKeys)
|
|
39
39
|
def cosmx(
|
|
40
40
|
path: str | Path,
|
|
41
|
-
dataset_id:
|
|
41
|
+
dataset_id: str | None = None,
|
|
42
42
|
transcripts: bool = True,
|
|
43
43
|
imread_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
44
44
|
image_models_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
@@ -200,6 +200,7 @@ def cosmx(
|
|
|
200
200
|
"global_only_image": aff,
|
|
201
201
|
},
|
|
202
202
|
dims=("y", "x", "c"),
|
|
203
|
+
rgb=None,
|
|
203
204
|
**image_models_kwargs,
|
|
204
205
|
)
|
|
205
206
|
images[f"{fov}_image"] = parsed_im
|
|
@@ -4,7 +4,6 @@ import os
|
|
|
4
4
|
import re
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from re import Pattern
|
|
7
|
-
from typing import Optional, Union
|
|
8
7
|
|
|
9
8
|
import anndata as ad
|
|
10
9
|
import numpy as np
|
|
@@ -27,9 +26,9 @@ def _check_path(
|
|
|
27
26
|
path: Path,
|
|
28
27
|
pattern: Pattern[str],
|
|
29
28
|
key: DbitKeys,
|
|
30
|
-
path_specific:
|
|
29
|
+
path_specific: str | Path | None = None,
|
|
31
30
|
optional_arg: bool = False,
|
|
32
|
-
) -> tuple[
|
|
31
|
+
) -> tuple[Path | None, bool]:
|
|
33
32
|
"""
|
|
34
33
|
Check that the path is valid and match a regex pattern.
|
|
35
34
|
|
|
@@ -218,11 +217,11 @@ def _xy2edges(xy: list[int], scale: float = 1.0, border: bool = True, border_sca
|
|
|
218
217
|
|
|
219
218
|
@inject_docs(vx=DbitKeys)
|
|
220
219
|
def dbit(
|
|
221
|
-
path:
|
|
222
|
-
anndata_path:
|
|
223
|
-
barcode_position:
|
|
224
|
-
image_path:
|
|
225
|
-
dataset_id:
|
|
220
|
+
path: str | Path | None = None,
|
|
221
|
+
anndata_path: str | None = None,
|
|
222
|
+
barcode_position: str | None = None,
|
|
223
|
+
image_path: str | None = None,
|
|
224
|
+
dataset_id: str | None = None,
|
|
226
225
|
border: bool = True,
|
|
227
226
|
border_scale: float = 1,
|
|
228
227
|
) -> SpatialData:
|
|
@@ -100,7 +100,7 @@ def mcmicro(
|
|
|
100
100
|
assert len(samples) == 1
|
|
101
101
|
data = imread(samples[0], **imread_kwargs)
|
|
102
102
|
# , scale_factors=[2, 2]
|
|
103
|
-
data = Image2DModel.parse(data, transformations=_get_transformation(), **image_models_kwargs)
|
|
103
|
+
data = Image2DModel.parse(data, transformations=_get_transformation(), rgb=None, **image_models_kwargs)
|
|
104
104
|
images["tma_map"] = data
|
|
105
105
|
|
|
106
106
|
image_dir = path / McmicroKeys.IMAGES_DIR_TMA
|
|
@@ -116,7 +116,7 @@ def mcmicro(
|
|
|
116
116
|
image_id = core_id
|
|
117
117
|
|
|
118
118
|
data = imread(sample, **imread_kwargs)
|
|
119
|
-
data = Image2DModel.parse(data, c_coords=marker_names, **image_models_kwargs)
|
|
119
|
+
data = Image2DModel.parse(data, c_coords=marker_names, rgb=None, **image_models_kwargs)
|
|
120
120
|
transformations = _get_transformation(
|
|
121
121
|
tma=int(core_id) if tma else None, tma_centroids=tma_centroids, raster_data=data
|
|
122
122
|
)
|
|
@@ -131,7 +131,9 @@ def mcmicro(
|
|
|
131
131
|
raw_name = raw_image.with_name(raw_image.stem).with_suffix("").stem
|
|
132
132
|
|
|
133
133
|
data = imread(raw_image, **imread_kwargs)
|
|
134
|
-
images[raw_name] = Image2DModel.parse(
|
|
134
|
+
images[raw_name] = Image2DModel.parse(
|
|
135
|
+
data, transformations={raw_name: Identity()}, rgb=None, **image_models_kwargs
|
|
136
|
+
)
|
|
135
137
|
|
|
136
138
|
illumination_dir = path / McmicroKeys.ILLUMINATION_DIR
|
|
137
139
|
if illumination_dir.exists():
|
|
@@ -143,7 +145,7 @@ def mcmicro(
|
|
|
143
145
|
|
|
144
146
|
data = imread(illumination_image, **imread_kwargs)
|
|
145
147
|
images[illumination_name] = Image2DModel.parse(
|
|
146
|
-
data, transformations={raw_name: Identity()}, **image_models_kwargs
|
|
148
|
+
data, transformations={raw_name: Identity()}, rgb=None, **image_models_kwargs
|
|
147
149
|
)
|
|
148
150
|
|
|
149
151
|
samples_labels = list((path / McmicroKeys.LABELS_DIR).glob("*/*" + McmicroKeys.IMAGE_SUFFIX))
|
|
@@ -2,10 +2,10 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
import warnings
|
|
5
|
-
from collections.abc import Mapping
|
|
5
|
+
from collections.abc import Callable, Mapping
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from types import MappingProxyType
|
|
8
|
-
from typing import Any,
|
|
8
|
+
from typing import Any, Literal
|
|
9
9
|
|
|
10
10
|
import anndata
|
|
11
11
|
import dask.dataframe as dd
|
|
@@ -273,7 +273,7 @@ def _rioxarray_load_merscope(
|
|
|
273
273
|
dim="c",
|
|
274
274
|
)
|
|
275
275
|
|
|
276
|
-
return Image2DModel.parse(im, c_coords=stainings, **image_models_kwargs)
|
|
276
|
+
return Image2DModel.parse(im, c_coords=stainings, rgb=None, **image_models_kwargs)
|
|
277
277
|
|
|
278
278
|
|
|
279
279
|
def _dask_image_load_merscope(
|
|
@@ -292,6 +292,7 @@ def _dask_image_load_merscope(
|
|
|
292
292
|
im,
|
|
293
293
|
dims=("c", "y", "x"),
|
|
294
294
|
c_coords=stainings,
|
|
295
|
+
rgb=None,
|
|
295
296
|
**image_models_kwargs,
|
|
296
297
|
)
|
|
297
298
|
|
|
@@ -302,6 +303,7 @@ def _get_points(transcript_path: Path, transformations: dict[str, BaseTransforma
|
|
|
302
303
|
transcript_df,
|
|
303
304
|
coordinates={"x": MerscopeKeys.GLOBAL_X, "y": MerscopeKeys.GLOBAL_Y},
|
|
304
305
|
transformations=transformations,
|
|
306
|
+
feature_key=MerscopeKeys.GENE_KEY,
|
|
305
307
|
)
|
|
306
308
|
transcripts["gene"] = transcripts["gene"].astype("category")
|
|
307
309
|
return transcripts
|
|
@@ -4,7 +4,7 @@ import os
|
|
|
4
4
|
from collections.abc import Mapping
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from types import MappingProxyType
|
|
7
|
-
from typing import Any, Literal
|
|
7
|
+
from typing import Any, Literal
|
|
8
8
|
|
|
9
9
|
import anndata as ad
|
|
10
10
|
from dask_image.imread import imread
|
|
@@ -95,9 +95,9 @@ def _get_images(
|
|
|
95
95
|
sample: str,
|
|
96
96
|
imread_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
97
97
|
image_models_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
98
|
-
) ->
|
|
98
|
+
) -> SpatialImage | MultiscaleSpatialImage:
|
|
99
99
|
image = imread(path / SteinbockKeys.IMAGES_DIR / f"{sample}{SteinbockKeys.IMAGE_SUFFIX}", **imread_kwargs)
|
|
100
|
-
return Image2DModel.parse(data=image, transformations={sample: Identity()}, **image_models_kwargs)
|
|
100
|
+
return Image2DModel.parse(data=image, transformations={sample: Identity()}, rgb=None, **image_models_kwargs)
|
|
101
101
|
|
|
102
102
|
|
|
103
103
|
def _get_labels(
|
|
@@ -106,6 +106,6 @@ def _get_labels(
|
|
|
106
106
|
labels_kind: str,
|
|
107
107
|
imread_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
108
108
|
image_models_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
109
|
-
) ->
|
|
109
|
+
) -> SpatialImage | MultiscaleSpatialImage:
|
|
110
110
|
image = imread(path / labels_kind / f"{sample}{SteinbockKeys.LABEL_SUFFIX}", **imread_kwargs).squeeze()
|
|
111
111
|
return Labels2DModel.parse(data=image, transformations={sample: Identity()}, **image_models_kwargs)
|
|
@@ -5,7 +5,7 @@ import re
|
|
|
5
5
|
from collections.abc import Mapping
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from types import MappingProxyType
|
|
8
|
-
from typing import Any
|
|
8
|
+
from typing import Any
|
|
9
9
|
|
|
10
10
|
import anndata as ad
|
|
11
11
|
import h5py
|
|
@@ -29,7 +29,7 @@ __all__ = ["stereoseq"]
|
|
|
29
29
|
@inject_docs(xx=SK)
|
|
30
30
|
def stereoseq(
|
|
31
31
|
path: str | Path,
|
|
32
|
-
dataset_id:
|
|
32
|
+
dataset_id: str | None = None,
|
|
33
33
|
read_square_bin: bool = True,
|
|
34
34
|
optional_tif: bool = False,
|
|
35
35
|
imread_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
@@ -4,7 +4,6 @@ import json
|
|
|
4
4
|
import os
|
|
5
5
|
import re
|
|
6
6
|
from collections.abc import Mapping
|
|
7
|
-
from functools import partial
|
|
8
7
|
from pathlib import Path
|
|
9
8
|
from types import MappingProxyType
|
|
10
9
|
from typing import Any
|
|
@@ -138,7 +137,6 @@ def visium(
|
|
|
138
137
|
if (path / "spatial" / VisiumKeys.SPOTS_FILE_1).exists() or (
|
|
139
138
|
tissue_positions_file is not None and str(VisiumKeys.SPOTS_FILE_1) in str(tissue_positions_file)
|
|
140
139
|
):
|
|
141
|
-
read_coords = partial(pd.read_csv, header=None, index_col=0)
|
|
142
140
|
tissue_positions_file = (
|
|
143
141
|
path / "spatial" / VisiumKeys.SPOTS_FILE_1
|
|
144
142
|
if tissue_positions_file is None
|
|
@@ -147,7 +145,6 @@ def visium(
|
|
|
147
145
|
elif (path / "spatial" / VisiumKeys.SPOTS_FILE_2).exists() or (
|
|
148
146
|
tissue_positions_file is not None and str(VisiumKeys.SPOTS_FILE_2) in str(tissue_positions_file)
|
|
149
147
|
):
|
|
150
|
-
read_coords = partial(pd.read_csv, header=0, index_col=0)
|
|
151
148
|
tissue_positions_file = (
|
|
152
149
|
path / "spatial" / VisiumKeys.SPOTS_FILE_2
|
|
153
150
|
if tissue_positions_file is None
|
|
@@ -155,11 +152,18 @@ def visium(
|
|
|
155
152
|
)
|
|
156
153
|
else:
|
|
157
154
|
raise ValueError(f"Cannot find `tissue_positions` file in `{path}`.")
|
|
158
|
-
coords =
|
|
155
|
+
coords = pd.read_csv(tissue_positions_file, header=None, index_col=0)
|
|
159
156
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
157
|
+
if tissue_positions_file.name == VisiumKeys.SPOTS_FILE_1:
|
|
158
|
+
# spaceranger_version < 2.0.0 but header detected: https://github.com/scverse/spatialdata-io/issues/146
|
|
159
|
+
if "in_tissue" in coords.iloc[0].tolist():
|
|
160
|
+
coords = pd.read_csv(tissue_positions_file, header=0, index_col=0)
|
|
161
|
+
else:
|
|
162
|
+
assert "in_tissue" not in coords.columns
|
|
163
|
+
coords.columns = ["in_tissue", "array_row", "array_col", "pxl_row_in_fullres", "pxl_col_in_fullres"]
|
|
164
|
+
else:
|
|
165
|
+
assert tissue_positions_file.name == VisiumKeys.SPOTS_FILE_2
|
|
166
|
+
coords = pd.read_csv(tissue_positions_file, header=0, index_col=0)
|
|
163
167
|
|
|
164
168
|
adata.obs = pd.merge(adata.obs, coords, how="left", left_index=True, right_index=True)
|
|
165
169
|
coords = adata.obs[[VisiumKeys.SPOTS_X, VisiumKeys.SPOTS_Y]].values
|
|
@@ -217,6 +221,7 @@ def visium(
|
|
|
217
221
|
full_image,
|
|
218
222
|
scale_factors=[2, 2, 2, 2],
|
|
219
223
|
transformations={"global": transform_original},
|
|
224
|
+
rgb=None,
|
|
220
225
|
**image_models_kwargs,
|
|
221
226
|
)
|
|
222
227
|
else:
|
|
@@ -226,13 +231,15 @@ def visium(
|
|
|
226
231
|
image_hires = imread(path / VisiumKeys.IMAGE_HIRES_FILE, **imread_kwargs).squeeze().transpose(2, 0, 1)
|
|
227
232
|
image_hires = DataArray(image_hires, dims=("c", "y", "x"))
|
|
228
233
|
images[dataset_id + "_hires_image"] = Image2DModel.parse(
|
|
229
|
-
image_hires, transformations={"downscaled_hires": Identity()}
|
|
234
|
+
image_hires, transformations={"downscaled_hires": Identity()}, rgb=None
|
|
230
235
|
)
|
|
231
236
|
if (path / VisiumKeys.IMAGE_LOWRES_FILE).exists():
|
|
232
237
|
image_lowres = imread(path / VisiumKeys.IMAGE_LOWRES_FILE, **imread_kwargs).squeeze().transpose(2, 0, 1)
|
|
233
238
|
image_lowres = DataArray(image_lowres, dims=("c", "y", "x"))
|
|
234
239
|
images[dataset_id + "_lowres_image"] = Image2DModel.parse(
|
|
235
|
-
image_lowres,
|
|
240
|
+
image_lowres,
|
|
241
|
+
transformations={"downscaled_lowres": Identity()},
|
|
242
|
+
rgb=None,
|
|
236
243
|
)
|
|
237
244
|
|
|
238
245
|
return SpatialData(images=images, shapes=shapes, table=table)
|
|
@@ -79,22 +79,21 @@ def visium_hd(
|
|
|
79
79
|
image_models_kwargs
|
|
80
80
|
Keyword arguments for :class:`spatialdata.models.Image2DModel`.
|
|
81
81
|
anndata_kwargs
|
|
82
|
-
Keyword arguments for :func:`anndata.read_h5ad`.
|
|
82
|
+
Keyword arguments for :func:`anndata.io.read_h5ad`.
|
|
83
83
|
|
|
84
84
|
Returns
|
|
85
85
|
-------
|
|
86
86
|
SpatialData object for the Visium HD data.
|
|
87
87
|
"""
|
|
88
88
|
path = Path(path)
|
|
89
|
+
all_files = [file for file in path.rglob("*") if file.is_file()]
|
|
89
90
|
tables = {}
|
|
90
91
|
shapes = {}
|
|
91
92
|
images: dict[str, Any] = {}
|
|
92
93
|
|
|
93
94
|
if dataset_id is None:
|
|
94
95
|
dataset_id = _infer_dataset_id(path)
|
|
95
|
-
|
|
96
|
-
else:
|
|
97
|
-
filename_prefix = ""
|
|
96
|
+
filename_prefix = f"{dataset_id}_"
|
|
98
97
|
|
|
99
98
|
def load_image(path: Path, suffix: str, scale_factors: list[int] | None = None) -> None:
|
|
100
99
|
_load_image(
|
|
@@ -108,7 +107,6 @@ def visium_hd(
|
|
|
108
107
|
)
|
|
109
108
|
|
|
110
109
|
metadata, hd_layout = _parse_metadata(path, filename_prefix)
|
|
111
|
-
transform_matrices = _get_transform_matrices(metadata, hd_layout)
|
|
112
110
|
file_format = hd_layout[VisiumHDKeys.FILE_FORMAT]
|
|
113
111
|
if file_format != "1.0":
|
|
114
112
|
warnings.warn(
|
|
@@ -127,8 +125,12 @@ def visium_hd(
|
|
|
127
125
|
]
|
|
128
126
|
)
|
|
129
127
|
|
|
130
|
-
if VisiumHDKeys.BINNED_OUTPUTS in
|
|
131
|
-
|
|
128
|
+
all_path_bins = [path_bin for path_bin in all_files if VisiumHDKeys.BINNED_OUTPUTS in str(path_bin)]
|
|
129
|
+
if len(all_path_bins) != 0:
|
|
130
|
+
path_bins_parts = all_path_bins[
|
|
131
|
+
-1
|
|
132
|
+
].parts # just choosing last one here as users might have tar file which would be first
|
|
133
|
+
path_bins = Path(*path_bins_parts[: path_bins_parts.index(VisiumHDKeys.BINNED_OUTPUTS) + 1])
|
|
132
134
|
else:
|
|
133
135
|
path_bins = path
|
|
134
136
|
all_bin_sizes = _get_bins(path_bins)
|
|
@@ -266,15 +268,26 @@ def visium_hd(
|
|
|
266
268
|
fullres_image_filenames = [
|
|
267
269
|
f for f in os.listdir(path_fullres) if os.path.isfile(os.path.join(path_fullres, f))
|
|
268
270
|
]
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
271
|
+
fullres_image_paths = [path_fullres / image_filename for image_filename in fullres_image_filenames]
|
|
272
|
+
elif list((path_fullres := (path / f"{filename_prefix}tissue_image")).parent.glob(f"{path_fullres.name}.*")):
|
|
273
|
+
fullres_image_paths = list(path_fullres.parent.glob(f"{path_fullres.name}.*"))
|
|
274
|
+
else:
|
|
275
|
+
fullres_image_paths = []
|
|
276
|
+
if len(fullres_image_paths) > 1:
|
|
277
|
+
warnings.warn(
|
|
278
|
+
f"Multiple files found in {path_fullres}, using the first one: {fullres_image_paths[0].stem}. Please"
|
|
279
|
+
" specify the path to the full resolution image manually using the `fullres_image_file` argument.",
|
|
280
|
+
UserWarning,
|
|
281
|
+
stacklevel=2,
|
|
282
|
+
)
|
|
283
|
+
if len(fullres_image_paths) == 0:
|
|
284
|
+
warnings.warn(
|
|
285
|
+
"No full resolution image found. If incorrect, please specify the path in the "
|
|
286
|
+
"`fullres_image_file` parameter when calling the `visium_hd` reader function.",
|
|
287
|
+
UserWarning,
|
|
288
|
+
stacklevel=2,
|
|
289
|
+
)
|
|
290
|
+
fullres_image_file = fullres_image_paths[0] if len(fullres_image_paths) > 0 else None
|
|
278
291
|
|
|
279
292
|
if fullres_image_file is not None:
|
|
280
293
|
load_image(
|
|
@@ -284,8 +297,15 @@ def visium_hd(
|
|
|
284
297
|
)
|
|
285
298
|
|
|
286
299
|
# hires image
|
|
300
|
+
hires_image_path = [path for path in all_files if VisiumHDKeys.IMAGE_HIRES_FILE in str(path)]
|
|
301
|
+
if len(hires_image_path) == 0:
|
|
302
|
+
warnings.warn(
|
|
303
|
+
f"No image path found containing the hires image: {VisiumHDKeys.IMAGE_HIRES_FILE}",
|
|
304
|
+
UserWarning,
|
|
305
|
+
stacklevel=2,
|
|
306
|
+
)
|
|
287
307
|
load_image(
|
|
288
|
-
path=
|
|
308
|
+
path=hires_image_path[0],
|
|
289
309
|
suffix="_hires_image",
|
|
290
310
|
)
|
|
291
311
|
set_transformation(
|
|
@@ -295,8 +315,15 @@ def visium_hd(
|
|
|
295
315
|
)
|
|
296
316
|
|
|
297
317
|
# lowres image
|
|
318
|
+
lowres_image_path = [path for path in all_files if VisiumHDKeys.IMAGE_LOWRES_FILE in str(path)]
|
|
319
|
+
if len(lowres_image_path) == 0:
|
|
320
|
+
warnings.warn(
|
|
321
|
+
f"No image path found containing the lowres image: {VisiumHDKeys.IMAGE_LOWRES_FILE}",
|
|
322
|
+
UserWarning,
|
|
323
|
+
stacklevel=2,
|
|
324
|
+
)
|
|
298
325
|
load_image(
|
|
299
|
-
path=
|
|
326
|
+
path=lowres_image_path[0],
|
|
300
327
|
suffix="_lowres_image",
|
|
301
328
|
)
|
|
302
329
|
set_transformation(
|
|
@@ -306,12 +333,20 @@ def visium_hd(
|
|
|
306
333
|
)
|
|
307
334
|
|
|
308
335
|
# cytassist image
|
|
336
|
+
cytassist_path = [path for path in all_files if VisiumHDKeys.IMAGE_CYTASSIST in str(path)]
|
|
337
|
+
if len(cytassist_path) == 0:
|
|
338
|
+
warnings.warn(
|
|
339
|
+
f"No image path found containing the cytassist image: {VisiumHDKeys.IMAGE_CYTASSIST}",
|
|
340
|
+
UserWarning,
|
|
341
|
+
stacklevel=2,
|
|
342
|
+
)
|
|
309
343
|
if load_all_images:
|
|
310
344
|
load_image(
|
|
311
|
-
path=
|
|
345
|
+
path=cytassist_path[0],
|
|
312
346
|
suffix="_cytassist_image",
|
|
313
347
|
)
|
|
314
348
|
image = images[dataset_id + "_cytassist_image"]
|
|
349
|
+
transform_matrices = _get_transform_matrices(metadata, hd_layout)
|
|
315
350
|
affine0 = transform_matrices["cytassist_colrow_to_spot_colrow"]
|
|
316
351
|
affine1 = transform_matrices["spot_colrow_to_microscope_colrow"]
|
|
317
352
|
set_transformation(image, Sequence([affine0, affine1]), "global")
|
|
@@ -352,7 +387,7 @@ def _load_image(
|
|
|
352
387
|
# dask_image doesn't recognize .btf automatically and imageio v3 throws error due to pixel limit -> use imageio v2
|
|
353
388
|
data = imread2(path, **imread_kwargs).squeeze().transpose(2, 0, 1)
|
|
354
389
|
image = DataArray(data, dims=("c", "y", "x"))
|
|
355
|
-
parsed = Image2DModel.parse(image, scale_factors=scale_factors, **image_models_kwargs)
|
|
390
|
+
parsed = Image2DModel.parse(image, scale_factors=scale_factors, rgb=None, **image_models_kwargs)
|
|
356
391
|
images[dataset_id + suffix] = parsed
|
|
357
392
|
else:
|
|
358
393
|
warnings.warn(f"File {path} does not exist, skipping it.", UserWarning, stacklevel=2)
|
|
@@ -10,7 +10,7 @@ import zipfile
|
|
|
10
10
|
from collections.abc import Mapping
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
from types import MappingProxyType
|
|
13
|
-
from typing import Any
|
|
13
|
+
from typing import Any
|
|
14
14
|
|
|
15
15
|
import dask.array as da
|
|
16
16
|
import numpy as np
|
|
@@ -24,10 +24,8 @@ from dask.dataframe import read_parquet
|
|
|
24
24
|
from dask_image.imread import imread
|
|
25
25
|
from geopandas import GeoDataFrame
|
|
26
26
|
from joblib import Parallel, delayed
|
|
27
|
-
from multiscale_spatial_image.multiscale_spatial_image import MultiscaleSpatialImage
|
|
28
27
|
from pyarrow import Table
|
|
29
28
|
from shapely import Polygon
|
|
30
|
-
from spatial_image import SpatialImage
|
|
31
29
|
from spatialdata import SpatialData
|
|
32
30
|
from spatialdata._core.query.relational_query import get_element_instances
|
|
33
31
|
from spatialdata._types import ArrayLike
|
|
@@ -39,6 +37,7 @@ from spatialdata.models import (
|
|
|
39
37
|
TableModel,
|
|
40
38
|
)
|
|
41
39
|
from spatialdata.transformations.transformations import Affine, Identity, Scale
|
|
40
|
+
from xarray import DataArray, DataTree
|
|
42
41
|
|
|
43
42
|
from spatialdata_io._constants._constants import XeniumKeys
|
|
44
43
|
from spatialdata_io._docs import inject_docs
|
|
@@ -111,7 +110,9 @@ def xenium(
|
|
|
111
110
|
morphology_focus
|
|
112
111
|
Whether to read the morphology focus image.
|
|
113
112
|
aligned_images
|
|
114
|
-
Whether to also parse, when available, additional H&E or IF aligned images.
|
|
113
|
+
Whether to also parse, when available, additional H&E or IF aligned images. For more control over the aligned
|
|
114
|
+
images being read, in particular, to specify the axes of the aligned images, please set this parameter to
|
|
115
|
+
`False` and use the `xenium_aligned_image` function directly.
|
|
115
116
|
cells_table
|
|
116
117
|
Whether to read the cell annotations in the `AnnData` table.
|
|
117
118
|
n_jobs
|
|
@@ -362,7 +363,7 @@ def _decode_cell_id_column(cell_id_column: pd.Series) -> pd.Series:
|
|
|
362
363
|
|
|
363
364
|
|
|
364
365
|
def _get_polygons(
|
|
365
|
-
path: Path, file: str, specs: dict[str, Any], n_jobs: int, idx:
|
|
366
|
+
path: Path, file: str, specs: dict[str, Any], n_jobs: int, idx: ArrayLike | None = None
|
|
366
367
|
) -> GeoDataFrame:
|
|
367
368
|
def _poly(arr: ArrayLike) -> Polygon:
|
|
368
369
|
return Polygon(arr[:-1])
|
|
@@ -513,6 +514,7 @@ def _get_points(path: Path, specs: dict[str, Any]) -> Table:
|
|
|
513
514
|
feature_key=XeniumKeys.FEATURE_NAME,
|
|
514
515
|
instance_key=XeniumKeys.CELL_ID,
|
|
515
516
|
transformations={"global": transform},
|
|
517
|
+
sort=True,
|
|
516
518
|
)
|
|
517
519
|
return points
|
|
518
520
|
|
|
@@ -550,7 +552,7 @@ def _get_images(
|
|
|
550
552
|
file: str,
|
|
551
553
|
imread_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
552
554
|
image_models_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
553
|
-
) ->
|
|
555
|
+
) -> DataArray | DataTree:
|
|
554
556
|
image = imread(path / file, **imread_kwargs)
|
|
555
557
|
if "c_coords" in image_models_kwargs and "dummy" in image_models_kwargs["c_coords"]:
|
|
556
558
|
# Napari currently interprets 4 channel images as RGB; a series of PRs to fix this is almost ready but they will
|
|
@@ -559,7 +561,7 @@ def _get_images(
|
|
|
559
561
|
# let's add a dummy channel as a temporary workaround.
|
|
560
562
|
image = da.concatenate([image, da.zeros_like(image[0:1])], axis=0)
|
|
561
563
|
return Image2DModel.parse(
|
|
562
|
-
image, transformations={"global": Identity()}, dims=("c", "y", "x"), **image_models_kwargs
|
|
564
|
+
image, transformations={"global": Identity()}, dims=("c", "y", "x"), rgb=None, **image_models_kwargs
|
|
563
565
|
)
|
|
564
566
|
|
|
565
567
|
|
|
@@ -567,7 +569,7 @@ def _add_aligned_images(
|
|
|
567
569
|
path: Path,
|
|
568
570
|
imread_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
569
571
|
image_models_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
570
|
-
) -> dict[str,
|
|
572
|
+
) -> dict[str, DataTree]:
|
|
571
573
|
"""Discover and parse aligned images."""
|
|
572
574
|
images = {}
|
|
573
575
|
ome_tif_files = list(path.glob("*.ome.tif"))
|
|
@@ -598,7 +600,8 @@ def xenium_aligned_image(
|
|
|
598
600
|
alignment_file: str | Path | None,
|
|
599
601
|
imread_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
600
602
|
image_models_kwargs: Mapping[str, Any] = MappingProxyType({}),
|
|
601
|
-
|
|
603
|
+
dims: tuple[str, ...] | None = None,
|
|
604
|
+
) -> DataTree:
|
|
602
605
|
"""
|
|
603
606
|
Read an image aligned to a Xenium dataset, with an optional alignment file.
|
|
604
607
|
|
|
@@ -610,6 +613,12 @@ def xenium_aligned_image(
|
|
|
610
613
|
Path to the alignment file, if not passed it is assumed that the image is aligned.
|
|
611
614
|
image_models_kwargs
|
|
612
615
|
Keyword arguments to pass to the image models.
|
|
616
|
+
dims
|
|
617
|
+
Dimensions of the image (tuple of axes names); valid strings are "c", "x" and "y". If not passed, the function
|
|
618
|
+
will try to infer the dimensions from the image shape. Please use this argument when the default behavior fails.
|
|
619
|
+
Example: for an image with shape (1, y, 1, x, 3), use dims=("anystring", "y", "dummy", "x", "c"). Values that
|
|
620
|
+
are not "c", "x" or "y" are considered dummy dimensions and will be squeezed (the data must have len 1 for
|
|
621
|
+
those axes).
|
|
613
622
|
|
|
614
623
|
Returns
|
|
615
624
|
-------
|
|
@@ -627,19 +636,28 @@ def xenium_aligned_image(
|
|
|
627
636
|
# In fact, it could be that the len(image.shape) == 4 has actually dimes (1, x, y, c) and not (1, y, x, c). This is
|
|
628
637
|
# not a problem because the transformation is constructed to be consistent, but if is the case, the data orientation
|
|
629
638
|
# would be transposed compared to the original image, not ideal.
|
|
630
|
-
if
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
639
|
+
if dims is None:
|
|
640
|
+
if len(image.shape) == 4:
|
|
641
|
+
assert image.shape[0] == 1
|
|
642
|
+
assert image.shape[-1] == 3
|
|
643
|
+
image = image.squeeze(0)
|
|
644
|
+
dims = ("y", "x", "c")
|
|
645
|
+
else:
|
|
646
|
+
assert len(image.shape) == 3
|
|
647
|
+
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)
|
|
652
|
+
dims = ("c", "y", "x")
|
|
635
653
|
else:
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
654
|
+
logging.info(f"Image has shape {image.shape}, parsing with dims={dims}.")
|
|
655
|
+
image = DataArray(image, dims=dims)
|
|
656
|
+
# squeeze spurious dimensions away
|
|
657
|
+
to_squeeze = [dim for dim in dims if dim not in ["c", "x", "y"]]
|
|
658
|
+
dims = tuple(dim for dim in dims if dim in ["c", "x", "y"])
|
|
659
|
+
for dim in to_squeeze:
|
|
660
|
+
image = image.squeeze(dim)
|
|
643
661
|
|
|
644
662
|
if alignment_file is None:
|
|
645
663
|
transformation = Identity()
|
|
@@ -762,3 +780,6 @@ def prefix_suffix_uint32_from_cell_id_str(cell_id_str: ArrayLike) -> tuple[Array
|
|
|
762
780
|
cell_id_prefix = [int(x, 16) for x in cell_id_prefix_hex]
|
|
763
781
|
|
|
764
782
|
return np.array(cell_id_prefix, dtype=np.uint32), np.array(dataset_suffix_int)
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
##
|
|
@@ -46,7 +46,7 @@ def test_roundtrip_with_data_limits() -> None:
|
|
|
46
46
|
# pointing to "data".
|
|
47
47
|
# The GitHub workflow "prepare_test_data.yaml" takes care of downloading the datasets and uploading an artifact for the
|
|
48
48
|
# tests to use
|
|
49
|
-
@pytest.mark.skipif(sys.version_info < (3,
|
|
49
|
+
@pytest.mark.skipif(sys.version_info < (3, 12), reason="Test requires Python 3.10 or higher")
|
|
50
50
|
@pytest.mark.parametrize(
|
|
51
51
|
"dataset,expected",
|
|
52
52
|
[
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spatialdata_io-0.1.4 → spatialdata_io-0.1.6}/src/spatialdata_io/converters/legacy_anndata.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|