Skip to content

Commit 5eac22c

Browse files
committed
made reader more flexible
1 parent fb57846 commit 5eac22c

1 file changed

Lines changed: 62 additions & 54 deletions

File tree

src/spatialdata_io/converters/legacy_anndata.py

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

3-
import warnings
3+
import logging
4+
from collections.abc import Mapping
45
from typing import TYPE_CHECKING
56

67
import numpy as np
@@ -239,6 +240,7 @@ def from_legacy_anndata(adata: AnnData) -> SpatialData:
239240
-----
240241
The SpatialData object will have one hires and one lowres image for each dataset in the AnnData object.
241242
"""
243+
logger = logging.getLogger(__name__)
242244
# AnnData keys
243245
SPATIAL = "spatial"
244246

@@ -250,6 +252,7 @@ def from_legacy_anndata(adata: AnnData) -> SpatialData:
250252
IMAGES = "images"
251253
HIRES = "hires"
252254
LOWRES = "lowres"
255+
DEFAULT_SCALE_FACTOR = 1.0
253256

254257
# SpatialData keys
255258
REGION = "locations"
@@ -265,72 +268,77 @@ def from_legacy_anndata(adata: AnnData) -> SpatialData:
265268
if SPATIAL in adata.uns:
266269
dataset_ids = list(adata.uns[SPATIAL].keys())
267270
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"
271+
dataset_data = adata.uns[SPATIAL][dataset_id]
272+
keys = set(dataset_data.keys())
273+
scalefactors_raw = dataset_data.get(SCALEFACTORS, {}) if isinstance(dataset_data, Mapping) else {}
274+
scalefactors = scalefactors_raw if isinstance(scalefactors_raw, Mapping) else {}
275+
276+
if SPOT_DIAMETER_FULLRES in scalefactors:
277+
spot_diameter_fullres_list.append(scalefactors[SPOT_DIAMETER_FULLRES])
278+
279+
image_data = dataset_data.get(IMAGES, {}) if IMAGES in keys and isinstance(dataset_data, Mapping) else {}
280+
if not isinstance(image_data, Mapping):
281+
image_data = {}
282+
for image_key, image_value in image_data.items():
283+
if image_key not in (HIRES, LOWRES):
284+
logger.warning(
285+
"Found non-standard image key '%s' in dataset '%s' - attempting to parse.",
286+
image_key,
287+
dataset_id,
288+
)
289+
# prefer scalefactors keyed by the image name, fall back to legacy hires/lowres names,
290+
# then to tissue_<image_key>_scalef, finally default to 1.0.
291+
scalefactor = None
292+
scalefactor_source = None
293+
if image_key in scalefactors:
294+
scalefactor = scalefactors[image_key]
295+
scalefactor_source = image_key
296+
elif f"tissue_{image_key}_scalef" in scalefactors:
297+
scalefactor = scalefactors[f"tissue_{image_key}_scalef"]
298+
scalefactor_source = f"tissue_{image_key}_scalef"
299+
elif image_key == HIRES and TISSUE_HIRES_SCALEF in scalefactors:
300+
scalefactor = scalefactors[TISSUE_HIRES_SCALEF]
301+
scalefactor_source = TISSUE_HIRES_SCALEF
302+
elif image_key == LOWRES and TISSUE_LOWRES_SCALEF in scalefactors:
303+
scalefactor = scalefactors[TISSUE_LOWRES_SCALEF]
304+
scalefactor_source = TISSUE_LOWRES_SCALEF
305+
306+
if scalefactor is None:
307+
scalefactor = DEFAULT_SCALE_FACTOR
308+
logger.warning(
309+
"Scale factor missing for image '%s' in dataset '%s'; defaulting to %s",
310+
image_key,
311+
dataset_id,
312+
DEFAULT_SCALE_FACTOR,
313+
)
314+
else:
315+
logger.warning(
316+
"Using scalefactor '%s' for image '%s' in dataset '%s'",
317+
scalefactor_source,
318+
image_key,
319+
dataset_id,
320+
)
321+
322+
transform_name = f"{dataset_id}_{image_key}"
323+
image_name = f"{dataset_id}_{image_key}"
324+
images[image_name] = Image2DModel.parse(
325+
image_value, dims=("y", "x", "c"), transformations={transform_name: Identity()}
294326
)
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
327+
shapes_transformations[transform_name] = Scale([scalefactor, scalefactor], axes=("x", "y"))
316328

317329
# validate the spot_diameter_fullres value
318330
if len(spot_diameter_fullres_list) > 0:
319331
d = np.array(spot_diameter_fullres_list)
320332
if not np.allclose(d, d[0]):
321-
warnings.warn(
333+
logger.warning(
322334
"spot_diameter_fullres is not constant across datasets. Using the average value.",
323-
UserWarning,
324-
stacklevel=2,
325335
)
326336
spot_diameter_fullres = d.mean()
327337
else:
328338
spot_diameter_fullres = d[0]
329339
else:
330-
warnings.warn(
340+
logger.warning(
331341
f"spot_diameter_fullres is not present. Using {SPOT_DIAMETER_FULLRES_DEFAULT} as default value.",
332-
UserWarning,
333-
stacklevel=2,
334342
)
335343
spot_diameter_fullres = SPOT_DIAMETER_FULLRES_DEFAULT
336344

0 commit comments

Comments
 (0)