From 5bec8cbeaf8038c6f7a4c6df1644ccff20d698aa Mon Sep 17 00:00:00 2001 From: Meredith Rawls Date: Wed, 29 Jan 2025 15:54:11 -0800 Subject: [PATCH 1/2] Fix bug in deblending diaSources with negative fluxes. This does not fix a separate footprint merging bug. --- python/lsst/ip/diffim/detectAndMeasure.py | 28 ++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/python/lsst/ip/diffim/detectAndMeasure.py b/python/lsst/ip/diffim/detectAndMeasure.py index 7d2b0bd6b..8119f2790 100644 --- a/python/lsst/ip/diffim/detectAndMeasure.py +++ b/python/lsst/ip/diffim/detectAndMeasure.py @@ -526,13 +526,35 @@ def makeFootprints(sources): footprints.setFootprints([src.getFootprint() for src in sources]) return footprints - def deblend(footprints): + def deblend(footprints, negative=False): """Deblend a positive or negative footprint set, and return the deblended children. + + Parameters + ---------- + footprints : `lsst.afw.detection.FootprintSet` + negative : `bool` + Set True if the footprints contain negative fluxes + + Returns + ------- + sources : `lsst.afw.table.SourceCatalog` """ sources = afwTable.SourceCatalog(self.schema) footprints.makeSources(sources) - self.deblend.run(exposure=difference, sources=sources) + if negative: + # Invert the image so the deblender can run on positive peaks + difference_inverted = difference.clone() + difference_inverted.image *= -1 + self.deblend.run(exposure=difference_inverted, sources=sources) + children = sources[sources["parent"] != 0] + # Set the heavy footprint pixel values back to reality + for child in children: + footprint = child.getFootprint() + array = footprint.getImageArray() + array *= -1 + else: + self.deblend.run(exposure=difference, sources=sources) self.setPrimaryFlags.run(sources) children = sources["detect_isDeblendedSource"] == 1 sources = sources[children].copy(deep=True) @@ -541,7 +563,7 @@ def deblend(footprints): return sources.copy(deep=True) positives = deblend(positiveFootprints) - negatives = deblend(negativeFootprints) + negatives = deblend(negativeFootprints, negative=True) sources = afwTable.SourceCatalog(self.schema) sources.reserve(len(positives) + len(negatives)) From 34afd926125e420a92a8b63bd18c480cd572e922 Mon Sep 17 00:00:00 2001 From: Meredith Rawls Date: Tue, 4 Feb 2025 15:07:43 -0800 Subject: [PATCH 2/2] Add nearby transient diaSource as a deblending test. While this test passes, it does NOT catch the original failure condition, i.e., raising if deblending sources yields entirely nan peaks. --- tests/test_detectAndMeasure.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/test_detectAndMeasure.py b/tests/test_detectAndMeasure.py index 474318ebc..ebbe3b680 100644 --- a/tests/test_detectAndMeasure.py +++ b/tests/test_detectAndMeasure.py @@ -305,9 +305,33 @@ def test_detect_transients(self): kwargs["nSrc"] = 10 kwargs["fluxLevel"] = 1000 + blendedKwargs = kwargs + blendedKwargs["nSrc"] = 1 + # Run detection and check the results def _detection_wrapper(positive=True): transients, transientSources = makeTestImage(noiseLevel=noiseLevel, noiseSeed=8, **kwargs) + # Make a nearby transient source that doesn't fall off the image + # so we can test deblending + transientsBbox = transients.getBBox() + oneSourceX = transientSources[0].getX() + oneSourceY = transientSources[0].getY() + if oneSourceX < transientsBbox.getCenter()[0]: + blendedSourceX = oneSourceX + 10 + else: + blendedSourceX = oneSourceX - 10 + if oneSourceY < transientsBbox.getCenter()[1]: + blendedSourceY = oneSourceY + 10 + else: + blendedSourceY = oneSourceY - 10 + blendedTransients, blendedTransientSources = makeTestImage(noiseLevel=0, + noiseSeed=8, + xLoc=[blendedSourceX,], + yLoc=[blendedSourceY,], + **blendedKwargs) + blendedTransientSources["id"][0] = np.max(transientSources["id"]) + 1 + transientSources.extend(blendedTransientSources) + transients.maskedImage += blendedTransients.maskedImage difference = science.clone() difference.maskedImage -= matchedTemplate.maskedImage if positive: @@ -322,7 +346,7 @@ def _detection_wrapper(positive=True): refIds = [] scale = 1. if positive else -1. for diaSource in output.diaSources: - self._check_diaSource(transientSources, diaSource, refIds=refIds, scale=scale) + self._check_diaSource(transientSources.copy(deep=True), diaSource, refIds=refIds, scale=scale) _detection_wrapper(positive=True) _detection_wrapper(positive=False)