csi_images.csi_tiles API documentation

# csi_images.csi_tiles

Contains the Tile class, which represents a collection of frames at the same position in a scan. The module comes with several helper functions that allow for gathering tiles based on their position in the scan.

View Source

      1"""
      2Contains the Tile class, which represents a collection of frames at the same position
      3in a scan. The module comes with several helper functions that allow for gathering tiles
      4based on their position in the scan.
      5"""
      6
      7from typing import Self, Iterable
      8
      9import numpy as np
     10
     11from csi_images.csi_scans import Scan
     12
     13
     14class Tile:
     15    """
     16    A class that represents a tile in a scan. This class encodes the position of a group
     17    of frames in a scan, based on the scan's metadata. The module comes with several
     18    helper functions that allow for gathering tiles based on their position in the scan.
     19    """
     20
     21    def __init__(self, scan: Scan, coordinates: int | tuple[int, int], n_roi: int = 0):
     22        self.scan = scan
     23
     24        # Check that the n_roi is valid
     25        if n_roi >= len(self.scan.roi):
     26            raise ValueError(f"n_roi {n_roi} is out of bounds for scan.")
     27        self.n_roi = int(n_roi)
     28
     29        # Check that the coordinates are valid
     30        tile_rows = scan.roi[n_roi].tile_rows
     31        tile_cols = scan.roi[n_roi].tile_cols
     32        total_tiles = tile_rows * tile_cols
     33        if np.issubdtype(type(coordinates), np.integer):
     34            # We received "n" as the coordinates
     35            if 0 > coordinates or coordinates > total_tiles:
     36                raise ValueError(
     37                    f"n ({coordinates}) must be between 0 and the "
     38                    f"number of tiles in ROI {self.n_roi} ({total_tiles})."
     39                )
     40            self.n = int(coordinates)
     41            self.x, self.y = self.n_to_position()
     42        elif (
     43            (isinstance(coordinates, tuple) or isinstance(coordinates, list))
     44            and len(coordinates) == 2
     45            and all([np.issubdtype(type(coord), np.integer) for coord in coordinates])
     46        ):
     47            # We received (x, y) as the coordinates
     48            if 0 > coordinates[0] or coordinates[0] >= tile_cols:
     49                raise ValueError(
     50                    f"x ({coordinates[0]}) must be between 0 and the "
     51                    f"number of columns in ROI {self.n_roi} ({tile_cols})."
     52                )
     53            if 0 > coordinates[1] or coordinates[1] >= tile_rows:
     54                raise ValueError(
     55                    f"y ({coordinates[1]}) must be between 0 and the "
     56                    f"number of rows in ROI {self.n_roi} ({tile_rows})."
     57                )
     58            self.x, self.y = int(coordinates[0]), int(coordinates[1])
     59            self.n = self.position_to_n()
     60        else:
     61            raise ValueError(
     62                "Coordinates must be an integer n or a tuple of (x, y) coordinates."
     63            )
     64
     65    def __key(self) -> tuple:
     66        return self.scan.slide_id, self.n_roi, self.n
     67
     68    def __hash__(self) -> int:
     69        return hash(self.__key())
     70
     71    def __repr__(self) -> str:
     72        return f"{self.scan.slide_id}-{self.n_roi}-{self.n}"
     73
     74    def __eq__(self, other) -> bool:
     75        return self.__repr__() == other.__repr__()
     76
     77    # Helper functions that convert ***indices***, which are 0-indexed
     78    def position_to_n(self, position: tuple[int, int] = (-1, -1)) -> int:
     79        """
     80        Convert the x, y coordinates to the n coordinate, based on this tile's scan
     81        metadata and ROI. Can be provided alternative x, y to convert for convenience.
     82        :param position: optional (x, y) coordinates to find the n for.
     83                         If none provided, this tile's (x, y) will be used.
     84        :return: the coordinate n, which depends on the scanner and scan layout.
     85        """
     86        if position == (-1, -1):
     87            position = self.x, self.y
     88        x, y = position
     89        if self.scan.scanner_id.startswith(self.scan.Type.AXIOSCAN7.value):
     90            n = y * self.scan.roi[self.n_roi].tile_cols + x
     91        elif self.scan.scanner_id.startswith(self.scan.Type.BZSCANNER.value):
     92            n = y * self.scan.roi[self.n_roi].tile_cols
     93            if y % 2 == 0:
     94                n += x
     95            else:
     96                n += self.scan.roi[0].tile_cols - x
     97        else:
     98            raise ValueError(f"Scanner type {self.scan.scanner_id} not supported.")
     99        return n
    100
    101    def n_to_position(self, n: int = -1) -> tuple[int, int]:
    102        """
    103        Convert the n coordinate to x, y coordinates, based on this tile's scan
    104        metadata and ROI. Can be provided alternative n to convert for convenience.
    105        :param n: an optional n coordinate to find the position for.
    106                  If none provided, this tile's n will be used.
    107        :return: x, y coordinates of the tile in the scan's coordinate system.
    108        """
    109        if n == -1:
    110            n = self.n
    111        if n < 0:
    112            raise ValueError(f"n ({n}) must be non-negative.")
    113        if self.scan.scanner_id.startswith(self.scan.Type.AXIOSCAN7.value):
    114            x = n % self.scan.roi[0].tile_cols
    115            y = n // self.scan.roi[0].tile_cols
    116            return x, y
    117        elif self.scan.scanner_id.startswith(self.scan.Type.BZSCANNER.value):
    118            y = n // self.scan.roi[0].tile_cols
    119            if y % 2 == 0:
    120                x = n % self.scan.roi[0].tile_cols
    121            else:
    122                x = (self.scan.roi[0].tile_cols - 1) - (n % self.scan.roi[0].tile_cols)
    123        else:
    124            raise ValueError(f"Scanner type {self.scan.scanner_id} not supported.")
    125        return x, y
    126
    127    @classmethod
    128    def get_tiles(
    129        cls,
    130        scan: Scan,
    131        coordinates: Iterable[int] | Iterable[tuple[int, int]] = None,
    132        n_roi: int = 0,
    133        as_flat: bool = True,
    134    ) -> list[Self] | list[list[Self]]:
    135        """
    136        The simplest way to gather a list of Tile objects. By default, it will gather all
    137        tiles in the scan. To gather specific tiles, provide a list of coordinates.
    138        :param scan: the scan metadata.
    139        :param coordinates: a list of n-based indices or (x, y) coordinates.
    140                            Leave as None to include all tiles.
    141        :param n_roi: the region of interest to use. Defaults to 0.
    142        :param as_flat: whether to return a flat list of Tile objects or a list of lists.
    143        :return: if as_flat: a list of Tile objects in the same order as the coordinates;
    144                 if not as_flat: a list of lists of Tile objects in their relative coordinates.
    145        """
    146        if as_flat:
    147            if coordinates is None:
    148                # Populate coordinates with all n's.
    149                coordinates = list(
    150                    range(scan.roi[n_roi].tile_rows * scan.roi[n_roi].tile_cols)
    151                )
    152            tiles = []
    153            for coordinate in coordinates:
    154                tiles.append(cls(scan, coordinate, n_roi))
    155        else:
    156            if coordinates is None:
    157                # Populate coordinates with all (x, y) pairs in row-major order
    158                coordinates = []
    159                for y in range(scan.roi[n_roi].tile_rows):
    160                    for x in range(scan.roi[n_roi].tile_cols):
    161                        coordinates.append((x, y))
    162            # Check that the coordinates are contiguous, otherwise we can't make a grid
    163            # Find the min and max x, y values
    164            x_min = scan.roi[n_roi].tile_cols
    165            x_max = 0
    166            y_min = scan.roi[n_roi].tile_rows
    167            y_max = 0
    168            for x, y in coordinates:
    169                x_min = min(x_min, x)
    170                x_max = max(x_max, x)
    171                y_min = min(y_min, y)
    172                y_max = max(y_max, y)
    173
    174            # Check that the coordinates are contiguous
    175            if (x_max - x_min + 1) * (y_max - y_min + 1) != len(coordinates):
    176                raise ValueError(
    177                    "Coordinates must be a contiguous square to form "
    178                    "a grid; number of coordinates does not match."
    179                )
    180
    181            tiles = [[None] * (x_max - x_min + 1)] * (y_max - y_min + 1)
    182            for coordinate in coordinates:
    183                x, y = coordinate
    184                tiles[y][x] = cls(scan, coordinate, n_roi)
    185
    186        return tiles
    187
    188    @classmethod
    189    def get_tiles_by_row_col(
    190        cls,
    191        scan: Scan,
    192        rows: Iterable[int] = None,
    193        cols: Iterable[int] = None,
    194        n_roi: int = 0,
    195        as_flat: bool = True,
    196    ) -> list[Self] | list[list[Self]]:
    197        """
    198        Gather a list of Tile objects based on the row and column indices provided.
    199        If left as None, it will gather all rows and/or columns.
    200        :param scan: the scan metadata.
    201        :param rows: a list of 0-indexed rows (y-positions) in the scan axes.
    202                     Leave as None to include all rows.
    203        :param cols: a list of 0-indexed columns (x-positions) in the scan axes.
    204                     Leave as None to include all columns.
    205        :param n_roi: the region of interest to use. Defaults to 0.
    206        :param as_flat: whether to return a flat list of Tile objects or a list of lists.
    207        :return: if as_flat: a list of Tile objects in row-major order;
    208                 if not as_flat: a list of lists of Tile objects in their relative coordinates
    209        """
    210        if rows is None:
    211            rows = range(scan.roi[n_roi].tile_rows)
    212        if cols is None:
    213            cols = range(scan.roi[n_roi].tile_cols)
    214
    215        # Populate coordinates
    216        coordinates = []
    217        for row in rows:
    218            for col in cols:
    219                coordinates.append((col, row))
    220
    221        return cls.get_tiles(scan, coordinates, n_roi, as_flat)
    222
    223    @classmethod
    224    def get_tiles_by_xy_bounds(
    225        cls,
    226        scan: Scan,
    227        bounds: tuple[int, int, int, int],
    228        n_roi: int = 0,
    229        as_flat: bool = True,
    230    ) -> list[Self] | list[list[Self]]:
    231        """
    232        Gather a list of Tile objects based on the x, y bounds provided. The bounds are
    233        exclusive, like indices, so the tiles at the corners are NOT included in the list.
    234        :param scan: the scan metadata.
    235        :param bounds: a tuple of (x_0, y_0, x_1, y_1) in the scan axes.
    236        :param n_roi: the region of interest to use. Defaults to 0.
    237        :param as_flat: whether to return a flat list of Tile objects or a list of lists.
    238        :return: if as_flat: a list of Tile objects in row-major order;
    239                 if not as_flat: a list of lists of Tile objects in their relative coordinates
    240        """
    241        x_0, y_0, x_1, y_1 = bounds
    242        coordinates = []
    243        for y in range(y_0, y_1):
    244            for x in range(x_0, x_1):
    245                coordinates.append((x, y))
    246        return cls.get_tiles(scan, coordinates, n_roi, as_flat)

class Tile: View Source

     15class Tile:
     16    """
     17    A class that represents a tile in a scan. This class encodes the position of a group
     18    of frames in a scan, based on the scan's metadata. The module comes with several
     19    helper functions that allow for gathering tiles based on their position in the scan.
     20    """
     21
     22    def __init__(self, scan: Scan, coordinates: int | tuple[int, int], n_roi: int = 0):
     23        self.scan = scan
     24
     25        # Check that the n_roi is valid
     26        if n_roi >= len(self.scan.roi):
     27            raise ValueError(f"n_roi {n_roi} is out of bounds for scan.")
     28        self.n_roi = int(n_roi)
     29
     30        # Check that the coordinates are valid
     31        tile_rows = scan.roi[n_roi].tile_rows
     32        tile_cols = scan.roi[n_roi].tile_cols
     33        total_tiles = tile_rows * tile_cols
     34        if np.issubdtype(type(coordinates), np.integer):
     35            # We received "n" as the coordinates
     36            if 0 > coordinates or coordinates > total_tiles:
     37                raise ValueError(
     38                    f"n ({coordinates}) must be between 0 and the "
     39                    f"number of tiles in ROI {self.n_roi} ({total_tiles})."
     40                )
     41            self.n = int(coordinates)
     42            self.x, self.y = self.n_to_position()
     43        elif (
     44            (isinstance(coordinates, tuple) or isinstance(coordinates, list))
     45            and len(coordinates) == 2
     46            and all([np.issubdtype(type(coord), np.integer) for coord in coordinates])
     47        ):
     48            # We received (x, y) as the coordinates
     49            if 0 > coordinates[0] or coordinates[0] >= tile_cols:
     50                raise ValueError(
     51                    f"x ({coordinates[0]}) must be between 0 and the "
     52                    f"number of columns in ROI {self.n_roi} ({tile_cols})."
     53                )
     54            if 0 > coordinates[1] or coordinates[1] >= tile_rows:
     55                raise ValueError(
     56                    f"y ({coordinates[1]}) must be between 0 and the "
     57                    f"number of rows in ROI {self.n_roi} ({tile_rows})."
     58                )
     59            self.x, self.y = int(coordinates[0]), int(coordinates[1])
     60            self.n = self.position_to_n()
     61        else:
     62            raise ValueError(
     63                "Coordinates must be an integer n or a tuple of (x, y) coordinates."
     64            )
     65
     66    def __key(self) -> tuple:
     67        return self.scan.slide_id, self.n_roi, self.n
     68
     69    def __hash__(self) -> int:
     70        return hash(self.__key())
     71
     72    def __repr__(self) -> str:
     73        return f"{self.scan.slide_id}-{self.n_roi}-{self.n}"
     74
     75    def __eq__(self, other) -> bool:
     76        return self.__repr__() == other.__repr__()
     77
     78    # Helper functions that convert ***indices***, which are 0-indexed
     79    def position_to_n(self, position: tuple[int, int] = (-1, -1)) -> int:
     80        """
     81        Convert the x, y coordinates to the n coordinate, based on this tile's scan
     82        metadata and ROI. Can be provided alternative x, y to convert for convenience.
     83        :param position: optional (x, y) coordinates to find the n for.
     84                         If none provided, this tile's (x, y) will be used.
     85        :return: the coordinate n, which depends on the scanner and scan layout.
     86        """
     87        if position == (-1, -1):
     88            position = self.x, self.y
     89        x, y = position
     90        if self.scan.scanner_id.startswith(self.scan.Type.AXIOSCAN7.value):
     91            n = y * self.scan.roi[self.n_roi].tile_cols + x
     92        elif self.scan.scanner_id.startswith(self.scan.Type.BZSCANNER.value):
     93            n = y * self.scan.roi[self.n_roi].tile_cols
     94            if y % 2 == 0:
     95                n += x
     96            else:
     97                n += self.scan.roi[0].tile_cols - x
     98        else:
     99            raise ValueError(f"Scanner type {self.scan.scanner_id} not supported.")
    100        return n
    101
    102    def n_to_position(self, n: int = -1) -> tuple[int, int]:
    103        """
    104        Convert the n coordinate to x, y coordinates, based on this tile's scan
    105        metadata and ROI. Can be provided alternative n to convert for convenience.
    106        :param n: an optional n coordinate to find the position for.
    107                  If none provided, this tile's n will be used.
    108        :return: x, y coordinates of the tile in the scan's coordinate system.
    109        """
    110        if n == -1:
    111            n = self.n
    112        if n < 0:
    113            raise ValueError(f"n ({n}) must be non-negative.")
    114        if self.scan.scanner_id.startswith(self.scan.Type.AXIOSCAN7.value):
    115            x = n % self.scan.roi[0].tile_cols
    116            y = n // self.scan.roi[0].tile_cols
    117            return x, y
    118        elif self.scan.scanner_id.startswith(self.scan.Type.BZSCANNER.value):
    119            y = n // self.scan.roi[0].tile_cols
    120            if y % 2 == 0:
    121                x = n % self.scan.roi[0].tile_cols
    122            else:
    123                x = (self.scan.roi[0].tile_cols - 1) - (n % self.scan.roi[0].tile_cols)
    124        else:
    125            raise ValueError(f"Scanner type {self.scan.scanner_id} not supported.")
    126        return x, y
    127
    128    @classmethod
    129    def get_tiles(
    130        cls,
    131        scan: Scan,
    132        coordinates: Iterable[int] | Iterable[tuple[int, int]] = None,
    133        n_roi: int = 0,
    134        as_flat: bool = True,
    135    ) -> list[Self] | list[list[Self]]:
    136        """
    137        The simplest way to gather a list of Tile objects. By default, it will gather all
    138        tiles in the scan. To gather specific tiles, provide a list of coordinates.
    139        :param scan: the scan metadata.
    140        :param coordinates: a list of n-based indices or (x, y) coordinates.
    141                            Leave as None to include all tiles.
    142        :param n_roi: the region of interest to use. Defaults to 0.
    143        :param as_flat: whether to return a flat list of Tile objects or a list of lists.
    144        :return: if as_flat: a list of Tile objects in the same order as the coordinates;
    145                 if not as_flat: a list of lists of Tile objects in their relative coordinates.
    146        """
    147        if as_flat:
    148            if coordinates is None:
    149                # Populate coordinates with all n's.
    150                coordinates = list(
    151                    range(scan.roi[n_roi].tile_rows * scan.roi[n_roi].tile_cols)
    152                )
    153            tiles = []
    154            for coordinate in coordinates:
    155                tiles.append(cls(scan, coordinate, n_roi))
    156        else:
    157            if coordinates is None:
    158                # Populate coordinates with all (x, y) pairs in row-major order
    159                coordinates = []
    160                for y in range(scan.roi[n_roi].tile_rows):
    161                    for x in range(scan.roi[n_roi].tile_cols):
    162                        coordinates.append((x, y))
    163            # Check that the coordinates are contiguous, otherwise we can't make a grid
    164            # Find the min and max x, y values
    165            x_min = scan.roi[n_roi].tile_cols
    166            x_max = 0
    167            y_min = scan.roi[n_roi].tile_rows
    168            y_max = 0
    169            for x, y in coordinates:
    170                x_min = min(x_min, x)
    171                x_max = max(x_max, x)
    172                y_min = min(y_min, y)
    173                y_max = max(y_max, y)
    174
    175            # Check that the coordinates are contiguous
    176            if (x_max - x_min + 1) * (y_max - y_min + 1) != len(coordinates):
    177                raise ValueError(
    178                    "Coordinates must be a contiguous square to form "
    179                    "a grid; number of coordinates does not match."
    180                )
    181
    182            tiles = [[None] * (x_max - x_min + 1)] * (y_max - y_min + 1)
    183            for coordinate in coordinates:
    184                x, y = coordinate
    185                tiles[y][x] = cls(scan, coordinate, n_roi)
    186
    187        return tiles
    188
    189    @classmethod
    190    def get_tiles_by_row_col(
    191        cls,
    192        scan: Scan,
    193        rows: Iterable[int] = None,
    194        cols: Iterable[int] = None,
    195        n_roi: int = 0,
    196        as_flat: bool = True,
    197    ) -> list[Self] | list[list[Self]]:
    198        """
    199        Gather a list of Tile objects based on the row and column indices provided.
    200        If left as None, it will gather all rows and/or columns.
    201        :param scan: the scan metadata.
    202        :param rows: a list of 0-indexed rows (y-positions) in the scan axes.
    203                     Leave as None to include all rows.
    204        :param cols: a list of 0-indexed columns (x-positions) in the scan axes.
    205                     Leave as None to include all columns.
    206        :param n_roi: the region of interest to use. Defaults to 0.
    207        :param as_flat: whether to return a flat list of Tile objects or a list of lists.
    208        :return: if as_flat: a list of Tile objects in row-major order;
    209                 if not as_flat: a list of lists of Tile objects in their relative coordinates
    210        """
    211        if rows is None:
    212            rows = range(scan.roi[n_roi].tile_rows)
    213        if cols is None:
    214            cols = range(scan.roi[n_roi].tile_cols)
    215
    216        # Populate coordinates
    217        coordinates = []
    218        for row in rows:
    219            for col in cols:
    220                coordinates.append((col, row))
    221
    222        return cls.get_tiles(scan, coordinates, n_roi, as_flat)
    223
    224    @classmethod
    225    def get_tiles_by_xy_bounds(
    226        cls,
    227        scan: Scan,
    228        bounds: tuple[int, int, int, int],
    229        n_roi: int = 0,
    230        as_flat: bool = True,
    231    ) -> list[Self] | list[list[Self]]:
    232        """
    233        Gather a list of Tile objects based on the x, y bounds provided. The bounds are
    234        exclusive, like indices, so the tiles at the corners are NOT included in the list.
    235        :param scan: the scan metadata.
    236        :param bounds: a tuple of (x_0, y_0, x_1, y_1) in the scan axes.
    237        :param n_roi: the region of interest to use. Defaults to 0.
    238        :param as_flat: whether to return a flat list of Tile objects or a list of lists.
    239        :return: if as_flat: a list of Tile objects in row-major order;
    240                 if not as_flat: a list of lists of Tile objects in their relative coordinates
    241        """
    242        x_0, y_0, x_1, y_1 = bounds
    243        coordinates = []
    244        for y in range(y_0, y_1):
    245            for x in range(x_0, x_1):
    246                coordinates.append((x, y))
    247        return cls.get_tiles(scan, coordinates, n_roi, as_flat)

A class that represents a tile in a scan. This class encodes the position of a group of frames in a scan, based on the scan's metadata. The module comes with several helper functions that allow for gathering tiles based on their position in the scan.

Tile( scan: csi_images.csi_scans.Scan, coordinates: int | tuple[int, int], n_roi: int = 0) View Source

    22    def __init__(self, scan: Scan, coordinates: int | tuple[int, int], n_roi: int = 0):
    23        self.scan = scan
    24
    25        # Check that the n_roi is valid
    26        if n_roi >= len(self.scan.roi):
    27            raise ValueError(f"n_roi {n_roi} is out of bounds for scan.")
    28        self.n_roi = int(n_roi)
    29
    30        # Check that the coordinates are valid
    31        tile_rows = scan.roi[n_roi].tile_rows
    32        tile_cols = scan.roi[n_roi].tile_cols
    33        total_tiles = tile_rows * tile_cols
    34        if np.issubdtype(type(coordinates), np.integer):
    35            # We received "n" as the coordinates
    36            if 0 > coordinates or coordinates > total_tiles:
    37                raise ValueError(
    38                    f"n ({coordinates}) must be between 0 and the "
    39                    f"number of tiles in ROI {self.n_roi} ({total_tiles})."
    40                )
    41            self.n = int(coordinates)
    42            self.x, self.y = self.n_to_position()
    43        elif (
    44            (isinstance(coordinates, tuple) or isinstance(coordinates, list))
    45            and len(coordinates) == 2
    46            and all([np.issubdtype(type(coord), np.integer) for coord in coordinates])
    47        ):
    48            # We received (x, y) as the coordinates
    49            if 0 > coordinates[0] or coordinates[0] >= tile_cols:
    50                raise ValueError(
    51                    f"x ({coordinates[0]}) must be between 0 and the "
    52                    f"number of columns in ROI {self.n_roi} ({tile_cols})."
    53                )
    54            if 0 > coordinates[1] or coordinates[1] >= tile_rows:
    55                raise ValueError(
    56                    f"y ({coordinates[1]}) must be between 0 and the "
    57                    f"number of rows in ROI {self.n_roi} ({tile_rows})."
    58                )
    59            self.x, self.y = int(coordinates[0]), int(coordinates[1])
    60            self.n = self.position_to_n()
    61        else:
    62            raise ValueError(
    63                "Coordinates must be an integer n or a tuple of (x, y) coordinates."
    64            )

scan
n_roi
def position_to_n(self, position: tuple[int, int] = (-1, -1)) -> int: View Source

     79    def position_to_n(self, position: tuple[int, int] = (-1, -1)) -> int:
     80        """
     81        Convert the x, y coordinates to the n coordinate, based on this tile's scan
     82        metadata and ROI. Can be provided alternative x, y to convert for convenience.
     83        :param position: optional (x, y) coordinates to find the n for.
     84                         If none provided, this tile's (x, y) will be used.
     85        :return: the coordinate n, which depends on the scanner and scan layout.
     86        """
     87        if position == (-1, -1):
     88            position = self.x, self.y
     89        x, y = position
     90        if self.scan.scanner_id.startswith(self.scan.Type.AXIOSCAN7.value):
     91            n = y * self.scan.roi[self.n_roi].tile_cols + x
     92        elif self.scan.scanner_id.startswith(self.scan.Type.BZSCANNER.value):
     93            n = y * self.scan.roi[self.n_roi].tile_cols
     94            if y % 2 == 0:
     95                n += x
     96            else:
     97                n += self.scan.roi[0].tile_cols - x
     98        else:
     99            raise ValueError(f"Scanner type {self.scan.scanner_id} not supported.")
    100        return n

Convert the x, y coordinates to the n coordinate, based on this tile's scan metadata and ROI. Can be provided alternative x, y to convert for convenience.

###### Parameters

- position: optional (x, y) coordinates to find the n for. If none provided, this tile's (x, y) will be used.

###### Returns

> the coordinate n, which depends on the scanner and scan layout.

def n_to_position(self, n: int = -1) -> tuple[int, int]: View Source

    102    def n_to_position(self, n: int = -1) -> tuple[int, int]:
    103        """
    104        Convert the n coordinate to x, y coordinates, based on this tile's scan
    105        metadata and ROI. Can be provided alternative n to convert for convenience.
    106        :param n: an optional n coordinate to find the position for.
    107                  If none provided, this tile's n will be used.
    108        :return: x, y coordinates of the tile in the scan's coordinate system.
    109        """
    110        if n == -1:
    111            n = self.n
    112        if n < 0:
    113            raise ValueError(f"n ({n}) must be non-negative.")
    114        if self.scan.scanner_id.startswith(self.scan.Type.AXIOSCAN7.value):
    115            x = n % self.scan.roi[0].tile_cols
    116            y = n // self.scan.roi[0].tile_cols
    117            return x, y
    118        elif self.scan.scanner_id.startswith(self.scan.Type.BZSCANNER.value):
    119            y = n // self.scan.roi[0].tile_cols
    120            if y % 2 == 0:
    121                x = n % self.scan.roi[0].tile_cols
    122            else:
    123                x = (self.scan.roi[0].tile_cols - 1) - (n % self.scan.roi[0].tile_cols)
    124        else:
    125            raise ValueError(f"Scanner type {self.scan.scanner_id} not supported.")
    126        return x, y

Convert the n coordinate to x, y coordinates, based on this tile's scan metadata and ROI. Can be provided alternative n to convert for convenience.

###### Parameters

- n: an optional n coordinate to find the position for. If none provided, this tile's n will be used.

###### Returns

> x, y coordinates of the tile in the scan's coordinate system.

@classmethod
def get_tiles( cls, scan: csi_images.csi_scans.Scan, coordinates: Union[Iterable[int], Iterable[tuple[int, int]]] = None, n_roi: int = 0, as_flat: bool = True) -> list[typing.Self] | list[list[typing.Self]]: View Source

    128    @classmethod
    129    def get_tiles(
    130        cls,
    131        scan: Scan,
    132        coordinates: Iterable[int] | Iterable[tuple[int, int]] = None,
    133        n_roi: int = 0,
    134        as_flat: bool = True,
    135    ) -> list[Self] | list[list[Self]]:
    136        """
    137        The simplest way to gather a list of Tile objects. By default, it will gather all
    138        tiles in the scan. To gather specific tiles, provide a list of coordinates.
    139        :param scan: the scan metadata.
    140        :param coordinates: a list of n-based indices or (x, y) coordinates.
    141                            Leave as None to include all tiles.
    142        :param n_roi: the region of interest to use. Defaults to 0.
    143        :param as_flat: whether to return a flat list of Tile objects or a list of lists.
    144        :return: if as_flat: a list of Tile objects in the same order as the coordinates;
    145                 if not as_flat: a list of lists of Tile objects in their relative coordinates.
    146        """
    147        if as_flat:
    148            if coordinates is None:
    149                # Populate coordinates with all n's.
    150                coordinates = list(
    151                    range(scan.roi[n_roi].tile_rows * scan.roi[n_roi].tile_cols)
    152                )
    153            tiles = []
    154            for coordinate in coordinates:
    155                tiles.append(cls(scan, coordinate, n_roi))
    156        else:
    157            if coordinates is None:
    158                # Populate coordinates with all (x, y) pairs in row-major order
    159                coordinates = []
    160                for y in range(scan.roi[n_roi].tile_rows):
    161                    for x in range(scan.roi[n_roi].tile_cols):
    162                        coordinates.append((x, y))
    163            # Check that the coordinates are contiguous, otherwise we can't make a grid
    164            # Find the min and max x, y values
    165            x_min = scan.roi[n_roi].tile_cols
    166            x_max = 0
    167            y_min = scan.roi[n_roi].tile_rows
    168            y_max = 0
    169            for x, y in coordinates:
    170                x_min = min(x_min, x)
    171                x_max = max(x_max, x)
    172                y_min = min(y_min, y)
    173                y_max = max(y_max, y)
    174
    175            # Check that the coordinates are contiguous
    176            if (x_max - x_min + 1) * (y_max - y_min + 1) != len(coordinates):
    177                raise ValueError(
    178                    "Coordinates must be a contiguous square to form "
    179                    "a grid; number of coordinates does not match."
    180                )
    181
    182            tiles = [[None] * (x_max - x_min + 1)] * (y_max - y_min + 1)
    183            for coordinate in coordinates:
    184                x, y = coordinate
    185                tiles[y][x] = cls(scan, coordinate, n_roi)
    186
    187        return tiles

The simplest way to gather a list of Tile objects. By default, it will gather all tiles in the scan. To gather specific tiles, provide a list of coordinates.

###### Parameters

- scan: the scan metadata.
- coordinates: a list of n-based indices or (x, y) coordinates. Leave as None to include all tiles.
- n_roi: the region of interest to use. Defaults to 0.
- as_flat: whether to return a flat list of Tile objects or a list of lists.

###### Returns

> if as_flat: a list of Tile objects in the same order as the coordinates; if not as_flat: a list of lists of Tile objects in their relative coordinates.

@classmethod
def get_tiles_by_row_col( cls, scan: csi_images.csi_scans.Scan, rows: Iterable[int] = None, cols: Iterable[int] = None, n_roi: int = 0, as_flat: bool = True) -> list[typing.Self] | list[list[typing.Self]]: View Source

    189    @classmethod
    190    def get_tiles_by_row_col(
    191        cls,
    192        scan: Scan,
    193        rows: Iterable[int] = None,
    194        cols: Iterable[int] = None,
    195        n_roi: int = 0,
    196        as_flat: bool = True,
    197    ) -> list[Self] | list[list[Self]]:
    198        """
    199        Gather a list of Tile objects based on the row and column indices provided.
    200        If left as None, it will gather all rows and/or columns.
    201        :param scan: the scan metadata.
    202        :param rows: a list of 0-indexed rows (y-positions) in the scan axes.
    203                     Leave as None to include all rows.
    204        :param cols: a list of 0-indexed columns (x-positions) in the scan axes.
    205                     Leave as None to include all columns.
    206        :param n_roi: the region of interest to use. Defaults to 0.
    207        :param as_flat: whether to return a flat list of Tile objects or a list of lists.
    208        :return: if as_flat: a list of Tile objects in row-major order;
    209                 if not as_flat: a list of lists of Tile objects in their relative coordinates
    210        """
    211        if rows is None:
    212            rows = range(scan.roi[n_roi].tile_rows)
    213        if cols is None:
    214            cols = range(scan.roi[n_roi].tile_cols)
    215
    216        # Populate coordinates
    217        coordinates = []
    218        for row in rows:
    219            for col in cols:
    220                coordinates.append((col, row))
    221
    222        return cls.get_tiles(scan, coordinates, n_roi, as_flat)

Gather a list of Tile objects based on the row and column indices provided. If left as None, it will gather all rows and/or columns.

###### Parameters

- scan: the scan metadata.
- rows: a list of 0-indexed rows (y-positions) in the scan axes. Leave as None to include all rows.
- cols: a list of 0-indexed columns (x-positions) in the scan axes. Leave as None to include all columns.
- n_roi: the region of interest to use. Defaults to 0.
- as_flat: whether to return a flat list of Tile objects or a list of lists.

###### Returns

> if as_flat: a list of Tile objects in row-major order; if not as_flat: a list of lists of Tile objects in their relative coordinates

@classmethod
def get_tiles_by_xy_bounds( cls, scan: csi_images.csi_scans.Scan, bounds: tuple[int, int, int, int], n_roi: int = 0, as_flat: bool = True) -> list[typing.Self] | list[list[typing.Self]]: View Source

    224    @classmethod
    225    def get_tiles_by_xy_bounds(
    226        cls,
    227        scan: Scan,
    228        bounds: tuple[int, int, int, int],
    229        n_roi: int = 0,
    230        as_flat: bool = True,
    231    ) -> list[Self] | list[list[Self]]:
    232        """
    233        Gather a list of Tile objects based on the x, y bounds provided. The bounds are
    234        exclusive, like indices, so the tiles at the corners are NOT included in the list.
    235        :param scan: the scan metadata.
    236        :param bounds: a tuple of (x_0, y_0, x_1, y_1) in the scan axes.
    237        :param n_roi: the region of interest to use. Defaults to 0.
    238        :param as_flat: whether to return a flat list of Tile objects or a list of lists.
    239        :return: if as_flat: a list of Tile objects in row-major order;
    240                 if not as_flat: a list of lists of Tile objects in their relative coordinates
    241        """
    242        x_0, y_0, x_1, y_1 = bounds
    243        coordinates = []
    244        for y in range(y_0, y_1):
    245            for x in range(x_0, x_1):
    246                coordinates.append((x, y))
    247        return cls.get_tiles(scan, coordinates, n_roi, as_flat)

Gather a list of Tile objects based on the x, y bounds provided. The bounds are exclusive, like indices, so the tiles at the corners are NOT included in the list.

###### Parameters

- scan: the scan metadata.
- bounds: a tuple of (x_0, y_0, x_1, y_1) in the scan axes.
- n_roi: the region of interest to use. Defaults to 0.
- as_flat: whether to return a flat list of Tile objects or a list of lists.

###### Returns

> if as_flat: a list of Tile objects in row-major order; if not as_flat: a list of lists of Tile objects in their relative coordinates