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 mask planes from template and science fake plane into difference #280

Merged
merged 3 commits into from
Oct 18, 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
60 changes: 47 additions & 13 deletions python/lsst/ip/diffim/subtractImages.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,12 +195,12 @@ class AlardLuptonSubtractBaseConfig(lsst.pex.config.Config):
)
badMaskPlanes = lsst.pex.config.ListField(
dtype=str,
default=("NO_DATA", "BAD", "SAT", "EDGE"),
default=("NO_DATA", "BAD", "SAT", "EDGE", "FAKE"),
doc="Mask planes to exclude when selecting sources for PSF matching."
)
preserveTemplateMask = lsst.pex.config.ListField(
dtype=str,
default=("NO_DATA", "BAD", "SAT", "INJECTED", "INJECTED_CORE"),
default=("NO_DATA", "BAD", "SAT", "FAKE", "INJECTED", "INJECTED_CORE"),
doc="Mask planes from the template to propagate to the image difference."
)

Expand Down Expand Up @@ -461,7 +461,8 @@ def runConvolveTemplate(self, template, science, selectSources):
psfMatchingKernel=kernelResult.psfMatchingKernel)

def runConvolveScience(self, template, science, selectSources):
"""Convolve the science image with a PSF-matching kernel and subtract the template image.
"""Convolve the science image with a PSF-matching kernel and subtract
the template image.

Parameters
----------
Expand Down Expand Up @@ -561,20 +562,14 @@ def finalize(self, template, science, difference, kernel,
"""
# Erase existing detection mask planes.
# We don't want the detection mask from the science image
mask = difference.mask
mask &= ~(mask.getPlaneBitMask("DETECTED") | mask.getPlaneBitMask("DETECTED_NEGATIVE"))

# We have cleared the template mask plane, so copy the mask plane of
# the image difference so that we can calculate correct statistics
# during decorrelation. Do this regardless of whether decorrelation is
# used for consistency.
template[science.getBBox()].mask.array[...] = difference.mask.array[...]
self.updateMasks(template, science, difference)

if self.config.doDecorrelation:
self.log.info("Decorrelating image difference.")
# We have cleared the template mask plane, so copy the mask plane of
# the image difference so that we can calculate correct statistics
# during decorrelation
template[science.getBBox()].mask.array[...] = difference.mask.array[...]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also remove the associated code comment above.

correctedExposure = self.decorrelate.run(science, template[science.getBBox()], difference, kernel,
templateMatched=templateMatched,
preConvMode=preConvMode,
Expand All @@ -585,6 +580,36 @@ def finalize(self, template, science, difference, kernel,
correctedExposure = difference
return correctedExposure

def updateMasks(self, template, science, difference):
"""Update the mask planes on images for finalizing."""

bbox = science.getBBox()
mask = difference.mask
mask &= ~(mask.getPlaneBitMask("DETECTED") | mask.getPlaneBitMask("DETECTED_NEGATIVE"))

if "FAKE" in science.mask.getMaskPlaneDict().keys():
# propagate the mask plane related to Fake source injection
# NOTE: the fake source injection sets FAKE plane, but it should be INJECTED
# NOTE: This can be removed in DM-40796

self.log.info("Adding injected mask planes")
mask.addMaskPlane("INJECTED")
diffInjectedBitMask = mask.getPlaneBitMask("INJECTED")

mask.addMaskPlane("INJECTED_TEMPLATE")
diffInjTmpltBitMask = mask.getPlaneBitMask("INJECTED_TEMPLATE")

scienceFakeBitMask = science.mask.getPlaneBitMask('FAKE')
tmpltFakeBitMask = template[bbox].mask.getPlaneBitMask('FAKE')

injScienceMaskArray = ((science.mask.array & scienceFakeBitMask) > 0) * diffInjectedBitMask
injTemplateMaskArray = ((template[bbox].mask.array & tmpltFakeBitMask) > 0) * diffInjTmpltBitMask

mask.array |= injScienceMaskArray
Copy link
Contributor

@isullivan isullivan Sep 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my requested modification in the comment above. Looks resolved with the above changes.

mask.array |= injTemplateMaskArray

template[bbox].mask.array[...] = difference.mask.array[...]

@staticmethod
def _validateExposures(template, science):
"""Check that the WCS of the two Exposures match, and the template bbox
Expand Down Expand Up @@ -722,7 +747,12 @@ def _checkMask(mask, sources, badMaskPlanes):
kept (True) or rejected (False) based on the value of the
mask plane at its location.
"""
badPixelMask = lsst.afw.image.Mask.getPlaneBitMask(badMaskPlanes)
setBadMaskPlanes = [
maskPlane for maskPlane in badMaskPlanes if maskPlane in mask.getMaskPlaneDict()
]

badPixelMask = mask.getPlaneBitMask(setBadMaskPlanes)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for changing this. We need to switch from lsst.afw.image.Mask.getPlaneBitMask to mask.getPlaneBitMask in general (see lsst/meas_algorithms#329 (comment))


xv = np.rint(sources.getX() - mask.getX0())
yv = np.rint(sources.getY() - mask.getY0())

Expand Down Expand Up @@ -1057,8 +1087,12 @@ def _interpolateImage(maskedImage, badMaskPlanes, fallbackValue=None):
result: `float`
The number of masked pixels that were replaced.
"""
imgBadMaskPlanes = [
maskPlane for maskPlane in badMaskPlanes if maskPlane in maskedImage.mask.getMaskPlaneDict()
]

image = maskedImage.image.array
badPixels = (maskedImage.mask.array & maskedImage.mask.getPlaneBitMask(badMaskPlanes)) > 0
badPixels = (maskedImage.mask.array & maskedImage.mask.getPlaneBitMask(imgBadMaskPlanes)) > 0
image[badPixels] = np.nan
if fallbackValue is None:
fallbackValue = np.nanmedian(image)
Expand Down
57 changes: 57 additions & 0 deletions tests/test_subtractTask.py
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,63 @@ def _compare_apCorrMaps(self, a, b):
self.assertFloatsAlmostEqual(
value.getCoefficients(), value2.getCoefficients(), rtol=0.0)

def test_fake_mask_plane_propagation(self):
"""Test that we have the mask planes related to fakes in diffim images.
This is testing method called updateMasks
"""
xSize = 200
ySize = 200
science, sources = makeTestImage(psfSize=2.4, xSize=xSize, ySize=ySize)
science_fake_img, science_fake_sources = makeTestImage(
psfSize=2.4, xSize=xSize, ySize=ySize, seed=7, nSrc=2, noiseLevel=0.25, fluxRange=1
)
template, _ = makeTestImage(psfSize=2.4, xSize=xSize, ySize=ySize, doApplyCalibration=True)
tmplt_fake_img, tmplt_fake_sources = makeTestImage(
psfSize=2.4, xSize=xSize, ySize=ySize, seed=9, nSrc=2, noiseLevel=0.25, fluxRange=1
)
# created fakes and added them to the images
science.image.array += science_fake_img.image.array
template.image.array += tmplt_fake_img.image.array

# TODO: DM-40796 update to INJECTED names when source injection gets refactored
# adding mask planes to both science and template images
science_mask_planes = science.mask.addMaskPlane("FAKE")
template_mask_planes = template.mask.addMaskPlane("FAKE")

for a_science_source in science_fake_sources:
# 3 x 3 masking of the source locations is fine
bbox = lsst.geom.Box2I(
lsst.geom.Point2I(a_science_source.getX(), a_science_source.getY()), lsst.geom.Extent2I(3, 3)
)
science[bbox].mask.array |= science_mask_planes

for a_template_source in tmplt_fake_sources:
# 3 x 3 masking of the source locations is fine
bbox = lsst.geom.Box2I(
lsst.geom.Point2I(a_template_source.getX(), a_template_source.getY()),
lsst.geom.Extent2I(3, 3)
)
template[bbox].mask.array |= template_mask_planes

science_fake_masked = (science.mask.array & science.mask.getPlaneBitMask("FAKE")) > 0
template_fake_masked = (template.mask.array & template.mask.getPlaneBitMask("FAKE")) > 0

config = subtractImages.AlardLuptonSubtractTask.ConfigClass()
task = subtractImages.AlardLuptonSubtractTask(config=config)
subtraction = task.run(template, science, sources)

# check subtraction mask plane is set where we set the previous masks
diff_mask = subtraction.difference.mask

# science mask should be now in INJECTED
inj_masked = (diff_mask.array & diff_mask.getPlaneBitMask("INJECTED")) > 0

# template mask should be now in INJECTED_TEMPLATE
injTmplt_masked = (diff_mask.array & diff_mask.getPlaneBitMask("INJECTED_TEMPLATE")) > 0

self.assertEqual(np.sum(inj_masked.astype(int)-science_fake_masked.astype(int)), 0)
self.assertEqual(np.sum(injTmplt_masked.astype(int)-template_fake_masked.astype(int)), 0)
Comment on lines +767 to +773
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the mask plane is changing, this looks like a good way to do the test. For future reference, see https://pipelines.lsst.io/py-api/lsst.utils.tests.TestCase.html for useful built-in asserts, though I don't think any would quite work here.



class AlardLuptonPreconvolveSubtractTest(lsst.utils.tests.TestCase):

Expand Down