def parse_coordinate_groups(tileset: TileSet) -> List["CropParameters"]: """Takes a tileset and compares the physical coordinates on each tile to create aligned coordinate groups (groups of tiles that have the same physical coordinates) Returns ------- A list of CropParameters. Each entry describes the r/ch/z values of tiles that are aligned (have matching coordinates) """ coord_groups: OrderedDict[tuple, CropParameters] = OrderedDict() for tile in tileset.tiles(): x_y_coords = (tile.coordinates[Coordinates.X][0], tile.coordinates[Coordinates.X][1], tile.coordinates[Coordinates.Y][0], tile.coordinates[Coordinates.Y][1]) # A tile with this (x, y) has already been seen, add tile's Indices to CropParameters if x_y_coords in coord_groups: crop_params = coord_groups[x_y_coords] crop_params._add_permitted_axes(Axes.CH, tile.indices[Axes.CH]) crop_params._add_permitted_axes(Axes.ROUND, tile.indices[Axes.ROUND]) if Axes.ZPLANE in tile.indices: crop_params._add_permitted_axes(Axes.ZPLANE, tile.indices[Axes.ZPLANE]) else: coord_groups[x_y_coords] = CropParameters( permitted_chs=[tile.indices[Axes.CH]], permitted_rounds=[tile.indices[Axes.ROUND]], permitted_zplanes=[tile.indices[Axes.ZPLANE]] if Axes.ZPLANE in tile.indices else None) return list(coord_groups.values())
def __init__(self, tileset: TileSet) -> None: self.tiles: MutableMapping[TileKey, Tile] = dict() for tile in tileset.tiles(): key = TileKey( round=tile.indices[Axes.ROUND], ch=tile.indices[Axes.CH], zplane=tile.indices.get(Axes.ZPLANE, 0)) self.tiles[key] = tile self._extras = tileset.extras self._expectations = _TileSetConsistencyDetector()
def __init__(self, tileset: TileSet) -> None: tile_extras: MutableMapping[TileKey, dict] = dict() for tile in tileset.tiles(): round_ = tile.indices[Indices.ROUND] ch = tile.indices[Indices.CH] z = tile.indices.get(Indices.Z, 0) tile_extras[TileKey(round=round_, ch=ch, z=z)] = tile.extras self.tile_extras: Mapping[TileKey, dict] = tile_extras self._extras = tileset.extras
def __init__(self, tileset: TileSet) -> None: self._tile_shape = tileset.default_tile_shape self.tiles: MutableMapping[TileKey, Tile] = dict() for tile in tileset.tiles(): key = TileKey(round=tile.indices[Axes.ROUND], ch=tile.indices[Axes.CH], zplane=tile.indices.get(Axes.ZPLANE, 0)) self.tiles[key] = tile # if we don't have the tile shape, then we peek at the tile and get its shape. if self._tile_shape is None: self._tile_shape = tile.tile_shape self._extras = tileset.extras
def parse_aligned_groups( tileset: TileSet, rounds: Optional[Collection[int]] = None, chs: Optional[Collection[int]] = None, zplanes: Optional[Collection[int]] = None, x: Optional[Union[int, slice]] = None, y: Optional[Union[int, slice]] = None) -> List["CropParameters"]: """Takes a tileset and any optional selected axes lists compares the physical coordinates on each tile to create aligned coordinate groups (groups of tiles that have the same physical coordinates) Parameters ---------- tileset: TileSet The TileSet to parse rounds: Optional[Collection[int]] The rounds in the tileset to include in the final aligned groups. If this is not set, then all rounds are included. chs: Optional[Collection[int]] The chs in the tileset to include in the final aligned groups. If this is not set, then all chs are included. zplanes: Optional[Collection[int]] The zplanes in the tileset to include in the final aligned groups. If this is not set, then all zplanes are included. x: Optional[Union[int, slice]] The x-range in the x-y tile to include in the final aligned groups. If this is not set, then the entire x-y tile is included. y: Optional[Union[int, slice]] The y-range in the x-y tile to include in the final aligned groups. If this is not set, then the entire x-y tile is included. Returns ------- List["CropParameters"] A list of CropParameters. Each entry describes the r/ch/z values of tiles that are aligned (have matching coordinates) and are within the selected_axes if provided. """ coord_groups: OrderedDict[tuple, CropParameters] = OrderedDict() for tile in tileset.tiles(): if CropParameters.tile_in_selected_axes(tile, rounds, chs, zplanes): x_y_coords = (tile.coordinates[Coordinates.X][0], tile.coordinates[Coordinates.X][1], tile.coordinates[Coordinates.Y][0], tile.coordinates[Coordinates.Y][1]) # A tile with this (x, y) has already been seen, add tile's indices to # CropParameters if x_y_coords in coord_groups: crop_params = coord_groups[x_y_coords] crop_params._add_permitted_axes(Axes.CH, tile.indices[Axes.CH]) crop_params._add_permitted_axes(Axes.ROUND, tile.indices[Axes.ROUND]) if Axes.ZPLANE in tile.indices: crop_params._add_permitted_axes( Axes.ZPLANE, tile.indices[Axes.ZPLANE]) else: coord_groups[x_y_coords] = CropParameters( permitted_chs=[tile.indices[Axes.CH]], permitted_rounds=[tile.indices[Axes.ROUND]], permitted_zplanes=[tile.indices[Axes.ZPLANE]] if Axes.ZPLANE in tile.indices else None, x_slice=x, y_slice=y) return list(coord_groups.values())
def __init__(self, tileset: TileSet) -> None: self._num_rounds = ImageStack._get_dimension_size( tileset, Indices.ROUND) self._num_chs = ImageStack._get_dimension_size(tileset, Indices.CH) self._num_zlayers = ImageStack._get_dimension_size(tileset, Indices.Z) self._tile_metadata = TileSetData(tileset) self._tile_shape = tileset.default_tile_shape # Examine the tiles to figure out the right kind (int, float, etc.) and size. We require # that all the tiles have the same kind of data type, but we do not require that they all # have the same size of data type. The # allocated array is the highest size we encounter. kind = None max_size = 0 for tile in tqdm(tileset.tiles(), disable=(not StarfishConfig().verbose)): dtype = tile.numpy_array.dtype if kind is None: kind = dtype.kind else: if kind != dtype.kind: raise TypeError( "All tiles should have the same kind of dtype") if dtype.itemsize > max_size: max_size = dtype.itemsize if self._tile_shape is None: self._tile_shape = tile.tile_shape elif tile.tile_shape is not None and self._tile_shape != tile.tile_shape: raise ValueError( "Starfish does not support tiles that are not identical in shape" ) shape: MutableSequence[int] = [] dims: MutableSequence[str] = [] coordinates_shape: MutableSequence[int] = [] coordinates_dimensions: MutableSequence[str] = [] coordinates_tick_marks: MutableMapping[str, Sequence[Union[int, str]]] = dict() for ix in range(N_AXES): size_for_axis: Optional[int] = None dim_for_axis: Optional[Indices] = None for axis_name, axis_data in AXES_DATA.items(): if ix == axis_data.order: size_for_axis = ImageStack._get_dimension_size( tileset, axis_name) dim_for_axis = axis_name break if size_for_axis is None or dim_for_axis is None: raise ValueError( f"Could not find entry for the {ix}th axis in AXES_DATA") shape.append(size_for_axis) dims.append(dim_for_axis.value) coordinates_shape.append(size_for_axis) coordinates_dimensions.append(dim_for_axis.value) coordinates_tick_marks[dim_for_axis.value] = list( range(size_for_axis)) shape.extend(self._tile_shape) dims.extend([Indices.Y.value, Indices.X.value]) coordinates_shape.append(6) coordinates_dimensions.append(PHYSICAL_COORDINATE_DIMENSION) coordinates_tick_marks[PHYSICAL_COORDINATE_DIMENSION] = [ PhysicalCoordinateTypes.X_MIN.value, PhysicalCoordinateTypes.X_MAX.value, PhysicalCoordinateTypes.Y_MIN.value, PhysicalCoordinateTypes.Y_MAX.value, PhysicalCoordinateTypes.Z_MIN.value, PhysicalCoordinateTypes.Z_MAX.value, ] # now that we know the tile data type (kind and size), we can allocate the data array. self._data = MPDataArray.from_shape_and_dtype( shape=shape, dtype=np.float32, initial_value=0, dims=dims, ) self._coordinates = xr.DataArray( np.empty( shape=coordinates_shape, dtype=np.float32, ), dims=coordinates_dimensions, coords=coordinates_tick_marks, ) # iterate through the tiles and set the data. for tile in tileset.tiles(): h = tile.indices[Indices.ROUND] c = tile.indices[Indices.CH] zlayer = tile.indices.get(Indices.Z, 0) data = tile.numpy_array if max_size != data.dtype.itemsize: warnings.warn( f"Tile " f"(R: {tile.indices[Indices.ROUND]} C: {tile.indices[Indices.CH]} " f"Z: {tile.indices[Indices.Z]}) has " f"dtype {data.dtype}. One or more tiles is of a larger dtype " f"{self._data.dtype}.", DataFormatWarning) data = img_as_float32(data) self.set_slice(indices={ Indices.ROUND: h, Indices.CH: c, Indices.Z: zlayer }, data=data) coordinate_selector = { Indices.ROUND.value: h, Indices.CH.value: c, Indices.Z.value: zlayer, } coordinates_values = [ tile.coordinates[Coordinates.X][0], tile.coordinates[Coordinates.X][1], tile.coordinates[Coordinates.Y][0], tile.coordinates[Coordinates.Y][1], ] if Coordinates.Z in tile.coordinates: coordinates_values.extend([ tile.coordinates[Coordinates.Z][0], tile.coordinates[Coordinates.Z][1], ]) else: coordinates_values.extend([np.nan, np.nan]) self._coordinates.loc[coordinate_selector] = np.array( coordinates_values)