@@ -263,16 +263,55 @@ def __init__(self, tcfname: str, channel: int = 0):
263263 with h5py .File (self .tcfname ) as f :
264264 self .max_channels = self .get_attr (f , f'/Data/{ self .imgtype } ' , 'Channels' )
265265
266- def __getitem__ (self , key : int , array_type = 'numpy' ) -> np .ndarray :
266+ def get_data_location (self , key : int ) -> str :
267+ length = len (self )
268+ if not isinstance (key , int ):
269+ raise TypeError (f'{ self .__class__ } indices must be integer, not { type (key )} ' )
270+ if key < - length or key >= length :
271+ raise IndexError (f'{ self .__class__ } index out of range' )
272+ key = (key + length ) % length
273+ # Build the path with the correct channel:
274+ return f'/Data/{ self .imgtype } /CH{ self .channel } /{ key :06d} '
275+
276+ def __getitem__ (self , key : int , array_type = 'numpy' ) -> np .ndarray :
267277 if array_type == 'numpy' :
268278 into_array = np .asarray
279+ zeros = np .zeros
269280 elif array_type == 'dask' :
270281 into_array = da .from_array
282+ zeros = da .zeros
271283 else :
272284 raise TypeError ('array_type must be either "numpy" or "dask"' )
273- self .imgtype = f'3DFL/CH{ self .channel } '
274- data_path = self .get_data_location (key )
275- self .imgtype = '3DFL'
276- data = into_array (h5py .File (self .tcfname )[data_path ])
277- return data
278285
286+ data_path = self .get_data_location (key )
287+ with h5py .File (self .tcfname , 'r' ) as f :
288+ obj = f [data_path ]
289+ # If it's a dataset, do a direct read:
290+ if isinstance (obj , h5py .Dataset ):
291+ data = into_array (obj )
292+ return data
293+ # Otherwise, if it's a group, do tile stitching:
294+ elif isinstance (obj , h5py .Group ):
295+ # Prepare for stitching:
296+ get_data_attr = lambda attr_name : self .get_attr (f , data_path , attr_name )
297+ is_uint8 = get_data_attr ('ScalarType' )
298+ # Use proper numpy dtypes:
299+ data_type = np .uint8 if is_uint8 else np .uint16
300+ data = zeros (self .data_shape , dtype = data_type )
301+ tile_path_list = [p for p in obj .keys () if re .match (r'^TILE_\d+$' , p )]
302+ tile_path_list .sort ()
303+ for tile in tile_path_list :
304+ tile_path = f'{ data_path } /{ tile } '
305+ get_tile_attr = lambda attr_name : self .get_attr (f , tile_path , attr_name )
306+ if get_tile_attr ('SamplingStep' ) != 1 :
307+ continue # skip unsupported tiles
308+ # For each axis, get the tile’s placement info:
309+ offset = [get_tile_attr (f'DataIndexOffsetPoint{ axis } ' ) for axis in ('Z' , 'Y' , 'X' )[3 - self .data_ndim :]]
310+ last_idx = [get_tile_attr (f'DataIndexLastPoint{ axis } ' ) for axis in ('Z' , 'Y' , 'X' )[3 - self .data_ndim :]]
311+ mapping_range = tuple (slice (o , l + 1 ) for o , l in zip (offset , last_idx ))
312+ valid_range = tuple (slice (0 , l - o + 1 ) for o , l in zip (offset , last_idx ))
313+ tile_data = into_array (f [tile_path ])[valid_range ]
314+ data [mapping_range ] += tile_data
315+ return data
316+ else :
317+ raise TypeError ("Unexpected HDF5 object type at data_path" )
0 commit comments