lsst.ip.diffim g3a2e29f8c4+ceae6a3766
Loading...
Searching...
No Matches
lsst.ip.diffim.detectAndMeasure Namespace Reference

Classes

class  BadSubtractionError
class  NoDiaSourcesError
class  TooManyCosmicRays
class  DetectAndMeasureConnections

Variables

 science : `lsst.afw.image.ExposureF`
 matchedTemplate : `lsst.afw.image.ExposureF`
 difference : `lsst.afw.image.ExposureF`
 kernelSources : `lsst.afw.table.SourceCatalog`, optional
 idFactory : `lsst.afw.table.IdFactory`, optional
 measurementResults : `lsst.pipe.base.Struct`, optional
 differenceExposure : `lsst.afw.image.ExposureF`
 scoreExposure : `lsst.afw.image.ExposureF`, optional
 background : `lsst.afw.math.BackgroundList`
 sources : `lsst.afw.table.SourceCatalog`
 positives : `lsst.afw.table.SourceCatalog`, optional
 negatives : `lsst.afw.table.SourceCatalog`, optional
 table = afwTable.SourceTable.make(self.schema)
 detectResults
 differenceBackground
 scoreMeasuredExposure
 detectedBit = difference.mask.getPlaneBitMask(["DETECTED"])
int detectedPix = scoreExposure.mask.array & detectedBit > 0
 detectedNegativeBit = difference.mask.getPlaneBitMask(["DETECTED_NEGATIVE"])
int detectedNegativePix = scoreExposure.mask.array & detectedNegativeBit > 0
 edgeBit = difference.mask.getPlaneBitMask(["EDGE"])
int edgePix = scoreExposure.mask.array & edgeBit > 0

Variable Documentation

◆ background

lsst.ip.diffim.detectAndMeasure.background : `lsst.afw.math.BackgroundList`

Definition at line 763 of file detectAndMeasure.py.

◆ detectedBit

lsst.ip.diffim.detectAndMeasure.detectedBit = difference.mask.getPlaneBitMask(["DETECTED"])

Definition at line 1584 of file detectAndMeasure.py.

◆ detectedNegativeBit

lsst.ip.diffim.detectAndMeasure.detectedNegativeBit = difference.mask.getPlaneBitMask(["DETECTED_NEGATIVE"])

Definition at line 1586 of file detectAndMeasure.py.

◆ detectedNegativePix

int lsst.ip.diffim.detectAndMeasure.detectedNegativePix = scoreExposure.mask.array & detectedNegativeBit > 0

Definition at line 1587 of file detectAndMeasure.py.

◆ detectedPix

int lsst.ip.diffim.detectAndMeasure.detectedPix = scoreExposure.mask.array & detectedBit > 0

Definition at line 1585 of file detectAndMeasure.py.

◆ detectResults

lsst.ip.diffim.detectAndMeasure.detectResults
Initial value:
= self.detection.run(
table=table,
exposure=scoreExposure,
doSmooth=False,
background=background,
clearMask=True,
)

Definition at line 1571 of file detectAndMeasure.py.

◆ difference

lsst.ip.diffim.detectAndMeasure.difference : `lsst.afw.image.ExposureF`
if measurementResults is None:
    measurementResults = pipeBase.Struct()
if idFactory is None:
    idFactory = lsst.meas.base.IdGenerator().make_table_id_factory()

# Check image properties and clear detection mask planes.
self._prepareInputs(difference)
if self.config.doSubtractBackground:
    background = self._fitAndSubtractBackground(differenceExposure=difference)
else:
    background = afwMath.BackgroundList()

if self.config.doFindCosmicRays:
    self.findAndMaskCosmicRays(difference)

# Don't use the idFactory until after deblend+merge, so that we aren't
# generating ids that just get thrown away (footprint merge doesn't
# know about past ids).
table = afwTable.SourceTable.make(self.schema)
results = self.detection.run(
    table=table,
    exposure=difference,
    doSmooth=True,
    background=background,
    clearMask=True,
)
measurementResults.differenceBackground = background

if self.config.doDeblend:
    sources, positives, negatives = self._deblend(difference,
                                                  results.positive,
                                                  results.negative)

else:
    positives = afwTable.SourceCatalog(self.schema)
    results.positive.makeSources(positives)
    negatives = afwTable.SourceCatalog(self.schema)
    results.negative.makeSources(negatives)
    sources = results.sources

self.processResults(science, matchedTemplate, difference,
                    sources, idFactory, kernelSources,
                    positives=positives,
                    negatives=negatives,
                    measurementResults=measurementResults)
return measurementResults

def _prepareInputs(self, difference):

Definition at line 638 of file detectAndMeasure.py.

◆ differenceBackground

lsst.ip.diffim.detectAndMeasure.differenceBackground

Definition at line 1578 of file detectAndMeasure.py.

◆ differenceExposure

lsst.ip.diffim.detectAndMeasure.differenceExposure : `lsst.afw.image.ExposureF`
# Check that we have a valid PSF now before we do more work
sigma = difference.psf.computeShape(difference.psf.getAveragePosition()).getDeterminantRadius()
if np.isnan(sigma):
    raise pipeBase.UpstreamFailureNoWorkFound("Invalid PSF detected! PSF width evaluates to NaN.")
# Ensure that we start with an empty detection and deblended mask.
mask = difference.mask
for mp in self.config.clearMaskPlanes:
    if mp not in mask.getMaskPlaneDict():
        mask.addMaskPlane(mp)
mask &= ~mask.getPlaneBitMask(self.config.clearMaskPlanes)

def _fitAndSubtractBackground(self, differenceExposure, scoreExposure=None):

Definition at line 754 of file detectAndMeasure.py.

◆ edgeBit

lsst.ip.diffim.detectAndMeasure.edgeBit = difference.mask.getPlaneBitMask(["EDGE"])

Definition at line 1591 of file detectAndMeasure.py.

◆ edgePix

int lsst.ip.diffim.detectAndMeasure.edgePix = scoreExposure.mask.array & edgeBit > 0

Definition at line 1592 of file detectAndMeasure.py.

◆ idFactory

lsst.ip.diffim.detectAndMeasure.idFactory : `lsst.afw.table.IdFactory`, optional

Definition at line 642 of file detectAndMeasure.py.

◆ kernelSources

lsst.ip.diffim.detectAndMeasure.kernelSources : `lsst.afw.table.SourceCatalog`, optional

Definition at line 640 of file detectAndMeasure.py.

◆ matchedTemplate

lsst.ip.diffim.detectAndMeasure.matchedTemplate : `lsst.afw.image.ExposureF`

Definition at line 635 of file detectAndMeasure.py.

◆ measurementResults

lsst.ip.diffim.detectAndMeasure.measurementResults : `lsst.pipe.base.Struct`, optional

Definition at line 646 of file detectAndMeasure.py.

◆ negatives

lsst.ip.diffim.detectAndMeasure.negatives : `lsst.afw.table.SourceCatalog`, optional

Definition at line 823 of file detectAndMeasure.py.

◆ positives

lsst.ip.diffim.detectAndMeasure.positives : `lsst.afw.table.SourceCatalog`, optional

Definition at line 821 of file detectAndMeasure.py.

◆ science

lsst.ip.diffim.detectAndMeasure.science : `lsst.afw.image.ExposureF`
doMerge = pexConfig.Field(
    dtype=bool,
    default=True,
    doc="Merge positive and negative diaSources with grow radius "
        "set by growFootprint"
)
doForcedMeasurement = pexConfig.Field(
    dtype=bool,
    default=True,
    doc="Force photometer diaSource locations on PVI?"
)
doAddMetrics = pexConfig.Field(
    dtype=bool,
    default=False,
    doc="Add columns to the source table to hold analysis metrics?"
)
doSubtractBackground = pexConfig.Field(
    dtype=bool,
    doc="Subtract a background model from the image before detection?",
    default=True,
)
doWriteBackground = pexConfig.Field(
    dtype=bool,
    doc="Persist the fitted background model?",
    default=False,
)
doCalculateResidualMetics = pexConfig.Field(
    dtype=bool,
    doc="Calculate metrics to assess image subtraction quality for the task"
    "metadata?",
    default=True,
)
subtractInitialBackground = pexConfig.ConfigurableField(
    target=lsst.meas.algorithms.SubtractBackgroundTask,
    doc="Task to perform intial background subtraction, before first detection pass.",
)
subtractFinalBackground = pexConfig.ConfigurableField(
    target=lsst.meas.algorithms.SubtractBackgroundTask,
    doc="Task to perform final background subtraction, after first detection pass.",
)
doFindCosmicRays = pexConfig.Field(
    dtype=bool,
    doc="Detect and mask cosmic rays on the difference image?"
        "CRs can be interpolated over by setting cosmicray.keepCRs=False",
    default=True,
)
cosmicray = pexConfig.ConfigField(
    dtype=FindCosmicRaysConfig,
    doc="Options for finding and masking cosmic rays",
)
detection = pexConfig.ConfigurableField(
    target=SourceDetectionTask,
    doc="Final source detection for diaSource measurement",
)
streakDetection = pexConfig.ConfigurableField(
    target=SourceDetectionTask,
    doc="Separate source detection used only for streak masking",
)
doDeblend = pexConfig.Field(
    dtype=bool,
    default=False,
    doc="Deblend DIASources after detection?"
)
deblend = pexConfig.ConfigurableField(
    target=lsst.meas.deblender.SourceDeblendTask,
    doc="Task to split blended sources into their components."
)
measurement = pexConfig.ConfigurableField(
    target=DipoleFitTask,
    doc="Task to measure sources on the difference image.",
)
doApCorr = lsst.pex.config.Field(
    dtype=bool,
    default=True,
    doc="Run subtask to apply aperture corrections"
)
applyApCorr = lsst.pex.config.ConfigurableField(
    target=ApplyApCorrTask,
    doc="Task to apply aperture corrections"
)
forcedMeasurement = pexConfig.ConfigurableField(
    target=ForcedMeasurementTask,
    doc="Task to force photometer science image at diaSource locations.",
)
growFootprint = pexConfig.Field(
    dtype=int,
    default=2,
    doc="Grow positive and negative footprints by this many pixels before merging"
)
diaSourceMatchRadius = pexConfig.Field(
    dtype=float,
    default=0.5,
    doc="Match radius (in arcseconds) for DiaSource to Source association"
)
doSkySources = pexConfig.Field(
    dtype=bool,
    default=False,
    doc="Generate sky sources?",
)
skySources = pexConfig.ConfigurableField(
    target=SkyObjectsTask,
    doc="Generate sky sources",
)
doMaskStreaks = pexConfig.Field(
    dtype=bool,
    default=True,
    doc="Turn on streak masking",
)
maskStreaks = pexConfig.ConfigurableField(
    target=MaskStreaksTask,
    doc="Subtask for masking streaks. Only used if doMaskStreaks is True. "
        "Adds a mask plane to an exposure, with the mask plane name set by streakMaskName.",
)
streakBinFactor = pexConfig.Field(
    dtype=int,
    default=4,
    doc="Bin scale factor to use when rerunning detection for masking streaks. "
        "Only used if doMaskStreaks is True.",
)
writeStreakInfo = pexConfig.Field(
    dtype=bool,
    default=False,
    doc="Record the parameters of any detected streaks. For LSST, this should be turned off except for "
        "development work."
)
findGlints = pexConfig.ConfigurableField(
    target=FindGlintTrailsTask,
    doc="Subtask for finding glint trails, usually caused by satellites or debris."
)
writeGlintInfo = pexConfig.Field(
    dtype=bool,
    default=True,
    doc="Record the parameters of any detected glint trails."
)
setPrimaryFlags = pexConfig.ConfigurableField(
    target=SetPrimaryFlagsTask,
    doc="Task to add isPrimary and deblending-related flags to the catalog."
)
badSourceFlags = lsst.pex.config.ListField(
    dtype=str,
    doc="Sources with any of these flags set are removed before writing the output catalog.",
    default=("base_PixelFlags_flag_offimage",
             "base_PixelFlags_flag_interpolatedCenterAll",
             "base_PixelFlags_flag_badCenter",
             "base_PixelFlags_flag_edgeCenter",
             "base_PixelFlags_flag_nodataCenter",
             "base_PixelFlags_flag_saturatedCenter",
             "base_PixelFlags_flag_sat_templateCenter",
             "base_PixelFlags_flag_spikeCenter",
             ),
)
clearMaskPlanes = lsst.pex.config.ListField(
    dtype=str,
    doc="Mask planes to clear before running detection.",
    default=("DETECTED", "DETECTED_NEGATIVE", "NOT_DEBLENDED", "STREAK"),
)
doRejectBadMaskPlaneDetections = pexConfig.Field(
    dtype=bool,
    default=True,
    doc="Reject any peaks detected on ``badMaskPlanes`` before measurement."
        "These should all be rejected downstream by ``badSourceFlags``, "
        "but filtering earlier can save time."
)
badMaskPlanes = lsst.pex.config.ListField(
    dtype=str,
    doc="Detections whose footprint peak lies on a pixel with any of these"
        " mask planes set will be rejected before measurement."
        " Any missing mask planes will be silently ignored.",
    default=("NO_DATA", "BAD", "SAT", "EDGE"),
)
raiseOnBadSubtractionRatio = pexConfig.Field(
    dtype=bool,
    default=True,
    doc="Raise an error if the ratio of power in detected footprints"
        " on the difference image to the power in footprints on the science"
        " image exceeds ``badSubtractionRatioThreshold``",
)
badSubtractionRatioThreshold = pexConfig.Field(
    dtype=float,
    default=0.2,
    doc="Maximum ratio of power in footprints on the difference image to"
        " the same footprints on the science image."
        "Only used if ``raiseOnBadSubtractionRatio`` is set",
)
badSubtractionVariationThreshold = pexConfig.Field(
    dtype=float,
    default=0.4,
    doc="Maximum standard deviation of the ratio of power in footprints on"
        " the difference image to the same footprints on the science image."
        "Only used if ``raiseOnBadSubtractionRatio`` is set",
)
raiseOnNoDiaSources = pexConfig.Field(
    dtype=bool,
    default=True,
    doc="Raise an algorithm error if no diaSources are detected.",
)
run_sattle = pexConfig.Field(
    dtype=bool,
    default=False,
    doc="If true, dia source bounding boxes will be sent for verification"
        "to the sattle service."
)
sattle_historical = pexConfig.Field(
    dtype=bool,
    default=False,
    doc="If re-running a pipeline that requires sattle, this should be set "
        "to True. This will populate sattle's cache with the historic data "
        "closest in time to the exposure."
)
idGenerator = DetectorVisitIdGeneratorConfig.make_field()

def setDefaults(self):
    # Background subtraction
    # Use a small binsize for the first pass to reduce detections on glints
    #  and extended structures. Should not affect the detectability of
    #  faint diaSources
    self.subtractInitialBackground.binSize = 8
    self.subtractInitialBackground.useApprox = False
    self.subtractInitialBackground.statisticsProperty = "MEDIAN"
    self.subtractInitialBackground.doFilterSuperPixels = True
    self.subtractInitialBackground.ignoredPixelMask = ["BAD",
                                                       "EDGE",
                                                       "DETECTED",
                                                       "DETECTED_NEGATIVE",
                                                       "NO_DATA",
                                                       ]
    # Use a larger binsize for the final background subtraction, to reduce
    #  over-subtraction of bright objects.
    self.subtractFinalBackground.binSize = 40
    self.subtractFinalBackground.useApprox = False
    self.subtractFinalBackground.statisticsProperty = "MEDIAN"
    self.subtractFinalBackground.doFilterSuperPixels = True
    self.subtractFinalBackground.ignoredPixelMask = ["BAD",
                                                     "EDGE",
                                                     "DETECTED",
                                                     "DETECTED_NEGATIVE",
                                                     "NO_DATA",
                                                     ]
    # Cosmic ray detection
    self.cosmicray.keepCRs = True  # do not interpolate over detected CRs
    # DiaSource Detection
    self.detection.thresholdPolarity = "both"
    self.detection.thresholdValue = 5.0
    self.detection.reEstimateBackground = False
    self.detection.thresholdType = "pixel_stdev"
    self.detection.excludeMaskPlanes = []

    # Copy configs for binned streak detection from the base detection task
    self.streakDetection.thresholdType = self.detection.thresholdType
    self.streakDetection.reEstimateBackground = False
    self.streakDetection.excludeMaskPlanes = self.detection.excludeMaskPlanes
    self.streakDetection.thresholdValue = self.detection.thresholdValue
    # Only detect positive streaks
    self.streakDetection.thresholdPolarity = "positive"
    # Do not grow detected mask for streaks
    self.streakDetection.nSigmaToGrow = 0
    # Set the streak mask along the entire fit line, not only where the
    # detected mask is set.
    self.maskStreaks.onlyMaskDetected = False
    # Restrict streak masking from growing too large
    self.maskStreaks.maxStreakWidth = 100
    # Restrict the number of iterations allowed for fitting streaks
    # When the fit is good it should solve quickly, and exit a bad fit quickly
    self.maskStreaks.maxFitIter = 10
    # Only mask to 2 sigma in width
    self.maskStreaks.nSigmaMask = 2
    # Threshold for including streaks after the Hough Transform.
    # A lower value will detect more features that are less linear.
    self.maskStreaks.absMinimumKernelHeight = 2

    self.measurement.plugins.names |= ["ext_trailedSources_Naive",
                                       "base_LocalPhotoCalib",
                                       "base_LocalWcs",
                                       "ext_shapeHSM_HsmSourceMoments",
                                       "ext_shapeHSM_HsmPsfMoments",
                                       "base_ClassificationSizeExtendedness",
                                       ]
    self.measurement.slots.psfShape = "ext_shapeHSM_HsmPsfMoments"
    self.measurement.slots.shape = "ext_shapeHSM_HsmSourceMoments"
    self.measurement.plugins["base_SdssCentroid"].maxDistToPeak = 5.0
    self.forcedMeasurement.plugins = ["base_TransformedCentroid", "base_PsfFlux"]
    self.forcedMeasurement.copyColumns = {
        "id": "objectId", "parent": "parentObjectId", "coord_ra": "coord_ra", "coord_dec": "coord_dec"}
    self.forcedMeasurement.slots.centroid = "base_TransformedCentroid"
    self.forcedMeasurement.slots.shape = None

    # Keep track of which footprints contain streaks
    self.measurement.plugins["base_PixelFlags"].masksFpAnywhere = [
        "STREAK", "INJECTED", "INJECTED_TEMPLATE", "HIGH_VARIANCE", "SAT_TEMPLATE", "SPIKE"]
    self.measurement.plugins["base_PixelFlags"].masksFpCenter = [
        "STREAK", "INJECTED", "INJECTED_TEMPLATE", "HIGH_VARIANCE", "SAT_TEMPLATE", "SPIKE"]
    self.skySources.avoidMask = ["DETECTED", "DETECTED_NEGATIVE", "BAD", "NO_DATA", "EDGE"]

def validate(self):
    super().validate()

    if self.run_sattle:
        if not os.getenv("SATTLE_URI_BASE"):
            raise pexConfig.FieldValidationError(DetectAndMeasureConfig.run_sattle, self,
                                                 "Sattle requested but SATTLE_URI_BASE "
                                                 "environment variable not set.")


class DetectAndMeasureTask(lsst.pipe.base.PipelineTask):
ConfigClass = DetectAndMeasureConfig
_DefaultName = "detectAndMeasure"

def __init__(self, **kwargs):
    super().__init__(**kwargs)
    self.schema = afwTable.SourceTable.makeMinimalSchema()

    self.algMetadata = dafBase.PropertyList()
    if self.config.doSubtractBackground:
        self.makeSubtask("subtractInitialBackground")
        self.makeSubtask("subtractFinalBackground")
    self.makeSubtask("detection", schema=self.schema)
    if self.config.doDeblend:
        self.makeSubtask("deblend", schema=self.schema)
    self.makeSubtask("setPrimaryFlags", schema=self.schema, isSingleFrame=True)
    self.makeSubtask("measurement", schema=self.schema,
                     algMetadata=self.algMetadata)
    if self.config.doApCorr:
        self.makeSubtask("applyApCorr", schema=self.measurement.schema)
    if self.config.doForcedMeasurement:
        self.schema.addField(
            "ip_diffim_forced_PsfFlux_instFlux", "D",
            "Forced PSF flux measured on the direct image.",
            units="count")
        self.schema.addField(
            "ip_diffim_forced_PsfFlux_instFluxErr", "D",
            "Forced PSF flux error measured on the direct image.",
            units="count")
        self.schema.addField(
            "ip_diffim_forced_PsfFlux_area", "F",
            "Forced PSF flux effective area of PSF.",
            units="pixel")
        self.schema.addField(
            "ip_diffim_forced_PsfFlux_flag", "Flag",
            "Forced PSF flux general failure flag.")
        self.schema.addField(
            "ip_diffim_forced_PsfFlux_flag_noGoodPixels", "Flag",
            "Forced PSF flux not enough non-rejected pixels in data to attempt the fit.")
        self.schema.addField(
            "ip_diffim_forced_PsfFlux_flag_edge", "Flag",
            "Forced PSF flux object was too close to the edge of the image to use the full PSF model.")
        self.schema.addField(
            "ip_diffim_forced_template_PsfFlux_instFlux", "D",
            "Forced PSF flux measured on the template image.",
            units="count")
        self.schema.addField(
            "ip_diffim_forced_template_PsfFlux_instFluxErr", "D",
            "Forced PSF flux error measured on the template image.",
            units="count")
        self.schema.addField(
            "ip_diffim_forced_template_PsfFlux_area", "F",
            "Forced template PSF flux effective area of PSF.",
            units="pixel")
        self.schema.addField(
            "ip_diffim_forced_template_PsfFlux_flag", "Flag",
            "Forced template PSF flux general failure flag.")
        self.schema.addField(
            "ip_diffim_forced_template_PsfFlux_flag_noGoodPixels", "Flag",
            "Forced template PSF flux not enough non-rejected pixels in data to attempt the fit.")
        self.schema.addField(
            "ip_diffim_forced_template_PsfFlux_flag_edge", "Flag",
)
        self.makeSubtask("forcedMeasurement", refSchema=self.schema)

    self.schema.addField("refMatchId", "L", "unique id of reference catalog match")
    self.schema.addField("srcMatchId", "L", "unique id of source match")
    # Create the sky source task for use by metrics,
    # even if sky sources are not added to the diaSource catalog
    self.makeSubtask("skySources", schema=self.schema)
    if self.config.doMaskStreaks:
        self.makeSubtask("maskStreaks")
        self.makeSubtask("streakDetection")
    self.makeSubtask("findGlints")
    self.schema.addField("glint_trail", "Flag", "DiaSource is part of a glint trail.")
    self.schema.addField("reliability", type="F", doc="Reliability score of the DiaSource")
    self.schema.addField("reliabilityVersion", type=str, size=7, doc="Version of the reliability model")

    # To get the "merge_*" fields in the schema; have to re-initialize
    # this later, once we have a peak schema post-detection.
    lsst.afw.detection.FootprintMergeList(self.schema, ["positive", "negative"])

    # Check that the schema and config are consistent
    for flag in self.config.badSourceFlags:
        if flag not in self.schema:
            raise pipeBase.InvalidQuantumError("Field %s not in schema" % flag)

    # initialize InitOutputs
    self.outputSchema = afwTable.SourceCatalog(self.schema)
    self.outputSchema.getTable().setMetadata(self.algMetadata)

def runQuantum(self, butlerQC: pipeBase.QuantumContext,
               inputRefs: pipeBase.InputQuantizedConnection,
               outputRefs: pipeBase.OutputQuantizedConnection):
    inputs = butlerQC.get(inputRefs)
    idGenerator = self.config.idGenerator.apply(butlerQC.quantum.dataId)
    idFactory = idGenerator.make_table_id_factory()
    # Specify the fields that `annotate` needs below, to ensure they
    # exist, even as None.
    measurementResults = pipeBase.Struct(
        subtractedMeasuredExposure=None,
        diaSources=None,
        maskedStreaks=None,
        differenceBackground=None,
    )
    try:
        self.run(**inputs, idFactory=idFactory, measurementResults=measurementResults)
    except pipeBase.AlgorithmError as e:
        error = pipeBase.AnnotatedPartialOutputsError.annotate(
            e,
            self,
            measurementResults.subtractedMeasuredExposure,
            measurementResults.diaSources,
            measurementResults.maskedStreaks,
            log=self.log
        )
        butlerQC.put(measurementResults, outputRefs)
        raise error from e
    butlerQC.put(measurementResults, outputRefs)

@timeMethod
def run(self, science, matchedTemplate, difference, kernelSources=None,
        idFactory=None, measurementResults=None):
if scoreExposure is None:
    detectionExposure = differenceExposure.clone()
    background = self.subtractInitialBackground.run(detectionExposure).background
    doSmooth = True
else:
    # Use a clone of differenceExposure because the background is
    # subtracted in place.
    background = self.subtractInitialBackground.run(differenceExposure.clone()).background
    detectionExposure = scoreExposure.clone()
    detectionExposure.image -= background.getImage()
    doSmooth = False
table = afwTable.SourceTable.make(self.schema)
self.detection.run(
    table=table,
    exposure=detectionExposure,
    doSmooth=doSmooth,
    background=background,
    clearMask=True,
)
# Use the temporary detection mask for the final background subtraction.
# The detection mask planes will be cleared before the final detection
# step, so it is OK if they get set for differenceExposure.
detectedBit = differenceExposure.mask.getPlaneBitMask(["DETECTED"])
detectedPix = detectionExposure.mask.array & detectedBit > 0
detectedNegativeBit = differenceExposure.mask.getPlaneBitMask(["DETECTED_NEGATIVE"])
detectedNegativePix = detectionExposure.mask.array & detectedNegativeBit > 0
differenceExposure.mask.array[detectedPix] |= detectedBit
differenceExposure.mask.array[detectedNegativePix] |= detectedNegativeBit
background = self.subtractFinalBackground.run(differenceExposure).background
if scoreExposure is not None:
    # The preconvolution kernel is normalized to 1, so the same
    # background level applies to the difference and score images.
    scoreExposure.image -= background.getImage()
return background

def processResults(self, science, matchedTemplate, difference, sources, idFactory,
               kernelSources=None, positives=None, negatives=None, measurementResults=None):

Definition at line 633 of file detectAndMeasure.py.

◆ scoreExposure

lsst.ip.diffim.detectAndMeasure.scoreExposure : `lsst.afw.image.ExposureF`, optional

Definition at line 757 of file detectAndMeasure.py.

◆ scoreMeasuredExposure

lsst.ip.diffim.detectAndMeasure.scoreMeasuredExposure

Definition at line 1579 of file detectAndMeasure.py.

◆ sources

lsst.ip.diffim.detectAndMeasure.sources : `lsst.afw.table.SourceCatalog`

Definition at line 814 of file detectAndMeasure.py.

◆ table

lsst.ip.diffim.detectAndMeasure.table = afwTable.SourceTable.make(self.schema)
if measurementResults is None:
    measurementResults = pipeBase.Struct()
self.metadata["nUnmergedDiaSources"] = len(sources)
if self.config.doMerge:
    # preserve peak schema, if there are any footprints
    if len(positives) > 0:
        peakSchema = positives[0].getFootprint().peaks.schema
    elif len(negatives) > 0:
        peakSchema = negatives[0].getFootprint().peaks.schema
    else:
        peakSchema = afwDetection.PeakTable.makeMinimalSchema()
    mergeList = afwDetection.FootprintMergeList(self.schema,
                                                ["positive", "negative"], peakSchema)
    initialDiaSources = afwTable.SourceCatalog(self.schema)
    # Start with positive, as FootprintMergeList will self-merge the
    # subsequent added catalogs, and we want to try to preserve
    # deblended positive sources.
    mergeList.addCatalog(initialDiaSources.table, positives, "positive", minNewPeakDist=0)
    mergeList.addCatalog(initialDiaSources.table, negatives, "negative", minNewPeakDist=0)
    mergeList.getFinalSources(initialDiaSources)
    # Flag as negative those sources that *only* came from the negative
    # footprint set.
    initialDiaSources["is_negative"] = initialDiaSources["merge_footprint_negative"] & \
        ~initialDiaSources["merge_footprint_positive"]
    self.log.info("Merging detections into %d sources", len(initialDiaSources))
    # All positive peaks were added before any negative peaks, so
    # re-order the peaks in each footprint by significance.
    self._reorderPeaksBySignificance(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["nMergedDiaSources"] = len(initialDiaSources)

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

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

if self.config.doRejectBadMaskPlaneDetections:
    # Save time by rejecting peaks on bad mask planes prior to
    # measurement.
    initialDiaSources = self._rejectBadMaskedDetections(initialDiaSources, difference.mask)

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

self.measureDiaSources(initialDiaSources, science, difference, matchedTemplate)

# Remove unphysical diaSources per config.badSourceFlags
diaSources = self._removeBadSources(initialDiaSources)

if self.config.run_sattle:
    diaSources = self.filterSatellites(diaSources, science)

# Flag diaSources in glint trails, but do not remove them
diaSources, trail_parameters = self._find_glint_trails(diaSources)
if self.config.writeGlintInfo:
    measurementResults.mergeItems(trail_parameters, 'glintTrailInfo')

if self.config.doForcedMeasurement:
    self.measureForcedSources(diaSources, science, difference.getWcs())
    self.measureForcedSources(diaSources, matchedTemplate, difference.getWcs(),
                              template=True)

# Clear the image plane for regions with NO_DATA.
# These regions are most often caused by insufficient template coverage.
# Do this for the final difference image after detection and measurement
# since the subtasks should all be configured to handle NO_DATA properly
difference.image.array[difference.mask.array & difference.mask.getPlaneBitMask('NO_DATA') > 0] = 0

measurementResults.subtractedMeasuredExposure = difference

if self.config.doMaskStreaks and self.config.writeStreakInfo:
    measurementResults.maskedStreaks = streakInfo.maskedStreaks

if kernelSources is not None:
    self.calculateMetrics(science, difference, diaSources, kernelSources)

if np.count_nonzero(~diaSources["sky_source"]) > 0:
    measurementResults.diaSources = diaSources
elif self.config.raiseOnNoDiaSources:
    raise NoDiaSourcesError()
elif len(diaSources) > 0:
    # This option allows returning sky sources,
    # even if there are no diaSources
    measurementResults.diaSources = diaSources
self.log.info("Measured %d diaSources and %d sky sources",
              np.count_nonzero(~diaSources["sky_source"]),
              np.count_nonzero(diaSources["sky_source"])
              )
return measurementResults

def _reorderPeaksBySignificance(self, diaSources):

Definition at line 1570 of file detectAndMeasure.py.