28 from lsstDebug
import getDebugFrame
36 from .repair
import RepairTask
39 """!Describes the initial PSF used for detection and measurement before we do PSF determination."""
41 model = pexConfig.ChoiceField(
43 doc =
"PSF model type",
44 default =
"SingleGaussian",
46 "SingleGaussian":
"Single Gaussian model",
47 "DoubleGaussian":
"Double Gaussian model",
50 pixelScale = pexConfig.Field(
52 doc =
"Pixel size (arcsec). Only needed if no Wcs is provided",
55 fwhm = pexConfig.Field(
57 doc =
"FWHM of PSF model (arcsec)",
60 size = pexConfig.Field(
62 doc =
"Size of PSF model (pixels)",
67 doRepair = pexConfig.Field(
69 doc =
"Repair images (CR reject and interpolate) before combining",
72 repairPsfFwhm = pexConfig.Field(
74 doc =
"Psf FWHM (pixels) used to detect CRs",
77 doDiffIm = pexConfig.Field(
79 doc =
"Perform difference imaging before combining",
82 doPsfMatch = pexConfig.Field(
84 doc =
"Perform PSF matching for difference imaging (ignored if doDiffIm false)",
87 doMeasurement = pexConfig.Field(
89 doc =
"Measure difference sources (ignored if doDiffIm false)",
92 badMaskPlanes = pexConfig.ListField(
94 doc =
"Mask planes that, if set, the associated pixels are not included in the combined exposure; "
95 "DETECTED excludes cosmic rays",
96 default = (
"DETECTED",),
98 averageKeys = pexConfig.ListField(
100 doc =
"List of float metadata keys to average when combining snaps, e.g. float positions and dates; "
101 "non-float data must be handled by overriding the fixMetadata method",
105 sumKeys = pexConfig.ListField(
107 doc =
"List of float or int metadata keys to sum when combining snaps, e.g. exposure time; "
108 "non-float, non-int data must be handled by overriding the fixMetadata method",
112 repair = pexConfig.ConfigurableField(target = RepairTask, doc =
"")
113 diffim = pexConfig.ConfigurableField(target = SnapPsfMatchTask, doc =
"")
114 detection = pexConfig.ConfigurableField(target = SourceDetectionTask, doc =
"")
115 initialPsf = pexConfig.ConfigField(dtype = InitialPsfConfig, doc =
"")
116 measurement = pexConfig.ConfigurableField(target = SingleFrameMeasurementTask, doc =
"")
119 self.detection.thresholdPolarity =
"both"
122 if self.detection.thresholdPolarity !=
"both":
123 raise ValueError(
"detection.thresholdPolarity must be 'both' for SnapCombineTask")
134 \anchor SnapCombineTask_
136 \brief Combine snaps.
138 \section pipe_tasks_snapcombine_Contents Contents
140 - \ref pipe_tasks_snapcombine_Debug
142 \section pipe_tasks_snapcombine_Debug Debug variables
144 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
145 flag \c -d to import \b debug.py from your \c PYTHONPATH; see <a
146 href="http://lsst-web.ncsa.illinois.edu/~buildbot/doxygen/x_masterDoxyDoc/base_debug.html">
147 Using lsstDebug to control debugging output</a> for more about \b debug.py files.
149 The available variables in SnapCombineTask are:
152 <DD> A dictionary containing debug point names as keys with frame number as value. Valid keys are:
155 <DD> Display the first snap after repairing.
157 <DD> Display the second snap after repairing.
162 ConfigClass = SnapCombineConfig
163 _DefaultName =
"snapCombine"
166 pipeBase.Task.__init__(self, *args, **kwargs)
167 self.makeSubtask(
"repair")
168 self.makeSubtask(
"diffim")
169 self.
schema = afwTable.SourceTable.makeMinimalSchema()
171 self.makeSubtask(
"detection", schema=self.
schema)
172 if self.config.doMeasurement:
173 self.makeSubtask(
"measurement", schema=self.
schema, algMetadata=self.
algMetadata)
176 def run(self, snap0, snap1, defects=None):
179 @param[in] snap0: snapshot exposure 0
180 @param[in] snap1: snapshot exposure 1
181 @defects[in] defect list (for repair task)
182 @return a pipe_base Struct with fields:
183 - exposure: snap-combined exposure
184 - sources: detected sources, or None if detection not performed
189 if self.config.doRepair:
190 self.log.info(
"snapCombine repair")
191 psf = self.
makeInitialPsf(snap0, fwhmPix=self.config.repairPsfFwhm)
194 self.repair.run(snap0, defects=defects, keepCRs=
False)
195 self.repair.run(snap1, defects=defects, keepCRs=
False)
204 if self.config.doDiffIm:
205 if self.config.doPsfMatch:
206 self.log.info(
"snapCombine psfMatch")
207 diffRet = self.diffim.run(snap0, snap1,
"subtractExposures")
208 diffExp = diffRet.subtractedImage
212 diffKern = diffRet.psfMatchingKernel
213 width, height = diffKern.getDimensions()
218 diffExp = afwImage.ExposureF(snap0,
True)
219 diffMi = diffExp.getMaskedImage()
220 diffMi -= snap1.getMaskedImage()
224 table = afwTable.SourceTable.make(self.
schema)
226 detRet = self.detection.makeSourceCatalog(table, diffExp)
227 sources = detRet.sources
228 fpSets = detRet.fpSets
229 if self.config.doMeasurement:
230 self.measurement.measure(diffExp, sources)
232 mask0 = snap0.getMaskedImage().getMask()
233 mask1 = snap1.getMaskedImage().getMask()
234 fpSets.positive.setMask(mask0,
"DETECTED")
235 fpSets.negative.setMask(mask1,
"DETECTED")
237 maskD = diffExp.getMaskedImage().getMask()
238 fpSets.positive.setMask(maskD,
"DETECTED")
239 fpSets.negative.setMask(maskD,
"DETECTED_NEGATIVE")
241 combinedExp = self.
addSnaps(snap0, snap1)
243 return pipeBase.Struct(
244 exposure = combinedExp,
249 """Add two snap exposures together, returning a new exposure
251 @param[in] snap0 snap exposure 0
252 @param[in] snap1 snap exposure 1
253 @return combined exposure
255 self.log.info(
"snapCombine addSnaps")
257 combinedExp = snap0.Factory(snap0,
True)
258 combinedMi = combinedExp.getMaskedImage()
261 weightMap = combinedMi.getImage().Factory(combinedMi.getBBox())
263 badPixelMask = afwImage.MaskU.getPlaneBitMask(self.config.badMaskPlanes)
264 addToCoadd(combinedMi, weightMap, snap0.getMaskedImage(), badPixelMask, weight)
265 addToCoadd(combinedMi, weightMap, snap1.getMaskedImage(), badPixelMask, weight)
270 combinedMi /= weightMap
276 combinedMetadata = combinedExp.getMetadata()
277 metadata0 = snap0.getMetadata()
278 metadata1 = snap1.getMetadata()
279 self.
fixMetadata(combinedMetadata, metadata0, metadata1)
284 """Fix the metadata of the combined exposure (in place)
286 This implementation handles items specified by config.averageKeys and config.sumKeys,
287 which have data type restrictions. To handle other data types (such as sexagesimal
288 positions and ISO dates) you must supplement this method with your own code.
290 @param[in,out] combinedMetadata metadata of combined exposure;
291 on input this is a deep copy of metadata0 (a PropertySet)
292 @param[in] metadata0 metadata of snap0 (a PropertySet)
293 @param[in] metadata1 metadata of snap1 (a PropertySet)
295 @note the inputs are presently PropertySets due to ticket #2542. However, in some sense
296 they are just PropertyLists that are missing some methods. In particular: comments and order
297 are preserved if you alter an existing value with set(key, value).
300 if self.config.averageKeys:
301 keyDoAvgList += [(key, 1)
for key
in self.config.averageKeys]
302 if self.config.sumKeys:
303 keyDoAvgList += [(key, 0)
for key
in self.config.sumKeys]
304 for key, doAvg
in keyDoAvgList:
305 opStr =
"average" if doAvg
else "sum"
307 val0 = metadata0.get(key)
308 val1 = metadata1.get(key)
310 self.log.warn(
"Could not %s metadata %r: missing from one or both exposures" % (opStr, key,))
314 combinedVal = val0 + val1
318 self.log.warn(
"Could not %s metadata %r: value %r and/or %r not numeric" % \
319 (opStr, key, val0, val1))
322 combinedMetadata.set(key, combinedVal)
325 """Initialise the detection procedure by setting the PSF and WCS
327 @param exposure Exposure to process
330 assert exposure,
"No exposure provided"
331 wcs = exposure.getWcs()
332 assert wcs,
"No wcs in exposure"
335 fwhmPix = self.config.initialPsf.fwhm / wcs.pixelScale().asArcseconds()
337 size = self.config.initialPsf.size
338 model = self.config.initialPsf.model
339 self.log.info(
"installInitialPsf fwhm=%s pixels; size=%s pixels" % (fwhmPix, size))
340 psfCls = getattr(measAlg, model +
"Psf")
341 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
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