From 43b483209e34ce4fd46231292502fe3e0ee0b848 Mon Sep 17 00:00:00 2001 From: BrunoSanchez Date: Sun, 14 Jan 2024 03:02:52 -0800 Subject: [PATCH] Added flagging of sources near the INJECTED mask plane Added requested test. Modified a test from subtract and migrated it to detectandmeasure --- python/lsst/ip/diffim/detectAndMeasure.py | 18 ++--- python/lsst/ip/diffim/subtractImages.py | 9 ++- python/lsst/ip/diffim/utils.py | 3 + tests/test_detectAndMeasure.py | 81 +++++++++++++++++++++++ 4 files changed, 98 insertions(+), 13 deletions(-) diff --git a/python/lsst/ip/diffim/detectAndMeasure.py b/python/lsst/ip/diffim/detectAndMeasure.py index 1720e8de..357e7e43 100644 --- a/python/lsst/ip/diffim/detectAndMeasure.py +++ b/python/lsst/ip/diffim/detectAndMeasure.py @@ -156,12 +156,12 @@ def setDefaults(self): self.detection.excludeMaskPlanes = ["EDGE"] # Add filtered flux measurement, the correct measurement for pre-convolved images. - self.measurement.algorithms.names.add('base_PeakLikelihoodFlux') - self.measurement.plugins.names |= ['ext_trailedSources_Naive', - 'base_LocalPhotoCalib', - 'base_LocalWcs', - 'ext_shapeHSM_HsmSourceMoments', - 'ext_shapeHSM_HsmPsfMoments', + self.measurement.algorithms.names.add("base_PeakLikelihoodFlux") + self.measurement.plugins.names |= ["ext_trailedSources_Naive", + "base_LocalPhotoCalib", + "base_LocalWcs", + "ext_shapeHSM_HsmSourceMoments", + "ext_shapeHSM_HsmPsfMoments", ] self.measurement.slots.psfShape = "ext_shapeHSM_HsmPsfMoments" self.measurement.slots.shape = "ext_shapeHSM_HsmSourceMoments" @@ -174,8 +174,10 @@ def setDefaults(self): self.forcedMeasurement.slots.shape = None # Keep track of which footprints contain streaks - self.measurement.plugins['base_PixelFlags'].masksFpAnywhere = ['STREAK'] - self.measurement.plugins['base_PixelFlags'].masksFpCenter = ['STREAK'] + self.measurement.plugins["base_PixelFlags"].masksFpAnywhere = [ + "STREAK", "INJECTED", "INJECTED_TEMPLATE"] + self.measurement.plugins["base_PixelFlags"].masksFpCenter = [ + "STREAK", "INJECTED", "INJECTED_TEMPLATE"] class DetectAndMeasureTask(lsst.pipe.base.PipelineTask): diff --git a/python/lsst/ip/diffim/subtractImages.py b/python/lsst/ip/diffim/subtractImages.py index 84f2933c..4a442db3 100644 --- a/python/lsst/ip/diffim/subtractImages.py +++ b/python/lsst/ip/diffim/subtractImages.py @@ -628,16 +628,15 @@ def updateMasks(self, template, science, difference): mask = difference.mask mask &= ~(mask.getPlaneBitMask("DETECTED") | mask.getPlaneBitMask("DETECTED_NEGATIVE")) + self.log.info("Adding injected mask planes") + mask.addMaskPlane("INJECTED") + mask.addMaskPlane("INJECTED_TEMPLATE") + 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') diff --git a/python/lsst/ip/diffim/utils.py b/python/lsst/ip/diffim/utils.py index 058a644e..c31a6109 100644 --- a/python/lsst/ip/diffim/utils.py +++ b/python/lsst/ip/diffim/utils.py @@ -1266,6 +1266,9 @@ def detectTestSources(exposure): doSmooth=True ) exposure.mask.addMaskPlane("STREAK") # add empty streak mask plane in lieu of maskStreaksTask + exposure.mask.addMaskPlane("INJECTED") # add empty injected mask plane + exposure.mask.addMaskPlane("INJECTED_TEMPLATE") # add empty injected template mask plane + selectSources = detRet.sources selectMeasurement.run(measCat=selectSources, exposure=exposure) diff --git a/tests/test_detectAndMeasure.py b/tests/test_detectAndMeasure.py index 7fc3138f..06c3674b 100644 --- a/tests/test_detectAndMeasure.py +++ b/tests/test_detectAndMeasure.py @@ -431,6 +431,87 @@ def _detection_wrapper(setFlags=True): _detection_wrapper(setFlags=False) _detection_wrapper(setFlags=True) + 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 = 256 + ySize = 256 + science, sources = makeTestImage(psfSize=2.4, xSize=xSize, ySize=ySize, doApplyCalibration=True) + science_fake_img, science_fake_sources = makeTestImage( + psfSize=2.4, xSize=xSize, ySize=ySize, seed=5, nSrc=3, 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=3, 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.addMaskPlane("FAKE") + science_fake_bitmask = science.mask.getPlaneBitMask("FAKE") + template.mask.addMaskPlane("FAKE") + template_fake_bitmask = template.mask.getPlaneBitMask("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_fake_bitmask + + 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_fake_bitmask + + science_fake_masked = (science.mask.array & science_fake_bitmask) > 0 + template_fake_masked = (template.mask.array & template_fake_bitmask) > 0 + + subtractConfig = subtractImages.AlardLuptonSubtractTask.ConfigClass() + subtractTask = subtractImages.AlardLuptonSubtractTask(config=subtractConfig) + subtraction = subtractTask.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) + + # Now check that detection of fakes have the correct flag for injections + detectionTask = self._setup_detection() + excludeMaskPlanes = detectionTask.config.detection.excludeMaskPlanes + nBad = len(excludeMaskPlanes) + self.assertGreater(nBad, 0) + + output = detectionTask.run(subtraction.matchedScience, + subtraction.matchedTemplate, + subtraction.difference) + + sci_refIds = [] + tmpl_refIds = [] + for diaSrc in output.diaSources: + if diaSrc['base_PsfFlux_instFlux'] > 0: + self._check_diaSource(science_fake_sources, diaSrc, scale=1, refIds=sci_refIds) + self.assertTrue(diaSrc['base_PixelFlags_flag_injected']) + self.assertTrue(diaSrc['base_PixelFlags_flag_injectedCenter']) + else: + self._check_diaSource(tmplt_fake_sources, diaSrc, scale=-1, refIds=tmpl_refIds) + self.assertTrue(diaSrc['base_PixelFlags_flag_injected_template']) + self.assertTrue(diaSrc['base_PixelFlags_flag_injected_templateCenter']) + class DetectAndMeasureScoreTest(DetectAndMeasureTestBase): detectionTask = detectAndMeasure.DetectAndMeasureScoreTask