Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added new combine function to compute the minimum and maximum #369

Merged
merged 2 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 27 additions & 13 deletions reproject/mosaicking/coadd.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def reproject_and_coadd(
`~astropy.io.fits.HDUList` instance, specifies the HDU to use.
reproject_function : callable
The function to use for the reprojection
combine_function : { 'mean', 'sum', 'median', 'first', 'last' }
combine_function : { 'mean', 'sum', 'median', 'first', 'last', 'min', 'max' }
The type of function to use for combining the values into the final
image. For 'first' and 'last', respectively, the reprojected images are
simply overlaid on top of each other. With respect to the order of the
Expand All @@ -97,8 +97,8 @@ def reproject_and_coadd(

# Validate inputs

if combine_function not in ("mean", "sum", "median", "first", "last"):
raise ValueError("combine_function should be one of mean/sum/median/first/last")
if combine_function not in ("mean", "sum", "median", "first", "last", "min", "max"):
raise ValueError("combine_function should be one of mean/sum/median/first/last/min/max")

if reproject_function is None:
raise ValueError(
Expand Down Expand Up @@ -223,6 +223,11 @@ def reproject_and_coadd(
final_array = np.zeros(shape_out)
final_footprint = np.zeros(shape_out)

if combine_function == "min":
final_array[...] = np.inf
elif combine_function == "max":
final_array[...] = -np.inf

if combine_function in ("mean", "sum"):
for array in arrays:
# By default, values outside of the footprint are set to NaN
Expand All @@ -236,27 +241,36 @@ def reproject_and_coadd(
if combine_function == "mean":
with np.errstate(invalid="ignore"):
final_array /= final_footprint
elif combine_function == "first":

elif combine_function in ("first", "last", "min", "max"):
for array in arrays:
mask = final_footprint[array.view_in_original_array] == 0
if combine_function == "first":
mask = final_footprint[array.view_in_original_array] == 0
elif combine_function == "last":
mask = array.footprint > 0
elif combine_function == "min":
mask = (array.footprint > 0) & (
array.array < final_array[array.view_in_original_array]
)
elif combine_function == "max":
mask = (array.footprint > 0) & (
array.array > final_array[array.view_in_original_array]
)

final_footprint[array.view_in_original_array] = np.where(
mask, array.footprint, final_footprint[array.view_in_original_array]
)
final_array[array.view_in_original_array] = np.where(
mask, array.array, final_array[array.view_in_original_array]
)
elif combine_function == "last":
for array in arrays:
final_footprint[array.view_in_original_array] = np.where(
array.footprint, array.footprint, final_footprint[array.view_in_original_array]
)
final_array[array.view_in_original_array] = np.where(
array.footprint > 0, array.array, final_array[array.view_in_original_array]
)

elif combine_function == "median":
# Here we need to operate in chunks since we could otherwise run
# into memory issues

raise NotImplementedError("combine_function='median' is not yet implemented")

if combine_function in ("min", "max"):
final_array[final_footprint == 0] = 0.0

return final_array, final_footprint
21 changes: 15 additions & 6 deletions reproject/mosaicking/tests/test_coadd.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,17 @@ def test_coadd_with_overlap(self, reproject_function):

assert_allclose(array, self.array, atol=ATOL)

@pytest.mark.parametrize("combine_function", ["first", "last"])
@pytest.mark.parametrize("combine_function", ["first", "last", "min", "max"])
def test_coadd_with_overlap_first_last(self, reproject_function, combine_function):
views = self._overlapping_views
input_data = self._get_tiles(views)

# Make each of the overlapping tiles different
for i, (array, wcs) in enumerate(input_data):
input_data[i] = (np.full_like(array, i), wcs)
# We give each tile integer values that range from 0 to 19 but we
# deliberately don't make the first one 0 and the last one 19 so
# that min/max differs from first/last.
input_data[i] = (np.full_like(array, (i + 7) % 20), wcs)

array, footprint = reproject_and_coadd(
input_data,
Expand All @@ -127,17 +130,23 @@ def test_coadd_with_overlap_first_last(self, reproject_function, combine_functio

# Test that either the correct tile sets the output value in the overlap regions
test_sequence = list(enumerate(views))

if combine_function == "last":
test_sequence = test_sequence[::-1]
elif combine_function == "min":
test_sequence = test_sequence[13:] + test_sequence[:13]
elif combine_function == "max":
test_sequence = (test_sequence[13:] + test_sequence[:13])[::-1]

for i, view in test_sequence:
# Each tile in test_sequence should overwrite teh following tiles
# in the overlap regions. We'll use nans to mark pixels in the
# output array that have already been set by a preceeding tile, so
# Each tile in test_sequence should overwrite the following tiles
# in the overlap regions. We'll use NaNs to mark pixels in the
# output array that have already been set by a preceding tile, so
# we'll go through, check that each tile matches the non-nan pixels
# in its region, and then set that whole region to nan.
output_tile = array[view]
output_values = output_tile[np.isfinite(output_tile)]
assert_equal(output_values, i)
assert_equal(output_values, (i + 7) % 20)
array[view] = np.nan

def test_coadd_background_matching(self, reproject_function):
Expand Down