Skip to content

Commit 101fa39

Browse files
committed
Add trailed glint finding to detectAndMeasure.
Adds a new bool column trailed_glint to the diaSource catalog. In addition, a new catalog with fit trail info is persisted.
1 parent 7605de0 commit 101fa39

3 files changed

Lines changed: 72 additions & 0 deletions

File tree

python/lsst/ip/diffim/detectAndMeasure.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import lsst.geom
3030
from lsst.ip.diffim.utils import evaluateMaskFraction, computeDifferenceImageMetrics
3131
from lsst.meas.algorithms import SkyObjectsTask, SourceDetectionTask, SetPrimaryFlagsTask, MaskStreaksTask
32+
from lsst.meas.algorithms import FindGlintTrailsTask
3233
from lsst.meas.base import ForcedMeasurementTask, ApplyApCorrTask, DetectorVisitIdGeneratorConfig
3334
import lsst.meas.deblender
3435
import lsst.meas.extensions.trailedSources # noqa: F401
@@ -135,13 +136,21 @@ class DetectAndMeasureConnections(pipeBase.PipelineTaskConnections,
135136
dimensions=("instrument", "visit", "detector"),
136137
name="{fakesType}{coaddName}Diff_streaks",
137138
)
139+
trailedGlintInfo = pipeBase.connectionTypes.Output(
140+
doc='Dict of fit parameters for trailed glints in the catalog.',
141+
storageClass="ArrowNumpyDict",
142+
dimensions=("instrument", "visit", "detector"),
143+
name="trailed_glints",
144+
)
138145

139146
def __init__(self, *, config):
140147
super().__init__(config=config)
141148
if not (self.config.writeStreakInfo and self.config.doMaskStreaks):
142149
self.outputs.remove("maskedStreaks")
143150
if not (self.config.doSubtractBackground and self.config.doWriteBackground):
144151
self.outputs.remove("differenceBackground")
152+
if not (self.config.writeGlintInfo):
153+
self.outputs.remove("trailedGlintInfo")
145154

146155

147156
class DetectAndMeasureConfig(pipeBase.PipelineTaskConfig,
@@ -257,6 +266,15 @@ class DetectAndMeasureConfig(pipeBase.PipelineTaskConfig,
257266
doc="Record the parameters of any detected streaks. For LSST, this should be turned off except for "
258267
"development work."
259268
)
269+
findGlints = pexConfig.ConfigurableField(
270+
target=FindGlintTrailsTask,
271+
doc="Subtask for finding trailed glints, usually caused by satellites or debris."
272+
)
273+
writeGlintInfo = pexConfig.Field(
274+
dtype=bool,
275+
default=True,
276+
doc="Record the parameters of any detected trailed glints."
277+
)
260278
setPrimaryFlags = pexConfig.ConfigurableField(
261279
target=SetPrimaryFlagsTask,
262280
doc="Task to add isPrimary and deblending-related flags to the catalog."
@@ -430,6 +448,8 @@ def __init__(self, **kwargs):
430448
if self.config.doMaskStreaks:
431449
self.makeSubtask("maskStreaks")
432450
self.makeSubtask("streakDetection")
451+
self.makeSubtask("findGlints")
452+
self.schema.addField("trailed_glint", "Flag", "DiaSource is part of a glint trail.")
433453

434454
# To get the "merge_*" fields in the schema; have to re-initialize
435455
# this later, once we have a peak schema post-detection.
@@ -467,6 +487,7 @@ def runQuantum(self, butlerQC: pipeBase.QuantumContext,
467487
measurementResults.subtractedMeasuredExposure,
468488
measurementResults.diaSources,
469489
measurementResults.maskedStreaks,
490+
measurementResults.trailedGlintInfo,
470491
log=self.log
471492
)
472493
butlerQC.put(measurementResults, outputRefs)
@@ -690,6 +711,13 @@ def processResults(self, science, matchedTemplate, difference, sources, idFactor
690711
initialDiaSources = initialDiaSources.copy(deep=True)
691712

692713
self.measureDiaSources(initialDiaSources, science, difference, matchedTemplate)
714+
715+
# Add a column for trailed glint diaSources, but do not remove them
716+
initialDiaSources, trail_parameters = self._find_trailed_glints(initialDiaSources)
717+
if self.config.writeGlintInfo:
718+
measurementResults.mergeItems(trail_parameters, 'trailedGlintInfo')
719+
720+
# Remove unphysical diaSources per config.badSourceFlags
693721
diaSources = self._removeBadSources(initialDiaSources)
694722

695723
if self.config.doForcedMeasurement:
@@ -807,6 +835,39 @@ def _removeBadSources(self, diaSources):
807835
self.log.info("Removed %d unphysical sources.", nBadTotal)
808836
return diaSources[selector].copy(deep=True)
809837

838+
def _find_trailed_glints(self, diaSources):
839+
"""Define a new flag column for diaSources that are in a glint trail.
840+
841+
Parameters
842+
----------
843+
diaSources : `lsst.afw.table.SourceCatalog`
844+
The catalog of detected sources.
845+
846+
Returns
847+
-------
848+
diaSources : `lsst.afw.table.SourceCatalog`
849+
The updated catalog of detected sources, with a new bool column
850+
called 'trailed_glint' added.
851+
852+
trail_parameters : `dict`
853+
Parameters of all the trails that were found.
854+
"""
855+
trailed_glints = self.findGlints.run(diaSources)
856+
glint_mask = [True if id in trailed_glints.trailed_ids else False for id in diaSources['id']]
857+
diaSources['trailed_glint'] = np.array(glint_mask)
858+
859+
slopes = np.array([trail.slope for trail in trailed_glints.parameters])
860+
intercepts = np.array([trail.intercept for trail in trailed_glints.parameters])
861+
stderrs = np.array([trail.stderr for trail in trailed_glints.parameters])
862+
lengths = np.array([trail.length for trail in trailed_glints.parameters])
863+
angles = np.array([trail.angle for trail in trailed_glints.parameters])
864+
parameters = {'slopes': slopes, 'intercepts': intercepts, 'stderrs': stderrs, 'lengths': lengths,
865+
'angles': angles}
866+
867+
trail_parameters = pipeBase.Struct(trailedGlintInfo=parameters)
868+
869+
return diaSources, trail_parameters
870+
810871
def addSkySources(self, diaSources, mask, seed,
811872
subtask=None):
812873
"""Add sources in empty regions of the difference image

tests/test_detectAndMeasure.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,16 @@ def test_mask_streaks(self):
705705
# Check that the entire image was not masked STREAK
706706
self.assertFalse(np.all(streakMaskSet))
707707

708+
def test_trailed_glints(self):
709+
"""Test that the trailed_glint column works.
710+
"""
711+
# Set up a simulated image
712+
noiseLevel = 1.
713+
staticSeed = 1
714+
_, diaSources = makeTestImage(seed=staticSeed, noiseLevel=noiseLevel, noiseSeed=6,
715+
nSrc=10)
716+
self._check_values(diaSources['trailed_glint'])
717+
708718

709719
class DetectAndMeasureScoreTest(DetectAndMeasureTestBase, lsst.utils.tests.TestCase):
710720
detectionTask = detectAndMeasure.DetectAndMeasureScoreTask

tests/utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,7 @@ def _makeTruthSchema():
10751075
schema.getAliasMap().set("slot_CalibFlux", "truth")
10761076
schema.getAliasMap().set("slot_ApFlux", "truth")
10771077
schema.getAliasMap().set("slot_PsfFlux", "truth")
1078+
schema.addField("trailed_glint", "Flag", "testing flag.")
10781079
return keys, schema
10791080

10801081

0 commit comments

Comments
 (0)