11from __future__ import annotations
22
3- import warnings
3+ import logging
4+ from collections .abc import Mapping
45from typing import TYPE_CHECKING
56
67import 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