28 from lsstDebug
import getDebugFrame
36 from .repair
import RepairTask
40 """!Describes the initial PSF used for detection and measurement before we do PSF determination."""
42 model = pexConfig.ChoiceField(
45 default=
"SingleGaussian",
47 "SingleGaussian":
"Single Gaussian model",
48 "DoubleGaussian":
"Double Gaussian model",
51 pixelScale = pexConfig.Field(
53 doc=
"Pixel size (arcsec). Only needed if no Wcs is provided",
56 fwhm = pexConfig.Field(
58 doc=
"FWHM of PSF model (arcsec)",
61 size = pexConfig.Field(
63 doc=
"Size of PSF model (pixels)",
69 doRepair = pexConfig.Field(
71 doc=
"Repair images (CR reject and interpolate) before combining",
74 repairPsfFwhm = pexConfig.Field(
76 doc=
"Psf FWHM (pixels) used to detect CRs",
79 doDiffIm = pexConfig.Field(
81 doc=
"Perform difference imaging before combining",
84 doPsfMatch = pexConfig.Field(
86 doc=
"Perform PSF matching for difference imaging (ignored if doDiffIm false)",
89 doMeasurement = pexConfig.Field(
91 doc=
"Measure difference sources (ignored if doDiffIm false)",
94 badMaskPlanes = pexConfig.ListField(
96 doc=
"Mask planes that, if set, the associated pixels are not included in the combined exposure; "
97 "DETECTED excludes cosmic rays",
98 default=(
"DETECTED",),
100 averageKeys = pexConfig.ListField(
102 doc=
"List of float metadata keys to average when combining snaps, e.g. float positions and dates; "
103 "non-float data must be handled by overriding the fixMetadata method",
107 sumKeys = pexConfig.ListField(
109 doc=
"List of float or int metadata keys to sum when combining snaps, e.g. exposure time; "
110 "non-float, non-int data must be handled by overriding the fixMetadata method",
114 repair = pexConfig.ConfigurableField(target=RepairTask, doc=
"")
115 diffim = pexConfig.ConfigurableField(target=SnapPsfMatchTask, doc=
"")
116 detection = pexConfig.ConfigurableField(target=SourceDetectionTask, doc=
"")
117 initialPsf = pexConfig.ConfigField(dtype=InitialPsfConfig, doc=
"")
118 measurement = pexConfig.ConfigurableField(target=SingleFrameMeasurementTask, doc=
"")
121 self.
detectiondetection.thresholdPolarity =
"both"
124 if self.
detectiondetection.thresholdPolarity !=
"both":
125 raise ValueError(
"detection.thresholdPolarity must be 'both' for SnapCombineTask")
137 \anchor SnapCombineTask_
139 \brief Combine snaps.
141 \section pipe_tasks_snapcombine_Contents Contents
143 - \ref pipe_tasks_snapcombine_Debug
145 \section pipe_tasks_snapcombine_Debug Debug variables
147 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
148 flag \c -d to import \b debug.py from your \c PYTHONPATH; see <a
149 href="https://developer.lsst.io/stack/debug.html">Debugging Tasks with lsstDebug</a> for more
150 about \b debug.py files.
152 The available variables in SnapCombineTask are:
155 <DD> A dictionary containing debug point names as keys with frame number as value. Valid keys are:
158 <DD> Display the first snap after repairing.
160 <DD> Display the second snap after repairing.
165 ConfigClass = SnapCombineConfig
166 _DefaultName =
"snapCombine"
169 pipeBase.Task.__init__(self, *args, **kwargs)
170 self.makeSubtask(
"repair")
171 self.makeSubtask(
"diffim")
172 self.
schemaschema = afwTable.SourceTable.makeMinimalSchema()
174 self.makeSubtask(
"detection", schema=self.
schemaschema)
175 if self.config.doMeasurement:
176 self.makeSubtask(
"measurement", schema=self.
schemaschema, algMetadata=self.
algMetadataalgMetadata)
179 def run(self, snap0, snap1, defects=None):
182 @param[in] snap0: snapshot exposure 0
183 @param[in] snap1: snapshot exposure 1
184 @defects[in] defect list (for repair task)
185 @return a pipe_base Struct with fields:
186 - exposure: snap-combined exposure
187 - sources: detected sources, or None if detection not performed
192 if self.config.doRepair:
193 self.log.
info(
"snapCombine repair")
194 psf = self.
makeInitialPsfmakeInitialPsf(snap0, fwhmPix=self.config.repairPsfFwhm)
197 self.repair.
run(snap0, defects=defects, keepCRs=
False)
198 self.repair.
run(snap1, defects=defects, keepCRs=
False)
207 if self.config.doDiffIm:
208 if self.config.doPsfMatch:
209 self.log.
info(
"snapCombine psfMatch")
210 diffRet = self.diffim.
run(snap0, snap1,
"subtractExposures")
211 diffExp = diffRet.subtractedImage
215 diffKern = diffRet.psfMatchingKernel
216 width, height = diffKern.getDimensions()
219 diffExp = afwImage.ExposureF(snap0,
True)
220 diffMi = diffExp.getMaskedImage()
221 diffMi -= snap1.getMaskedImage()
225 table = afwTable.SourceTable.make(self.
schemaschema)
227 detRet = self.detection.
run(table, diffExp)
228 sources = detRet.sources
229 fpSets = detRet.fpSets
230 if self.config.doMeasurement:
231 self.measurement.
measure(diffExp, sources)
233 mask0 = snap0.getMaskedImage().getMask()
234 mask1 = snap1.getMaskedImage().getMask()
235 fpSets.positive.setMask(mask0,
"DETECTED")
236 fpSets.negative.setMask(mask1,
"DETECTED")
238 maskD = diffExp.getMaskedImage().getMask()
239 fpSets.positive.setMask(maskD,
"DETECTED")
240 fpSets.negative.setMask(maskD,
"DETECTED_NEGATIVE")
242 combinedExp = self.
addSnapsaddSnaps(snap0, snap1)
244 return pipeBase.Struct(
245 exposure=combinedExp,
250 """Add two snap exposures together, returning a new exposure
252 @param[in] snap0 snap exposure 0
253 @param[in] snap1 snap exposure 1
254 @return combined exposure
256 self.log.
info(
"snapCombine addSnaps")
258 combinedExp = snap0.Factory(snap0,
True)
259 combinedMi = combinedExp.getMaskedImage()
262 weightMap = combinedMi.getImage().
Factory(combinedMi.getBBox())
264 badPixelMask = afwImage.Mask.getPlaneBitMask(self.config.badMaskPlanes)
265 addToCoadd(combinedMi, weightMap, snap0.getMaskedImage(), badPixelMask, weight)
266 addToCoadd(combinedMi, weightMap, snap1.getMaskedImage(), badPixelMask, weight)
271 combinedMi /= weightMap
277 combinedMetadata = combinedExp.getMetadata()
278 metadata0 = snap0.getMetadata()
279 metadata1 = snap1.getMetadata()
280 self.
fixMetadatafixMetadata(combinedMetadata, metadata0, metadata1)
285 """Fix the metadata of the combined exposure (in place)
287 This implementation handles items specified by config.averageKeys and config.sumKeys,
288 which have data type restrictions. To handle other data types (such as sexagesimal
289 positions and ISO dates) you must supplement this method with your own code.
291 @param[in,out] combinedMetadata metadata of combined exposure;
292 on input this is a deep copy of metadata0 (a PropertySet)
293 @param[in] metadata0 metadata of snap0 (a PropertySet)
294 @param[in] metadata1 metadata of snap1 (a PropertySet)
296 @note the inputs are presently PropertySets due to ticket #2542. However, in some sense
297 they are just PropertyLists that are missing some methods. In particular: comments and order
298 are preserved if you alter an existing value with set(key, value).
301 if self.config.averageKeys:
302 keyDoAvgList += [(key, 1)
for key
in self.config.averageKeys]
303 if self.config.sumKeys:
304 keyDoAvgList += [(key, 0)
for key
in self.config.sumKeys]
305 for key, doAvg
in keyDoAvgList:
306 opStr =
"average" if doAvg
else "sum"
308 val0 = metadata0.getScalar(key)
309 val1 = metadata1.getScalar(key)
311 self.log.
warning(
"Could not %s metadata %r: missing from one or both exposures", opStr, key)
315 combinedVal = val0 + val1
319 self.log.
warning(
"Could not %s metadata %r: value %r and/or %r not numeric",
320 opStr, key, val0, val1)
323 combinedMetadata.set(key, combinedVal)
326 """Initialise the detection procedure by setting the PSF and WCS
328 @param exposure Exposure to process
331 assert exposure,
"No exposure provided"
332 wcs = exposure.getWcs()
333 assert wcs,
"No wcs in exposure"
336 fwhmPix = self.config.initialPsf.fwhm / wcs.getPixelScale().asArcseconds()
338 size = self.config.initialPsf.size
339 model = self.config.initialPsf.model
340 self.log.
info(
"installInitialPsf fwhm=%s pixels; size=%s pixels", fwhmPix, size)
341 psfCls = getattr(measAlg, model +
"Psf")
342 psf = psfCls(size, size, fwhmPix/(2.0*num.sqrt(2*num.log(2.0))))
Class for storing ordered metadata with comments.
Describes the initial PSF used for detection and measurement before we do PSF determination.
def fixMetadata(self, combinedMetadata, metadata0, metadata1)
def makeInitialPsf(self, exposure, fwhmPix=None)
def run(self, snap0, snap1, defects=None)
def __init__(self, *args, **kwargs)
def addSnaps(self, snap0, snap1)
def mtv(data, frame=None, title="", wcs=None, *args, **kwargs)
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
def measure(mi, x, y, size, statistic, stats)
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations.
def getDebugFrame(debugDisplay, name)