From 744b139af7ca1e237d0ae0c8dfcd1920bbe3d224 Mon Sep 17 00:00:00 2001 From: Jammy2211 Date: Fri, 10 Apr 2026 15:17:10 +0100 Subject: [PATCH] refactor: clamp brightest_coordinate_in_region_from to array bounds Compute each pixel bound (py_min/py_max/px_min/px_max) via separate pixel_coordinates_2d_from calls and clamp against shape_native so a region that extends past the array edge no longer slices off-array. Co-Authored-By: Claude Opus 4.6 --- autoarray/structures/arrays/uniform_2d.py | 31 +++++---- .../structures/arrays/test_uniform_2d.py | 68 +++++++++++++++++++ 2 files changed, 87 insertions(+), 12 deletions(-) diff --git a/autoarray/structures/arrays/uniform_2d.py b/autoarray/structures/arrays/uniform_2d.py index c37fc8bc8..d21f6bfbc 100644 --- a/autoarray/structures/arrays/uniform_2d.py +++ b/autoarray/structures/arrays/uniform_2d.py @@ -426,24 +426,31 @@ def brightest_coordinate_in_region_from( The coordinates of the brightest pixel in scaled units (converted from pixels units). """ - y1, y0 = self.geometry.pixel_coordinates_2d_from( - scaled_coordinates_2d=( - region[0] - self.pixel_scales[0] / 2.0, - region[2] + self.pixel_scales[0] / 2.0, - ) + ps_y, ps_x = self.pixel_scales + + py_min, _ = self.geometry.pixel_coordinates_2d_from( + scaled_coordinates_2d=(region[1] - ps_y / 2.0, 0.0) ) - x0, x1 = self.geometry.pixel_coordinates_2d_from( - scaled_coordinates_2d=( - region[1] - self.pixel_scales[1] / 2.0, - region[3] + self.pixel_scales[1] / 2.0, - ) + py_max, _ = self.geometry.pixel_coordinates_2d_from( + scaled_coordinates_2d=(region[0] + ps_y / 2.0, 0.0) ) + _, px_min = self.geometry.pixel_coordinates_2d_from( + scaled_coordinates_2d=(0.0, region[2] + ps_x / 2.0) + ) + _, px_max = self.geometry.pixel_coordinates_2d_from( + scaled_coordinates_2d=(0.0, region[3] - ps_x / 2.0) + ) + + py_min = max(0, py_min) + px_min = max(0, px_min) + py_max = min(self.shape_native[0] - 1, py_max) + px_max = min(self.shape_native[1] - 1, px_max) - extracted_region = self.native[y0:y1, x0:x1] + extracted_region = self.native[py_min : py_max + 1, px_min : px_max + 1] brightest_pixel_value = np.max(extracted_region) extracted_pixels = np.argwhere(extracted_region == brightest_pixel_value)[0] - pixel_coordinates_2d = (y0 + extracted_pixels[0], x0 + extracted_pixels[1]) + pixel_coordinates_2d = (py_min + extracted_pixels[0], px_min + extracted_pixels[1]) if return_in_pixels: return pixel_coordinates_2d diff --git a/test_autoarray/structures/arrays/test_uniform_2d.py b/test_autoarray/structures/arrays/test_uniform_2d.py index 89eee3394..89d645fc4 100644 --- a/test_autoarray/structures/arrays/test_uniform_2d.py +++ b/test_autoarray/structures/arrays/test_uniform_2d.py @@ -514,6 +514,74 @@ def test__brightest_coordinate_in_region_from__5x5_array_asymmetric_region__corr assert brightest_coordinate == pytest.approx((-0.1, 0.2), 1.0e-4) +def test__brightest_coordinate_in_region_from__region_offset_from_origin__correct_peak_coordinate(): + mask = aa.Mask2D.all_false(shape_native=(7, 7), pixel_scales=0.1) + values = np.zeros((7, 7)) + values[5, 1] = 99.0 + array_2d = aa.Array2D(values=values, mask=mask) + + brightest_coordinate = array_2d.brightest_coordinate_in_region_from( + region=(-0.25, -0.05, -0.25, -0.05) + ) + + assert brightest_coordinate == pytest.approx((-0.2, -0.2), 1.0e-4) + + +def test__brightest_coordinate_in_region_from__region_fully_offset_negative_quadrant__correct_peak_coordinate(): + mask = aa.Mask2D.all_false(shape_native=(11, 11), pixel_scales=0.1) + values = np.zeros((11, 11)) + values[8, 2] = 77.0 + array_2d = aa.Array2D(values=values, mask=mask) + + brightest_coordinate = array_2d.brightest_coordinate_in_region_from( + region=(-0.45, -0.15, -0.45, -0.15) + ) + + assert brightest_coordinate == pytest.approx((-0.3, -0.3), 1.0e-4) + + +def test__brightest_coordinate_in_region_from__region_offset_positive_quadrant__correct_peak_coordinate(): + mask = aa.Mask2D.all_false(shape_native=(11, 11), pixel_scales=0.1) + values = np.zeros((11, 11)) + values[2, 8] = 55.0 + array_2d = aa.Array2D(values=values, mask=mask) + + brightest_coordinate = array_2d.brightest_coordinate_in_region_from( + region=(0.15, 0.45, 0.15, 0.45) + ) + + assert brightest_coordinate == pytest.approx((0.3, 0.3), 1.0e-4) + + +def test__brightest_coordinate_in_region_from__region_clipped_to_array_bounds__correct_peak_coordinate(): + mask = aa.Mask2D.all_false(shape_native=(5, 5), pixel_scales=0.1) + values = np.zeros((5, 5)) + values[0, 0] = 42.0 + array_2d = aa.Array2D(values=values, mask=mask) + + brightest_coordinate = array_2d.brightest_coordinate_in_region_from( + region=(0.15, 0.45, -0.45, -0.15) + ) + + assert brightest_coordinate == pytest.approx((0.2, -0.2), 1.0e-4) + + +def test__brightest_sub_pixel_coordinate_in_region_from__region_offset_from_origin__correct_sub_pixel_peak(): + mask = aa.Mask2D.all_false(shape_native=(7, 7), pixel_scales=0.1) + values = np.zeros((7, 7)) + values[5, 1] = 100.0 + values[5, 2] = 50.0 + values[4, 1] = 50.0 + array_2d = aa.Array2D(values=values, mask=mask) + + brightest_coordinate = array_2d.brightest_sub_pixel_coordinate_in_region_from( + region=(-0.25, -0.05, -0.25, -0.05), box_size=1 + ) + + assert brightest_coordinate[0] < -0.15 + assert brightest_coordinate[1] > -0.2 + + def test__brightest_sub_pixel_coordinate_in_region_from__4x4_array__correct_sub_pixel_peak(): mask = aa.Mask2D.all_false(shape_native=(4, 4), pixel_scales=0.1) array_2d = aa.Array2D(