Skip to content

Commit

Permalink
Add 'first' and 'last' modes for mosaicking
Browse files Browse the repository at this point in the history
  • Loading branch information
svank committed Aug 26, 2023
1 parent 09f2adc commit 5d33832
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 7 deletions.
29 changes: 24 additions & 5 deletions reproject/mosaicking/coadd.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,12 @@ 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' }
combine_function : { 'mean', 'sum', 'median', 'first', 'last' }
The type of function to use for combining the values into the final
image.
image. For 'first' and 'last', respectively, the reprojected images are
simply overlaid on top of each other. With respect to the order of the
input images in `input_data`, either the first or the last image to
cover a region of overlap determines the output data for that region.
match_background : bool
Whether to match the backgrounds of the images.
background_reference : `None` or `int`
Expand All @@ -94,8 +97,8 @@ def reproject_and_coadd(

# Validate inputs

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

Check warning on line 101 in reproject/mosaicking/coadd.py

View check run for this annotation

Codecov / codecov/patch

reproject/mosaicking/coadd.py#L101

Added line #L101 was not covered by tests

if reproject_function is None:
raise ValueError(
Expand Down Expand Up @@ -233,7 +236,23 @@ def reproject_and_coadd(
if combine_function == "mean":
with np.errstate(invalid="ignore"):
final_array /= final_footprint

elif combine_function == "first":
for array in arrays:
mask = final_footprint[array.view_in_original_array] == 0
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
Expand Down
36 changes: 34 additions & 2 deletions reproject/mosaicking/tests/test_coadd.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from astropy.io import fits
from astropy.io.fits import Header
from astropy.wcs import WCS
from numpy.testing import assert_allclose
from numpy.testing import assert_allclose, assert_equal

from reproject import reproject_exact, reproject_interp
from reproject.mosaicking.coadd import reproject_and_coadd
Expand Down Expand Up @@ -73,7 +73,7 @@ def _overlapping_views(self):

return views

@pytest.mark.parametrize("combine_function", ["mean", "sum"])
@pytest.mark.parametrize("combine_function", ["mean", "sum", "first", "last"])
def test_coadd_no_overlap(self, combine_function, reproject_function):
# Make sure that if all tiles are exactly non-overlapping, and
# we use 'sum' or 'mean', we get the exact input array back.
Expand Down Expand Up @@ -108,6 +108,38 @@ def test_coadd_with_overlap(self, reproject_function):

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

@pytest.mark.parametrize("combine_function", ["first", "last"])
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)

array, footprint = reproject_and_coadd(
input_data,
self.wcs,
shape_out=self.array.shape,
combine_function=combine_function,
reproject_function=reproject_function,
)

# 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]
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
# 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)
array[view] = np.nan

def test_coadd_background_matching(self, reproject_function):
# Test out the background matching

Expand Down

0 comments on commit 5d33832

Please sign in to comment.