1 from __future__
import division, absolute_import
30 from .coaddBase
import CoaddBaseTask
31 from .warpAndPsfMatch
import WarpAndPsfMatchTask
32 from .coaddHelpers
import groupPatchExposures, getGroupDataRef
34 __all__ = [
"MakeCoaddTempExpTask"]
37 """Config for MakeCoaddTempExpTask
39 warpAndPsfMatch = pexConfig.ConfigurableField(
40 target = WarpAndPsfMatchTask,
41 doc =
"Task to warp and PSF-match calexp",
43 doWrite = pexConfig.Field(
44 doc =
"persist <coaddName>Coadd_tempExp",
48 doOverwrite = pexConfig.Field(
49 doc =
"overwrite <coaddName>Coadd_tempExp; If False, continue if the file exists on disk",
53 bgSubtracted = pexConfig.Field(
54 doc =
"Work with a background subtracted calexp?",
61 """Task to produce <coaddName>Coadd_tempExp images
63 ConfigClass = MakeCoaddTempExpConfig
64 _DefaultName =
"makeCoaddTempExp"
67 CoaddBaseTask.__init__(self, *args, **kwargs)
68 self.makeSubtask(
"warpAndPsfMatch")
71 def run(self, patchRef, selectDataList=[]):
72 """Produce <coaddName>Coadd_tempExp images
74 <coaddName>Coadd_tempExp are produced by PSF-matching (optional) and warping.
76 @param[in] patchRef: data reference for sky map patch. Must include keys "tract", "patch",
77 plus the camera-specific filter key (e.g. "filter" or "band")
78 @return: dataRefList: a list of data references for the new <coaddName>Coadd_tempExp
80 @warning: this task assumes that all exposures in a coaddTempExp have the same filter.
82 @warning: this task sets the Calib of the coaddTempExp to the Calib of the first calexp
83 with any good pixels in the patch. For a mosaic camera the resulting Calib should be ignored
84 (assembleCoadd should determine zeropoint scaling without referring to it).
86 skyInfo = self.getSkyInfo(patchRef)
88 calExpRefList = self.selectExposures(patchRef, skyInfo, selectDataList=selectDataList)
89 if len(calExpRefList) == 0:
90 self.log.warn(
"No exposures to coadd for patch %s" % patchRef.dataId)
92 self.log.info(
"Selected %d calexps for patch %s" % (len(calExpRefList), patchRef.dataId))
93 calExpRefList = [calExpRef
for calExpRef
in calExpRefList
if calExpRef.datasetExists(
"calexp")]
94 self.log.info(
"Processing %d existing calexps for patch %s" % (len(calExpRefList), patchRef.dataId))
97 self.getTempExpDatasetName())
98 self.log.info(
"Processing %d tempExps for patch %s" % (len(groupData.groups), patchRef.dataId))
101 for i, (tempExpTuple, calexpRefList)
in enumerate(groupData.groups.iteritems()):
102 tempExpRef =
getGroupDataRef(patchRef.getButler(), self.getTempExpDatasetName(),
103 tempExpTuple, groupData.keys)
104 if not self.config.doOverwrite
and tempExpRef.datasetExists(datasetType=self.getTempExpDatasetName()):
105 self.log.info(
"tempCoaddExp %s exists; skipping" % (tempExpRef.dataId,))
106 dataRefList.append(tempExpRef)
108 self.log.info(
"Processing tempExp %d/%d: id=%s" % (i, len(groupData.groups), tempExpRef.dataId))
114 visitId = long(tempExpRef.dataId[
"visit"])
115 except (KeyError, ValueError):
117 inputRecorder = self.inputRecorder.makeCoaddTempExpRecorder(visitId)
119 exp = self.
createTempExp(calexpRefList, skyInfo, inputRecorder)
121 dataRefList.append(tempExpRef)
122 if self.config.doWrite:
123 self.writeCoaddOutput(tempExpRef, exp,
"tempExp")
125 self.log.warn(
"tempExp %s could not be created" % (tempExpRef.dataId,))
129 """Create a tempExp from inputs
131 We iterate over the multiple calexps in a single exposure to construct
132 the warp ("tempExp") of that exposure to the supplied tract/patch.
134 Pixels that receive no pixels are set to NAN; this is not correct
135 (violates LSST algorithms group policy), but will be fixed up by
136 interpolating after the coaddition.
138 @param calexpRefList: List of data references for calexps that (may)
139 overlap the patch of interest
140 @param skyInfo: Struct from CoaddBaseTask.getSkyInfo() with geometric
141 information about the patch
142 @param inputRecorder: CoaddTempExpInputRecorder that builds a catalog
143 of calexps that went into this tempExp.
144 @return warped exposure, or None if no pixels overlap
146 coaddTempExp = afwImage.ExposureF(skyInfo.bbox, skyInfo.wcs)
147 edgeMask = afwImage.MaskU.getPlaneBitMask(
"EDGE")
148 coaddTempExp.getMaskedImage().set(numpy.nan, edgeMask, numpy.inf)
150 didSetMetadata =
False
151 modelPsf = self.config.modelPsf.apply()
if self.config.doPsfMatch
else None
152 for calExpInd, calExpRef
in enumerate(calexpRefList):
153 self.log.info(
"Processing calexp %d of %d for this tempExp: id=%s" %
154 (calExpInd+1, len(calexpRefList), calExpRef.dataId))
156 ccdId = calExpRef.get(
"ccdExposureId", immediate=
True)
161 calExp = self.getCalExp(calExpRef, bgSubtracted=self.config.bgSubtracted)
162 exposure = self.warpAndPsfMatch.run(calExp, modelPsf=modelPsf, wcs=skyInfo.wcs,
163 maxBBox=skyInfo.bbox).exposure
165 coaddTempExp.getMaskedImage(), exposure.getMaskedImage(), self.getBadPixelMask())
166 totGoodPix += numGoodPix
167 self.log.logdebug(
"Calexp %s has %d good pixels in this patch (%.1f%%)" %
168 (calExpRef.dataId, numGoodPix, 100.0*numGoodPix/skyInfo.bbox.getArea()))
169 if numGoodPix > 0
and not didSetMetadata:
170 coaddTempExp.setCalib(exposure.getCalib())
171 coaddTempExp.setFilter(exposure.getFilter())
172 didSetMetadata =
True
174 self.log.warn(
"Error processing calexp %s; skipping it: %s" % (calExpRef.dataId, e))
176 inputRecorder.addCalExp(calExp, ccdId, numGoodPix)
178 inputRecorder.finish(coaddTempExp, totGoodPix)
180 self.log.info(
"coaddTempExp has %d good pixels (%.1f%%)" %
181 (totGoodPix, 100.0*totGoodPix/skyInfo.bbox.getArea()))
182 return coaddTempExp
if totGoodPix > 0
and didSetMetadata
else None
int copyGoodPixels(lsst::afw::image::MaskedImage< ImagePixelT, lsst::afw::image::MaskPixel, lsst::afw::image::VariancePixel > &destImage, lsst::afw::image::MaskedImage< ImagePixelT, lsst::afw::image::MaskPixel, lsst::afw::image::VariancePixel > const &srcImage, lsst::afw::image::MaskPixel const badPixelMask)
copy good pixels from one masked image to another