diff --git a/autoarray/layout/layout.py b/autoarray/layout/layout.py index 896b3a48f..0881cfaf0 100644 --- a/autoarray/layout/layout.py +++ b/autoarray/layout/layout.py @@ -1,3 +1,5 @@ +from typing import Tuple, List + from autoarray.structures.arrays.one_d.array_1d import Array1D from autoarray.structures.arrays.two_d.array_2d import Array2D from autoarray.layout.region import Region1D @@ -35,23 +37,36 @@ def extract_overscan_array_1d_from(self, array): class Layout2D: def __init__( self, - shape_2d, - original_roe_corner=(1, 0), - parallel_overscan=None, - serial_prescan=None, - serial_overscan=None, + shape_2d: Tuple[int, int], + original_roe_corner: Tuple[int, int] = (1, 0), + parallel_overscan: Tuple[int, int, int, int] = None, + serial_prescan: Tuple[int, int, int, int] = None, + serial_overscan: Tuple[int, int, int, int] = None, ): """ - Abstract base class for a charge injection pattern_ci, which defines the regions charge injections appears \ - on a charge-injection frame_ci, the input normalization and other properties. + Abstract base class for a layout of a 2D array, which defines specific regions on the array, for example + where the parallel overscan and serial prescan are. + + This can be inherited from for arrays with additional regions, for example a charge injeciton image in the + project **PyAutoCTI** where rectangles of injected charge are contained on the image. Parameters ----------- - normalization - The normalization of the charge injection lines. - regions: [(int,)] - A list of the integer coordinates specifying the corners of each charge injection region \ - (top-row, bottom-row, left-column, right-column). + shape_2d + The two dimensional shape of the charge injection imaging, corresponding to the number of rows (pixels + in parallel direction) and columns (pixels in serial direction). + original_roe_corner + The original read-out electronics corner of the charge injeciton imaging, which is internally rotated to a + common orientation in **PyAutoCTI**. + parallel_overscan + Integer pixel coordinates specifying the corners of the parallel overscan (top-row, bottom-row, + left-column, right-column). + serial_prescan + Integer pixel coordinates specifying the corners of the serial prescan (top-row, bottom-row, + left-column, right-column). + serial_overscan + Integer pixel coordinates specifying the corners of the serial overscan (top-row, bottom-row, + left-column, right-column). """ self.shape_2d = shape_2d @@ -98,7 +113,7 @@ def rotated_from_roe_corner( serial_overscan=serial_overscan, ) - def after_extraction(self, extraction_region): + def after_extraction_from(self, extraction_region): parallel_overscan = layout_util.region_after_extraction( original_region=self.parallel_overscan, extraction_region=extraction_region diff --git a/autoarray/layout/region.py b/autoarray/layout/region.py index 2764146b5..85be36001 100644 --- a/autoarray/layout/region.py +++ b/autoarray/layout/region.py @@ -84,15 +84,17 @@ def trails_region_from(self, pixels): class Region2D: - def __init__(self, region): - """Setup a region of an image, which could be where the parallel overscan, serial overscan, etc. are. + def __init__(self, region: Tuple[int, int, int, int]): + """ + A region of a 2D array defined as a tuple (y0, y1, x0, x1) = (top-row, bottom-row, left-column, right-column). - This is defined as a tuple (y0, y1, x0, x1). + For example, a the parallel overscan of an image may be defined by the coordinates (100, 120, 10, 30), + indicating it spans 20 rows at the end of the array over the columns defined between 10 and 30. Parameters ----------- - region : Tuple[int] - The coordinates on the image of the region (y0, y1, x0, y1). + region + The coordinates on the image of the region defined following the convention (y0, y1, x0, y1). """ if region[0] < 0 or region[1] < 0 or region[2] < 0 or region[3] < 0: @@ -111,6 +113,17 @@ def __init__(self, region): ) self.region = region + def __getitem__(self, item): + return self.region[item] + + def __eq__(self, other): + if self.region == other: + return True + return super().__eq__(other) + + def __repr__(self): + return "".format(*self) + @property def total_rows(self): return self.y1 - self.y0 @@ -135,17 +148,6 @@ def x0(self): def x1(self): return self[3] - def __getitem__(self, item): - return self.region[item] - - def __eq__(self, other): - if self.region == other: - return True - return super().__eq__(other) - - def __repr__(self): - return "".format(*self) - @property def slice(self): return np.s_[self.y0 : self.y1, self.x0 : self.x1] @@ -162,47 +164,150 @@ def x_slice(self): def shape(self): return self.y1 - self.y0, self.x1 - self.x0 - def x_limits_from(self, columns): + def serial_x_front_range_from(self, pixels: Tuple[int, int]) -> Tuple[int, int]: + """ + Returns pixels defining the x range from the serial front edge of the region. + + For example, if the `Region2D` covers the pixels (5, 10, 0, 20) and we input `pixels=(1,3)` this will return + the coordinates (6,8). + Parameters + ---------- + pixels + A tuple defining the pixel columns used to compute the serial front edge range. + """ x_coord = self.x0 - x_min = x_coord + columns[0] - x_max = x_coord + columns[1] + x_min = x_coord + pixels[0] + x_max = x_coord + pixels[1] return x_min, x_max - def parallel_front_edge_region_from(self, rows): + def parallel_front_region_from(self, pixels: Tuple[int, int]) -> "Region2D": + """ + Returns a `Region2D` corresponding to the front pixels in this region in the parallel clocking direction + (e.g. the rows of pixels in the region that are closest to the CCD read-out electronics). + + For example, if the `Region2D` covers the pixels (0, 10, 0, 20) and we input `pixels=(1,6)` this will return + the 2nd to 6th rows of the region corresponding to (1, 6, 0, 20). + + Other functions use the computed region to extract the parallel front region from 2D arrays containing data. + + Parameters + ---------- + pixels + A tuple defining the pixel rows used to compute the parallel front region. + """ y_coord = self.y0 - y_min = y_coord + rows[0] - y_max = y_coord + rows[1] + y_min = y_coord + pixels[0] + y_max = y_coord + pixels[1] return Region2D((y_min, y_max, self.x0, self.x1)) - def parallel_trails_region_from(self, rows=(0, 1)): + def parallel_trailing_region_from( + self, pixels: Tuple[int, int] = (0, 1) + ) -> "Region2D": + """ + Returns a `Region2D` corresponding to the pixels trailing this region in the parallel clocking direction + (e.g. the rows of pixels outside this region and in the direction away from the CCD read-out electronics). + + For example, if the `Region2D` covers the pixels (0, 10, 0, 20) and we input `pixels=(1,6)` this will return + the 2nd to 6th rows of the trailing region corresponding to (10, 16, 0, 20). + + Other functions use the computed region to extract the parallel trailing region from 2D arrays containing data. + Parameters + ---------- + pixels + A tuple defining the pixel rows used to compute the parallel trailing region. + """ y_coord = self.y1 - y_min = y_coord + rows[0] - y_max = y_coord + rows[1] + y_min = y_coord + pixels[0] + y_max = y_coord + pixels[1] return Region2D((y_min, y_max, self.x0, self.x1)) - def parallel_side_nearest_read_out_region_from(self, shape_2d, columns=(0, 1)): + def parallel_full_region_from(self, shape_2d: Tuple[int, int]) -> "Region2D": + """ + Returns a `Region2D` corresponding to pixels which are inside this region and span all columns of the CCD. - x_min, x_max = self.x_limits_from(columns) + The returned region spans every column, for example if the image has a 2D shape (10, 20) the region will span + all 20 columns irrespective of this region's coordinates. - return Region2D(region=(0, shape_2d[0], x_min, x_max)) + For example, if the `Region2D` covers the pixels (5, 10, 10, 20) and we input `shape_2d=(40, 60) this will + return the 1st to 5th rows of the region towards the roe over the full 2D array corresponding to (5, 10, 0, 60). - def serial_front_edge_region_from(self, columns=(0, 1)): - x_min, x_max = self.x_limits_from(columns) + Other functions use the computed region to extract the parallel front region from 2D arrays containing data. + + Parameters + ---------- + pixels + A tuple defining the pixel columns which are retained in the region. + """ + return Region2D(region=(self.y0, self.y1, 0, shape_2d[1])) + + def serial_front_region_from(self, pixels: Tuple[int, int] = (0, 1)) -> "Region2D": + """ + Returns a `Region2D` corresponding to the front pixels in this region in the serial clocking direction + (e.g. the columns of pixels in the region that are closest to the CCD read-out electronics). + + For example, if the `Region2D` covers the pixels (0, 10, 0, 20) and we input `pixels=(1,6)` this will return + the 2nd to 6th rows of the region corresponding to (1, 6, 0, 20). + + Other functions use the computed region to extract the parallel front region from 2D arrays containing data. + + Parameters + ---------- + pixels + A tuple defining the pixel columns used to compute the serial front region. + """ + x_min, x_max = self.serial_x_front_range_from(pixels) return Region2D(region=(self.y0, self.y1, x_min, x_max)) - def serial_trails_region_from(self, columns=(0, 1)): + def serial_trailing_region_from( + self, pixels: Tuple[int, int] = (0, 1) + ) -> "Region2D": + """ + Returns a `Region2D` corresponding to the pixels trailing this region in the serial clocking direction + (e.g. the columns of pixels outside this region and in the direction away from the CCD read-out electronics). + + For example, if the `Region2D` covers the pixels (0, 10, 0, 20) and we input `pixels=(1,6)` this will return + the 2nd to 6th rows of the trailing region corresponding to (0, 10, 20, 26). + + Other functions use the computed region to extract the parallel trailing region from 2D arrays containing data. + Parameters + ---------- + pixels + A tuple defining the pixel columns used to compute the parallel trailing region. + """ x_coord = self.x1 - x_min = x_coord + columns[0] - x_max = x_coord + columns[1] + x_min = x_coord + pixels[0] + x_max = x_coord + pixels[1] return Region2D(region=(self.y0, self.y1, x_min, x_max)) - def serial_entire_rows_of_region_from(self, shape_2d): - return Region2D(region=(self.y0, self.y1, 0, shape_2d[1])) + def serial_towards_roe_full_region_from( + self, shape_2d: Tuple[int, int], pixels: Tuple[int, int] = (0, 1) + ) -> "Region2D": + """ + Returns a `Region2D` corresponding to pixels which are inside this region and towards the CCD read-out + electronics (which **PyAutoArray** internally defines at (0, 0)). + + The returned region spans every row, for example if the image has a 2D shape (20, 10) the region will span + all 20 rows irrespective of this region's coordinates. + + For example, if the `Region2D` covers the pixels (5, 10, 10, 20) and we input `shape_2d=(40, 60) and + `pixels=(0,5)` this will return the 1st to 5th columns of the region towards the roe + over the full 2D array corresponding to (0, 40, 10, 15). + + Other functions use the computed region to extract the parallel front region from 2D arrays containing data. + + Parameters + ---------- + pixels + A tuple defining the pixel columns which are retained in the region. + """ + x_min, x_max = self.serial_x_front_range_from(pixels) + + return Region2D(region=(0, shape_2d[0], x_min, x_max)) diff --git a/eden.ini b/eden.ini index cf90207e5..cdecc0237 100644 --- a/eden.ini +++ b/eden.ini @@ -1,3 +1,7 @@ [eden] name=autoarray -prefix=aa \ No newline at end of file +prefix=aa +eden_prefix=VIS_CTI +eden_dependencies=autoconf +should_rename_modules=False +should_remove_type_annotations=True \ No newline at end of file diff --git a/root.log b/root.log index e755e1abb..af3d09195 100644 --- a/root.log +++ b/root.log @@ -1771,3 +1771,46 @@ 2022-01-29 12:04:32,551 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels 2022-01-29 12:04:32,552 - autoarray.dataset.imaging - INFO - IMAGING - Computing W-Tilde... May take a moment. 2022-01-29 12:04:35,413 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:20:54,325 - autoarray.preloads - INFO - PRELOADS - Relocated grid of pxielization preloaded for this model-fit. +2022-02-02 18:20:54,327 - autoarray.preloads - INFO - PRELOADS - Mappers of planes preloaded for this model-fit. +2022-02-02 18:20:54,327 - autoarray.preloads - INFO - PRELOADS - Mappers of planes preloaded for this model-fit. +2022-02-02 18:20:54,328 - autoarray.preloads - INFO - PRELOADS - LEq linear algebra quantities preloaded for this model-fit. +2022-02-02 18:20:54,329 - autoarray.preloads - INFO - PRELOADS - LEq Log Det Regularization Matrix Term preloaded for this model-fit. +2022-02-02 18:20:56,030 - autoarray.dataset.imaging - INFO - The image and noise map of the `Imaging` objected have been padded to the dimensions(9,). This is because the blurring region around the mask (which defines wherePSF flux may be convolved into the masked region) extended beyond the edge of the image.This can be prevented by using a smaller mask, smaller PSF kernel size or manually paddingthe image and noise-map yourself. +2022-02-02 18:20:56,128 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:20:56,130 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:20:56,130 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:20:56,132 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:20:56,137 - autoarray.dataset.imaging - INFO - IMAGING - Computing W-Tilde... May take a moment. +2022-02-02 18:20:56,142 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:20:56,143 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:20:56,148 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 1 image-pixels +2022-02-02 18:20:57,646 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 4 image-pixels +2022-02-02 18:20:57,650 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 4 image-pixels +2022-02-02 18:20:57,678 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 4 image-pixels +2022-02-02 18:20:57,754 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:20:58,111 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:20:58,293 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:21:21,661 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:21:21,662 - autoarray.dataset.imaging - INFO - IMAGING - Computing W-Tilde... May take a moment. +2022-02-02 18:21:21,679 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:21:21,685 - autoarray.dataset.imaging - INFO - IMAGING - Computing W-Tilde... May take a moment. +2022-02-02 18:21:21,722 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:21:21,724 - autoarray.dataset.imaging - INFO - IMAGING - Computing W-Tilde... May take a moment. +2022-02-02 18:21:22,441 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:21:22,443 - autoarray.dataset.imaging - INFO - IMAGING - Computing W-Tilde... May take a moment. +2022-02-02 18:21:22,461 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:21:22,462 - autoarray.dataset.imaging - INFO - IMAGING - Computing W-Tilde... May take a moment. +2022-02-02 18:21:22,520 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 5 image-pixels +2022-02-02 18:21:22,521 - autoarray.dataset.imaging - INFO - IMAGING - Computing W-Tilde... May take a moment. +2022-02-02 18:21:22,684 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 5 image-pixels +2022-02-02 18:21:22,685 - autoarray.dataset.imaging - INFO - IMAGING - Computing W-Tilde... May take a moment. +2022-02-02 18:21:24,207 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:21:24,208 - autoarray.dataset.imaging - INFO - IMAGING - Computing W-Tilde... May take a moment. +2022-02-02 18:21:24,209 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:21:24,209 - autoarray.dataset.imaging - INFO - IMAGING - Computing W-Tilde... May take a moment. +2022-02-02 18:21:25,113 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:21:25,114 - autoarray.dataset.imaging - INFO - IMAGING - Computing W-Tilde... May take a moment. +2022-02-02 18:21:25,115 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels +2022-02-02 18:21:25,115 - autoarray.dataset.imaging - INFO - IMAGING - Computing W-Tilde... May take a moment. +2022-02-02 18:21:28,305 - autoarray.dataset.imaging - INFO - IMAGING - Data masked, contains a total of 9 image-pixels diff --git a/test_autoarray/layout/test_region.py b/test_autoarray/layout/test_region.py index 53810b86b..aaa64a36c 100644 --- a/test_autoarray/layout/test_region.py +++ b/test_autoarray/layout/test_region.py @@ -242,146 +242,124 @@ def test__set_region_to_zero_via_slice(self): array == np.array([[1.0, 1.0, 1.0], [1.0, 1.0, 0.0], [1.0, 1.0, 0.0]]) ).all() - def test__parallel_front_edge_region_from__extracts_rows_within_bottom_of_region( - self, - ): + def test__parallel_front_region_from(self,): region = aa.Region2D(region=(0, 3, 0, 3)) - # Front edge is row 0, so for 1 row we extract 0 -> 1 - - front_edge = region.parallel_front_edge_region_from(rows=(0, 1)) + front_edge = region.parallel_front_region_from(pixels=(0, 1)) assert front_edge == (0, 1, 0, 3) - # Front edge is row 0, so for 2 rows we extract 0 -> 2 - - front_edge = region.parallel_front_edge_region_from(rows=(0, 2)) + front_edge = region.parallel_front_region_from(pixels=(0, 2)) assert front_edge == (0, 2, 0, 3) - # Front edge is row 0, so for these 2 rows we extract 1 ->2 - - front_edge = region.parallel_front_edge_region_from(rows=(1, 3)) + front_edge = region.parallel_front_region_from(pixels=(1, 3)) assert front_edge == (1, 3, 0, 3) - def test__parallel_trails_of_region_from__extracts_rows_above_region(self): + def test__parallel_trailing_region_from(self): - region = aa.Region2D( - region=(0, 3, 0, 3) - ) # The trails are row 3 and above, so extract 3 -> 4 + region = aa.Region2D(region=(0, 3, 0, 3)) - trails = region.parallel_trails_region_from(rows=(0, 1)) + trails = region.parallel_trailing_region_from(pixels=(0, 1)) assert trails == (3, 4, 0, 3) - # The trails are row 3 and above, so extract 3 -> 5 - - trails = region.parallel_trails_region_from(rows=(0, 2)) + trails = region.parallel_trailing_region_from(pixels=(0, 2)) assert trails == (3, 5, 0, 3) - # The trails are row 3 and above, so extract 4 -> 6 - - trails = region.parallel_trails_region_from(rows=(1, 3)) + trails = region.parallel_trailing_region_from(pixels=(1, 3)) assert trails == (4, 6, 0, 3) - def test__parallel_side_nearest_read_out_region_from(self): + def test__parallel_full_region_from(self,): region = aa.Region2D(region=(1, 3, 0, 5)) - parallel_region = region.parallel_side_nearest_read_out_region_from( - shape_2d=(5, 5), columns=(0, 1) - ) - - assert parallel_region == (0, 5, 0, 1) + serial_region = region.parallel_full_region_from(shape_2d=(5, 5)) - parallel_region = region.parallel_side_nearest_read_out_region_from( - shape_2d=(4, 4), columns=(1, 3) - ) - - assert parallel_region == (0, 4, 1, 3) + assert serial_region == (1, 3, 0, 5) - region = aa.Region2D(region=(1, 3, 2, 5)) + region = aa.Region2D(region=(1, 3, 0, 5)) - parallel_region = region.parallel_side_nearest_read_out_region_from( - shape_2d=(4, 4), columns=(1, 3) - ) + serial_region = region.parallel_full_region_from(shape_2d=(5, 25)) - assert parallel_region == (0, 4, 3, 5) + assert serial_region == (1, 3, 0, 25) - region = aa.Region2D(region=(1, 3, 0, 5)) + region = aa.Region2D(region=(3, 5, 5, 30)) - parallel_region = region.parallel_side_nearest_read_out_region_from( - shape_2d=(2, 5), columns=(0, 1) - ) + serial_region = region.parallel_full_region_from(shape_2d=(8, 55)) - assert parallel_region == (0, 2, 0, 1) + assert serial_region == (3, 5, 0, 55) - def test__serial_front_edge_of_region__extracts_region_within_left_of_region(self): + def test__serial_front_region_from(self): - region = aa.Region2D( - region=(0, 3, 0, 3) - ) # Front edge is column 0, so for 1 column we extract 0 -> 1 + region = aa.Region2D(region=(0, 3, 0, 3)) - front_edge = region.serial_front_edge_region_from(columns=(0, 1)) + front_edge = region.serial_front_region_from(pixels=(0, 1)) assert front_edge == (0, 3, 0, 1) - # Front edge is column 0, so for 2 columns we extract 0 -> 2 - - front_edge = region.serial_front_edge_region_from(columns=(0, 2)) + front_edge = region.serial_front_region_from(pixels=(0, 2)) assert front_edge == (0, 3, 0, 2) - # Front edge is column 0, so for these 2 columns we extract 1 ->2 - - front_edge = region.serial_front_edge_region_from(columns=(1, 3)) + front_edge = region.serial_front_region_from(pixels=(1, 3)) assert front_edge == (0, 3, 1, 3) - def test__serial_trails_of_regions__extracts_region_to_right_of_region(self): + def test__serial_trailing_region_from(self): region = aa.Region2D( region=(0, 3, 0, 3) ) # The trails are column 3 and above, so extract 3 -> 4 - trails = region.serial_trails_region_from(columns=(0, 1)) + trails = region.serial_trailing_region_from(pixels=(0, 1)) assert trails == (0, 3, 3, 4) # The trails are column 3 and above, so extract 3 -> 5 - trails = region.serial_trails_region_from(columns=(0, 2)) + trails = region.serial_trailing_region_from(pixels=(0, 2)) assert trails == (0, 3, 3, 5) # The trails are column 3 and above, so extract 4 -> 6 - trails = region.serial_trails_region_from(columns=(1, 3)) + trails = region.serial_trailing_region_from(pixels=(1, 3)) assert trails == (0, 3, 4, 6) - def test__serial_entire_rows_of_regions__full_region_from_left_most_prescan_to_right_most_end_of_trails( - self, - ): + def test__serial_towards_roe_full_region_from(self): region = aa.Region2D(region=(1, 3, 0, 5)) - serial_region = region.serial_entire_rows_of_region_from(shape_2d=(5, 5)) + parallel_region = region.serial_towards_roe_full_region_from( + shape_2d=(5, 5), pixels=(0, 1) + ) - assert serial_region == (1, 3, 0, 5) + assert parallel_region == (0, 5, 0, 1) - region = aa.Region2D(region=(1, 3, 0, 5)) + parallel_region = region.serial_towards_roe_full_region_from( + shape_2d=(4, 4), pixels=(1, 3) + ) - serial_region = region.serial_entire_rows_of_region_from(shape_2d=(5, 25)) + assert parallel_region == (0, 4, 1, 3) - assert serial_region == (1, 3, 0, 25) + region = aa.Region2D(region=(1, 3, 2, 5)) - region = aa.Region2D(region=(3, 5, 5, 30)) + parallel_region = region.serial_towards_roe_full_region_from( + shape_2d=(4, 4), pixels=(1, 3) + ) + + assert parallel_region == (0, 4, 3, 5) - serial_region = region.serial_entire_rows_of_region_from(shape_2d=(8, 55)) + region = aa.Region2D(region=(1, 3, 0, 5)) - assert serial_region == (3, 5, 0, 55) + parallel_region = region.serial_towards_roe_full_region_from( + shape_2d=(2, 5), pixels=(0, 1) + ) + + assert parallel_region == (0, 2, 0, 1)