diff --git a/autoarray/mask/abstract_mask.py b/autoarray/mask/abstract_mask.py index 02d032cc8..95b4ffa7a 100644 --- a/autoarray/mask/abstract_mask.py +++ b/autoarray/mask/abstract_mask.py @@ -49,6 +49,9 @@ def __init__( converted to a (float, float) structure. origin The origin of the mask's coordinate system in scaled units. + xp + The array module to use (default `numpy`; pass `jax.numpy` for JAX support). Controls + whether internal index arrays are computed on CPU or GPU. """ # noinspection PyArgumentList @@ -62,6 +65,10 @@ def __init__( @property def _xp(self): + """ + Returns the array module in use (`numpy` or `jax.numpy`), determined by whether the mask was + constructed with `xp=jnp`. + """ if self.use_jax: import jax.numpy as jnp @@ -70,6 +77,10 @@ def _xp(self): @property def mask(self): + """ + The boolean ndarray of the mask, where `False` entries are unmasked (used in calculations) + and `True` entries are masked (excluded from calculations). + """ return self._array def __array_finalize__(self, obj): @@ -82,8 +93,10 @@ def __array_finalize__(self, obj): @property def pixel_scale(self) -> float: """ - For a mask with dimensions two or above check that are pixel scales are the same, and if so return this - single value as a float. + The pixel scale of the mask as a single float value. + + For masks with two or more dimensions this assumes all pixel scales are equal and returns + the first entry of ``pixel_scales``. """ return self.pixel_scales[0] @@ -102,6 +115,9 @@ def header_dict(self) -> Dict: @property def dimensions(self) -> int: + """ + The number of dimensions of the mask (e.g. 1 for a 1D mask, 2 for a 2D mask). + """ return len(self.shape) def output_to_fits(self, file_path, overwrite=False): @@ -154,7 +170,7 @@ def is_all_true(self) -> bool: @property def is_all_false(self) -> bool: """ - Returns `False` if all pixels in a mask are `False`, else returns `True`. + Returns `True` if all pixels in a mask are `False` (i.e. every pixel is unmasked), else returns `False`. """ return self.pixels_in_mask == np.size(self._array) diff --git a/autoarray/mask/derive/grid_1d.py b/autoarray/mask/derive/grid_1d.py index b877be139..df0a65ac1 100644 --- a/autoarray/mask/derive/grid_1d.py +++ b/autoarray/mask/derive/grid_1d.py @@ -23,18 +23,18 @@ def __init__(self, mask: Mask1D): omitted (for a full description see the :meth:`Mask1D class API documentation `). - From a ``Mask1D``,``Grid1D``s can be derived, which represent the (y,x) Cartesian coordinates of a subset of + From a ``Mask1D``, ``Grid1D``s can be derived, which represent the (x,) Cartesian coordinates of a subset of pixels with significance. For example: - - An ``all_false`` ``Grid1D``: the same shape as the original ``Mask1D`` but has unmasked ``False`` values - everywhere. + - An ``all_false`` ``Grid1D``: the (x,) coordinates of every pixel in the ``Mask1D`` regardless of whether + each pixel is masked or unmasked. Parameters ---------- mask - The ``Mask2D`` from which new ``Grid2D`` objects are derived. + The ``Mask1D`` from which new ``Grid1D`` objects are derived. Examples -------- @@ -43,20 +43,14 @@ def __init__(self, mask: Mask1D): 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], - ], + mask_1d = aa.Mask1D( + mask=[True, False, False, False, True], pixel_scales=1.0, ) - derive_grid_2d = aa.DeriveGrid2D(mask=mask_2d) + derive_grid_1d = aa.DeriveGrid1D(mask=mask_1d) - print(derive_grid_2d.border) + print(derive_grid_1d.all_false) """ self.mask = mask @@ -64,21 +58,21 @@ def __init__(self, mask: Mask1D): def all_false(self) -> Grid1D: """ Returns a ``Grid1D`` which uses the ``Mask1D`` - geometry (``shape_native`` / ``pixel_scales`` / ``origin``) and every pixel in the ``Mask2D`` - irrespective of whether pixels are masked or unmasked (given by ``True`` or``False``). + geometry (``shape_native`` / ``pixel_scales`` / ``origin``) and includes every pixel in the ``Mask1D``, + irrespective of whether pixels are masked or unmasked (given by ``True`` or ``False``). - For example, for the following ``Mask2D``: + For example, for the following ``Mask1D``: :: - mask_2d = aa.Mask1D( - mask=[False, False, True, True] + mask_1d = aa.Mask1D( + mask=[False, False, True, True], pixel_scales=1.0, ) The ``all_false`` ``Grid1D`` (given via ``mask_1d.derive_grid.all_false``) is: :: - [-2.0, -1.0, 1.0, 2.0] + [-1.5, -0.5, 0.5, 1.5] Examples -------- @@ -87,7 +81,7 @@ def all_false(self) -> Grid1D: import autoarray as aa - mask_1d = aa.Mask2D( + mask_1d = aa.Mask1D( mask=[False, False, True, True], pixel_scales=1.0, ) diff --git a/autoarray/mask/derive/grid_2d.py b/autoarray/mask/derive/grid_2d.py index 1b3c0fc6a..e7f951f40 100644 --- a/autoarray/mask/derive/grid_2d.py +++ b/autoarray/mask/derive/grid_2d.py @@ -263,7 +263,7 @@ def border(self) -> Grid2D: pixel_scales=1.0, ) - The ``edge`` grid (given via ``mask_2d.derive_grid.edge``) is given by: + The ``border`` grid (given via ``mask_2d.derive_grid.border``) is given by: :: [[3.0, -3.0], [3.0, -2.0], [3.0, -1.0], [3.0, 0.0], [3.0, 1.0], [3.0, 2.0], [ 3.0, 3.0], diff --git a/autoarray/mask/derive/indexes_2d.py b/autoarray/mask/derive/indexes_2d.py index 1a80b42c6..04ff1cf45 100644 --- a/autoarray/mask/derive/indexes_2d.py +++ b/autoarray/mask/derive/indexes_2d.py @@ -340,7 +340,7 @@ def border_native(self) -> np.ndarray: [ [1,1], [1,2], [1,3], [1,4], [1,5], [1,6], [1,7], [2,1], [2,7], [3,1], [3,7], [4,1], [4,7], [5,1], [5,7], [6,1], [6,7], - [7,1], [7,2], 71,3], [7,4], [7,5], [7,6], [7,7] + [7,1], [7,2], [7,3], [7,4], [7,5], [7,6], [7,7] ] The interior 8 ``False`` values (e.g. ``native`` index ``[3,3]``) are omitted, because although they are edge diff --git a/autoarray/mask/derive/mask_1d.py b/autoarray/mask/derive/mask_1d.py index af0dbe9c2..c0813e081 100644 --- a/autoarray/mask/derive/mask_1d.py +++ b/autoarray/mask/derive/mask_1d.py @@ -103,7 +103,7 @@ def to_mask_2d(self) -> Mask2D: :: [True, False, False, False, True] - The corresponding ``Mask2D`` (given via ``mask_1d.derive_mask.to_mask_1d``) is: + The corresponding ``Mask2D`` (given via ``mask_1d.derive_mask.to_mask_2d``) is: :: [[False, False, False, False, False]] diff --git a/autoarray/mask/derive/mask_2d.py b/autoarray/mask/derive/mask_2d.py index ad1e1c558..9216bcf90 100644 --- a/autoarray/mask/derive/mask_2d.py +++ b/autoarray/mask/derive/mask_2d.py @@ -174,6 +174,9 @@ def blurring_from( ---------- kernel_shape_native The 2D shape of the 2D convolution ``Convolver`` which defines the blurring region. + allow_padding + If ``True``, blurring pixels that extend beyond the mask boundary are allowed. If ``False`` (default), + an exception is raised if the blurring region extends beyond the mask boundary. Examples -------- @@ -277,7 +280,7 @@ def edge_buffed(self) -> Mask2D: """ Returns a buffed edge ``Mask2D``, representing all unmasked pixels (given by ``False``) which neighbor any masked value (give by ``True``) and therefore are on the edge of the 2D mask, but with a buffer of 1 pixel - applied such that everu pixel 1 pixel further out are included. + applied such that every pixel 1 pixel further out is included. For example, for the following ``Mask2D``: diff --git a/autoarray/mask/derive/zoom_2d.py b/autoarray/mask/derive/zoom_2d.py index f3a3f5dac..79762199a 100644 --- a/autoarray/mask/derive/zoom_2d.py +++ b/autoarray/mask/derive/zoom_2d.py @@ -22,7 +22,8 @@ def __init__(self, mask: Union[np.ndarray, List]): omitted (for a full description see the :meth:`Mask2D` class API documentation `). - The `Zoom2D` object calculations many different zoomed in qu + The ``Zoom2D`` object computes the zoomed region around the unmasked pixels, including its centre, + pixel offsets, scaled offsets, and extracted sub-arrays for visualization. Parameters ---------- @@ -90,15 +91,14 @@ def centre(self) -> Tuple[float, float]: @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. + Returns the (y,x) pixel offset of the centre of the zoomed region from the centre of the ``Mask2D``. - This is computed by subtracting the pixel coordinates of the `Mask2D` object from the pixel coordinates of - the zoomed in region. + This is the difference between the pixel coordinates of the zoomed region's centre and the pixel + coordinates of the full mask's central pixel. Returns ------- - The offset of the zoomed in region from the centre of the `Mask2D` object in pixel units. + The (y,x) offset of the zoomed region centre from the mask centre in pixel units. """ if self.mask.pixel_scales is None: return self.mask.geometry.central_pixel_coordinates @@ -111,15 +111,13 @@ def offset_pixels(self) -> Tuple[float, float]: @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. + Returns the (y,x) scaled offset of the centre of the zoomed region from the centre of the ``Mask2D``. - This is computed by subtracting the pixel coordinates of the `Mask2D` object from the pixel coordinates of - the zoomed in region. + This converts ``offset_pixels`` to scaled units using the mask's ``pixel_scales``. Returns ------- - The offset of the zoomed in region from the centre of the `Mask2D` object in scaled units. + The (y,x) offset of the zoomed region centre from the mask centre in scaled units. """ return ( -self.mask.pixel_scales[0] * self.offset_pixels[0], @@ -173,17 +171,19 @@ def shape_native(self) -> Tuple[int, int]: 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. + Compute the extent of the zoomed region in scaled coordinates, including an optional pixel buffer. - 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. + The extent is returned as a tuple of the form ``(x_min, x_max, y_min, y_max)`` which matches the + ``extent`` format expected by ``matplotlib.imshow``. Parameters ---------- buffer - The number pixels around the extracted array used as a buffer. + The number of pixels around the unmasked region used as a buffer when computing the extent. + + Returns + ------- + The extent of the zoomed region as ``(x_min, x_max, y_min, y_max)`` in scaled units. """ from autoarray.mask.mask_2d import Mask2D diff --git a/autoarray/mask/mask_1d.py b/autoarray/mask/mask_1d.py index a4525aa99..b7bc8c590 100644 --- a/autoarray/mask/mask_1d.py +++ b/autoarray/mask/mask_1d.py @@ -53,7 +53,13 @@ def __init__( pixel_scales The scaled units to pixel units conversion factor of each pixel. origin - The x origin of the mask's coordinate system in scaled units. + The (x,) origin of the mask's coordinate system in scaled units. + invert + If `True`, the `bool`'s of the input `mask` are inverted, so `False` entries become `True` + and vice versa. + xp + The array module to use (default `numpy`; pass `jax.numpy` for JAX support). Controls + whether internal index arrays are computed on CPU or GPU. """ if type(mask) is list: @@ -102,10 +108,18 @@ def geometry(self) -> Geometry1D: @property def derive_mask(self) -> DeriveMask1D: + """ + Returns the ``DeriveMask1D`` object associated with the mask, which computes derived masks such as + the edge mask. + """ return DeriveMask1D(mask=self) @property def derive_grid(self) -> DeriveGrid1D: + """ + Returns the ``DeriveGrid1D`` object associated with the mask, which computes derived grids of (x,) + coordinates such as the unmasked pixel grid. + """ return DeriveGrid1D(mask=self) @classmethod @@ -122,9 +136,14 @@ def all_false( Parameters ---------- shape_slim - The (y,x) shape of the mask in units of pixels. + The 1D shape of the mask in units of pixels. pixel_scales The scaled units to pixel units conversion factor of each pixel. + origin + The (x,) scaled units origin of the mask's coordinate system. + invert + If `True`, the `bool`'s of the input `mask` are inverted, so `False` entries become `True` + and vice versa. """ return cls( mask=np.full(shape=shape_slim, fill_value=False), @@ -149,9 +168,16 @@ def from_fits( file_path The full path of the fits file. hdu - The HDU number in the fits file containing the image image. + The HDU number in the ``.fits`` file containing the mask array. pixel_scales The scaled units to pixel units conversion factor of each pixel. + origin + The (x,) scaled units origin of the mask's coordinate system. + + Returns + ------- + Mask1D + The mask loaded from the ``.fits`` file. """ return cls( @@ -164,10 +190,17 @@ def from_fits( @property def shape_native(self) -> Tuple[int]: + """ + The 1D shape of the mask in its native representation, equal to the shape of the underlying boolean ndarray. + """ return self.shape @property def shape_slim(self) -> Tuple[int]: + """ + The 1D shape of the mask in its slim representation. For a 1D mask this is the same as ``shape_native`` + since there is no native/slim distinction — every pixel is on the same 1D line. + """ return self.shape @property @@ -175,8 +208,7 @@ def header_dict(self) -> Dict: """ Returns the pixel scales of the mask as a header dictionary, which can be written to a .fits file. - A 2D mask has different pixel scale variables for each dimension, the header therefore contain both pixel - scales as separate y and x entries. + A 1D mask has a single pixel scale, so the header contains one pixel scale entry alongside the origin. Returns ------- diff --git a/autoarray/mask/mask_2d.py b/autoarray/mask/mask_2d.py index 94c0029c7..1fd5e722e 100644 --- a/autoarray/mask/mask_2d.py +++ b/autoarray/mask/mask_2d.py @@ -199,6 +199,12 @@ def __init__( it is converted to a (float, float) structure. origin The (y,x) scaled units origin of the mask's coordinate system. + invert + If `True`, the `bool`'s of the input `mask` are inverted, so `False` entries become `True` + and vice versa. + xp + The array module to use (default `numpy`; pass `jax.numpy` for JAX support). Controls + whether internal index arrays are computed on CPU or GPU. """ if type(mask) is list: @@ -224,6 +230,13 @@ def __init__( @property def native_for_slim(self): + """ + A 2D array of shape [total_unmasked_pixels, 2] that maps every unmasked pixel's slim index to its + (y, x) native 2D index. + + For example, if ``slim_to_native[3] = [2, 5]``, the 4th unmasked pixel (slim index 3) is located at + row 2, column 5 in the native 2D array. + """ return self.derive_indexes.native_for_slim __no_flatten__ = ("derive_indexes",) @@ -252,18 +265,34 @@ def geometry(self) -> Geometry2D: @property def derive_indexes(self) -> DeriveIndexes2D: + """ + Returns the ``DeriveIndexes2D`` object associated with the mask, which contains derived index arrays + used to map data between ``slim`` (1D unmasked) and ``native`` (2D full-shape) representations. + """ return DeriveIndexes2D(mask=self, xp=self._xp) @property def derive_mask(self) -> DeriveMask2D: + """ + Returns the ``DeriveMask2D`` object associated with the mask, which computes derived masks such as + the edge mask, border mask, and blurring mask. + """ return DeriveMask2D(mask=self) @property def derive_grid(self) -> DeriveGrid2D: + """ + Returns the ``DeriveGrid2D`` object associated with the mask, which computes derived grids of (y,x) + coordinates such as the unmasked pixel grid, edge grid, and border grid. + """ return DeriveGrid2D(mask=self) @property def zoom(self) -> Zoom2D: + """ + Returns the ``Zoom2D`` object associated with the mask, which computes the zoomed region of the mask + around its unmasked pixels for use in visualization. + """ return Zoom2D(mask=self) @classmethod @@ -586,18 +615,29 @@ def from_fits( invert: bool = False, ) -> "Mask2D": """ - Loads the image from a .fits file. + Load a ``Mask2D`` from a 2D boolean array stored in a ``.fits`` file. Parameters ---------- file_path - The full path of the fits file. + The full path of the ``.fits`` file, including the file name and extension. + pixel_scales + The (y,x) scaled units to pixel units conversion factors of every pixel. If this is input as a + `float`, it is converted to a (float, float) structure. hdu - The HDU number in the fits file containing the image image. - pixel_scales or (float, float) - The scaled units to pixel units conversion factor of each pixel. + The HDU number in the ``.fits`` file containing the mask array. origin The (y,x) scaled units origin of the mask's coordinate system. + resized_mask_shape + If provided, the loaded mask is resized to this (y,x) shape after loading. + invert + If `True`, the `bool`'s of the loaded mask are inverted, so `False` entries become `True` + and vice versa. + + Returns + ------- + Mask2D + The mask loaded from the ``.fits`` file. """ pixel_scales = geometry_util.convert_pixel_scales_2d(pixel_scales=pixel_scales) @@ -619,6 +659,9 @@ def from_fits( @property def shape_native(self) -> Tuple[int, ...]: + """ + The 2D shape of the mask in its native representation, equal to the shape of the underlying boolean ndarray. + """ return self.shape @cached_property @@ -689,17 +732,24 @@ def trimmed_array_from(self, padded_array, image_shape) -> Array2D: def unmasked_blurred_array_from(self, padded_array, psf, image_shape) -> Array2D: """ - For a padded grid and psf, compute an unmasked blurred image from an unmasked unblurred image. + Convolve a padded array with the PSF and trim it back to the original image shape. - This relies on using the lens dataset's padded-grid, which is a grid of (y,x) coordinates which extends over - the entire image as opposed to just the masked region. + This relies on a padded array whose shape extends beyond the masked region, so that PSF convolution + does not suffer from edge effects. The result is trimmed back to ``image_shape`` after convolution. Parameters ---------- - psf : aa.Convolver - The PSF of the image used for convolution. - unmasked_image_1d - The 1D unmasked image which is blurred. + padded_array + The padded ``Array2D`` (or slim 1D representation) of values to be convolved with the PSF. + psf + The PSF convolver used to blur the padded array. + image_shape + The (y,x) shape of the original (unpadded) image, used to trim the blurred result. + + Returns + ------- + Array2D + The blurred and trimmed ``Array2D`` of shape ``image_shape``. """ blurred_image = psf.convolved_image_from( @@ -732,6 +782,15 @@ def header_dict(self) -> Dict: @property def mask_centre(self) -> Tuple[float, float]: + """ + The (y,x) scaled coordinate centre of the unmasked pixels in the mask. + + This is computed as the mean of the (y,x) scaled coordinates of all unmasked pixels. + + Returns + ------- + The (y,x) centre of the unmasked region of the mask in scaled units. + """ grid = grid_2d_util.grid_2d_slim_via_mask_from( mask_2d=self, pixel_scales=self.pixel_scales, @@ -812,7 +871,7 @@ def rescaled_from(self, rescale_factor) -> Mask2D: def resized_from(self, new_shape, pad_value: int = 0.0) -> Mask2D: """ - Returns the ``Mask2D`` resized to a small or bigger ``ndarraay``, but with the same distribution of + Returns the ``Mask2D`` resized to a small or bigger ``ndarray``, but with the same distribution of ``False`` and ``True`` entries. Resizing which increases the ``Mask2D`` shape pads it with values on its edge.