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 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)
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.
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 )
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.
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.
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.
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
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