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.detection.thresholdPolarity =
"both"
124 if self.detection.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="http://lsst-web.ncsa.illinois.edu/~buildbot/doxygen/x_masterDoxyDoc/base_debug.html">
150 Using lsstDebug to control debugging output</a> for more 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.
schema = afwTable.SourceTable.makeMinimalSchema()
174 self.makeSubtask(
"detection", schema=self.
schema)
175 if self.config.doMeasurement:
176 self.makeSubtask(
"measurement", schema=self.
schema, algMetadata=self.
algMetadata)
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.
makeInitialPsf(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()
221 diffExp = afwImage.ExposureF(snap0,
True)
222 diffMi = diffExp.getMaskedImage()
223 diffMi -= snap1.getMaskedImage()
227 table = afwTable.SourceTable.make(self.
schema)
229 detRet = self.detection.makeSourceCatalog(table, diffExp)
230 sources = detRet.sources
231 fpSets = detRet.fpSets
232 if self.config.doMeasurement:
233 self.measurement.measure(diffExp, sources)
235 mask0 = snap0.getMaskedImage().getMask()
236 mask1 = snap1.getMaskedImage().getMask()
237 fpSets.positive.setMask(mask0,
"DETECTED")
238 fpSets.negative.setMask(mask1,
"DETECTED")
240 maskD = diffExp.getMaskedImage().getMask()
241 fpSets.positive.setMask(maskD,
"DETECTED")
242 fpSets.negative.setMask(maskD,
"DETECTED_NEGATIVE")
244 combinedExp = self.
addSnaps(snap0, snap1)
246 return pipeBase.Struct(
247 exposure=combinedExp,
252 """Add two snap exposures together, returning a new exposure
254 @param[in] snap0 snap exposure 0
255 @param[in] snap1 snap exposure 1
256 @return combined exposure
258 self.log.info(
"snapCombine addSnaps")
260 combinedExp = snap0.Factory(snap0,
True)
261 combinedMi = combinedExp.getMaskedImage()
264 weightMap = combinedMi.getImage().Factory(combinedMi.getBBox())
266 badPixelMask = afwImage.MaskU.getPlaneBitMask(self.config.badMaskPlanes)
267 addToCoadd(combinedMi, weightMap, snap0.getMaskedImage(), badPixelMask, weight)
268 addToCoadd(combinedMi, weightMap, snap1.getMaskedImage(), badPixelMask, weight)
273 combinedMi /= weightMap
279 combinedMetadata = combinedExp.getMetadata()
280 metadata0 = snap0.getMetadata()
281 metadata1 = snap1.getMetadata()
282 self.
fixMetadata(combinedMetadata, metadata0, metadata1)
287 """Fix the metadata of the combined exposure (in place)
289 This implementation handles items specified by config.averageKeys and config.sumKeys,
290 which have data type restrictions. To handle other data types (such as sexagesimal
291 positions and ISO dates) you must supplement this method with your own code.
293 @param[in,out] combinedMetadata metadata of combined exposure;
294 on input this is a deep copy of metadata0 (a PropertySet)
295 @param[in] metadata0 metadata of snap0 (a PropertySet)
296 @param[in] metadata1 metadata of snap1 (a PropertySet)
298 @note the inputs are presently PropertySets due to ticket #2542. However, in some sense
299 they are just PropertyLists that are missing some methods. In particular: comments and order
300 are preserved if you alter an existing value with set(key, value).
303 if self.config.averageKeys:
304 keyDoAvgList += [(key, 1)
for key
in self.config.averageKeys]
305 if self.config.sumKeys:
306 keyDoAvgList += [(key, 0)
for key
in self.config.sumKeys]
307 for key, doAvg
in keyDoAvgList:
308 opStr =
"average" if doAvg
else "sum"
310 val0 = metadata0.get(key)
311 val1 = metadata1.get(key)
313 self.log.warn(
"Could not %s metadata %r: missing from one or both exposures" % (opStr, key,))
317 combinedVal = val0 + val1
321 self.log.warn(
"Could not %s metadata %r: value %r and/or %r not numeric" %
322 (opStr, key, val0, val1))
325 combinedMetadata.set(key, combinedVal)
328 """Initialise the detection procedure by setting the PSF and WCS
330 @param exposure Exposure to process
333 assert exposure,
"No exposure provided"
334 wcs = exposure.getWcs()
335 assert wcs,
"No wcs in exposure"
338 fwhmPix = self.config.initialPsf.fwhm / wcs.pixelScale().asArcseconds()
340 size = self.config.initialPsf.size
341 model = self.config.initialPsf.model
342 self.log.info(
"installInitialPsf fwhm=%s pixels; size=%s pixels" % (fwhmPix, size))
343 psfCls = getattr(measAlg, model +
"Psf")
344 psf = psfCls(size, size, fwhmPix/(2.0*num.sqrt(2*num.log(2.0))))
Class for storing ordered metadata with comments.
void setCoaddEdgeBits(lsst::afw::image::Mask< lsst::afw::image::MaskPixel > &coaddMask, lsst::afw::image::Image< WeightPixelT > const &weightMap)
set edge bits of coadd mask based on weight map
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
Describes the initial PSF used for detection and measurement before we do PSF determination.
lsst::afw::geom::Box2I addToCoadd(lsst::afw::image::Image< CoaddPixelT > &coadd, lsst::afw::image::Image< WeightPixelT > &weightMap, lsst::afw::image::Image< CoaddPixelT > const &image, WeightPixelT weight)
add good pixels from an image to a coadd and associated weight map