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

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

    70    def position_to_n(self, position: tuple[int, int] = (-1, -1)) -> int:
    71        """
    72        Convert the x, y coordinates to the n coordinate, based on this tile's scan
    73        metadata and ROI. Can be provided alternative x, y to convert for convenience.
    74        :param position: optional (x, y) coordinates to find the n for.
    75                         If none provided, this tile's (x, y) will be used.
    76        :return: the coordinate n, which depends on the scanner and scan layout.
    77        """
    78        if position == (-1, -1):
    79            position = self.x, self.y
    80        x, y = position
    81        if self.scan.scanner_id.startswith(self.scan.Type.AXIOSCAN7.value):
    82            n = y * self.scan.roi[self.n_roi].tile_cols + x
    83        elif self.scan.scanner_id.startswith(self.scan.Type.BZSCANNER.value):
    84            n = y * self.scan.roi[self.n_roi].tile_cols
    85            if y % 2 == 0:
    86                n += x
    87            else:
    88                n += self.scan.roi[0].tile_cols - x
    89        else:
    90            raise ValueError(f"Scanner type {self.scan.scanner_id} not supported.")
    91        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

     93    def n_to_position(self, n: int = -1) -> tuple[int, int]:
     94        """
     95        Convert the n coordinate to x, y coordinates, based on this tile's scan
     96        metadata and ROI. Can be provided alternative n to convert for convenience.
     97        :param n: an optional n coordinate to find the position for.
     98                  If none provided, this tile's n will be used.
     99        :return: x, y coordinates of the tile in the scan's coordinate system.
    100        """
    101        if n == -1:
    102            n = self.n
    103        if n < 0:
    104            raise ValueError(f"n ({n}) must be non-negative.")
    105        if self.scan.scanner_id.startswith(self.scan.Type.AXIOSCAN7.value):
    106            x = n % self.scan.roi[0].tile_cols
    107            y = n // self.scan.roi[0].tile_cols
    108            return x, y
    109        elif self.scan.scanner_id.startswith(self.scan.Type.BZSCANNER.value):
    110            y = n // self.scan.roi[0].tile_cols
    111            if y % 2 == 0:
    112                x = n % self.scan.roi[0].tile_cols
    113            else:
    114                x = (self.scan.roi[0].tile_cols - 1) - (n % self.scan.roi[0].tile_cols)
    115        else:
    116            raise ValueError(f"Scanner type {self.scan.scanner_id} not supported.")
    117        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: list[int] | list[tuple[int, int]] = None, n_roi: int = 0, as_flat: bool = True) -> list[typing.Self] | list[list[typing.Self]]: View Source

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

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

    215    @classmethod
    216    def get_tiles_by_xy_bounds(
    217        cls,
    218        scan: Scan,
    219        bounds: tuple[int, int, int, int],
    220        n_roi: int = 0,
    221        as_flat: bool = True,
    222    ) -> list[typing.Self] | list[list[typing.Self]]:
    223        """
    224        Gather a list of Tile objects based on the x, y bounds provided. The bounds are
    225        exclusive, like indices, so the tiles at the corners are NOT included in the list.
    226        :param scan: the scan metadata.
    227        :param bounds: a tuple of (x_0, y_0, x_1, y_1) in the scan axes.
    228        :param n_roi: the region of interest to use. Defaults to 0.
    229        :param as_flat: whether to return a flat list of Tile objects or a list of lists.
    230        :return: if as_flat: a list of Tile objects in row-major order;
    231                 if not as_flat: a list of lists of Tile objects in their relative coordinates
    232        """
    233        x_0, y_0, x_1, y_1 = bounds
    234        coordinates = []
    235        for y in range(y_0, y_1):
    236            for x in range(x_0, x_1):
    237                coordinates.append((x, y))
    238        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