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.Mask\
95 .getPlaneBitMask(
"NO_DATA"), np.nan)
99 for patchInfo
in patchList:
100 patchSubBBox = patchInfo.getOuterBBox()
101 patchSubBBox.clip(coaddBBox)
103 datasetType=self.config.coaddName +
"Coadd_sub",
105 tract=tractInfo.getId(),
106 patch=
"%s,%s" % (patchInfo.getIndex()[0], patchInfo.getIndex()[1]),
108 if patchSubBBox.isEmpty():
109 self.log.info(
"skip tract=%(tract)s, patch=%(patch)s; no overlapping pixels" % patchArgDict)
111 if not sensorRef.datasetExists(**patchArgDict):
112 self.log.warn(
"%(datasetType)s, tract=%(tract)s, patch=%(patch)s does not exist"
117 self.log.info(
"Reading patch %s" % patchArgDict)
118 coaddPatch = sensorRef.get(**patchArgDict)
119 coaddExposure.getMaskedImage().assign(coaddPatch.getMaskedImage(), coaddPatch.getBBox())
120 if coaddFilter
is None:
121 coaddFilter = coaddPatch.getFilter()
124 if coaddPsf
is None and coaddPatch.hasPsf():
125 coaddPsf = coaddPatch.getPsf()
127 if nPatchesFound == 0:
128 raise RuntimeError(
"No patches found!")
131 raise RuntimeError(
"No coadd Psf found!")
133 coaddExposure.setPsf(coaddPsf)
134 coaddExposure.setFilter(coaddFilter)
135 return pipeBase.Struct(exposure=coaddExposure,
140 doAddCalexpBackground = pexConfig.Field(
143 doc=
"Add background to calexp before processing it."
148 """Subtask to retrieve calexp of the same ccd number as the science image SensorRef
149 for use as an image difference template.
151 To be run as a subtask by pipe.tasks.ImageDifferenceTask.
152 Intended for use with simulations and surveys that repeatedly visit the same pointing.
153 This code was originally part of Winter2013ImageDifferenceTask.
156 ConfigClass = GetCalexpAsTemplateConfig
157 _DefaultName =
"GetCalexpAsTemplateTask"
159 def run(self, exposure, sensorRef, templateIdList):
160 """!Return a calexp exposure with based on input sensorRef.
162 Construct a dataId based on the sensorRef.dataId combined
163 with the specifications from the first dataId in templateIdList
165 \param[in] exposure -- exposure (unused)
166 \param[in] sensorRef -- a Butler data reference
167 \param[in] templateIdList -- list of data ids, which should contain a single item.
168 If there are multiple items, only the first is used.
170 \return a pipeBase.Struct
171 - exposure: a template calexp
172 - sources: source catalog measured on the template
175 if len(templateIdList) == 0:
176 raise RuntimeError(
"No template supplied! Please supply a template visit id.")
177 if len(templateIdList) > 1:
178 self.log.warn(
"Multiple template visits supplied. Getting template from first visit: %s" %
179 (templateIdList[0][
'visit']))
181 templateId = sensorRef.dataId.copy()
182 templateId.update(templateIdList[0])
184 self.log.info(
"Fetching calexp (%s) as template." % (templateId))
186 butler = sensorRef.getButler()
187 template = butler.get(datasetType=
"calexp", dataId=templateId)
188 if self.config.doAddCalexpBackground:
189 templateBg = butler.get(datasetType=
"calexpBackground", dataId=templateId)
190 mi = template.getMaskedImage()
191 mi += templateBg.getImage()
193 if not template.hasPsf():
194 raise pipeBase.TaskError(
"Template has no psf")
196 templateSources = butler.get(datasetType=
"src", dataId=templateId)
197 return pipeBase.Struct(exposure=template,
198 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.