Skip to content

Commit

Permalink
Merge pull request #317 from lsst/tickets/DM-44319-revert
Browse files Browse the repository at this point in the history
Revert "Merge pull request #316 from lsst/tickets/DM-44319"
  • Loading branch information
fred3m authored May 21, 2024
2 parents 9621bc4 + 9638b5c commit ab192c5
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 83 deletions.
154 changes: 79 additions & 75 deletions python/lsst/ip/diffim/detectAndMeasure.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import numpy as np

import lsst.afw.detection as afwDetection
import lsst.afw.table as afwTable
import lsst.daf.base as dafBase
import lsst.geom
Expand Down Expand Up @@ -103,8 +104,7 @@ class DetectAndMeasureConfig(pipeBase.PipelineTaskConfig,
dtype=bool,
default=True,
doc="Merge positive and negative diaSources with grow radius "
"set by growFootprint",
deprecated="This field is no longer used and will be removed after v28."
"set by growFootprint"
)
doForcedMeasurement = pexConfig.Field(
dtype=bool,
Expand Down Expand Up @@ -143,8 +143,7 @@ class DetectAndMeasureConfig(pipeBase.PipelineTaskConfig,
growFootprint = pexConfig.Field(
dtype=int,
default=2,
doc="Grow positive and negative footprints by this many pixels before merging",
deprecated="This field is no longer used and will be removed after v28."
doc="Grow positive and negative footprints by this many pixels before merging"
)
diaSourceMatchRadius = pexConfig.Field(
dtype=float,
Expand Down Expand Up @@ -347,11 +346,16 @@ def run(self, science, matchedTemplate, difference,
doSmooth=True,
)

sources = self._buildCatalogAndDeblend(difference, results.positive, results.negative, idFactory)
sources, positives, negatives = self._deblend(difference,
results.positive,
results.negative)

return self._measureSources(science, matchedTemplate, difference, sources)
return self.processResults(science, matchedTemplate, difference, sources, idFactory,
positiveFootprints=positives,
negativeFootprints=negatives)

def _measureSources(self, science, matchedTemplate, difference, initialDiaSources):
def processResults(self, science, matchedTemplate, difference, sources, idFactory,
positiveFootprints=None, negativeFootprints=None,):
"""Measure and process the results of source detection.
Parameters
Expand All @@ -363,8 +367,15 @@ def _measureSources(self, science, matchedTemplate, difference, initialDiaSource
difference image.
difference : `lsst.afw.image.ExposureF`
Result of subtracting template from the science image.
initialDiaSources : `lsst.afw.table.SourceCatalog`
sources : `lsst.afw.table.SourceCatalog`
Detected sources on the difference exposure.
idFactory : `lsst.afw.table.IdFactory`
Generator object used to assign ids to detected sources in the
difference image.
positiveFootprints : `lsst.afw.detection.FootprintSet`, optional
Positive polarity footprints.
negativeFootprints : `lsst.afw.detection.FootprintSet`, optional
Negative polarity footprints.
Returns
-------
Expand All @@ -375,12 +386,36 @@ def _measureSources(self, science, matchedTemplate, difference, initialDiaSource
``diaSources`` : `lsst.afw.table.SourceCatalog`
The catalog of detected sources.
"""
self.metadata.add("nDiaSources", len(initialDiaSources))
self.metadata.add("nUnmergedDiaSources", len(sources))
if self.config.doMerge:
fpSet = positiveFootprints
fpSet.merge(negativeFootprints, self.config.growFootprint,
self.config.growFootprint, False)
initialDiaSources = afwTable.SourceCatalog(self.schema)
fpSet.makeSources(initialDiaSources)
self.log.info("Merging detections into %d sources", len(initialDiaSources))
else:
initialDiaSources = sources

# Assign source ids at the end: deblend/merge mean that we don't keep
# track of parents and children, we only care about the final ids.
for source in initialDiaSources:
source.setId(idFactory())
# Ensure sources added after this get correct ids.
initialDiaSources.getTable().setIdFactory(idFactory)
initialDiaSources.setMetadata(self.algMetadata)

self.metadata.add("nMergedDiaSources", len(initialDiaSources))

if self.config.doMaskStreaks:
streakInfo = self._runStreakMasking(difference.maskedImage)

if self.config.doSkySources:
self.addSkySources(initialDiaSources, difference.mask, difference.info.id)

if not initialDiaSources.isContiguous():
initialDiaSources = initialDiaSources.copy(deep=True)

self.measureDiaSources(initialDiaSources, science, difference, matchedTemplate)
diaSources = self._removeBadSources(initialDiaSources)

Expand All @@ -398,14 +433,9 @@ def _measureSources(self, science, matchedTemplate, difference, initialDiaSource

return measurementResults

def _buildCatalogAndDeblend(self, difference, positiveFootprints, negativeFootprints, idFactory):
"""Create a source catalog and deblend when possible.
This method creates a source catalog from the positive and negative
footprints, and then deblends the footprints that have only positive
peaks. This is because `lsst.meas.deblender.SourceDeblendTask` is not
designed to handle footprints that have negative flux, resulting in
garbage output.
def _deblend(self, difference, positiveFootprints, negativeFootprints):
"""Deblend the positive and negative footprints and return a catalog
containing just the children, and the deblended footprints.
Parameters
----------
Expand All @@ -414,9 +444,6 @@ def _buildCatalogAndDeblend(self, difference, positiveFootprints, negativeFootpr
positiveFootprints, negativeFootprints : `lsst.afw.detection.FootprintSet`
Positive and negative polarity footprints measured on
``difference`` to be deblended separately.
idFactory : `lsst.afw.table.IdFactory`
Generator object used to assign ids to detected sources in the
difference image.
Returns
-------
Expand All @@ -426,59 +453,33 @@ def _buildCatalogAndDeblend(self, difference, positiveFootprints, negativeFootpr
Deblended positive and negative polarity footprints measured on
``difference``.
"""
# Merge the positive and negative footprints.
# The original detection FootprintSets already grew each detection,
# so there is no need to grow them again.
merged_footprints = positiveFootprints
merged_footprints.merge(negativeFootprints, 0, 0, False)

# Create a source catalog from the footprints.
table = afwTable.SourceTable.make(self.schema, idFactory)
sources = afwTable.SourceCatalog(table)
merged_footprints.makeSources(sources)

# Sky sources must be added before deblending, otherwise the
# sources with parent == 0 will be out of order and
# downstream measurement tasks cannot run.
if self.config.doSkySources:
self.addSkySources(sources, difference.mask, difference.info.id)

# Find the footprints with only positive peaks and no negative peaks.
footprints = [src.getFootprint() for src in sources]
nPeaks = np.array([len(fp.peaks) for fp in footprints])
blend_ids = []
skipped_ids = []
for src in sources[nPeaks > 1]:
peaks = src.getFootprint().peaks
peak_flux = np.array([peak.getPeakValue() for peak in peaks])
if np.all(peak_flux > 0) or np.all(peak_flux < 0):
blend_ids.append(src.getId())
else:
skipped_ids.append(src.getId())

# Deblend the footprints that can be deblended with SourceDeblendTask.
temp_cat = afwTable.SourceCatalog(sources.table)
for sid in blend_ids:
temp_cat.append(sources.find(sid))
first_child_index = len(temp_cat)
self.deblend.run(exposure=difference, sources=temp_cat)

# Update the deblended parents and add their children
# to the sources catalog.
sources.extend(temp_cat[first_child_index:], deep=False)

# Set deblending flags.
# Since SourceDeblendTask already has a method for setting
# flags for skipped parents, we just call that method here
# instead of setting the flags manually.
for sid in skipped_ids:
src = sources.find(sid)
self.deblend.skipParent(src, difference.mask)

# Set detection and primary flags
self.setPrimaryFlags.run(sources)

return sources
def makeFootprints(sources):
footprints = afwDetection.FootprintSet(difference.getBBox())
footprints.setFootprints([src.getFootprint() for src in sources])
return footprints

def deblend(footprints):
"""Deblend a positive or negative footprint set,
and return the deblended children.
"""
sources = afwTable.SourceCatalog(self.schema)
footprints.makeSources(sources)
self.deblend.run(exposure=difference, sources=sources)
self.setPrimaryFlags.run(sources)
children = sources["detect_isDeblendedSource"] == 1
sources = sources[children].copy(deep=True)
# Clear parents, so that measurement plugins behave correctly.
sources['parent'] = 0
return sources.copy(deep=True)

positives = deblend(positiveFootprints)
negatives = deblend(negativeFootprints)

sources = afwTable.SourceCatalog(self.schema)
sources.reserve(len(positives) + len(negatives))
sources.extend(positives, deep=True)
sources.extend(negatives, deep=True)
return sources, makeFootprints(positives), makeFootprints(negatives)

def _removeBadSources(self, diaSources):
"""Remove unphysical diaSources from the catalog.
Expand Down Expand Up @@ -726,6 +727,9 @@ def run(self, science, matchedTemplate, difference, scoreExposure,
# Copy the detection mask from the Score image to the difference image
difference.mask.assign(scoreExposure.mask, scoreExposure.getBBox())

sources = self._buildCatalogAndDeblend(difference, results.positive, results.negative, idFactory)
sources, positives, negatives = self._deblend(difference,
results.positive,
results.negative)

return self._measureSources(science, matchedTemplate, difference, sources)
return self.processResults(science, matchedTemplate, difference, sources, idFactory,
positiveFootprints=positives, negativeFootprints=negatives)
11 changes: 3 additions & 8 deletions tests/test_detectAndMeasure.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,9 +288,7 @@ def _detection_wrapper(positive=True):
output = detectionTask.run(science.clone(), matchedTemplate, difference)
refIds = []
scale = 1. if positive else -1.
# Deblending provides multiple copies of the main parent source,
# so we only check the parents. Deblending is checked in another test.
for diaSource in output.diaSources[output.diaSources["parent"] == 0]:
for diaSource in output.diaSources:
self._check_diaSource(transientSources, diaSource, refIds=refIds, scale=scale)
_detection_wrapper(positive=True)
_detection_wrapper(positive=False)
Expand Down Expand Up @@ -519,7 +517,7 @@ def test_fake_mask_plane_propagation(self):

sci_refIds = []
tmpl_refIds = []
for diaSrc in output.diaSources[output.diaSources["parent"] == 0]:
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'])
Expand Down Expand Up @@ -689,10 +687,7 @@ def _detection_wrapper(positive=True):
scale = 1. if positive else -1.
# sources near the edge may have untrustworthy centroids
goodSrcFlags = ~output.diaSources['base_PixelFlags_flag_edge']
# Deblending provides multiple copies of the main parent source,
# so we only check the parents. Deblending is checked in another test.
for diaSource, goodSrcFlag in zip(output.diaSources[output.diaSources["parent"] == 0],
goodSrcFlags):
for diaSource, goodSrcFlag in zip(output.diaSources, goodSrcFlags):
if goodSrcFlag:
self._check_diaSource(transientSources, diaSource, refIds=refIds, scale=scale)
_detection_wrapper(positive=True)
Expand Down

0 comments on commit ab192c5

Please sign in to comment.