28 from lsstDebug
import getDebugFrame
35 from lsst.utils.timer
import timeMethod
37 from .repair
import RepairTask
41 """!Describes the initial PSF used for detection and measurement before we do PSF determination."""
43 model = pexConfig.ChoiceField(
46 default=
"SingleGaussian",
48 "SingleGaussian":
"Single Gaussian model",
49 "DoubleGaussian":
"Double Gaussian model",
52 pixelScale = pexConfig.Field(
54 doc=
"Pixel size (arcsec). Only needed if no Wcs is provided",
57 fwhm = pexConfig.Field(
59 doc=
"FWHM of PSF model (arcsec)",
62 size = pexConfig.Field(
64 doc=
"Size of PSF model (pixels)",
70 doRepair = pexConfig.Field(
72 doc=
"Repair images (CR reject and interpolate) before combining",
75 repairPsfFwhm = pexConfig.Field(
77 doc=
"Psf FWHM (pixels) used to detect CRs",
80 doDiffIm = pexConfig.Field(
82 doc=
"Perform difference imaging before combining",
85 doPsfMatch = pexConfig.Field(
87 doc=
"Perform PSF matching for difference imaging (ignored if doDiffIm false)",
90 doMeasurement = pexConfig.Field(
92 doc=
"Measure difference sources (ignored if doDiffIm false)",
95 badMaskPlanes = pexConfig.ListField(
97 doc=
"Mask planes that, if set, the associated pixels are not included in the combined exposure; "
98 "DETECTED excludes cosmic rays",
99 default=(
"DETECTED",),
101 averageKeys = pexConfig.ListField(
103 doc=
"List of float metadata keys to average when combining snaps, e.g. float positions and dates; "
104 "non-float data must be handled by overriding the fixMetadata method",
108 sumKeys = pexConfig.ListField(
110 doc=
"List of float or int metadata keys to sum when combining snaps, e.g. exposure time; "
111 "non-float, non-int data must be handled by overriding the fixMetadata method",
115 repair = pexConfig.ConfigurableField(target=RepairTask, doc=
"")
116 diffim = pexConfig.ConfigurableField(target=SnapPsfMatchTask, doc=
"")
117 detection = pexConfig.ConfigurableField(target=SourceDetectionTask, doc=
"")
118 initialPsf = pexConfig.ConfigField(dtype=InitialPsfConfig, doc=
"")
119 measurement = pexConfig.ConfigurableField(target=SingleFrameMeasurementTask, doc=
"")
122 self.
detectiondetection.thresholdPolarity =
"both"
125 if self.
detectiondetection.thresholdPolarity !=
"both":
126 raise ValueError(
"detection.thresholdPolarity must be 'both' for SnapCombineTask")
138 \anchor SnapCombineTask_
140 \brief Combine snaps.
142 \section pipe_tasks_snapcombine_Contents Contents
144 - \ref pipe_tasks_snapcombine_Debug
146 \section pipe_tasks_snapcombine_Debug Debug variables
148 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
149 flag \c -d to import \b debug.py from your \c PYTHONPATH; see <a
150 href="https://developer.lsst.io/stack/debug.html">Debugging Tasks with lsstDebug</a> for more
151 about \b debug.py files.
153 The available variables in SnapCombineTask are:
156 <DD> A dictionary containing debug point names as keys with frame number as value. Valid keys are:
159 <DD> Display the first snap after repairing.
161 <DD> Display the second snap after repairing.
166 ConfigClass = SnapCombineConfig
167 _DefaultName =
"snapCombine"
170 pipeBase.Task.__init__(self, *args, **kwargs)
171 self.makeSubtask(
"repair")
172 self.makeSubtask(
"diffim")
173 self.
schemaschema = afwTable.SourceTable.makeMinimalSchema()
175 self.makeSubtask(
"detection", schema=self.
schemaschema)
176 if self.config.doMeasurement:
177 self.makeSubtask(
"measurement", schema=self.
schemaschema, algMetadata=self.
algMetadataalgMetadata)
180 def run(self, snap0, snap1, defects=None):
183 @param[in] snap0: snapshot exposure 0
184 @param[in] snap1: snapshot exposure 1
185 @defects[in] defect list (for repair task)
186 @return a pipe_base Struct with fields:
187 - exposure: snap-combined exposure
188 - sources: detected sources, or None if detection not performed
193 if self.config.doRepair:
194 self.log.
info(
"snapCombine repair")
195 psf = self.
makeInitialPsfmakeInitialPsf(snap0, fwhmPix=self.config.repairPsfFwhm)
198 self.repair.
run(snap0, defects=defects, keepCRs=
False)
199 self.repair.
run(snap1, defects=defects, keepCRs=
False)
208 if self.config.doDiffIm:
209 if self.config.doPsfMatch:
210 self.log.
info(
"snapCombine psfMatch")
211 diffRet = self.diffim.
run(snap0, snap1,
"subtractExposures")
212 diffExp = diffRet.subtractedImage
216 diffKern = diffRet.psfMatchingKernel
217 width, height = diffKern.getDimensions()
220 diffExp = afwImage.ExposureF(snap0,
True)
221 diffMi = diffExp.getMaskedImage()
222 diffMi -= snap1.getMaskedImage()
226 table = afwTable.SourceTable.make(self.
schemaschema)
228 detRet = self.detection.
run(table, diffExp)
229 sources = detRet.sources
230 fpSets = detRet.fpSets
231 if self.config.doMeasurement:
232 self.measurement.
measure(diffExp, sources)
234 mask0 = snap0.getMaskedImage().getMask()
235 mask1 = snap1.getMaskedImage().getMask()
236 fpSets.positive.setMask(mask0,
"DETECTED")
237 fpSets.negative.setMask(mask1,
"DETECTED")
239 maskD = diffExp.getMaskedImage().getMask()
240 fpSets.positive.setMask(maskD,
"DETECTED")
241 fpSets.negative.setMask(maskD,
"DETECTED_NEGATIVE")
243 combinedExp = self.
addSnapsaddSnaps(snap0, snap1)
245 return pipeBase.Struct(
246 exposure=combinedExp,
251 """Add two snap exposures together, returning a new exposure
253 @param[in] snap0 snap exposure 0
254 @param[in] snap1 snap exposure 1
255 @return combined exposure
257 self.log.
info(
"snapCombine addSnaps")
259 combinedExp = snap0.Factory(snap0,
True)
260 combinedMi = combinedExp.getMaskedImage()
263 weightMap = combinedMi.getImage().
Factory(combinedMi.getBBox())
265 badPixelMask = afwImage.Mask.getPlaneBitMask(self.config.badMaskPlanes)
266 addToCoadd(combinedMi, weightMap, snap0.getMaskedImage(), badPixelMask, weight)
267 addToCoadd(combinedMi, weightMap, snap1.getMaskedImage(), badPixelMask, weight)
272 combinedMi /= weightMap
278 combinedMetadata = combinedExp.getMetadata()
279 metadata0 = snap0.getMetadata()
280 metadata1 = snap1.getMetadata()
281 self.
fixMetadatafixMetadata(combinedMetadata, metadata0, metadata1)
286 """Fix the metadata of the combined exposure (in place)
288 This implementation handles items specified by config.averageKeys and config.sumKeys,
289 which have data type restrictions. To handle other data types (such as sexagesimal
290 positions and ISO dates) you must supplement this method with your own code.
292 @param[in,out] combinedMetadata metadata of combined exposure;
293 on input this is a deep copy of metadata0 (a PropertySet)
294 @param[in] metadata0 metadata of snap0 (a PropertySet)
295 @param[in] metadata1 metadata of snap1 (a PropertySet)
297 @note the inputs are presently PropertySets due to ticket #2542. However, in some sense
298 they are just PropertyLists that are missing some methods. In particular: comments and order
299 are preserved if you alter an existing value with set(key, value).
302 if self.config.averageKeys:
303 keyDoAvgList += [(key, 1)
for key
in self.config.averageKeys]
304 if self.config.sumKeys:
305 keyDoAvgList += [(key, 0)
for key
in self.config.sumKeys]
306 for key, doAvg
in keyDoAvgList:
307 opStr =
"average" if doAvg
else "sum"
309 val0 = metadata0.getScalar(key)
310 val1 = metadata1.getScalar(key)
312 self.log.
warning(
"Could not %s metadata %r: missing from one or both exposures", opStr, key)
316 combinedVal = val0 + val1
320 self.log.
warning(
"Could not %s metadata %r: value %r and/or %r not numeric",
321 opStr, key, val0, val1)
324 combinedMetadata.set(key, combinedVal)
327 """Initialise the detection procedure by setting the PSF and WCS
329 @param exposure Exposure to process
332 assert exposure,
"No exposure provided"
333 wcs = exposure.getWcs()
334 assert wcs,
"No wcs in exposure"
337 fwhmPix = self.config.initialPsf.fwhm / wcs.getPixelScale().asArcseconds()
339 size = self.config.initialPsf.size
340 model = self.config.initialPsf.model
341 self.log.
info(
"installInitialPsf fwhm=%s pixels; size=%s pixels", fwhmPix, size)
342 psfCls = getattr(measAlg, model +
"Psf")
343 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)