lsst.ip.diffim gc0f3af6251+10a3fd39cd
Loading...
Searching...
No Matches
Classes | Variables
lsst.ip.diffim.detectAndMeasure Namespace Reference

Classes

class  DetectAndMeasureConnections
 

Variables

 science : `lsst.afw.image.ExposureF`
 
 matchedTemplate : `lsst.afw.image.ExposureF`
 
 difference : `lsst.afw.image.ExposureF`
 
 idFactory : `lsst.afw.table.IdFactory`, optional
 
 measurementResults : `lsst.pipe.base.Struct`
 
 sources : `lsst.afw.table.SourceCatalog`
 
 positiveFootprints : `lsst.afw.detection.FootprintSet`, optional
 
 negativeFootprints : `lsst.afw.detection.FootprintSet`, optional
 
 diaSources : `lsst.afw.table.SourceCatalog`
 
 mask : `lsst.afw.image.Mask`
 
 seed : `int`
 
 wcs : `lsst.afw.geom.SkyWcs`
 
 spatiallySampledMetrics : `lsst.afw.table.SourceCatalog`, or `None`
 
 src : `lsst.afw.table.SourceRecord`
 
 metricsMaskPlanes : `list` of `str`
 
 scoreExposure : `lsst.afw.image.ExposureF`
 

Variable Documentation

◆ diaSources

lsst.ip.diffim.detectAndMeasure.diaSources : `lsst.afw.table.SourceCatalog`
def makeFootprints(sources):
    footprints = afwDetection.FootprintSet(difference.getBBox())
    footprints.setFootprints([src.getFootprint() for src in sources])
    return footprints

def deblend(footprints):
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):
selector = np.ones(len(diaSources), dtype=bool)
for flag in self.config.badSourceFlags:
    flags = diaSources[flag]
    nBad = np.count_nonzero(flags)
    if nBad > 0:
        self.log.debug("Found %d unphysical sources with flag %s.", nBad, flag)
        selector &= ~flags
nBadTotal = np.count_nonzero(~selector)
self.metadata.add("nRemovedBadFlaggedSources", nBadTotal)
self.log.info("Removed %d unphysical sources.", nBadTotal)
return diaSources[selector].copy(deep=True)

def addSkySources(self, diaSources, mask, seed,
              subtask=None):
if subtask is None:
    subtask = self.skySources
skySourceFootprints = subtask.run(mask=mask, seed=seed, catalog=diaSources)
self.metadata.add(f"n_{subtask.getName()}", len(skySourceFootprints))

def measureDiaSources(self, diaSources, science, difference, matchedTemplate):
# Ensure that the required mask planes are present
for mp in self.config.measurement.plugins["base_PixelFlags"].masksFpAnywhere:
    difference.mask.addMaskPlane(mp)
# Note that this may not be correct if we convolved the science image.
# In the future we may wish to persist the matchedScience image.
self.measurement.run(diaSources, difference, science, matchedTemplate)
if self.config.doApCorr:
    apCorrMap = difference.getInfo().getApCorrMap()
    if apCorrMap is None:
        self.log.warning("Difference image does not have valid aperture correction; skipping.")
    else:
        self.applyApCorr.run(
            catalog=diaSources,
            apCorrMap=apCorrMap,
        )

def measureForcedSources(self, diaSources, science, wcs):

Definition at line 543 of file detectAndMeasure.py.

◆ difference

lsst.ip.diffim.detectAndMeasure.difference : `lsst.afw.image.ExposureF`
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.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)

if self.config.doForcedMeasurement:
    self.measureForcedSources(diaSources, science, difference.getWcs())

spatiallySampledMetrics = self.calculateMetrics(difference, diaSources, science, matchedTemplate,
                                                idFactory)

measurementResults = pipeBase.Struct(
    subtractedMeasuredExposure=difference,
    diaSources=diaSources,
    spatiallySampledMetrics=spatiallySampledMetrics,
)

return measurementResults

def _deblend(self, difference, positiveFootprints, negativeFootprints):
# Run forced psf photometry on the PVI at the diaSource locations.
# Copy the measured flux and error into the diaSource.
forcedSources = self.forcedMeasurement.generateMeasCat(science, diaSources, wcs)
self.forcedMeasurement.run(forcedSources, science, diaSources, wcs)
mapper = afwTable.SchemaMapper(forcedSources.schema, diaSources.schema)
mapper.addMapping(forcedSources.schema.find("base_PsfFlux_instFlux")[0],
                  "ip_diffim_forced_PsfFlux_instFlux", True)
mapper.addMapping(forcedSources.schema.find("base_PsfFlux_instFluxErr")[0],
                  "ip_diffim_forced_PsfFlux_instFluxErr", True)
mapper.addMapping(forcedSources.schema.find("base_PsfFlux_area")[0],
                  "ip_diffim_forced_PsfFlux_area", True)
mapper.addMapping(forcedSources.schema.find("base_PsfFlux_flag")[0],
                  "ip_diffim_forced_PsfFlux_flag", True)
mapper.addMapping(forcedSources.schema.find("base_PsfFlux_flag_noGoodPixels")[0],
                  "ip_diffim_forced_PsfFlux_flag_noGoodPixels", True)
mapper.addMapping(forcedSources.schema.find("base_PsfFlux_flag_edge")[0],
                  "ip_diffim_forced_PsfFlux_flag_edge", True)
for diaSource, forcedSource in zip(diaSources, forcedSources):
    diaSource.assign(forcedSource, mapper)

def calculateMetrics(self, difference, diaSources, science, matchedTemplate, idFactory):

Definition at line 373 of file detectAndMeasure.py.

◆ idFactory

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

Definition at line 375 of file detectAndMeasure.py.

◆ mask

lsst.ip.diffim.detectAndMeasure.mask : `lsst.afw.image.Mask`

Definition at line 573 of file detectAndMeasure.py.

◆ matchedTemplate

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

Definition at line 370 of file detectAndMeasure.py.

◆ measurementResults

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

Definition at line 382 of file detectAndMeasure.py.

◆ metricsMaskPlanes

lsst.ip.diffim.detectAndMeasure.metricsMaskPlanes : `list` of `str`

Definition at line 717 of file detectAndMeasure.py.

◆ negativeFootprints

lsst.ip.diffim.detectAndMeasure.negativeFootprints : `lsst.afw.detection.FootprintSet`, optional

Definition at line 434 of file detectAndMeasure.py.

◆ positiveFootprints

lsst.ip.diffim.detectAndMeasure.positiveFootprints : `lsst.afw.detection.FootprintSet`, optional

Definition at line 432 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?"
)
detection = pexConfig.ConfigurableField(
    target=SourceDetectionTask,
    doc="Final source detection for diaSource measurement",
)
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",
)
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_badCenterAll",
             "base_PixelFlags_flag_edgeCenterAll",
             "base_PixelFlags_flag_saturatedCenterAll",
             ),
)
metricsMaskPlanes = lsst.pex.config.ListField(
    dtype=str,
    doc="List of mask planes to include in metrics",
    default=('BAD', 'CLIPPED', 'CR', 'DETECTED', 'DETECTED_NEGATIVE', 'EDGE',
             'INEXACT_PSF', 'INJECTED', 'INJECTED_TEMPLATE', 'INTRP', 'NOT_DEBLENDED',
             'NO_DATA', 'REJECTED', 'SAT', 'SAT_TEMPLATE', 'SENSOR_EDGE', 'STREAK', 'SUSPECT',
             'UNMASKEDNAN',
             ),
)
metricSources = pexConfig.ConfigurableField(
    target=SkyObjectsTask,
    doc="Generate QA metric sources",
)
doWriteMetrics = lsst.pex.config.Field(
    dtype=bool,
    default=True,
    doc="Compute and write summary metrics."
)
idGenerator = DetectorVisitIdGeneratorConfig.make_field()

def setDefaults(self):
    # DiaSource Detection
    self.detection.thresholdPolarity = "both"
    self.detection.thresholdValue = 5.0
    self.detection.reEstimateBackground = False
    self.detection.thresholdType = "pixel_stdev"
    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.slots.psfShape = "ext_shapeHSM_HsmPsfMoments"
    self.measurement.slots.shape = "ext_shapeHSM_HsmSourceMoments"
    self.measurement.plugins["base_NaiveCentroid"].maxDistToPeak = 5.0
    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"]
    self.measurement.plugins["base_PixelFlags"].masksFpCenter = [
        "STREAK", "INJECTED", "INJECTED_TEMPLATE"]
    self.skySources.avoidMask = ["DETECTED", "DETECTED_NEGATIVE", "BAD", "NO_DATA", "EDGE"]
    self.metricSources.avoidMask = ["NO_DATA", "EDGE"]


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

def __init__(self, **kwargs):
    super().__init__(**kwargs)
    self.schema = afwTable.SourceTable.makeMinimalSchema()
    # Add coordinate error fields:
    afwTable.CoordKey.addErrorFields(self.schema)

    self.algMetadata = dafBase.PropertyList()
    self.makeSubtask("detection", schema=self.schema)
    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.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")
    if self.config.doSkySources:
        self.makeSubtask("skySources", schema=self.schema)

    # 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)

    if self.config.doWriteMetrics:
        self.makeSubtask("metricSources")
        self.metricSchema = afwTable.SourceTable.makeMinimalSchema()
        self.metricSchema.addField(
            "x", "F",
            "X location of the metric evaluation.",
            units="pixel")
        self.metricSchema.addField(
            "y", "F",
            "Y location of the metric evaluation.",
            units="pixel")
        self.metricSources.skySourceKey = self.metricSchema.addField("sky_source", type="Flag",
                                                                     doc="Metric evaluation objects.")
        self.metricSchema.addField(
            "source_density", "F",
            "Density of diaSources at location.",
            units="count/degree^2")
        self.metricSchema.addField(
            "dipole_density", "F",
            "Density of dipoles at location.",
            units="count/degree^2")
        self.metricSchema.addField(
            "dipole_direction", "F",
            "Mean dipole orientation.",
            units="radian")
        self.metricSchema.addField(
            "dipole_separation", "F",
            "Mean dipole separation.",
            units="pixel")
        self.metricSchema.addField(
            "template_value", "F",
            "Median of template at location.",
            units="nJy")
        self.metricSchema.addField(
            "science_value", "F",
            "Median of science at location.",
            units="nJy")
        self.metricSchema.addField(
            "diffim_value", "F",
            "Median of diffim at location.",
            units="nJy")
        self.metricSchema.addField(
            "science_psfSize", "F",
            "Width of the science image PSF at location.",
            units="pixel")
        self.metricSchema.addField(
            "template_psfSize", "F",
            "Width of the template image PSF at location.",
            units="pixel")
        for maskPlane in self.config.metricsMaskPlanes:
            self.metricSchema.addField(
                "%s_mask_fraction"%maskPlane.lower(), "F",
                "Fraction of pixels with %s mask"%maskPlane
            )

    # 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()
    outputs = self.run(**inputs, idFactory=idFactory)
    butlerQC.put(outputs, outputRefs)

@timeMethod
def run(self, science, matchedTemplate, difference,
        idFactory=None):
if idFactory is None:
    idFactory = lsst.meas.base.IdGenerator().make_table_id_factory()

# Ensure that we start with an empty detection mask.
mask = difference.mask
mask &= ~(mask.getPlaneBitMask("DETECTED") | mask.getPlaneBitMask("DETECTED_NEGATIVE"))

# 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,
)

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

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

def processResults(self, science, matchedTemplate, difference, sources, idFactory,
               positiveFootprints=None, negativeFootprints=None,):
bbox = src.getFootprint().getBBox()
pix = bbox.getCenter()
src.set('science_psfSize', getPsfFwhm(science.psf, position=pix))
src.set('template_psfSize', getPsfFwhm(matchedTemplate.psf, position=pix))

metricRegionSize = 100
bbox.grow(metricRegionSize)
bbox = bbox.clippedTo(science.getBBox())
nPix = bbox.getArea()
pixScale = science.wcs.getPixelScale()
area = nPix*pixScale.asDegrees()**2
peak = src.getFootprint().getPeaks()[0]
src.set('x', peak['i_x'])
src.set('y', peak['i_y'])
src.setCoord(science.wcs.pixelToSky(peak['i_x'], peak['i_y']))
selectSources = diaSources[bbox.contains(diaSources.getX(), diaSources.getY())]
if self.config.doSkySources:
    selectSources = selectSources[~selectSources["sky_source"]]
sourceDensity = len(selectSources)/area
dipoleSources = selectSources[selectSources["ip_diffim_DipoleFit_flag_classification"]]
dipoleDensity = len(dipoleSources)/area
if dipoleSources:
    meanDipoleOrientation = angleMean(dipoleSources["ip_diffim_DipoleFit_orientation"])
    src.set('dipole_direction', meanDipoleOrientation)
    meanDipoleSeparation = np.mean(dipoleSources["ip_diffim_DipoleFit_separation"])
    src.set('dipole_separation', meanDipoleSeparation)
templateVal = np.median(matchedTemplate[bbox].image.array)
scienceVal = np.median(science[bbox].image.array)
diffimVal = np.median(difference[bbox].image.array)
src.set('source_density', sourceDensity)
src.set('dipole_density', dipoleDensity)
src.set('template_value', templateVal)
src.set('science_value', scienceVal)
src.set('diffim_value', diffimVal)
for maskPlane in metricsMaskPlanes:
    src.set("%s_mask_fraction"%maskPlane.lower(),
            evaluateMaskFraction(difference.mask[bbox], maskPlane)
            )


class DetectAndMeasureScoreConnections(DetectAndMeasureConnections):
scoreExposure = pipeBase.connectionTypes.Input(
doc="Maximum likelihood image for detection.",
dimensions=("instrument", "visit", "detector"),
storageClass="ExposureF",
name="{fakesType}{coaddName}Diff_scoreExp",
)


class DetectAndMeasureScoreConfig(DetectAndMeasureConfig,
                          pipelineConnections=DetectAndMeasureScoreConnections):
pass


class DetectAndMeasureScoreTask(DetectAndMeasureTask):
ConfigClass = DetectAndMeasureScoreConfig
_DefaultName = "detectAndMeasureScore"

@timeMethod
def run(self, science, matchedTemplate, difference, scoreExposure,
        idFactory=None):

Definition at line 368 of file detectAndMeasure.py.

◆ scoreExposure

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

Definition at line 803 of file detectAndMeasure.py.

◆ seed

lsst.ip.diffim.detectAndMeasure.seed : `int`

Definition at line 575 of file detectAndMeasure.py.

◆ sources

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

Definition at line 427 of file detectAndMeasure.py.

◆ spatiallySampledMetrics

lsst.ip.diffim.detectAndMeasure.spatiallySampledMetrics : `lsst.afw.table.SourceCatalog`, or `None`

Definition at line 665 of file detectAndMeasure.py.

◆ src

lsst.ip.diffim.detectAndMeasure.src : `lsst.afw.table.SourceRecord`
mask = difference.mask
badPix = (mask.array & mask.getPlaneBitMask(self.config.detection.excludeMaskPlanes)) > 0
self.metadata.add("nGoodPixels", np.sum(~badPix))
self.metadata.add("nBadPixels", np.sum(badPix))
detPosPix = (mask.array & mask.getPlaneBitMask("DETECTED")) > 0
detNegPix = (mask.array & mask.getPlaneBitMask("DETECTED_NEGATIVE")) > 0
self.metadata.add("nPixelsDetectedPositive", np.sum(detPosPix))
self.metadata.add("nPixelsDetectedNegative", np.sum(detNegPix))
detPosPix &= badPix
detNegPix &= badPix
self.metadata.add("nBadPixelsDetectedPositive", np.sum(detPosPix))
self.metadata.add("nBadPixelsDetectedNegative", np.sum(detNegPix))
metricsMaskPlanes = []
for maskPlane in self.config.metricsMaskPlanes:
    try:
        self.metadata.add("%s_mask_fraction"%maskPlane.lower(), evaluateMaskFraction(mask, maskPlane))
        metricsMaskPlanes.append(maskPlane)
    except InvalidParameterError:
        self.metadata.add("%s_mask_fraction"%maskPlane.lower(), -1)
        self.log.info("Unable to calculate metrics for mask plane %s: not in image"%maskPlane)

if self.config.doWriteMetrics:
    spatiallySampledMetrics = afwTable.SourceCatalog(self.metricSchema)
    spatiallySampledMetrics.getTable().setIdFactory(idFactory)
    self.addSkySources(spatiallySampledMetrics, science.mask, difference.info.id,
                       subtask=self.metricSources)
    for src in spatiallySampledMetrics:
        self._evaluateLocalMetric(src, diaSources, science, matchedTemplate, difference,
                                  metricsMaskPlanes=metricsMaskPlanes)

    return spatiallySampledMetrics.asAstropy()

def _evaluateLocalMetric(self, src, diaSources, science, matchedTemplate, difference,
                     metricsMaskPlanes):

Definition at line 707 of file detectAndMeasure.py.

◆ wcs

lsst.ip.diffim.detectAndMeasure.wcs : `lsst.afw.geom.SkyWcs`

Definition at line 623 of file detectAndMeasure.py.