Skip to content

Commit 6be7296

Browse files
Merge pull request #347 from scverse/improve_legacy_adata_reader
Modify `from_legacy_anndata` so that it can read older `ST` datasets
2 parents 28d05a2 + 6080158 commit 6be7296

File tree

1 file changed

+61
-54
lines changed

1 file changed

+61
-54
lines changed

src/spatialdata_io/converters/legacy_anndata.py

Lines changed: 61 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
import warnings
3+
from collections.abc import Mapping
44
from typing import TYPE_CHECKING
55

66
import numpy as np
@@ -12,6 +12,7 @@
1212
to_circles,
1313
)
1414
from spatialdata._core.operations._utils import transform_to_data_extent
15+
from spatialdata._logging import logger
1516
from spatialdata.models import Image2DModel, ShapesModel, TableModel, get_table_keys
1617
from spatialdata.transformations import Identity, Scale
1718

@@ -250,6 +251,7 @@ def from_legacy_anndata(adata: AnnData) -> SpatialData:
250251
IMAGES = "images"
251252
HIRES = "hires"
252253
LOWRES = "lowres"
254+
DEFAULT_SCALE_FACTOR = 1.0
253255

254256
# SpatialData keys
255257
REGION = "locations"
@@ -265,72 +267,77 @@ def from_legacy_anndata(adata: AnnData) -> SpatialData:
265267
if SPATIAL in adata.uns:
266268
dataset_ids = list(adata.uns[SPATIAL].keys())
267269
for dataset_id in dataset_ids:
268-
# read the image data and the scale factors for the shapes
269-
keys = set(adata.uns[SPATIAL][dataset_id].keys())
270-
tissue_hires_scalef = None
271-
tissue_lowres_scalef = None
272-
hires = None
273-
lowres = None
274-
if SCALEFACTORS in keys:
275-
scalefactors = adata.uns[SPATIAL][dataset_id][SCALEFACTORS]
276-
if TISSUE_HIRES_SCALEF in scalefactors:
277-
tissue_hires_scalef = scalefactors[TISSUE_HIRES_SCALEF]
278-
if TISSUE_LOWRES_SCALEF in scalefactors:
279-
tissue_lowres_scalef = scalefactors[TISSUE_LOWRES_SCALEF]
280-
if SPOT_DIAMETER_FULLRES in scalefactors:
281-
spot_diameter_fullres_list.append(scalefactors[SPOT_DIAMETER_FULLRES])
282-
if IMAGES in keys:
283-
image_data = adata.uns[SPATIAL][dataset_id][IMAGES]
284-
if HIRES in image_data:
285-
hires = image_data[HIRES]
286-
if LOWRES in image_data:
287-
lowres = image_data[LOWRES]
288-
289-
# construct the spatialdata elements
290-
if hires is not None:
291-
# prepare the hires image
292-
assert tissue_hires_scalef is not None, (
293-
"tissue_hires_scalef is required when an the hires image is present"
270+
dataset_data = adata.uns[SPATIAL][dataset_id]
271+
keys = set(dataset_data.keys())
272+
scalefactors_raw = dataset_data.get(SCALEFACTORS, {}) if isinstance(dataset_data, Mapping) else {}
273+
scalefactors = scalefactors_raw if isinstance(scalefactors_raw, Mapping) else {}
274+
275+
if SPOT_DIAMETER_FULLRES in scalefactors:
276+
spot_diameter_fullres_list.append(scalefactors[SPOT_DIAMETER_FULLRES])
277+
278+
image_data = dataset_data.get(IMAGES, {}) if IMAGES in keys and isinstance(dataset_data, Mapping) else {}
279+
if not isinstance(image_data, Mapping):
280+
image_data = {}
281+
for image_key, image_value in image_data.items():
282+
if image_key not in (HIRES, LOWRES):
283+
logger.warning(
284+
"Found non-standard image key '%s' in dataset '%s' - attempting to parse.",
285+
image_key,
286+
dataset_id,
287+
)
288+
# prefer scalefactors keyed by the image name, fall back to tissue_<image_key>_scalef
289+
# then to legacy hires/lowres names, finally default to 1.0.
290+
scalefactor = None
291+
scalefactor_source = None
292+
if image_key in scalefactors:
293+
scalefactor = scalefactors[image_key]
294+
scalefactor_source = image_key
295+
elif f"tissue_{image_key}_scalef" in scalefactors:
296+
scalefactor = scalefactors[f"tissue_{image_key}_scalef"]
297+
scalefactor_source = f"tissue_{image_key}_scalef"
298+
elif image_key == HIRES and TISSUE_HIRES_SCALEF in scalefactors:
299+
scalefactor = scalefactors[TISSUE_HIRES_SCALEF]
300+
scalefactor_source = TISSUE_HIRES_SCALEF
301+
elif image_key == LOWRES and TISSUE_LOWRES_SCALEF in scalefactors:
302+
scalefactor = scalefactors[TISSUE_LOWRES_SCALEF]
303+
scalefactor_source = TISSUE_LOWRES_SCALEF
304+
305+
if scalefactor is None:
306+
scalefactor = DEFAULT_SCALE_FACTOR
307+
logger.warning(
308+
"Scale factor missing for image '%s' in dataset '%s'; defaulting to %s",
309+
image_key,
310+
dataset_id,
311+
DEFAULT_SCALE_FACTOR,
312+
)
313+
else:
314+
logger.debug(
315+
"Using scalefactor '%s' for image '%s' in dataset '%s'",
316+
scalefactor_source,
317+
image_key,
318+
dataset_id,
319+
)
320+
321+
transform_name = f"{dataset_id}_{image_key}"
322+
image_name = f"{dataset_id}_{image_key}"
323+
images[image_name] = Image2DModel.parse(
324+
image_value, dims=("y", "x", "c"), transformations={transform_name: Identity()}
294325
)
295-
hires_image = Image2DModel.parse(
296-
hires, dims=("y", "x", "c"), transformations={f"{dataset_id}_downscaled_hires": Identity()}
297-
)
298-
images[f"{dataset_id}_hires_image"] = hires_image
299-
300-
# prepare the transformation to the hires image for the shapes
301-
scale_hires = Scale([tissue_hires_scalef, tissue_hires_scalef], axes=("x", "y"))
302-
shapes_transformations[f"{dataset_id}_downscaled_hires"] = scale_hires
303-
if lowres is not None:
304-
# prepare the lowres image
305-
assert tissue_lowres_scalef is not None, (
306-
"tissue_lowres_scalef is required when an the lowres image is present"
307-
)
308-
lowres_image = Image2DModel.parse(
309-
lowres, dims=("y", "x", "c"), transformations={f"{dataset_id}_downscaled_lowres": Identity()}
310-
)
311-
images[f"{dataset_id}_lowres_image"] = lowres_image
312-
313-
# prepare the transformation to the lowres image for the shapes
314-
scale_lowres = Scale([tissue_lowres_scalef, tissue_lowres_scalef], axes=("x", "y"))
315-
shapes_transformations[f"{dataset_id}_downscaled_lowres"] = scale_lowres
326+
shapes_transformations[transform_name] = Scale([scalefactor, scalefactor], axes=("x", "y"))
316327

317328
# validate the spot_diameter_fullres value
318329
if len(spot_diameter_fullres_list) > 0:
319330
d = np.array(spot_diameter_fullres_list)
320331
if not np.allclose(d, d[0]):
321-
warnings.warn(
332+
logger.warning(
322333
"spot_diameter_fullres is not constant across datasets. Using the average value.",
323-
UserWarning,
324-
stacklevel=2,
325334
)
326335
spot_diameter_fullres = d.mean()
327336
else:
328337
spot_diameter_fullres = d[0]
329338
else:
330-
warnings.warn(
339+
logger.warning(
331340
f"spot_diameter_fullres is not present. Using {SPOT_DIAMETER_FULLRES_DEFAULT} as default value.",
332-
UserWarning,
333-
stacklevel=2,
334341
)
335342
spot_diameter_fullres = SPOT_DIAMETER_FULLRES_DEFAULT
336343

0 commit comments

Comments
 (0)