1616from dask_image .imread import imread
1717from scipy .sparse import csr_matrix
1818from skimage .transform import estimate_transform
19- from spatialdata import SpatialData
19+ from spatialdata import SpatialData , read_zarr
2020from spatialdata ._logging import logger
2121from spatialdata .models import Image2DModel , Labels2DModel , PointsModel , TableModel
2222from spatialdata .transformations .transformations import Affine , Identity
@@ -34,6 +34,7 @@ def cosmx(
3434 transcripts : bool = True ,
3535 imread_kwargs : Mapping [str , Any ] = MappingProxyType ({}),
3636 image_models_kwargs : Mapping [str , Any ] = MappingProxyType ({}),
37+ output_path : str | Path | None = None ,
3738) -> SpatialData :
3839 """
3940 Read *Cosmx Nanostring* data.
@@ -62,12 +63,20 @@ def cosmx(
6263 Keyword arguments passed to :func:`dask_image.imread.imread`.
6364 image_models_kwargs
6465 Keyword arguments passed to :class:`spatialdata.models.Image2DModel`.
66+ output_path
67+ Path where the output will be saved. If ``None``, the output will not be saved.
6568
6669 Returns
6770 -------
6871 :class:`spatialdata.SpatialData`
6972 """
7073 path = Path (path )
74+ output_path = Path (output_path ) if output_path is not None else None
75+ sdata = SpatialData ()
76+
77+ # If output path is provided, save the empty SpatialData object to create directories and hierarchy
78+ if output_path is not None :
79+ sdata .write (output_path )
7180
7281 # tries to infer dataset_id from the name of the counts file
7382 if dataset_id is None :
@@ -151,6 +160,14 @@ def cosmx(
151160 inplace = True ,
152161 )
153162
163+ # Add table to SpatialData object, write it and delete temporary objects to save memory
164+ sdata .tables ["table" ] = table
165+ if output_path is not None :
166+ sdata .write_element (element_name = "table" )
167+ del adata
168+ del table
169+ del sdata .tables
170+
154171 # prepare to read images and labels
155172 file_extensions = (".jpg" , ".png" , ".jpeg" , ".tif" , ".tiff" )
156173 pat = re .compile (r".*_F(\d+)" )
@@ -195,7 +212,14 @@ def cosmx(
195212 rgb = None ,
196213 ** image_models_kwargs ,
197214 )
198- images [f"{ fov } _image" ] = parsed_im
215+ image_name = f"{ fov } _image"
216+ images [image_name ] = parsed_im
217+ if output_path is not None :
218+ sdata .images [image_name ] = parsed_im
219+ sdata .write_element (element_name = image_name )
220+ del parsed_im
221+ del images [image_name ]
222+ del sdata .images [image_name ]
199223 else :
200224 logger .warning (f"FOV { fov } not found in counts file. Skipping image { fname } ." )
201225
@@ -218,7 +242,14 @@ def cosmx(
218242 dims = ("y" , "x" ),
219243 ** image_models_kwargs ,
220244 )
221- labels [f"{ fov } _labels" ] = parsed_la
245+ label_name = f"{ fov } _labels"
246+ labels [label_name ] = parsed_la
247+ if output_path is not None :
248+ sdata .labels [label_name ] = parsed_la
249+ sdata .write_element (element_name = label_name )
250+ del parsed_la
251+ del labels [label_name ]
252+ del sdata .labels [label_name ]
222253 else :
223254 logger .warning (f"FOV { fov } not found in counts file. Skipping labels { fname } ." )
224255
@@ -265,7 +296,8 @@ def cosmx(
265296 # we rename z because we want to treat the data as 2d
266297 sub_table .rename (columns = {"z" : "z_raw" }, inplace = True )
267298 if len (sub_table ) > 0 :
268- points [f"{ fov } _points" ] = PointsModel .parse (
299+ point_name = f"{ fov } _points"
300+ points [point_name ] = PointsModel .parse (
269301 sub_table ,
270302 coordinates = {"x" : CosmxKeys .X_LOCAL_TRANSCRIPT , "y" : CosmxKeys .Y_LOCAL_TRANSCRIPT },
271303 feature_key = CosmxKeys .TARGET_OF_TRANSCRIPT ,
@@ -276,6 +308,11 @@ def cosmx(
276308 "global_only_labels" : aff ,
277309 },
278310 )
311+ if output_path is not None :
312+ sdata .points [point_name ] = points [point_name ]
313+ sdata .write_element (element_name = point_name )
314+ del points [point_name ]
315+ del sdata .points [point_name ]
279316
280317 # TODO: what to do with fov file?
281318 # if fov_file is not None:
@@ -286,5 +323,14 @@ def cosmx(
286323 # except KeyError:
287324 # logg.warning(f"FOV `{str(fov)}` does not exist, skipping it.")
288325 # continue
289-
326+ if output_path is not None :
327+ return read_zarr (output_path )
290328 return SpatialData (images = images , labels = labels , points = points , table = table )
329+
330+ if __name__ == "__main__" :
331+ cosmx (
332+ path = "/Users/ldumont/git/cosmx_data" ,
333+ dataset_id = "1" ,
334+ transcripts = True ,
335+ output_path = "/Users/ldumont/cosmx_data_output" ,
336+ )
0 commit comments