34 from .repair
import RepairTask
35 from .calibrate
import InitialPsfConfig
38 doRepair = pexConfig.Field(
40 doc =
"Repair images (CR reject and interpolate) before combining",
43 repairPsfFwhm = pexConfig.Field(
45 doc =
"Psf FWHM (pixels) used to detect CRs",
48 doDiffIm = pexConfig.Field(
50 doc =
"Perform difference imaging before combining",
53 doPsfMatch = pexConfig.Field(
55 doc =
"Perform PSF matching for difference imaging (ignored if doDiffIm false)",
58 doMeasurement = pexConfig.Field(
60 doc =
"Measure difference sources (ignored if doDiffIm false)",
63 badMaskPlanes = pexConfig.ListField(
65 doc =
"Mask planes that, if set, the associated pixels are not included in the combined exposure; "
66 "DETECTED excludes cosmic rays",
67 default = (
"DETECTED",),
69 averageKeys = pexConfig.ListField(
71 doc =
"List of float metadata keys to average when combining snaps, e.g. float positions and dates; "
72 "non-float data must be handled by overriding the fixMetadata method",
76 sumKeys = pexConfig.ListField(
78 doc =
"List of float or int metadata keys to sum when combining snaps, e.g. exposure time; "
79 "non-float, non-int data must be handled by overriding the fixMetadata method",
83 repair = pexConfig.ConfigurableField(target = RepairTask, doc =
"")
84 diffim = pexConfig.ConfigurableField(target = SnapPsfMatchTask, doc =
"")
85 detection = pexConfig.ConfigurableField(target = SourceDetectionTask, doc =
"")
86 initialPsf = pexConfig.ConfigField(dtype = InitialPsfConfig, doc =
"")
87 measurement = pexConfig.ConfigurableField(target = SingleFrameMeasurementTask, doc =
"")
90 self.detection.thresholdPolarity =
"both"
93 if self.detection.thresholdPolarity !=
"both":
94 raise ValueError(
"detection.thresholdPolarity must be 'both' for SnapCombineTask")
97 ConfigClass = SnapCombineConfig
98 _DefaultName =
"snapCombine"
101 pipeBase.Task.__init__(self, *args, **kwargs)
102 self.makeSubtask(
"repair")
103 self.makeSubtask(
"diffim")
104 self.
schema = afwTable.SourceTable.makeMinimalSchema()
106 self.makeSubtask(
"detection", schema=self.
schema)
107 if self.config.doMeasurement:
108 self.makeSubtask(
"measurement", schema=self.
schema, algMetadata=self.
algMetadata)
111 def run(self, snap0, snap1, defects=None):
114 @param[in] snap0: snapshot exposure 0
115 @param[in] snap1: snapshot exposure 1
116 @defects[in] defect list (for repair task)
117 @return a pipe_base Struct with fields:
118 - exposure: snap-combined exposure
119 - sources: detected sources, or None if detection not performed
124 if self.config.doRepair:
125 self.log.info(
"snapCombine repair")
126 psf = self.
makeInitialPsf(snap0, fwhmPix=self.config.repairPsfFwhm)
129 self.repair.run(snap0, defects=defects, keepCRs=
False)
130 self.repair.run(snap1, defects=defects, keepCRs=
False)
131 self.display(
'repair0', exposure=snap0)
132 self.display(
'repair1', exposure=snap1)
134 if self.config.doDiffIm:
135 if self.config.doPsfMatch:
136 self.log.info(
"snapCombine psfMatch")
137 diffRet = self.diffim.run(snap0, snap1,
"subtractExposures")
138 diffExp = diffRet.subtractedImage
142 diffKern = diffRet.psfMatchingKernel
143 width, height = diffKern.getDimensions()
148 diffExp = afwImage.ExposureF(snap0,
True)
149 diffMi = diffExp.getMaskedImage()
150 diffMi -= snap1.getMaskedImage()
154 table = afwTable.SourceTable.make(self.
schema)
156 detRet = self.detection.makeSourceCatalog(table, diffExp)
157 sources = detRet.sources
158 fpSets = detRet.fpSets
159 if self.config.doMeasurement:
160 self.measurement.measure(diffExp, sources)
162 mask0 = snap0.getMaskedImage().getMask()
163 mask1 = snap1.getMaskedImage().getMask()
164 fpSets.positive.setMask(mask0,
"DETECTED")
165 fpSets.negative.setMask(mask1,
"DETECTED")
167 maskD = diffExp.getMaskedImage().getMask()
168 fpSets.positive.setMask(maskD,
"DETECTED")
169 fpSets.negative.setMask(maskD,
"DETECTED_NEGATIVE")
171 combinedExp = self.
addSnaps(snap0, snap1)
173 return pipeBase.Struct(
174 exposure = combinedExp,
179 """Add two snap exposures together, returning a new exposure
181 @param[in] snap0 snap exposure 0
182 @param[in] snap1 snap exposure 1
183 @return combined exposure
185 self.log.info(
"snapCombine addSnaps")
187 combinedExp = snap0.Factory(snap0,
True)
188 combinedMi = combinedExp.getMaskedImage()
191 weightMap = combinedMi.getImage().Factory(combinedMi.getBBox())
193 badPixelMask = afwImage.MaskU.getPlaneBitMask(self.config.badMaskPlanes)
194 addToCoadd(combinedMi, weightMap, snap0.getMaskedImage(), badPixelMask, weight)
195 addToCoadd(combinedMi, weightMap, snap1.getMaskedImage(), badPixelMask, weight)
200 combinedMi /= weightMap
206 combinedMetadata = combinedExp.getMetadata()
207 metadata0 = snap0.getMetadata()
208 metadata1 = snap1.getMetadata()
209 self.
fixMetadata(combinedMetadata, metadata0, metadata1)
214 """Fix the metadata of the combined exposure (in place)
216 This implementation handles items specified by config.averageKeys and config.sumKeys,
217 which have data type restrictions. To handle other data types (such as sexagesimal
218 positions and ISO dates) you must supplement this method with your own code.
220 @param[in,out] combinedMetadata metadata of combined exposure;
221 on input this is a deep copy of metadata0 (a PropertySet)
222 @param[in] metadata0 metadata of snap0 (a PropertySet)
223 @param[in] metadata1 metadata of snap1 (a PropertySet)
225 @note the inputs are presently PropertySets due to ticket #2542. However, in some sense
226 they are just PropertyLists that are missing some methods. In particular: comments and order
227 are preserved if you alter an existing value with set(key, value).
230 if self.config.averageKeys:
231 keyDoAvgList += [(key, 1)
for key
in self.config.averageKeys]
232 if self.config.sumKeys:
233 keyDoAvgList += [(key, 0)
for key
in self.config.sumKeys]
234 for key, doAvg
in keyDoAvgList:
235 opStr =
"average" if doAvg
else "sum"
237 val0 = metadata0.get(key)
238 val1 = metadata1.get(key)
240 self.log.warn(
"Could not %s metadata %r: missing from one or both exposures" % (opStr, key,))
244 combinedVal = val0 + val1
248 self.log.warn(
"Could not %s metadata %r: value %r and/or %r not numeric" % \
249 (opStr, key, val0, val1))
252 combinedMetadata.set(key, combinedVal)
255 """Initialise the detection procedure by setting the PSF and WCS
257 @param exposure Exposure to process
260 assert exposure,
"No exposure provided"
261 wcs = exposure.getWcs()
262 assert wcs,
"No wcs in exposure"
265 fwhmPix = self.config.initialPsf.fwhm / wcs.pixelScale().asArcseconds()
267 size = self.config.initialPsf.size
268 model = self.config.initialPsf.model
269 self.log.info(
"installInitialPsf fwhm=%s pixels; size=%s pixels" % (fwhmPix, size))
270 psfCls = getattr(measAlg, model +
"Psf")
271 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