33 from .repair
import RepairTask
34 from .calibrate
import InitialPsfConfig
37 doRepair = pexConfig.Field(
39 doc =
"Repair images (CR reject and interpolate) before combining",
42 repairPsfFwhm = pexConfig.Field(
44 doc =
"Psf FWHM (pixels) used to detect CRs",
47 doDiffIm = pexConfig.Field(
49 doc =
"Perform difference imaging before combining",
52 doPsfMatch = pexConfig.Field(
54 doc =
"Perform PSF matching for difference imaging (ignored if doDiffIm false)",
57 doMeasurement = pexConfig.Field(
59 doc =
"Measure difference sources (ignored if doDiffIm false)",
62 badMaskPlanes = pexConfig.ListField(
64 doc =
"Mask planes that, if set, the associated pixels are not included in the combined exposure; "
65 "DETECTED excludes cosmic rays",
66 default = (
"DETECTED",),
68 averageKeys = pexConfig.ListField(
70 doc =
"List of float metadata keys to average when combining snaps, e.g. float positions and dates; "
71 "non-float data must be handled by overriding the fixMetadata method",
75 sumKeys = pexConfig.ListField(
77 doc =
"List of float or int metadata keys to sum when combining snaps, e.g. exposure time; "
78 "non-float, non-int data must be handled by overriding the fixMetadata method",
82 repair = pexConfig.ConfigurableField(target = RepairTask, doc =
"")
83 diffim = pexConfig.ConfigurableField(target = SnapPsfMatchTask, doc =
"")
84 detection = pexConfig.ConfigurableField(target = SourceDetectionTask, doc =
"")
85 initialPsf = pexConfig.ConfigField(dtype = InitialPsfConfig, doc =
"")
86 measurement = pexConfig.ConfigurableField(target = SourceMeasurementTask, doc =
"")
89 self.detection.thresholdPolarity =
"both"
92 if self.detection.thresholdPolarity !=
"both":
93 raise ValueError(
"detection.thresholdPolarity must be 'both' for SnapCombineTask")
96 ConfigClass = SnapCombineConfig
97 _DefaultName =
"snapCombine"
100 pipeBase.Task.__init__(self, *args, **kwargs)
101 self.makeSubtask(
"repair")
102 self.makeSubtask(
"diffim")
103 self.
schema = afwTable.SourceTable.makeMinimalSchema()
105 self.makeSubtask(
"detection", schema=self.
schema)
106 if self.config.doMeasurement:
107 self.makeSubtask(
"measurement", schema=self.
schema, algMetadata=self.
algMetadata)
110 def run(self, snap0, snap1, defects=None):
113 @param[in] snap0: snapshot exposure 0
114 @param[in] snap1: snapshot exposure 1
115 @defects[in] defect list (for repair task)
116 @return a pipe_base Struct with fields:
117 - exposure: snap-combined exposure
118 - sources: detected sources, or None if detection not performed
123 if self.config.doRepair:
124 self.log.info(
"snapCombine repair")
125 psf = self.
makeInitialPsf(snap0, fwhmPix=self.config.repairPsfFwhm)
128 self.repair.run(snap0, defects=defects, keepCRs=
False)
129 self.repair.run(snap1, defects=defects, keepCRs=
False)
130 self.display(
'repair0', exposure=snap0)
131 self.display(
'repair1', exposure=snap1)
133 if self.config.doDiffIm:
134 if self.config.doPsfMatch:
135 self.log.info(
"snapCombine psfMatch")
136 diffRet = self.diffim.run(snap0, snap1,
"subtractExposures")
137 diffExp = diffRet.subtractedImage
141 diffKern = diffRet.psfMatchingKernel
142 width, height = diffKern.getDimensions()
147 diffExp = afwImage.ExposureF(snap0,
True)
148 diffMi = diffExp.getMaskedImage()
149 diffMi -= snap1.getMaskedImage()
153 table = afwTable.SourceTable.make(self.
schema)
155 detRet = self.detection.makeSourceCatalog(table, diffExp)
156 sources = detRet.sources
157 fpSets = detRet.fpSets
158 if self.config.doMeasurement:
159 self.measurement.measure(diffExp, sources)
161 mask0 = snap0.getMaskedImage().getMask()
162 mask1 = snap1.getMaskedImage().getMask()
163 fpSets.positive.setMask(mask0,
"DETECTED")
164 fpSets.negative.setMask(mask1,
"DETECTED")
166 maskD = diffExp.getMaskedImage().getMask()
167 fpSets.positive.setMask(maskD,
"DETECTED")
168 fpSets.negative.setMask(maskD,
"DETECTED_NEGATIVE")
170 combinedExp = self.
addSnaps(snap0, snap1)
172 return pipeBase.Struct(
173 exposure = combinedExp,
178 """Add two snap exposures together, returning a new exposure
180 @param[in] snap0 snap exposure 0
181 @param[in] snap1 snap exposure 1
182 @return combined exposure
184 self.log.info(
"snapCombine addSnaps")
186 combinedExp = snap0.Factory(snap0,
True)
187 combinedMi = combinedExp.getMaskedImage()
190 weightMap = combinedMi.getImage().Factory(combinedMi.getBBox())
192 badPixelMask = afwImage.MaskU.getPlaneBitMask(self.config.badMaskPlanes)
193 addToCoadd(combinedMi, weightMap, snap0.getMaskedImage(), badPixelMask, weight)
194 addToCoadd(combinedMi, weightMap, snap1.getMaskedImage(), badPixelMask, weight)
199 combinedMi /= weightMap
205 combinedMetadata = combinedExp.getMetadata()
206 metadata0 = snap0.getMetadata()
207 metadata1 = snap1.getMetadata()
208 self.
fixMetadata(combinedMetadata, metadata0, metadata1)
213 """Fix the metadata of the combined exposure (in place)
215 This implementation handles items specified by config.averageKeys and config.sumKeys,
216 which have data type restrictions. To handle other data types (such as sexagesimal
217 positions and ISO dates) you must supplement this method with your own code.
219 @param[in,out] combinedMetadata metadata of combined exposure;
220 on input this is a deep copy of metadata0 (a PropertySet)
221 @param[in] metadata0 metadata of snap0 (a PropertySet)
222 @param[in] metadata1 metadata of snap1 (a PropertySet)
224 @note the inputs are presently PropertySets due to ticket #2542. However, in some sense
225 they are just PropertyLists that are missing some methods. In particular: comments and order
226 are preserved if you alter an existing value with set(key, value).
229 if self.config.averageKeys:
230 keyDoAvgList += [(key, 1)
for key
in self.config.averageKeys]
231 if self.config.sumKeys:
232 keyDoAvgList += [(key, 0)
for key
in self.config.sumKeys]
233 for key, doAvg
in keyDoAvgList:
234 opStr =
"average" if doAvg
else "sum"
236 val0 = metadata0.get(key)
237 val1 = metadata1.get(key)
239 self.log.warn(
"Could not %s metadata %r: missing from one or both exposures" % (opStr, key,))
243 combinedVal = val0 + val1
247 self.log.warn(
"Could not %s metadata %r: value %r and/or %r not numeric" % \
248 (opStr, key, val0, val1))
251 combinedMetadata.set(key, combinedVal)
254 """Initialise the detection procedure by setting the PSF and WCS
256 @param exposure Exposure to process
259 assert exposure,
"No exposure provided"
260 wcs = exposure.getWcs()
261 assert wcs,
"No wcs in exposure"
264 fwhmPix = self.config.initialPsf.fwhm / wcs.pixelScale().asArcseconds()
266 size = self.config.initialPsf.size
267 model = self.config.initialPsf.model
268 self.log.info(
"installInitialPsf fwhm=%s pixels; size=%s pixels" % (fwhmPix, size))
269 psfCls = getattr(measAlg, model +
"Psf")
270 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
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