Skip to content

Commit 191e69c

Browse files
Jammy2211Jammy2211
authored andcommitted
updated to use new zoom API
1 parent ec82687 commit 191e69c

File tree

7 files changed

+223
-56
lines changed

7 files changed

+223
-56
lines changed

autoarray/config/visualize/general.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ general:
44
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).
55
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).
66
zoom_around_mask: true # If True, plots of data structures with a mask automatically zoom in the masked region.
7-
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.
87
inversion:
98
reconstruction_vmax_factor: 0.5
109
total_mappings_pixels : 8 # The number of source pixels used when plotting the subplot_mappings of a pixelization.

autoarray/mask/derive/zoom_2d.py

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
from __future__ import annotations
22
import numpy as np
3-
from typing import List, Tuple, Union
3+
from typing import TYPE_CHECKING, List, Tuple, Union
44

5+
if TYPE_CHECKING:
6+
from autoarray.structures.arrays.uniform_2d import Array2D
7+
8+
from autoarray.structures.arrays import array_2d_util
59
from autoarray.structures.grids import grid_2d_util
610

711

@@ -50,6 +54,17 @@ def __init__(self, mask: Union[np.ndarray, List]):
5054

5155
@property
5256
def centre(self) -> Tuple[float, float]:
57+
"""
58+
Returns the centre of the zoomed in region, which is the average of the maximum and minimum y and x pixel values
59+
of the unmasked region.
60+
61+
The y and x pixel values are the pixel coordinates of the unmasked region, which are derived from the
62+
`Mask2D` object. The pixel coordinates are in the same units as the pixel scales of the `Mask2D` object.
63+
64+
Returns
65+
-------
66+
The centre of the zoomed in region.
67+
"""
5368
from autoarray.structures.grids.uniform_2d import Grid2D
5469

5570
grid = grid_2d_util.grid_2d_slim_via_mask_from(
@@ -73,6 +88,17 @@ def centre(self) -> Tuple[float, float]:
7388

7489
@property
7590
def offset_pixels(self) -> Tuple[float, float]:
91+
"""
92+
Returns the offset of the centred of the zoomed in region from the centre of the `Mask2D` object in pixel
93+
units.
94+
95+
This is computed by subtracting the pixel coordinates of the `Mask2D` object from the pixel coordinates of
96+
the zoomed in region.
97+
98+
Returns
99+
-------
100+
The offset of the zoomed in region from the centre of the `Mask2D` object in pixel units.
101+
"""
76102
if self.mask.pixel_scales is None:
77103
return self.mask.geometry.central_pixel_coordinates
78104

@@ -83,6 +109,17 @@ def offset_pixels(self) -> Tuple[float, float]:
83109

84110
@property
85111
def offset_scaled(self) -> Tuple[float, float]:
112+
"""
113+
Returns the offset of the centred of the zoomed in region from the centre of the `Mask2D` object in scaled
114+
units.
115+
116+
This is computed by subtracting the pixel coordinates of the `Mask2D` object from the pixel coordinates of
117+
the zoomed in region.
118+
119+
Returns
120+
-------
121+
The offset of the zoomed in region from the centre of the `Mask2D` object in scaled units.
122+
"""
86123
return (
87124
-self.mask.pixel_scales[0] * self.offset_pixels[0],
88125
self.mask.pixel_scales[1] * self.offset_pixels[1],
@@ -91,10 +128,12 @@ def offset_scaled(self) -> Tuple[float, float]:
91128
@property
92129
def region(self) -> List[int]:
93130
"""
94-
The zoomed rectangular region corresponding to the square encompassing all unmasked values. This zoomed
95-
extraction region is a squuare, even if the mask is rectangular.
131+
The zoomed region corresponding to the square encompassing all unmasked values.
96132
97133
This is used to zoom in on the region of an image that is used in an analysis for visualization.
134+
135+
This zoomed extraction region is a square, even if the mask is rectangular, so that extraction regions are
136+
always squares which is important for ensuring visualization does not have aspect ratio issues.
98137
"""
99138

100139
where = np.array(np.where(np.invert(self.mask.astype("bool"))))
@@ -119,22 +158,48 @@ def region(self) -> List[int]:
119158

120159
@property
121160
def shape_native(self) -> Tuple[int, int]:
161+
"""
162+
The shape of the zoomed in region in pixels.
163+
164+
This is computed by subtracting the minimum and maximum y and x pixel values of the unmasked region.
165+
166+
Returns
167+
-------
168+
The shape of the zoomed in region in pixels.
169+
"""
122170
region = self.region
123171
return (region[1] - region[0], region[3] - region[2])
124172

125-
@property
126-
def mask_unmasked(self) -> "Mask2D":
173+
def array_2d_from(self, array : Array2D, buffer: int = 1) -> Array2D:
127174
"""
128-
The scaled-grid of (y,x) coordinates of every pixel.
175+
Extract the 2D region of an array corresponding to the rectangle encompassing all unmasked values.
129176
130-
This is defined from the top-left corner, such that the first pixel at location [0, 0] will have a negative x
131-
value y value in scaled units.
132-
"""
177+
This is used to extract and visualize only the region of an image that is used in an analysis.
133178
179+
Parameters
180+
----------
181+
buffer
182+
The number pixels around the extracted array used as a buffer.
183+
"""
184+
from autoarray.structures.arrays.uniform_2d import Array2D
134185
from autoarray.mask.mask_2d import Mask2D
135186

136-
return Mask2D.all_false(
137-
shape_native=self.shape_native,
138-
pixel_scales=self.mask.pixel_scales,
139-
origin=self.offset_scaled,
187+
extracted_array_2d = array_2d_util.extracted_array_2d_from(
188+
array_2d=np.array(array.native),
189+
y0=self.region[0] - buffer,
190+
y1=self.region[1] + buffer,
191+
x0=self.region[2] - buffer,
192+
x1=self.region[3] + buffer,
193+
)
194+
195+
mask = Mask2D.all_false(
196+
shape_native=extracted_array_2d.shape,
197+
pixel_scales=array.pixel_scales,
198+
origin=array.mask.mask_centre,
140199
)
200+
201+
arr = array_2d_util.convert_array_2d(
202+
array_2d=extracted_array_2d, mask_2d=mask
203+
)
204+
205+
return Array2D(values=arr, mask=mask, header=array.header)

autoarray/plot/mat_plot/two_d.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
)
1010
from autoarray.inversion.pixelization.mappers.delaunay import MapperDelaunay
1111
from autoarray.inversion.pixelization.mappers.voronoi import MapperVoronoi
12+
from autoarray.mask.derive.zoom_2d import Zoom2D
1213
from autoarray.plot.mat_plot.abstract import AbstractMatPlot
1314
from autoarray.plot.auto_labels import AutoLabels
1415
from autoarray.plot.visuals.two_d import Visuals2D
@@ -255,11 +256,15 @@ def plot_array(
255256
"a pixel scales attribute."
256257
)
257258

258-
if conf.instance["visualize"]["general"]["general"]["zoom_around_mask"]:
259+
if conf.instance["visualize"]["general"]["general"][
260+
"zoom_around_mask"
261+
]:
262+
263+
zoom = Zoom2D(mask=array.mask)
259264

260265
buffer = 0 if array.mask.is_all_false else 1
261266

262-
array = array.zoomed_around_mask(buffer=buffer)
267+
array = zoom.array_2d_from(array=array, buffer=buffer)
263268

264269
extent = array.geometry.extent
265270

autoarray/plot/wrap/two_d/array_overlay.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import matplotlib.pyplot as plt
22

33
from autoarray.plot.wrap.two_d.abstract import AbstractMatWrap2D
4+
from autoarray.mask.derive.zoom_2d import Zoom2D
45

56

67
class ArrayOverlay(AbstractMatWrap2D):
@@ -17,9 +18,9 @@ class ArrayOverlay(AbstractMatWrap2D):
1718

1819
def overlay_array(self, array, figure):
1920
aspect = figure.aspect_from(shape_native=array.shape_native)
20-
array_zoom = array.zoomed_around_mask(buffer=0)
21+
22+
zoom = Zoom2D(mask=array.mask)
23+
array_zoom = zoom.array_2d_from(array=array, buffer=0)
2124
extent = array_zoom.geometry.extent
2225

23-
plt.imshow(
24-
X=array.native._array, aspect=aspect, extent=extent, **self.config_dict
25-
)
26+
plt.imshow(X=array.native, aspect=aspect, extent=extent, **self.config_dict)

autoarray/structures/arrays/uniform_2d.py

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,31 @@ def native(self) -> "Array2D":
288288
values=self, mask=self.mask, header=self.header, store_native=True
289289
)
290290

291+
@property
292+
def native_for_fits(self) -> "Array2D":
293+
"""
294+
Return a `Array2D` for output to a .fits file, where the data is stored in its `native` representation,
295+
which is an ``ndarray`` of shape [total_y_pixels, total_x_pixels].
296+
297+
Depending on configuration files, this array could be zoomed in on such that only the unmasked region
298+
of the image is included in the .fits file, to save hard-disk space. Alternatively, the original `shape_native`
299+
of the data can be retained.
300+
301+
If it is already stored in its `native` representation it is return as it is. If not, it is mapped from
302+
`slim` to `native` and returned as a new `Array2D`.
303+
"""
304+
if conf.instance["visualize"]["plots"]["fits_are_zoomed"]:
305+
306+
zoom = Zoom2D(mask=self.mask)
307+
308+
buffer = 0 if self.mask.is_all_false else 1
309+
310+
return zoom.array_2d_from(array=self, buffer=buffer)
311+
312+
return Array2D(
313+
values=self, mask=self.mask, header=self.header, store_native=True
314+
)
315+
291316
@property
292317
def native_skip_mask(self) -> "Array2D":
293318
"""
@@ -447,40 +472,6 @@ def brightest_sub_pixel_coordinate_in_region_from(
447472
pixel_coordinates_2d=(subpixel_y, subpixel_x)
448473
)
449474

450-
def zoomed_around_mask(self, buffer: int = 1) -> "Array2D":
451-
"""
452-
Extract the 2D region of an array corresponding to the rectangle encompassing all unmasked values.
453-
454-
This is used to extract and visualize only the region of an image that is used in an analysis.
455-
456-
Parameters
457-
----------
458-
buffer
459-
The number pixels around the extracted array used as a buffer.
460-
"""
461-
462-
zoom = Zoom2D(mask=self.mask)
463-
464-
extracted_array_2d = array_2d_util.extracted_array_2d_from(
465-
array_2d=np.array(self.native),
466-
y0=zoom.region[0] - buffer,
467-
y1=zoom.region[1] + buffer,
468-
x0=zoom.region[2] - buffer,
469-
x1=zoom.region[3] + buffer,
470-
)
471-
472-
mask = Mask2D.all_false(
473-
shape_native=extracted_array_2d.shape,
474-
pixel_scales=self.pixel_scales,
475-
origin=self.mask.mask_centre,
476-
)
477-
478-
array = array_2d_util.convert_array_2d(
479-
array_2d=extracted_array_2d, mask_2d=mask
480-
)
481-
482-
return Array2D(values=array, mask=mask, header=self.header)
483-
484475
def resized_from(
485476
self, new_shape: Tuple[int, int], mask_pad_value: int = 0.0
486477
) -> "Array2D":

test_autoarray/mask/derive/test_zoom_2d.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,107 @@ def test__mask_unmasked():
146146
mask_unmasked == np.array([[False, False], [False, False], [False, False]])
147147
).all()
148148
assert mask_unmasked.origin == (0.0, -2.0)
149+
150+
151+
def test__array_2d_from():
152+
array_2d = [
153+
[1.0, 2.0, 3.0, 4.0],
154+
[5.0, 6.0, 7.0, 8.0],
155+
[9.0, 10.0, 11.0, 12.0],
156+
[13.0, 14.0, 15.0, 16.0],
157+
]
158+
159+
mask = aa.Mask2D(
160+
mask=[
161+
[True, True, True, True],
162+
[True, False, False, True],
163+
[True, False, False, True],
164+
[True, True, True, True],
165+
],
166+
pixel_scales=(1.0, 1.0),
167+
)
168+
169+
arr = aa.Array2D(values=array_2d, mask=mask)
170+
zoom = aa.Zoom2D(mask=mask)
171+
arr_zoomed = zoom.array_2d_from(array=arr, buffer=0)
172+
173+
assert (arr_zoomed.native == np.array([[6.0, 7.0], [10.0, 11.0]])).all()
174+
175+
mask = aa.Mask2D(
176+
mask=np.array(
177+
[
178+
[True, True, True, True],
179+
[True, False, False, True],
180+
[False, False, False, True],
181+
[True, True, True, True],
182+
]
183+
),
184+
pixel_scales=(1.0, 1.0),
185+
)
186+
187+
arr = aa.Array2D(values=array_2d, mask=mask)
188+
zoom = aa.Zoom2D(mask=mask)
189+
arr_zoomed = zoom.array_2d_from(array=arr, buffer=0)
190+
191+
assert (arr_zoomed.native == np.array([[0.0, 6.0, 7.0], [9.0, 10.0, 11.0]])).all()
192+
193+
mask = aa.Mask2D(
194+
mask=np.array(
195+
[
196+
[True, False, True, True],
197+
[True, False, False, True],
198+
[True, False, False, True],
199+
[True, True, True, True],
200+
]
201+
),
202+
pixel_scales=(1.0, 1.0),
203+
)
204+
205+
arr = aa.Array2D(values=array_2d, mask=mask)
206+
zoom = aa.Zoom2D(mask=mask)
207+
arr_zoomed = zoom.array_2d_from(array=arr, buffer=0)
208+
209+
assert (arr_zoomed.native == np.array([[2.0, 0.0], [6.0, 7.0], [10.0, 11.0]])).all()
210+
211+
array_2d = np.ones(shape=(4, 4))
212+
213+
mask = aa.Mask2D(
214+
mask=np.array(
215+
[
216+
[True, True, True, True],
217+
[True, False, False, True],
218+
[True, False, False, True],
219+
[True, True, True, True],
220+
]
221+
),
222+
pixel_scales=(1.0, 1.0),
223+
)
224+
225+
arr = aa.Array2D(values=array_2d, mask=mask)
226+
zoom = aa.Zoom2D(mask=mask)
227+
arr_zoomed = zoom.array_2d_from(array=arr, buffer=0)
228+
229+
assert arr_zoomed.mask.origin == (0.0, 0.0)
230+
231+
array_2d = np.ones(shape=(6, 6))
232+
233+
mask = aa.Mask2D(
234+
mask=np.array(
235+
[
236+
[True, True, True, True, True, True],
237+
[True, True, True, True, True, True],
238+
[True, True, True, False, False, True],
239+
[True, True, True, False, False, True],
240+
[True, True, True, True, True, True],
241+
[True, True, True, True, True, True],
242+
]
243+
),
244+
pixel_scales=(1.0, 1.0),
245+
)
246+
247+
arr = aa.Array2D(values=array_2d, mask=mask)
248+
zoom = aa.Zoom2D(mask=mask)
249+
arr_zoomed = zoom.array_2d_from(array=arr, buffer=0)
250+
251+
assert arr_zoomed.mask.origin == (0.0, 1.0)
252+

test_autoarray/plot/wrap/base/test_ticks.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ def test__yticks__set():
6060
units = aplt.Units(use_scaled=True, ticks_convert_factor=None)
6161

6262
yticks = aplt.YTicks(fontsize=34)
63-
array_zoom = array.zoomed_around_mask(buffer=1)
63+
zoom = aa.Zoom2D(mask=array.mask)
64+
array_zoom = zoom.array_2d_from(array=array, buffer=1)
6465
extent = array_zoom.geometry.extent
6566
yticks.set(min_value=extent[2], max_value=extent[3], units=units)
6667

@@ -106,7 +107,8 @@ def test__xticks__set():
106107
array = aa.Array2D.ones(shape_native=(2, 2), pixel_scales=1.0)
107108
units = aplt.Units(use_scaled=True, ticks_convert_factor=None)
108109
xticks = aplt.XTicks(fontsize=34)
109-
array_zoom = array.zoomed_around_mask(buffer=1)
110+
zoom = aa.Zoom2D(mask=array.mask)
111+
array_zoom = zoom.array_2d_from(array=array, buffer=1)
110112
extent = array_zoom.geometry.extent
111113
xticks.set(min_value=extent[0], max_value=extent[1], units=units)
112114

0 commit comments

Comments
 (0)