Skip to content

Commit

Permalink
Merge pull request #263 from lsst/tickets/DM-32337
Browse files Browse the repository at this point in the history
DM-32337: Add straight image stacking to AccumulatorMeanStack.
  • Loading branch information
erykoff committed Oct 26, 2021
2 parents 879f0a3 + e57b978 commit 0aba325
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 0 deletions.
38 changes: 38 additions & 0 deletions python/lsst/meas/algorithms/accumulator_mean_stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ def add_masked_image(self, masked_image, weight=1.0):
----------
masked_image : `lsst.afw.image.MaskedImage`
Masked image to add to the stack.
weight : `float` or `np.ndarray`, optional
Weight to apply for weighted mean. If an array,
must be same size and shape as input masked_image.
"""
good_pixels = np.where(((masked_image.mask.array & self.bit_mask_value) == 0)
& np.isfinite(masked_image.mask.array))
Expand Down Expand Up @@ -173,6 +176,41 @@ def fill_stacked_masked_image(self, stacked_masked_image):
bad_pixels = (self.sum_weight <= 0.0)
stacked_masked_image.mask.array[bad_pixels] |= no_good_pixels_mask

def add_image(self, image, weight=1.0):
"""Add an image to the stack.
No bit-filtering is performed when adding an image.
Parameters
----------
image : `lsst.afw.image.Image`
Image to add to the stack.
weight : `float` or `np.ndarray`, optional
Weight to apply for weighted mean. If an array,
must be same size and shape as input image.
"""
self.sum_weight[:, :] += weight
self.sum_wdata[:, :] += weight*image.array[:]

if self.compute_n_image:
self.n_image[:, :] += 1

def fill_stacked_image(self, stacked_image):
"""Fill the image after accumulation.
Parameters
----------
stacked_image : `lsst.afw.image.Image`
Total image.
"""
with np.warnings.catch_warnings():
# Let the NaNs through, this should only happen
# if we're stacking with no inputs.
np.warnings.simplefilter("ignore")

# The image plane is sum(weight*data)/sum(weight)
stacked_image.array[:, :] = self.sum_wdata/self.sum_weight

@staticmethod
def stats_ctrl_to_threshold_dict(stats_ctrl):
"""Convert stats control to threshold dict.
Expand Down
45 changes: 45 additions & 0 deletions tests/test_accumulator_mean_stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,51 @@ def test_online_coadd_input_variance_false(self):
testing.assert_array_equal(online_masked_image.mask.array,
afw_masked_image.mask.array)

def test_online_coadd_image(self):
"""Test online coaddition with regular non-masked images."""
exposures, weights = self.make_test_images_to_coadd()
coadd_exposure = self.make_coadd_exposure(exposures[0])
stats_ctrl = self.make_stats_ctrl()
stats_ctrl.setAndMask(0)
stats_ctrl.setCalcErrorFromInputVariance(True)
mask_map = self.make_mask_map(stats_ctrl)

stats_flags = afwMath.stringToStatisticsProperty("MEAN")
clipped = afwImage.Mask.getPlaneBitMask("CLIPPED")

masked_image_list = [exp.maskedImage for exp in exposures]

afw_masked_image = afwMath.statisticsStack(masked_image_list,
stats_flags,
stats_ctrl,
weights,
clipped,
mask_map)

# Make the stack with the online accumulator
stacker = AccumulatorMeanStack(
coadd_exposure.image.array.shape,
stats_ctrl.getAndMask(),
mask_map=mask_map,
no_good_pixels_mask=stats_ctrl.getNoGoodPixelsMask(),
calc_error_from_input_variance=stats_ctrl.getCalcErrorFromInputVariance(),
compute_n_image=True)

for exposure, weight in zip(exposures, weights):
stacker.add_image(exposure.image, weight=weight)

stacker.fill_stacked_image(coadd_exposure.image)

online_image = coadd_exposure.image

# The unmasked coadd good pixels should match at the <1e-5 level
# The masked pixels will not be correct for straight image stacking.
good_pixels = np.where(afw_masked_image.mask.array == 0)

testing.assert_array_almost_equal(online_image.array[good_pixels],
afw_masked_image.image.array[good_pixels],
decimal=5)


class TestMemory(lsst.utils.tests.MemoryTestCase):
pass
Expand Down

0 comments on commit 0aba325

Please sign in to comment.