22 from __future__
import absolute_import, division, print_function
26 import lsst.pex.config
as pexConfig
27 import lsst.pipe.base
as pipeBase
28 import lsst.afw.geom
as afwGeom
29 import lsst.afw.image
as afwImage
31 __all__ = [
"GetCoaddAsTemplateTask",
"GetCoaddAsTemplateConfig",
32 "GetCalexpAsTemplateTask",
"GetCalexpAsTemplateConfig"]
36 templateBorderSize = pexConfig.Field(
39 doc=
"Number of pixels to grow the requested template image to account for warping"
41 coaddName = pexConfig.Field(
42 doc=
"coadd name: typically one of deep or goodSeeing",
49 """Subtask to retrieve coadd for use as an image difference template.
51 This is the default getTemplate Task to be run as a subtask by
52 pipe.tasks.ImageDifferenceTask. The main method is run().
53 It assumes that coadds reside in the repository given by sensorRef.
55 ConfigClass = GetCoaddAsTemplateConfig
56 _DefaultName =
"GetCoaddAsTemplateTask"
58 def run(self, exposure, sensorRef, templateIdList=None):
59 """!Retrieve and mosaic a template coadd exposure that overlaps the exposure
61 \param[in] exposure -- an exposure for which to generate an overlapping template
62 \param[in] sensorRef -- a Butler data reference that can be used to obtain coadd data
63 \param[in] templateIdList -- list of data ids (unused)
65 \return a pipeBase.Struct
66 - exposure: a template coadd exposure assembled out of patches
67 - sources: None for this subtask
69 skyMap = sensorRef.get(datasetType=self.config.coaddName +
"Coadd_skyMap")
70 expWcs = exposure.getWcs()
71 expBoxD = afwGeom.Box2D(exposure.getBBox())
72 expBoxD.grow(self.config.templateBorderSize)
73 ctrSkyPos = expWcs.pixelToSky(expBoxD.getCenter())
74 tractInfo = skyMap.findTract(ctrSkyPos)
75 self.log.info(
"Using skyMap tract %s" % (tractInfo.getId(),))
76 skyCorners = [expWcs.pixelToSky(pixPos)
for pixPos
in expBoxD.getCorners()]
77 patchList = tractInfo.findPatchList(skyCorners)
80 raise RuntimeError(
"No suitable tract found")
81 self.log.info(
"Assembling %s coadd patches" % (len(patchList),))
84 coaddWcs = tractInfo.getWcs()
85 coaddBBox = afwGeom.Box2D()
86 for skyPos
in skyCorners:
87 coaddBBox.include(coaddWcs.skyToPixel(skyPos))
88 coaddBBox = afwGeom.Box2I(coaddBBox)
89 self.log.info(
"exposure dimensions=%s; coadd dimensions=%s" %
90 (exposure.getDimensions(), coaddBBox.getDimensions()))
93 coaddExposure = afwImage.ExposureF(coaddBBox, coaddWcs)
94 coaddExposure.getMaskedImage().set(np.nan, afwImage.MaskU.getPlaneBitMask(
"NO_DATA"), np.nan)
98 for patchInfo
in patchList:
99 patchSubBBox = patchInfo.getOuterBBox()
100 patchSubBBox.clip(coaddBBox)
102 datasetType=self.config.coaddName +
"Coadd_sub",
104 tract=tractInfo.getId(),
105 patch=
"%s,%s" % (patchInfo.getIndex()[0], patchInfo.getIndex()[1]),
107 if patchSubBBox.isEmpty():
108 self.log.info(
"skip tract=%(tract)s, patch=%(patch)s; no overlapping pixels" % patchArgDict)
110 if not sensorRef.datasetExists(**patchArgDict):
111 self.log.warn(
"%(datasetType)s, tract=%(tract)s, patch=%(patch)s does not exist"
116 self.log.info(
"Reading patch %s" % patchArgDict)
117 coaddPatch = sensorRef.get(**patchArgDict)
118 coaddExposure.getMaskedImage().assign(coaddPatch.getMaskedImage(), coaddPatch.getBBox())
119 if coaddFilter
is None:
120 coaddFilter = coaddPatch.getFilter()
123 if coaddPsf
is None and coaddPatch.hasPsf():
124 coaddPsf = coaddPatch.getPsf()
126 if nPatchesFound == 0:
127 raise RuntimeError(
"No patches found!")
130 raise RuntimeError(
"No coadd Psf found!")
132 coaddExposure.setPsf(coaddPsf)
133 coaddExposure.setFilter(coaddFilter)
134 return pipeBase.Struct(exposure=coaddExposure,
139 doAddCalexpBackground = pexConfig.Field(
142 doc=
"Add background to calexp before processing it."
147 """Subtask to retrieve calexp of the same ccd number as the science image SensorRef
148 for use as an image difference template.
150 To be run as a subtask by pipe.tasks.ImageDifferenceTask.
151 Intended for use with simulations and surveys that repeatedly visit the same pointing.
152 This code was originally part of Winter2013ImageDifferenceTask.
155 ConfigClass = GetCalexpAsTemplateConfig
156 _DefaultName =
"GetCalexpAsTemplateTask"
158 def run(self, exposure, sensorRef, templateIdList):
159 """!Return a calexp exposure with based on input sensorRef.
161 Construct a dataId based on the sensorRef.dataId combined
162 with the specifications from the first dataId in templateIdList
164 \param[in] exposure -- exposure (unused)
165 \param[in] sensorRef -- a Butler data reference
166 \param[in] templateIdList -- list of data ids, which should contain a single item.
167 If there are multiple items, only the first is used.
169 \return a pipeBase.Struct
170 - exposure: a template calexp
171 - sources: source catalog measured on the template
174 if len(templateIdList) == 0:
175 raise RuntimeError(
"No template supplied! Please supply a template visit id.")
176 if len(templateIdList) > 1:
177 self.log.warn(
"Multiple template visits supplied. Getting template from first visit: %s" %
178 (templateIdList[0][
'visit']))
180 templateId = sensorRef.dataId.copy()
181 templateId.update(templateIdList[0])
183 self.log.info(
"Fetching calexp (%s) as template." % (templateId))
185 butler = sensorRef.getButler()
186 template = butler.get(datasetType=
"calexp", dataId=templateId)
187 if self.config.doAddCalexpBackground:
188 templateBg = butler.get(datasetType=
"calexpBackground", dataId=templateId)
189 mi = template.getMaskedImage()
190 mi += templateBg.getImage()
192 if not template.hasPsf():
193 raise pipeBase.TaskError(
"Template has no psf")
195 templateSources = butler.get(datasetType=
"src", dataId=templateId)
196 return pipeBase.Struct(exposure=template,
197 sources=templateSources)
def run
Return a calexp exposure with based on input sensorRef.
def run
Retrieve and mosaic a template coadd exposure that overlaps the exposure.