diff --git a/autoarray/__init__.py b/autoarray/__init__.py index 18518971d..b48a9c2ae 100644 --- a/autoarray/__init__.py +++ b/autoarray/__init__.py @@ -53,6 +53,7 @@ from .mask.derive.mask_2d import DeriveMask2D from .mask.derive.grid_1d import DeriveGrid1D from .mask.derive.grid_2d import DeriveGrid2D +from .mask.derive.zoom_2d import Zoom2D from .mask.mask_1d import Mask1D from .mask.mask_2d import Mask2D from .operators.convolver import Convolver diff --git a/autoarray/config/visualize/general.yaml b/autoarray/config/visualize/general.yaml index 8bbf29e06..b6cecf50f 100644 --- a/autoarray/config/visualize/general.yaml +++ b/autoarray/config/visualize/general.yaml @@ -4,7 +4,6 @@ general: log10_min_value: 1.0e-4 # If negative values are being plotted on a log10 scale, values below this value are rounded up to it (e.g. to remove negative values). log10_max_value: 1.0e99 # If positive values are being plotted on a log10 scale, values above this value are rounded down to it (e.g. to prevent white blobs). zoom_around_mask: true # If True, plots of data structures with a mask automatically zoom in the masked region. - disable_zoom_for_fits: true # If True, the zoom-in around the masked region is disabled when outputting .fits files, which is useful to retain the same dimensions as the input data. inversion: reconstruction_vmax_factor: 0.5 total_mappings_pixels : 8 # The number of source pixels used when plotting the subplot_mappings of a pixelization. diff --git a/autoarray/config/visualize/plots.yaml b/autoarray/config/visualize/plots.yaml index aa2664506..adf47212a 100644 --- a/autoarray/config/visualize/plots.yaml +++ b/autoarray/config/visualize/plots.yaml @@ -3,6 +3,9 @@ # For example, if `plots: fit: subplot_fit=True``, the ``fit_dataset.png`` subplot file will # be plotted every time visualization is performed. +subplot_format: [png] # Output format of all subplots, can be png, pdf or both (e.g. [png, pdf]) +fits_are_zoomed: true # If true, output .fits files are zoomed in on the center of the unmasked region image, saving hard-disk space. + dataset: # Settings for plots of all datasets (e.g. ImagingPlotter, InterferometerPlotter). subplot_dataset: true # Plot subplot containing all dataset quantities (e.g. the data, noise-map, etc.)? imaging: # Settings for plots of imaging datasets (e.g. ImagingPlotter) diff --git a/autoarray/geometry/geometry_util.py b/autoarray/geometry/geometry_util.py index 44c71a977..fad238ca5 100644 --- a/autoarray/geometry/geometry_util.py +++ b/autoarray/geometry/geometry_util.py @@ -182,7 +182,7 @@ def convert_pixel_scales_2d(pixel_scales: ty.PixelScales) -> Tuple[float, float] @numba_util.jit() def central_pixel_coordinates_2d_from( - shape_native: Tuple[int, int] + shape_native: Tuple[int, int], ) -> Tuple[float, float]: """ Returns the central pixel coordinates of a 2D geometry (and therefore a 2D data structure like an ``Array2D``) @@ -737,7 +737,7 @@ def grid_pixel_centres_2d_from( def extent_symmetric_from( - extent: Tuple[float, float, float, float] + extent: Tuple[float, float, float, float], ) -> Tuple[float, float, float, float]: """ Given an input extent of the form (x_min, x_max, y_min, y_max), this function returns an extent which is diff --git a/autoarray/inversion/inversion/abstract.py b/autoarray/inversion/inversion/abstract.py index 685a152c2..ecc1fd02d 100644 --- a/autoarray/inversion/inversion/abstract.py +++ b/autoarray/inversion/inversion/abstract.py @@ -509,12 +509,12 @@ def reconstruction(self) -> np.ndarray: solutions = np.zeros(np.shape(self.curvature_reg_matrix)[0]) - solutions[ - values_to_solve - ] = inversion_util.reconstruction_positive_only_from( - data_vector=data_vector_input, - curvature_reg_matrix=curvature_reg_matrix_input, - settings=self.settings, + solutions[values_to_solve] = ( + inversion_util.reconstruction_positive_only_from( + data_vector=data_vector_input, + curvature_reg_matrix=curvature_reg_matrix_input, + settings=self.settings, + ) ) return solutions else: diff --git a/autoarray/inversion/inversion/imaging/abstract.py b/autoarray/inversion/inversion/imaging/abstract.py index 5eccfa7d1..d6fe6f61a 100644 --- a/autoarray/inversion/inversion/imaging/abstract.py +++ b/autoarray/inversion/inversion/imaging/abstract.py @@ -104,11 +104,13 @@ def operated_mapping_matrix_list(self) -> List[np.ndarray]: """ return [ - self.convolver.convolve_mapping_matrix( - mapping_matrix=linear_obj.mapping_matrix + ( + self.convolver.convolve_mapping_matrix( + mapping_matrix=linear_obj.mapping_matrix + ) + if linear_obj.operated_mapping_matrix_override is None + else self.linear_func_operated_mapping_matrix_dict[linear_obj] ) - if linear_obj.operated_mapping_matrix_override is None - else self.linear_func_operated_mapping_matrix_dict[linear_obj] for linear_obj in self.linear_obj_list ] @@ -156,9 +158,9 @@ def linear_func_operated_mapping_matrix_dict(self) -> Dict: mapping_matrix=linear_func.mapping_matrix ) - linear_func_operated_mapping_matrix_dict[ - linear_func - ] = operated_mapping_matrix + linear_func_operated_mapping_matrix_dict[linear_func] = ( + operated_mapping_matrix + ) return linear_func_operated_mapping_matrix_dict diff --git a/autoarray/inversion/pixelization/border_relocator.py b/autoarray/inversion/pixelization/border_relocator.py index 50acf10e1..73a8d0d28 100644 --- a/autoarray/inversion/pixelization/border_relocator.py +++ b/autoarray/inversion/pixelization/border_relocator.py @@ -117,12 +117,12 @@ def sub_border_pixel_slim_indexes_from( int(border_pixel) ] - sub_border_pixels[ - border_1d_index - ] = grid_2d_util.furthest_grid_2d_slim_index_from( - grid_2d_slim=sub_grid_2d_slim, - slim_indexes=sub_border_pixels_of_border_pixel, - coordinate=mask_centre, + sub_border_pixels[border_1d_index] = ( + grid_2d_util.furthest_grid_2d_slim_index_from( + grid_2d_slim=sub_grid_2d_slim, + slim_indexes=sub_border_pixels_of_border_pixel, + coordinate=mask_centre, + ) ) return sub_border_pixels diff --git a/autoarray/inversion/pixelization/mesh/mesh_util.py b/autoarray/inversion/pixelization/mesh/mesh_util.py index 78cb4a860..305b56b72 100644 --- a/autoarray/inversion/pixelization/mesh/mesh_util.py +++ b/autoarray/inversion/pixelization/mesh/mesh_util.py @@ -7,7 +7,7 @@ @numba_util.jit() def rectangular_neighbors_from( - shape_native: Tuple[int, int] + shape_native: Tuple[int, int], ) -> Tuple[np.ndarray, np.ndarray]: """ Returns the 4 (or less) adjacent neighbors of every pixel on a rectangular pixelization as an ndarray of shape diff --git a/autoarray/inversion/plot/inversion_plotters.py b/autoarray/inversion/plot/inversion_plotters.py index 098769e80..98cede938 100644 --- a/autoarray/inversion/plot/inversion_plotters.py +++ b/autoarray/inversion/plot/inversion_plotters.py @@ -215,10 +215,9 @@ def figures_2d_of_pixelization( "inversion" ]["reconstruction_vmax_factor"] - self.mat_plot_2d.cmap.kwargs[ - "vmax" - ] = reconstruction_vmax_factor * np.max( - self.inversion.reconstruction + self.mat_plot_2d.cmap.kwargs["vmax"] = ( + reconstruction_vmax_factor + * np.max(self.inversion.reconstruction) ) vmax_custom = True diff --git a/autoarray/mask/derive/zoom_2d.py b/autoarray/mask/derive/zoom_2d.py new file mode 100644 index 000000000..69c49b7cd --- /dev/null +++ b/autoarray/mask/derive/zoom_2d.py @@ -0,0 +1,271 @@ +from __future__ import annotations +import numpy as np +from typing import TYPE_CHECKING, List, Tuple, Union + +if TYPE_CHECKING: + from autoarray.structures.arrays.uniform_2d import Array2D + from autoarray.mask.mask_2d import Mask2D + +from autoarray.structures.arrays import array_2d_util +from autoarray.structures.grids import grid_2d_util + + +class Zoom2D: + + def __init__(self, mask: Union[np.ndarray, List]): + """ + Derives a zoomed in `Mask2D` object from a `Mask2D` object, which is typically used to visualize 2D arrays + zoomed in to only the unmasked region an analysis is performed on. + + A `Mask2D` masks values which are associated with a uniform 2D rectangular grid of pixels, where unmasked + entries (which are `False`) are used in subsequent calculations and masked values (which are `True`) are + omitted (for a full description see the :meth:`Mask2D` class API + documentation `). + + The `Zoom2D` object calculations many different zoomed in qu + + Parameters + ---------- + mask + The `Mask2D` from which zoomed in `Mask2D` objects are derived. + + Examples + -------- + + .. code-block:: python + + import autoarray as aa + + mask_2d = aa.Mask2D( + mask=[ + [True, True, True, True, True], + [True, False, False, False, True], + [True, False, False, False, True], + [True, False, False, False, True], + [True, True, True, True, True], + ], + pixel_scales=1.0, + ) + + zoom_2d = aa.Zoom2D(mask=mask_2d) + + print(zoom_2d.centre) + """ + self.mask = mask + + @property + def centre(self) -> Tuple[float, float]: + """ + Returns the centre of the zoomed in region, which is the average of the maximum and minimum y and x pixel values + of the unmasked region. + + The y and x pixel values are the pixel coordinates of the unmasked region, which are derived from the + `Mask2D` object. The pixel coordinates are in the same units as the pixel scales of the `Mask2D` object. + + Returns + ------- + The centre of the zoomed in region. + """ + from autoarray.structures.grids.uniform_2d import Grid2D + + grid = grid_2d_util.grid_2d_slim_via_mask_from( + mask_2d=np.array(self.mask), + pixel_scales=self.mask.pixel_scales, + origin=self.mask.origin, + ) + + grid = Grid2D(values=grid, mask=self.mask) + + extraction_grid_1d = self.mask.geometry.grid_pixels_2d_from(grid_scaled_2d=grid) + y_pixels_max = np.max(extraction_grid_1d[:, 0]) + y_pixels_min = np.min(extraction_grid_1d[:, 0]) + x_pixels_max = np.max(extraction_grid_1d[:, 1]) + x_pixels_min = np.min(extraction_grid_1d[:, 1]) + + return ( + ((y_pixels_max + y_pixels_min - 1.0) / 2.0), + ((x_pixels_max + x_pixels_min - 1.0) / 2.0), + ) + + @property + def offset_pixels(self) -> Tuple[float, float]: + """ + Returns the offset of the centred of the zoomed in region from the centre of the `Mask2D` object in pixel + units. + + This is computed by subtracting the pixel coordinates of the `Mask2D` object from the pixel coordinates of + the zoomed in region. + + Returns + ------- + The offset of the zoomed in region from the centre of the `Mask2D` object in pixel units. + """ + if self.mask.pixel_scales is None: + return self.mask.geometry.central_pixel_coordinates + + return ( + self.centre[0] - self.mask.geometry.central_pixel_coordinates[0], + self.centre[1] - self.mask.geometry.central_pixel_coordinates[1], + ) + + @property + def offset_scaled(self) -> Tuple[float, float]: + """ + Returns the offset of the centred of the zoomed in region from the centre of the `Mask2D` object in scaled + units. + + This is computed by subtracting the pixel coordinates of the `Mask2D` object from the pixel coordinates of + the zoomed in region. + + Returns + ------- + The offset of the zoomed in region from the centre of the `Mask2D` object in scaled units. + """ + return ( + -self.mask.pixel_scales[0] * self.offset_pixels[0], + self.mask.pixel_scales[1] * self.offset_pixels[1], + ) + + @property + def region(self) -> List[int]: + """ + The zoomed region corresponding to the square encompassing all unmasked values. + + This is used to zoom in on the region of an image that is used in an analysis for visualization. + + This zoomed extraction region is a square, even if the mask is rectangular, so that extraction regions are + always squares which is important for ensuring visualization does not have aspect ratio issues. + """ + + where = np.array(np.where(np.invert(self.mask.astype("bool")))) + y0, x0 = np.amin(where, axis=1) + y1, x1 = np.amax(where, axis=1) + + # Have to convert mask to bool for invert function to work. + + ylength = y1 - y0 + xlength = x1 - x0 + + if ylength > xlength: + length_difference = ylength - xlength + x1 += int(length_difference / 2.0) + x0 -= int(length_difference / 2.0) + elif xlength > ylength: + length_difference = xlength - ylength + y1 += int(length_difference / 2.0) + y0 -= int(length_difference / 2.0) + + return [y0, y1 + 1, x0, x1 + 1] + + @property + def shape_native(self) -> Tuple[int, int]: + """ + The shape of the zoomed in region in pixels. + + This is computed by subtracting the minimum and maximum y and x pixel values of the unmasked region. + + Returns + ------- + The shape of the zoomed in region in pixels. + """ + region = self.region + return (region[1] - region[0], region[3] - region[2]) + + def extent_from(self, buffer: int = 1) -> np.ndarray: + """ + For an extracted zoomed array computed from the method *zoomed_around_mask* compute its extent in scaled + coordinates. + + The extent of the grid in scaled units returned as an ``ndarray`` of the form [x_min, x_max, y_min, y_max]. + + This is used visualize zoomed and extracted arrays via the imshow() method. + + Parameters + ---------- + buffer + The number pixels around the extracted array used as a buffer. + """ + from autoarray.mask.mask_2d import Mask2D + + extracted_array_2d = array_2d_util.extracted_array_2d_from( + array_2d=np.array(self.mask), + y0=self.region[0] - buffer, + y1=self.region[1] + buffer, + x0=self.region[2] - buffer, + x1=self.region[3] + buffer, + ) + + mask = Mask2D.all_false( + shape_native=extracted_array_2d.shape, + pixel_scales=self.mask.pixel_scales, + origin=self.centre, + ) + + return mask.geometry.extent + + def mask_2d_from(self, buffer: int = 1) -> "Mask2D": + """ + Extract the 2D region of a mask corresponding to the rectangle encompassing all unmasked values. + + This is used to extract and visualize only the region of an image that is used in an analysis. + + Parameters + ---------- + buffer + The number pixels around the extracted array used as a buffer. + """ + from autoarray.mask.mask_2d import Mask2D + + extracted_mask_2d = array_2d_util.extracted_array_2d_from( + array_2d=np.array(self.mask), + y0=self.region[0] - buffer, + y1=self.region[1] + buffer, + x0=self.region[2] - buffer, + x1=self.region[3] + buffer, + ) + + return Mask2D( + mask=extracted_mask_2d, + pixel_scales=self.mask.pixel_scales, + origin=self.mask.origin, + ) + + def array_2d_from(self, array: Array2D, buffer: int = 1) -> Array2D: + """ + Extract the 2D region of an array corresponding to the rectangle encompassing all unmasked values. + + This is used to extract and visualize only the region of an image that is used in an analysis. + + Parameters + ---------- + buffer + The number pixels around the extracted array used as a buffer. + """ + from autoarray.structures.arrays.uniform_2d import Array2D + from autoarray.mask.mask_2d import Mask2D + + extracted_array_2d = array_2d_util.extracted_array_2d_from( + array_2d=np.array(array.native), + y0=self.region[0] - buffer, + y1=self.region[1] + buffer, + x0=self.region[2] - buffer, + x1=self.region[3] + buffer, + ) + + extracted_mask_2d = array_2d_util.extracted_array_2d_from( + array_2d=np.array(self.mask), + y0=self.region[0] - buffer, + y1=self.region[1] + buffer, + x0=self.region[2] - buffer, + x1=self.region[3] + buffer, + ) + + mask = Mask2D( + mask=extracted_mask_2d, + pixel_scales=array.pixel_scales, + origin=array.mask.mask_centre, + ) + + arr = array_2d_util.convert_array_2d(array_2d=extracted_array_2d, mask_2d=mask) + + return Array2D(values=arr, mask=mask, header=array.header) diff --git a/autoarray/mask/mask_1d_util.py b/autoarray/mask/mask_1d_util.py index 4a47bbc70..3d9943c19 100644 --- a/autoarray/mask/mask_1d_util.py +++ b/autoarray/mask/mask_1d_util.py @@ -80,4 +80,4 @@ def native_index_for_slim_index_1d_from( native_index_for_slim_index_1d[slim_index] = x slim_index += 1 - return native_index_for_slim_index_1d \ No newline at end of file + return native_index_for_slim_index_1d diff --git a/autoarray/mask/mask_2d.py b/autoarray/mask/mask_2d.py index b47b8eef7..6905d9b94 100644 --- a/autoarray/mask/mask_2d.py +++ b/autoarray/mask/mask_2d.py @@ -5,6 +5,7 @@ from pathlib import Path from typing import TYPE_CHECKING, Dict, List, Tuple, Union +from autoarray.mask.derive.zoom_2d import Zoom2D from autoarray.structures.abstract_structure import Structure if TYPE_CHECKING: @@ -253,6 +254,10 @@ def derive_mask(self) -> DeriveMask2D: def derive_grid(self) -> DeriveGrid2D: return DeriveGrid2D(mask=self) + @property + def zoom(self) -> Zoom2D: + return Zoom2D(mask=self) + @classmethod def all_false( cls, @@ -872,95 +877,6 @@ def resized_from(self, new_shape, pad_value: int = 0.0) -> Mask2D: origin=self.origin, ) - @property - def zoom_centre(self) -> Tuple[float, float]: - from autoarray.structures.grids.uniform_2d import Grid2D - - grid = grid_2d_util.grid_2d_slim_via_mask_from( - mask_2d=np.array(self), - pixel_scales=self.pixel_scales, - origin=self.origin, - ) - - grid = Grid2D(values=grid, mask=self) - - extraction_grid_1d = self.geometry.grid_pixels_2d_from(grid_scaled_2d=grid) - y_pixels_max = np.max(extraction_grid_1d[:, 0]) - y_pixels_min = np.min(extraction_grid_1d[:, 0]) - x_pixels_max = np.max(extraction_grid_1d[:, 1]) - x_pixels_min = np.min(extraction_grid_1d[:, 1]) - - return ( - ((y_pixels_max + y_pixels_min - 1.0) / 2.0), - ((x_pixels_max + x_pixels_min - 1.0) / 2.0), - ) - - @property - def zoom_offset_pixels(self) -> Tuple[float, float]: - if self.pixel_scales is None: - return self.geometry.central_pixel_coordinates - - return ( - self.zoom_centre[0] - self.geometry.central_pixel_coordinates[0], - self.zoom_centre[1] - self.geometry.central_pixel_coordinates[1], - ) - - @property - def zoom_offset_scaled(self) -> Tuple[float, float]: - return ( - -self.pixel_scales[0] * self.zoom_offset_pixels[0], - self.pixel_scales[1] * self.zoom_offset_pixels[1], - ) - - @property - def zoom_region(self) -> List[int]: - """ - The zoomed rectangular region corresponding to the square encompassing all unmasked values. This zoomed - extraction region is a squuare, even if the mask is rectangular. - - This is used to zoom in on the region of an image that is used in an analysis for visualization. - """ - - where = np.array(np.where(np.invert(self.astype("bool")))) - y0, x0 = np.amin(where, axis=1) - y1, x1 = np.amax(where, axis=1) - - # Have to convert mask to bool for invert function to work. - - ylength = y1 - y0 - xlength = x1 - x0 - - if ylength > xlength: - length_difference = ylength - xlength - x1 += int(length_difference / 2.0) - x0 -= int(length_difference / 2.0) - elif xlength > ylength: - length_difference = xlength - ylength - y1 += int(length_difference / 2.0) - y0 -= int(length_difference / 2.0) - - return [y0, y1 + 1, x0, x1 + 1] - - @property - def zoom_shape_native(self) -> Tuple[int, int]: - region = self.zoom_region - return (region[1] - region[0], region[3] - region[2]) - - @property - def zoom_mask_unmasked(self) -> "Mask2D": - """ - The scaled-grid of (y,x) coordinates of every pixel. - - This is defined from the top-left corner, such that the first pixel at location [0, 0] will have a negative x - value y value in scaled units. - """ - - return Mask2D.all_false( - shape_native=self.zoom_shape_native, - pixel_scales=self.pixel_scales, - origin=self.zoom_offset_scaled, - ) - @property def is_circular(self) -> bool: """ diff --git a/autoarray/mask/mask_2d_util.py b/autoarray/mask/mask_2d_util.py index db2751b04..47db2413b 100644 --- a/autoarray/mask/mask_2d_util.py +++ b/autoarray/mask/mask_2d_util.py @@ -316,9 +316,7 @@ def elliptical_radius_from( y_scaled_elliptical = r_scaled * np.sin(theta_rotated) x_scaled_elliptical = r_scaled * np.cos(theta_rotated) - return np.sqrt( - x_scaled_elliptical**2.0 + (y_scaled_elliptical / axis_ratio) ** 2.0 - ) + return np.sqrt(x_scaled_elliptical**2.0 + (y_scaled_elliptical / axis_ratio) ** 2.0) @numba_util.jit() diff --git a/autoarray/operators/convolver.py b/autoarray/operators/convolver.py index c963311a2..1e1cb07fa 100644 --- a/autoarray/operators/convolver.py +++ b/autoarray/operators/convolver.py @@ -215,12 +215,12 @@ def __init__(self, mask, kernel): mask_index_array=self.mask_index_array, kernel_2d=np.array(self.kernel.native[:, :]), ) - self.image_frame_1d_indexes[ - mask_1d_index, : - ] = image_frame_1d_indexes - self.image_frame_1d_kernels[ - mask_1d_index, : - ] = image_frame_1d_kernels + self.image_frame_1d_indexes[mask_1d_index, :] = ( + image_frame_1d_indexes + ) + self.image_frame_1d_kernels[mask_1d_index, :] = ( + image_frame_1d_kernels + ) self.image_frame_1d_lengths[mask_1d_index] = image_frame_1d_indexes[ image_frame_1d_indexes >= 0 ].shape[0] @@ -257,15 +257,15 @@ def __init__(self, mask, kernel): mask_index_array=np.array(self.mask_index_array), kernel_2d=np.array(self.kernel.native), ) - self.blurring_frame_1d_indexes[ - mask_1d_index, : - ] = image_frame_1d_indexes - self.blurring_frame_1d_kernels[ - mask_1d_index, : - ] = image_frame_1d_kernels - self.blurring_frame_1d_lengths[ - mask_1d_index - ] = image_frame_1d_indexes[image_frame_1d_indexes >= 0].shape[0] + self.blurring_frame_1d_indexes[mask_1d_index, :] = ( + image_frame_1d_indexes + ) + self.blurring_frame_1d_kernels[mask_1d_index, :] = ( + image_frame_1d_kernels + ) + self.blurring_frame_1d_lengths[mask_1d_index] = ( + image_frame_1d_indexes[image_frame_1d_indexes >= 0].shape[0] + ) mask_1d_index += 1 @staticmethod diff --git a/autoarray/operators/over_sampling/over_sample_util.py b/autoarray/operators/over_sampling/over_sample_util.py index 1d4637fc6..6aca49925 100644 --- a/autoarray/operators/over_sampling/over_sample_util.py +++ b/autoarray/operators/over_sampling/over_sample_util.py @@ -258,9 +258,9 @@ def sub_slim_index_for_sub_native_index_from(sub_mask_2d: np.ndarray): for sub_mask_y in range(sub_mask_2d.shape[0]): for sub_mask_x in range(sub_mask_2d.shape[1]): if sub_mask_2d[sub_mask_y, sub_mask_x] == False: - sub_slim_index_for_sub_native_index[ - sub_mask_y, sub_mask_x - ] = sub_mask_1d_index + sub_slim_index_for_sub_native_index[sub_mask_y, sub_mask_x] = ( + sub_mask_1d_index + ) sub_mask_1d_index += 1 return sub_slim_index_for_sub_native_index diff --git a/autoarray/plot/mat_plot/two_d.py b/autoarray/plot/mat_plot/two_d.py index 9fcec3657..e000a91a2 100644 --- a/autoarray/plot/mat_plot/two_d.py +++ b/autoarray/plot/mat_plot/two_d.py @@ -1,6 +1,6 @@ import matplotlib.pyplot as plt import numpy as np -from typing import Optional, List, Tuple, Union +from typing import Optional, List, Union from autoconf import conf @@ -12,6 +12,7 @@ from autoarray.plot.mat_plot.abstract import AbstractMatPlot from autoarray.plot.auto_labels import AutoLabels from autoarray.plot.visuals.two_d import Visuals2D +from autoarray.mask.derive.zoom_2d import Zoom2D from autoarray.structures.arrays.uniform_2d import Array2D from autoarray.structures.arrays import array_2d_util @@ -220,52 +221,6 @@ def __init__( self.is_for_subplot = False - def zoomed_array_and_extent_from(self, array) -> Tuple[np.ndarray, Tuple]: - """ - Returns the array and extent of the array, zoomed around the mask of the array, if the config file is set to - do this. - - Many plots zoom in around the mask of an array, to emphasize the signal of the data and not waste - plotting space on empty pixels. This function computes the zoomed array and extent of this array, given the - array. - - If the mask is all false, the array is returned without zooming by disabling the buffer. - - Parameters - ---------- - array - - Returns - ------- - - """ - - if array.mask.is_all_false: - buffer = 0 - else: - buffer = 1 - - zoom_around_mask = conf.instance["visualize"]["general"]["general"][ - "zoom_around_mask" - ] - - if ( - self.output.format == "fits" - and conf.instance["visualize"]["general"]["general"][ - "disable_zoom_for_fits" - ] - ): - zoom_around_mask = False - - if zoom_around_mask: - extent = array.extent_of_zoomed_array(buffer=buffer) - array = array.zoomed_around_mask(buffer=buffer) - - else: - extent = array.geometry.extent - - return array, extent - def plot_array( self, array: Array2D, @@ -301,7 +256,15 @@ def plot_array( "a pixel scales attribute." ) - array, extent = self.zoomed_array_and_extent_from(array=array) + if conf.instance["visualize"]["general"]["general"]["zoom_around_mask"]: + + zoom = Zoom2D(mask=array.mask) + + buffer = 0 if array.mask.is_all_false else 1 + + array = zoom.array_2d_from(array=array, buffer=buffer) + + extent = array.geometry.extent ax = None diff --git a/autoarray/plot/wrap/base/colorbar.py b/autoarray/plot/wrap/base/colorbar.py index 272d93fcd..b0650013a 100644 --- a/autoarray/plot/wrap/base/colorbar.py +++ b/autoarray/plot/wrap/base/colorbar.py @@ -130,9 +130,9 @@ def tick_labels_from( cb_unit = units.colorbar_label middle_index = (len(manual_tick_labels) - 1) // 2 - manual_tick_labels[ - middle_index - ] = rf"{manual_tick_labels[middle_index]}{cb_unit}" + manual_tick_labels[middle_index] = ( + rf"{manual_tick_labels[middle_index]}{cb_unit}" + ) return manual_tick_labels diff --git a/autoarray/plot/wrap/two_d/array_overlay.py b/autoarray/plot/wrap/two_d/array_overlay.py index 57652e8df..372bb5f6c 100644 --- a/autoarray/plot/wrap/two_d/array_overlay.py +++ b/autoarray/plot/wrap/two_d/array_overlay.py @@ -1,6 +1,7 @@ import matplotlib.pyplot as plt from autoarray.plot.wrap.two_d.abstract import AbstractMatWrap2D +from autoarray.mask.derive.zoom_2d import Zoom2D class ArrayOverlay(AbstractMatWrap2D): @@ -17,6 +18,9 @@ class ArrayOverlay(AbstractMatWrap2D): def overlay_array(self, array, figure): aspect = figure.aspect_from(shape_native=array.shape_native) - extent = array.extent_of_zoomed_array(buffer=0) + + zoom = Zoom2D(mask=array.mask) + array_zoom = zoom.array_2d_from(array=array, buffer=0) + extent = array_zoom.geometry.extent plt.imshow(X=array.native, aspect=aspect, extent=extent, **self.config_dict) diff --git a/autoarray/structures/arrays/uniform_2d.py b/autoarray/structures/arrays/uniform_2d.py index 4406db105..e9e68bcf3 100644 --- a/autoarray/structures/arrays/uniform_2d.py +++ b/autoarray/structures/arrays/uniform_2d.py @@ -7,6 +7,7 @@ from autoconf.fitsable import ndarray_via_fits_from, header_obj_from from autoarray.mask.mask_2d import Mask2D +from autoarray.mask.derive.zoom_2d import Zoom2D from autoarray.structures.abstract_structure import Structure from autoarray.structures.header import Header from autoarray.structures.arrays.uniform_1d import Array1D @@ -292,6 +293,31 @@ def native(self) -> "Array2D": values=self, mask=self.mask, header=self.header, store_native=True ) + @property + def native_for_fits(self) -> "Array2D": + """ + Return a `Array2D` for output to a .fits file, where the data is stored in its `native` representation, + which is an ``ndarray`` of shape [total_y_pixels, total_x_pixels]. + + Depending on configuration files, this array could be zoomed in on such that only the unmasked region + of the image is included in the .fits file, to save hard-disk space. Alternatively, the original `shape_native` + of the data can be retained. + + If it is already stored in its `native` representation it is return as it is. If not, it is mapped from + `slim` to `native` and returned as a new `Array2D`. + """ + if conf.instance["visualize"]["plots"]["fits_are_zoomed"]: + + zoom = Zoom2D(mask=self.mask) + + buffer = 0 if self.mask.is_all_false else 1 + + return zoom.array_2d_from(array=self, buffer=buffer).native + + return Array2D( + values=self, mask=self.mask, header=self.header, store_native=True + ) + @property def native_skip_mask(self) -> "Array2D": """ @@ -451,68 +477,6 @@ def brightest_sub_pixel_coordinate_in_region_from( pixel_coordinates_2d=(subpixel_y, subpixel_x) ) - def zoomed_around_mask(self, buffer: int = 1) -> "Array2D": - """ - Extract the 2D region of an array corresponding to the rectangle encompassing all unmasked values. - - This is used to extract and visualize only the region of an image that is used in an analysis. - - Parameters - ---------- - buffer - The number pixels around the extracted array used as a buffer. - """ - - extracted_array_2d = array_2d_util.extracted_array_2d_from( - array_2d=np.array(self.native), - y0=self.mask.zoom_region[0] - buffer, - y1=self.mask.zoom_region[1] + buffer, - x0=self.mask.zoom_region[2] - buffer, - x1=self.mask.zoom_region[3] + buffer, - ) - - mask = Mask2D.all_false( - shape_native=extracted_array_2d.shape, - pixel_scales=self.pixel_scales, - origin=self.mask.mask_centre, - ) - - array = array_2d_util.convert_array_2d( - array_2d=extracted_array_2d, mask_2d=mask - ) - - return Array2D(values=array, mask=mask, header=self.header) - - def extent_of_zoomed_array(self, buffer: int = 1) -> np.ndarray: - """ - For an extracted zoomed array computed from the method *zoomed_around_mask* compute its extent in scaled - coordinates. - - The extent of the grid in scaled units returned as an ``ndarray`` of the form [x_min, x_max, y_min, y_max]. - - This is used visualize zoomed and extracted arrays via the imshow() method. - - Parameters - ---------- - buffer - The number pixels around the extracted array used as a buffer. - """ - extracted_array_2d = array_2d_util.extracted_array_2d_from( - array_2d=np.array(self.native), - y0=self.mask.zoom_region[0] - buffer, - y1=self.mask.zoom_region[1] + buffer, - x0=self.mask.zoom_region[2] - buffer, - x1=self.mask.zoom_region[3] + buffer, - ) - - mask = Mask2D.all_false( - shape_native=extracted_array_2d.shape, - pixel_scales=self.pixel_scales, - origin=self.mask.mask_centre, - ) - - return mask.geometry.extent - def resized_from( self, new_shape: Tuple[int, int], mask_pad_value: int = 0.0 ) -> "Array2D": diff --git a/autoarray/structures/grids/grid_2d_util.py b/autoarray/structures/grids/grid_2d_util.py index 764f749b0..f21ff3db8 100644 --- a/autoarray/structures/grids/grid_2d_util.py +++ b/autoarray/structures/grids/grid_2d_util.py @@ -750,9 +750,7 @@ def grid_2d_slim_upscaled_from( The pixel scale of the uniform grid that laid over the irregular grid of (y,x) coordinates. """ - grid_2d_slim_upscaled = np.zeros( - shape=(grid_slim.shape[0] * upscale_factor**2, 2) - ) + grid_2d_slim_upscaled = np.zeros(shape=(grid_slim.shape[0] * upscale_factor**2, 2)) upscale_index = 0 diff --git a/test_autoarray/config/visualize.yaml b/test_autoarray/config/visualize.yaml index 568a11349..de62db024 100644 --- a/test_autoarray/config/visualize.yaml +++ b/test_autoarray/config/visualize.yaml @@ -3,7 +3,6 @@ general: backend: default imshow_origin: upper zoom_around_mask: true - disable_zoom_for_fits: true # If True, the zoom-in around the masked region is disabled when outputting .fits files, which is useful to retain the same dimensions as the input data. include_2d: border: true mapper_image_plane_mesh_grid: false diff --git a/test_autoarray/fit/plot/test_fit_imaging_plotters.py b/test_autoarray/fit/plot/test_fit_imaging_plotters.py index 4e5480ca7..31c288a4c 100644 --- a/test_autoarray/fit/plot/test_fit_imaging_plotters.py +++ b/test_autoarray/fit/plot/test_fit_imaging_plotters.py @@ -87,4 +87,4 @@ def test__output_as_fits__correct_output_format( file_path=path.join(plot_path, "data.fits"), hdu=0 ) - assert image_from_plot.shape == (7, 7) + assert image_from_plot.shape == (5, 5) diff --git a/test_autoarray/mask/derive/test_zoom_2d.py b/test_autoarray/mask/derive/test_zoom_2d.py new file mode 100644 index 000000000..dcf902bad --- /dev/null +++ b/test_autoarray/mask/derive/test_zoom_2d.py @@ -0,0 +1,214 @@ +import numpy as np + +import autoarray as aa + + +def test__quantities(): + + mask = aa.Mask2D.all_false(shape_native=(4, 6), pixel_scales=(1.0, 1.0)) + zoom = aa.Zoom2D(mask=mask) + + assert zoom.centre == (1.5, 2.5) + assert zoom.offset_pixels == (0, 0) + assert zoom.shape_native == (6, 6) + + mask = aa.Mask2D.all_false(shape_native=(6, 4), pixel_scales=(1.0, 1.0)) + zoom = aa.Zoom2D(mask=mask) + + assert zoom.centre == (2.5, 1.5) + assert zoom.offset_pixels == (0, 0) + assert zoom.shape_native == (6, 6) + + mask = aa.Mask2D( + mask=np.array([[True, True, True], [True, True, False], [True, True, True]]), + pixel_scales=(1.0, 1.0), + ) + zoom = aa.Zoom2D(mask=mask) + + assert zoom.centre == (1, 2) + assert zoom.offset_pixels == (0, 1) + assert zoom.shape_native == (1, 1) + + mask = aa.Mask2D( + mask=np.array([[True, True, True], [True, True, True], [True, False, True]]), + pixel_scales=(1.0, 1.0), + ) + zoom = aa.Zoom2D(mask=mask) + + assert zoom.centre == (2, 1) + assert zoom.offset_pixels == (1, 0) + assert zoom.shape_native == (1, 1) + + mask = aa.Mask2D( + mask=np.array([[False, True, False], [True, True, True], [True, True, True]]), + pixel_scales=(1.0, 1.0), + ) + zoom = aa.Zoom2D(mask=mask) + + assert zoom.centre == (0, 1) + assert zoom.offset_pixels == (-1, 0) + assert zoom.shape_native == (3, 3) + + mask = aa.Mask2D( + mask=np.array([[False, False, True], [True, True, True], [True, True, True]]), + pixel_scales=(1.0, 1.0), + ) + zoom = aa.Zoom2D(mask=mask) + + assert zoom.centre == (0, 0.5) + assert zoom.offset_pixels == (-1, -0.5) + assert zoom.shape_native == (1, 2) + + mask = aa.Mask2D( + mask=np.array( + [ + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, True], + [True, True, True, True, True, True, False], + ] + ), + pixel_scales=(1.0, 1.0), + ) + zoom = aa.Zoom2D(mask=mask) + + assert zoom.centre == (2, 6) + assert zoom.offset_pixels == (1, 3) + + mask = aa.Mask2D( + mask=np.array( + [ + [True, True, True], + [True, True, True], + [True, True, True], + [True, True, True], + [True, True, False], + ] + ), + pixel_scales=(1.0, 1.0), + ) + zoom = aa.Zoom2D(mask=mask) + + assert zoom.centre == (4, 2) + assert zoom.offset_pixels == (2, 1) + + mask = aa.Mask2D( + mask=np.array( + [ + [True, True, True], + [True, True, True], + [True, True, True], + [True, True, True], + [True, True, True], + [True, True, True], + [True, True, False], + ] + ), + pixel_scales=(1.0, 1.0), + ) + zoom = aa.Zoom2D(mask=mask) + + assert zoom.centre == (6, 2) + assert zoom.offset_pixels == (3, 1) + + +def test__array_2d_from(): + array_2d = [ + [1.0, 2.0, 3.0, 4.0], + [5.0, 6.0, 7.0, 8.0], + [9.0, 10.0, 11.0, 12.0], + [13.0, 14.0, 15.0, 16.0], + ] + + mask = aa.Mask2D( + mask=[ + [True, True, True, True], + [True, False, False, True], + [True, False, False, True], + [True, True, True, True], + ], + pixel_scales=(1.0, 1.0), + ) + + arr = aa.Array2D(values=array_2d, mask=mask) + zoom = aa.Zoom2D(mask=mask) + arr_zoomed = zoom.array_2d_from(array=arr, buffer=0) + + assert (arr_zoomed.native == np.array([[6.0, 7.0], [10.0, 11.0]])).all() + + mask = aa.Mask2D( + mask=np.array( + [ + [True, True, True, True], + [True, False, False, True], + [False, False, False, True], + [True, True, True, True], + ] + ), + pixel_scales=(1.0, 1.0), + ) + + arr = aa.Array2D(values=array_2d, mask=mask) + zoom = aa.Zoom2D(mask=mask) + arr_zoomed = zoom.array_2d_from(array=arr, buffer=0) + + assert (arr_zoomed.native == np.array([[0.0, 6.0, 7.0], [9.0, 10.0, 11.0]])).all() + + mask = aa.Mask2D( + mask=np.array( + [ + [True, False, True, True], + [True, False, False, True], + [True, False, False, True], + [True, True, True, True], + ] + ), + pixel_scales=(1.0, 1.0), + ) + + arr = aa.Array2D(values=array_2d, mask=mask) + zoom = aa.Zoom2D(mask=mask) + arr_zoomed = zoom.array_2d_from(array=arr, buffer=0) + + assert (arr_zoomed.native == np.array([[2.0, 0.0], [6.0, 7.0], [10.0, 11.0]])).all() + + array_2d = np.ones(shape=(4, 4)) + + mask = aa.Mask2D( + mask=np.array( + [ + [True, True, True, True], + [True, False, False, True], + [True, False, False, True], + [True, True, True, True], + ] + ), + pixel_scales=(1.0, 1.0), + ) + + arr = aa.Array2D(values=array_2d, mask=mask) + zoom = aa.Zoom2D(mask=mask) + arr_zoomed = zoom.array_2d_from(array=arr, buffer=0) + + assert arr_zoomed.mask.origin == (0.0, 0.0) + + array_2d = np.ones(shape=(6, 6)) + + mask = aa.Mask2D( + mask=np.array( + [ + [True, True, True, True, True, True], + [True, True, True, True, True, True], + [True, True, True, False, False, True], + [True, True, True, False, False, True], + [True, True, True, True, True, True], + [True, True, True, True, True, True], + ] + ), + pixel_scales=(1.0, 1.0), + ) + + arr = aa.Array2D(values=array_2d, mask=mask) + zoom = aa.Zoom2D(mask=mask) + arr_zoomed = zoom.array_2d_from(array=arr, buffer=0) + + assert arr_zoomed.mask.origin == (0.0, 1.0) diff --git a/test_autoarray/mask/test_mask_2d.py b/test_autoarray/mask/test_mask_2d.py index fda07c66d..ce902f545 100644 --- a/test_autoarray/mask/test_mask_2d.py +++ b/test_autoarray/mask/test_mask_2d.py @@ -1,4 +1,3 @@ -from astropy.io import fits import os from os import path import numpy as np @@ -528,256 +527,6 @@ def test__resized_from(): assert (mask_resized == mask_resized_manual).all() -def test__zoom_quantities(): - mask = aa.Mask2D.all_false(shape_native=(3, 5), pixel_scales=(1.0, 1.0)) - assert mask.zoom_centre == (1.0, 2.0) - assert mask.zoom_offset_pixels == (0, 0) - assert mask.zoom_shape_native == (5, 5) - - mask = aa.Mask2D.all_false(shape_native=(5, 3), pixel_scales=(1.0, 1.0)) - assert mask.zoom_centre == (2.0, 1.0) - assert mask.zoom_offset_pixels == (0, 0) - assert mask.zoom_shape_native == (5, 5) - - mask = aa.Mask2D.all_false(shape_native=(4, 6), pixel_scales=(1.0, 1.0)) - assert mask.zoom_centre == (1.5, 2.5) - assert mask.zoom_offset_pixels == (0, 0) - assert mask.zoom_shape_native == (6, 6) - - mask = aa.Mask2D.all_false(shape_native=(6, 4), pixel_scales=(1.0, 1.0)) - assert mask.zoom_centre == (2.5, 1.5) - assert mask.zoom_offset_pixels == (0, 0) - assert mask.zoom_shape_native == (6, 6) - - -def test__mask_is_single_false__extraction_centre_is_central_pixel(): - mask = aa.Mask2D( - mask=np.array([[False, True, True], [True, True, True], [True, True, True]]), - pixel_scales=(1.0, 1.0), - ) - assert mask.zoom_centre == (0, 0) - assert mask.zoom_offset_pixels == (-1, -1) - assert mask.zoom_shape_native == (1, 1) - - mask = aa.Mask2D( - mask=np.array([[True, True, False], [True, True, True], [True, True, True]]), - pixel_scales=(1.0, 1.0), - ) - assert mask.zoom_centre == (0, 2) - assert mask.zoom_offset_pixels == (-1, 1) - assert mask.zoom_shape_native == (1, 1) - - mask = aa.Mask2D( - mask=np.array([[True, True, True], [True, True, True], [False, True, True]]), - pixel_scales=(1.0, 1.0), - ) - assert mask.zoom_centre == (2, 0) - assert mask.zoom_offset_pixels == (1, -1) - assert mask.zoom_shape_native == (1, 1) - - mask = aa.Mask2D( - mask=np.array([[True, True, True], [True, True, True], [True, True, False]]), - pixel_scales=(1.0, 1.0), - ) - assert mask.zoom_centre == (2, 2) - assert mask.zoom_offset_pixels == (1, 1) - assert mask.zoom_shape_native == (1, 1) - - mask = aa.Mask2D( - mask=np.array([[True, False, True], [True, True, True], [True, True, True]]), - pixel_scales=(1.0, 1.0), - ) - assert mask.zoom_centre == (0, 1) - assert mask.zoom_offset_pixels == (-1, 0) - assert mask.zoom_shape_native == (1, 1) - - mask = aa.Mask2D( - mask=np.array([[True, True, True], [False, True, True], [True, True, True]]), - pixel_scales=(1.0, 1.0), - ) - assert mask.zoom_centre == (1, 0) - assert mask.zoom_offset_pixels == (0, -1) - assert mask.zoom_shape_native == (1, 1) - - mask = aa.Mask2D( - mask=np.array([[True, True, True], [True, True, False], [True, True, True]]), - pixel_scales=(1.0, 1.0), - ) - assert mask.zoom_centre == (1, 2) - assert mask.zoom_offset_pixels == (0, 1) - assert mask.zoom_shape_native == (1, 1) - - mask = aa.Mask2D( - mask=np.array([[True, True, True], [True, True, True], [True, False, True]]), - pixel_scales=(1.0, 1.0), - ) - assert mask.zoom_centre == (2, 1) - assert mask.zoom_offset_pixels == (1, 0) - assert mask.zoom_shape_native == (1, 1) - - -def test__mask_is_x2_false__extraction_centre_is_central_pixel(): - mask = aa.Mask2D( - mask=np.array([[False, True, True], [True, True, True], [True, True, False]]), - pixel_scales=(1.0, 1.0), - ) - assert mask.zoom_centre == (1, 1) - assert mask.zoom_offset_pixels == (0, 0) - assert mask.zoom_shape_native == (3, 3) - - mask = aa.Mask2D( - mask=np.array([[False, True, True], [True, True, True], [False, True, True]]), - pixel_scales=(1.0, 1.0), - ) - assert mask.zoom_centre == (1, 0) - assert mask.zoom_offset_pixels == (0, -1) - assert mask.zoom_shape_native == (3, 3) - - mask = aa.Mask2D( - mask=np.array([[False, True, False], [True, True, True], [True, True, True]]), - pixel_scales=(1.0, 1.0), - ) - assert mask.zoom_centre == (0, 1) - assert mask.zoom_offset_pixels == (-1, 0) - assert mask.zoom_shape_native == (3, 3) - - mask = aa.Mask2D( - mask=np.array([[False, False, True], [True, True, True], [True, True, True]]), - pixel_scales=(1.0, 1.0), - ) - assert mask.zoom_centre == (0, 0.5) - assert mask.zoom_offset_pixels == (-1, -0.5) - assert mask.zoom_shape_native == (1, 2) - - -def test__rectangular_mask(): - mask = aa.Mask2D( - mask=np.array( - [ - [False, True, True, True], - [True, True, True, True], - [True, True, True, True], - ] - ), - pixel_scales=(1.0, 1.0), - ) - - assert mask.zoom_centre == (0, 0) - assert mask.zoom_offset_pixels == (-1.0, -1.5) - - mask = aa.Mask2D( - mask=np.array( - [ - [True, True, True, True], - [True, True, True, True], - [True, True, True, False], - ] - ), - pixel_scales=(1.0, 1.0), - ) - - assert mask.zoom_centre == (2, 3) - assert mask.zoom_offset_pixels == (1.0, 1.5) - - mask = aa.Mask2D( - mask=np.array( - [ - [True, True, True, True, True], - [True, True, True, True, True], - [True, True, True, True, False], - ] - ), - pixel_scales=(1.0, 1.0), - ) - - assert mask.zoom_centre == (2, 4) - assert mask.zoom_offset_pixels == (1, 2) - - mask = aa.Mask2D( - mask=np.array( - [ - [True, True, True, True, True, True, True], - [True, True, True, True, True, True, True], - [True, True, True, True, True, True, False], - ] - ), - pixel_scales=(1.0, 1.0), - ) - - assert mask.zoom_centre == (2, 6) - assert mask.zoom_offset_pixels == (1, 3) - - mask = aa.Mask2D( - mask=np.array( - [ - [True, True, True], - [True, True, True], - [True, True, True], - [True, True, True], - [True, True, False], - ] - ), - pixel_scales=(1.0, 1.0), - ) - - assert mask.zoom_centre == (4, 2) - assert mask.zoom_offset_pixels == (2, 1) - - mask = aa.Mask2D( - mask=np.array( - [ - [True, True, True], - [True, True, True], - [True, True, True], - [True, True, True], - [True, True, True], - [True, True, True], - [True, True, False], - ] - ), - pixel_scales=(1.0, 1.0), - ) - - assert mask.zoom_centre == (6, 2) - assert mask.zoom_offset_pixels == (3, 1) - - -def test__zoom_mask_unmasked(): - mask = aa.Mask2D( - mask=np.array( - [ - [False, True, True, True], - [True, False, True, True], - [True, True, True, True], - ] - ), - pixel_scales=(1.0, 1.0), - ) - - zoom_mask = mask.zoom_mask_unmasked - - assert (zoom_mask == np.array([[False, False], [False, False]])).all() - assert zoom_mask.origin == (0.5, -1.0) - - mask = aa.Mask2D( - mask=np.array( - [ - [False, True, True, True], - [True, False, True, True], - [True, False, True, True], - ] - ), - pixel_scales=(1.0, 2.0), - ) - - zoom_mask = mask.zoom_mask_unmasked - - assert ( - zoom_mask == np.array([[False, False], [False, False], [False, False]]) - ).all() - assert zoom_mask.origin == (0.0, -2.0) - - def test__mask_centre(): mask = np.array( [ diff --git a/test_autoarray/plot/wrap/base/test_ticks.py b/test_autoarray/plot/wrap/base/test_ticks.py index ead11e885..ab9021eec 100644 --- a/test_autoarray/plot/wrap/base/test_ticks.py +++ b/test_autoarray/plot/wrap/base/test_ticks.py @@ -60,7 +60,9 @@ def test__yticks__set(): units = aplt.Units(use_scaled=True, ticks_convert_factor=None) yticks = aplt.YTicks(fontsize=34) - extent = array.extent_of_zoomed_array(buffer=1) + zoom = aa.Zoom2D(mask=array.mask) + array_zoom = zoom.array_2d_from(array=array, buffer=0) + extent = array_zoom.geometry.extent yticks.set(min_value=extent[2], max_value=extent[3], units=units) yticks = aplt.YTicks(fontsize=34) @@ -105,7 +107,9 @@ def test__xticks__set(): array = aa.Array2D.ones(shape_native=(2, 2), pixel_scales=1.0) units = aplt.Units(use_scaled=True, ticks_convert_factor=None) xticks = aplt.XTicks(fontsize=34) - extent = array.extent_of_zoomed_array(buffer=1) + zoom = aa.Zoom2D(mask=array.mask) + array_zoom = zoom.array_2d_from(array=array, buffer=0) + extent = array_zoom.geometry.extent xticks.set(min_value=extent[0], max_value=extent[1], units=units) xticks = aplt.XTicks(fontsize=34) diff --git a/test_autoarray/structures/arrays/test_uniform_2d.py b/test_autoarray/structures/arrays/test_uniform_2d.py index 39cbc71df..0c55b0c38 100644 --- a/test_autoarray/structures/arrays/test_uniform_2d.py +++ b/test_autoarray/structures/arrays/test_uniform_2d.py @@ -355,135 +355,6 @@ def test__trimmed_after_convolution_from(): assert new_arr.mask.pixel_scales == (1.0, 1.0) -def test__zoomed_around_mask(): - array_2d = [ - [1.0, 2.0, 3.0, 4.0], - [5.0, 6.0, 7.0, 8.0], - [9.0, 10.0, 11.0, 12.0], - [13.0, 14.0, 15.0, 16.0], - ] - - mask = aa.Mask2D( - mask=[ - [True, True, True, True], - [True, False, False, True], - [True, False, False, True], - [True, True, True, True], - ], - pixel_scales=(1.0, 1.0), - ) - - arr_masked = aa.Array2D(values=array_2d, mask=mask) - - arr_zoomed = arr_masked.zoomed_around_mask(buffer=0) - - assert (arr_zoomed.native == np.array([[6.0, 7.0], [10.0, 11.0]])).all() - - mask = aa.Mask2D( - mask=np.array( - [ - [True, True, True, True], - [True, False, False, True], - [False, False, False, True], - [True, True, True, True], - ] - ), - pixel_scales=(1.0, 1.0), - ) - - arr_masked = aa.Array2D(values=array_2d, mask=mask) - arr_zoomed = arr_masked.zoomed_around_mask(buffer=0) - - assert (arr_zoomed.native == np.array([[0.0, 6.0, 7.0], [9.0, 10.0, 11.0]])).all() - - mask = aa.Mask2D( - mask=np.array( - [ - [True, False, True, True], - [True, False, False, True], - [True, False, False, True], - [True, True, True, True], - ] - ), - pixel_scales=(1.0, 1.0), - ) - - arr_masked = aa.Array2D(values=array_2d, mask=mask) - arr_zoomed = arr_masked.zoomed_around_mask(buffer=0) - assert (arr_zoomed.native == np.array([[2.0, 0.0], [6.0, 7.0], [10.0, 11.0]])).all() - - -def test__zoomed_around_mask__origin_updated(): - array_2d = np.ones(shape=(4, 4)) - - mask = aa.Mask2D( - mask=np.array( - [ - [True, True, True, True], - [True, False, False, True], - [True, False, False, True], - [True, True, True, True], - ] - ), - pixel_scales=(1.0, 1.0), - ) - - arr_masked = aa.Array2D(values=array_2d, mask=mask) - - arr_zoomed = arr_masked.zoomed_around_mask(buffer=0) - - assert arr_zoomed.mask.origin == (0.0, 0.0) - - array_2d = np.ones(shape=(6, 6)) - - mask = aa.Mask2D( - mask=np.array( - [ - [True, True, True, True, True, True], - [True, True, True, True, True, True], - [True, True, True, False, False, True], - [True, True, True, False, False, True], - [True, True, True, True, True, True], - [True, True, True, True, True, True], - ] - ), - pixel_scales=(1.0, 1.0), - ) - - arr_masked = aa.Array2D(values=array_2d, mask=mask) - - arr_zoomed = arr_masked.zoomed_around_mask(buffer=0) - - assert arr_zoomed.mask.origin == (0.0, 1.0) - - -def test__extent_of_zoomed_array(): - array_2d = [ - [1.0, 2.0, 3.0, 4.0], - [5.0, 6.0, 7.0, 8.0], - [9.0, 10.0, 11.0, 12.0], - [13.0, 14.0, 15.0, 16.0], - ] - - mask = aa.Mask2D( - mask=np.array( - [ - [True, True, True, False], - [True, False, False, True], - [True, False, False, True], - [True, True, True, True], - ] - ), - pixel_scales=(1.0, 2.0), - ) - - arr_masked = aa.Array2D(values=array_2d, mask=mask) - - extent = arr_masked.extent_of_zoomed_array(buffer=1) - - assert extent == pytest.approx(np.array([-4.0, 6.0, -2.0, 3.0]), 1.0e-4) - - def test__binned_across_rows(): array = aa.Array2D.no_mask(values=np.ones((4, 3)), pixel_scales=1.0)