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.

  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        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: Iterable[int] | Iterable[tuple[int, int]] = None,
123        n_roi: int = 0,
124        as_flat: bool = True,
125    ) -> list[Self] | list[list[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: Iterable[int] = None,
184        cols: Iterable[int] = None,
185        n_roi: int = 0,
186        as_flat: bool = True,
187    ) -> list[Self] | list[list[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 = range(scan.roi[n_roi].tile_rows)
203        if cols is None:
204            cols = 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[Self] | list[list[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:
 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: Iterable[int] | Iterable[tuple[int, int]] = None,
124        n_roi: int = 0,
125        as_flat: bool = True,
126    ) -> list[Self] | list[list[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: Iterable[int] = None,
185        cols: Iterable[int] = None,
186        n_roi: int = 0,
187        as_flat: bool = True,
188    ) -> list[Self] | list[list[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 = range(scan.roi[n_roi].tile_rows)
204        if cols is None:
205            cols = 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[Self] | list[list[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)
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:
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]:
 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: Union[Iterable[int], Iterable[tuple[int, int]]] = None, n_roi: int = 0, as_flat: bool = True) -> list[typing.Self] | list[list[typing.Self]]:
119    @classmethod
120    def get_tiles(
121        cls,
122        scan: Scan,
123        coordinates: Iterable[int] | Iterable[tuple[int, int]] = None,
124        n_roi: int = 0,
125        as_flat: bool = True,
126    ) -> list[Self] | list[list[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: Iterable[int] = None, cols: Iterable[int] = None, n_roi: int = 0, as_flat: bool = True) -> list[typing.Self] | list[list[typing.Self]]:
180    @classmethod
181    def get_tiles_by_row_col(
182        cls,
183        scan: Scan,
184        rows: Iterable[int] = None,
185        cols: Iterable[int] = None,
186        n_roi: int = 0,
187        as_flat: bool = True,
188    ) -> list[Self] | list[list[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 = range(scan.roi[n_roi].tile_rows)
204        if cols is None:
205            cols = 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]]:
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[Self] | list[list[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