def get_tile(self, r: int, ch: int, z: int) -> TileData: return SlicedImageTile( self.tiles[TileKey(round=r, ch=ch, zplane=z)], r, ch, z, )
def keys(self) -> Collection[TileKey]: """Returns a Collection of the TileKey's for all the tiles.""" return [ TileKey(round=round_label, ch=ch_label, zplane=zplane_label) for round_label in self._rounds for ch_label in self._chs for zplane_label in self._zplanes ]
def keys(self) -> Collection[TileKey]: """Returns a Collection of the TileKey's for all the tiles.""" axes_sizes = join_axes_labels( self._axes_order, rounds=self._rounds, chs=self._chs, zplanes=self._zplanes) return [ TileKey(round=selector[Axes.ROUND], ch=selector[Axes.CH], zplane=selector[Axes.ZPLANE]) for selector in ordered_iterator(axes_sizes) ]
def tile_metadata(self) -> pd.DataFrame: """return a table containing Tile metadata Returns ------- pd.DataFrame : dataframe containing per-tile metadata information for each image. Guaranteed to include information on channel, imaging round, z plane, and barcode index. Also contains any information stored in the extras field for each tile. """ data: collections.defaultdict = collections.defaultdict(list) extras_keys: Set[str] = set() if self._tile_data is not None: tilekeys = self._tile_data.keys() extras_keys = set( key for tilekey in tilekeys for key in self._tile_data[tilekey].keys()) index_keys = set( key.value for key in AXES_DATA.keys() ) duplicate_keys = index_keys.intersection(extras_keys) if len(duplicate_keys) > 0: duplicate_keys_str = ", ".join([str(key) for key in duplicate_keys]) raise ValueError( f"keys ({duplicate_keys_str}) was found in both the Tile specification and extras " f"field. Tile specification keys may not be duplicated in the extras field.") for selector in self._iter_axes({Axes.ROUND, Axes.CH, Axes.ZPLANE}): tilekey = TileKey( round=selector[Axes.ROUND], ch=selector[Axes.CH], zplane=selector[Axes.ZPLANE]) extras = self._tile_data[tilekey] if self._tile_data else {} for index, index_value in selector.items(): data[index.value].append(index_value) for k in extras_keys: data[k].append(extras.get(k, None)) if 'barcode_index' not in extras: barcode_index = ((((selector[Axes.ZPLANE] * self.num_rounds) + selector[Axes.ROUND]) * self.num_chs) + selector[Axes.CH]) data['barcode_index'].append(barcode_index) return pd.DataFrame(data)
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 keys(self) -> Collection[TileKey]: """Returns a Collection of the TileKey's for all the tiles.""" keys: MutableSequence[TileKey] = list() axis_names: MutableSequence[Axes] = list() labels: MutableSequence[Sequence[int]] = list() for index_name, index_labels in self.index_labels.items(): axis_names.append(index_name) labels.append(index_labels) for indices in product(*labels): selector: MutableMapping[Axes, int] = dict() for index_name, index_value in zip(axis_names, indices): selector[index_name] = index_value keys.append( TileKey( round=selector[Axes.ROUND], ch=selector[Axes.CH], zplane=selector[Axes.ZPLANE], )) return keys
def export(self, filepath: str, tile_opener=None, tile_format: ImageFormat = ImageFormat.NUMPY) -> None: """write the image tensor to disk in spaceTx format Parameters ---------- filepath : str Path + prefix for the images and primary_images.json written by this function tile_opener : TODO ttung: doc me. tile_format : ImageFormat Format in which each 2D plane should be written. """ # Add log data to extras self._tile_data.extras[STARFISH_EXTRAS_KEY] = logging.LogEncoder( ).encode({LOG: self.log}) tileset = TileSet( dimensions={ Axes.ROUND, Axes.CH, Axes.ZPLANE, Axes.Y, Axes.X, }, shape={ Axes.ROUND: self.num_rounds, Axes.CH: self.num_chs, Axes.ZPLANE: self.num_zplanes, }, default_tile_shape={ Axes.Y: self.tile_shape[0], Axes.X: self.tile_shape[1] }, extras=self._tile_data.extras, ) for axis_val_map in self._iter_axes({Axes.ROUND, Axes.CH, Axes.ZPLANE}): tilekey = TileKey(round=axis_val_map[Axes.ROUND], ch=axis_val_map[Axes.CH], zplane=axis_val_map[Axes.ZPLANE]) round_, ch, zplane = tilekey.round, tilekey.ch, tilekey.z extras: dict = self._tile_data[tilekey] selector = { Axes.ROUND: round_, Axes.CH: ch, Axes.ZPLANE: zplane, } coordinates: MutableMapping[Coordinates, Union[Tuple[Number, Number], Number]] = dict() x_coordinates = (float(self.xarray[Coordinates.X.value][0]), float(self.xarray[Coordinates.X.value][-1])) y_coordinates = (float(self.xarray[Coordinates.Y.value][0]), float(self.xarray[Coordinates.Y.value][-1])) coordinates[Coordinates.X] = x_coordinates coordinates[Coordinates.Y] = y_coordinates if Coordinates.Z in self.xarray.coords: # set the z coord to the calculated value from the associated z plane z_coordinates = float(self.xarray[Coordinates.Z.value][zplane]) coordinates[Coordinates.Z] = z_coordinates tile = Tile( coordinates=coordinates, indices=selector, extras=extras, ) tile.numpy_array, _ = self.get_slice(selector={ Axes.ROUND: round_, Axes.CH: ch, Axes.ZPLANE: zplane }) tileset.add_tile(tile) if tile_opener is None: def tile_opener(tileset_path: Path, tile, ext): base = tileset_path.parent / tileset_path.stem if Axes.ZPLANE in tile.indices: zval = tile.indices[Axes.ZPLANE] zstr = "-Z{}".format(zval) else: zstr = "" return open( "{}-H{}-C{}{}.{}".format( str(base), tile.indices[Axes.ROUND], tile.indices[Axes.CH], zstr, ext, ), "wb") if not filepath.endswith('.json'): filepath += '.json' Writer.write_to_path(tileset, filepath, pretty=True, tile_opener=tile_opener, tile_format=tile_format)
def export(self, filepath: str, tile_opener: Optional[Callable[[PurePath, Tile, str], BinaryIO]] = None, tile_format: ImageFormat=ImageFormat.NUMPY) -> None: """write the image tensor to disk in spaceTx format Parameters ---------- filepath : str Path + prefix for the images and primary_images.json written by this function tile_opener : Optional[Callable[[PurePath, Tile, str], BinaryIO]] A callable responsible for opening the file that a tile's data is to be written to. The callable should accept three arguments -- the path of the tileset, the tile data, and the expected file extension. If this is not specified, a reasonable default is provided. tile_format : ImageFormat Format in which each 2D plane should be written. """ # Add log data to extras tileset_extras = self._tile_data.extras if self._tile_data else {} tileset_extras[STARFISH_EXTRAS_KEY] = self.log.encode() tileset = TileSet( dimensions={ Axes.ROUND, Axes.CH, Axes.ZPLANE, Axes.Y, Axes.X, }, shape={ Axes.ROUND: self.num_rounds, Axes.CH: self.num_chs, Axes.ZPLANE: self.num_zplanes, }, default_tile_shape={Axes.Y: self.tile_shape[0], Axes.X: self.tile_shape[1]}, extras=tileset_extras, ) for selector in self._iter_axes({Axes.ROUND, Axes.CH, Axes.ZPLANE}): tilekey = TileKey( round=selector[Axes.ROUND], ch=selector[Axes.CH], zplane=selector[Axes.ZPLANE]) extras: dict = self._tile_data[tilekey] if self._tile_data else {} coordinates: MutableMapping[Coordinates, Union[Tuple[Number, Number], Number]] = dict() x_coordinates = (float(self.xarray[Coordinates.X.value][0]), float(self.xarray[Coordinates.X.value][-1])) y_coordinates = (float(self.xarray[Coordinates.Y.value][0]), float(self.xarray[Coordinates.Y.value][-1])) coordinates[Coordinates.X] = x_coordinates coordinates[Coordinates.Y] = y_coordinates if Coordinates.Z in self.xarray.coords: # set the z coord to the calculated value from the associated z plane z_coordinates = float(self.xarray[Coordinates.Z.value][selector[Axes.ZPLANE]]) coordinates[Coordinates.Z] = z_coordinates tile = Tile( coordinates=coordinates, indices=selector, extras=extras, ) tile.numpy_array, _ = self.get_slice(selector) tileset.add_tile(tile) if tile_opener is None: def tile_opener(tileset_path: PurePath, tile: Tile, ext: str): base = tileset_path.parent / tileset_path.stem if Axes.ZPLANE in tile.indices: zval = tile.indices[Axes.ZPLANE] zstr = "-Z{}".format(zval) else: zstr = "" return open( "{}-H{}-C{}{}.{}".format( str(base), tile.indices[Axes.ROUND], tile.indices[Axes.CH], zstr, ext, ), "wb") if not filepath.endswith('.json'): filepath += '.json' Writer.write_to_path( tileset, filepath, pretty=True, tile_opener=tile_opener, tile_format=tile_format)