22 from __future__
import absolute_import, division, print_function
23 from builtins
import zip
30 __all__ = [
"BaseSelectImagesTask",
"BaseExposureInfo",
"WcsSelectImagesTask",
"PsfWcsSelectImagesTask",
31 "DatabaseSelectImagesConfig"]
35 """Base configuration for subclasses of BaseSelectImagesTask that use a database""" 36 host = pexConfig.Field(
37 doc=
"Database server host name",
40 port = pexConfig.Field(
41 doc=
"Database server port",
44 database = pexConfig.Field(
45 doc=
"Name of database",
48 maxExposures = pexConfig.Field(
49 doc=
"maximum exposures to select; intended for debugging; ignored if None",
56 """Data about a selected exposure 60 """Create exposure information that can be used to generate data references 62 The object has the following fields: 63 - dataId: data ID of exposure (a dict) 64 - coordList: ICRS coordinates of the corners of the exposure (list of lsst.afw.geom.SpherePoint) 65 plus any others items that are desired 67 super(BaseExposureInfo, self).
__init__(dataId=dataId, coordList=coordList)
71 """Base task for selecting images suitable for coaddition 73 ConfigClass = pexConfig.Config
74 _DefaultName =
"selectImages" 77 def run(self, coordList):
78 """Select images suitable for coaddition in a particular region 80 @param[in] coordList: list of coordinates defining region of interest; if None then select all images 81 subclasses may add additional keyword arguments, as required 83 @return a pipeBase Struct containing: 84 - exposureInfoList: a list of exposure information objects (subclasses of BaseExposureInfo), 85 which have at least the following fields: 86 - dataId: data ID dictionary 87 - coordList: ICRS coordinates of the corners of the exposure (list of lsst.afw.geom.SpherePoint) 89 raise NotImplementedError()
91 def _runArgDictFromDataId(self, dataId):
92 """Extract keyword arguments for run (other than coordList) from a data ID 94 @return keyword arguments for run (other than coordList), as a dict 96 raise NotImplementedError()
98 def runDataRef(self, dataRef, coordList, makeDataRefList=True, selectDataList=[]):
99 """Run based on a data reference 101 This delegates to run() and _runArgDictFromDataId() to do the actual 102 selection. In the event that the selectDataList is non-empty, this will 103 be used to further restrict the selection, providing the user with 104 additional control over the selection. 106 @param[in] dataRef: data reference; must contain any extra keys needed by the subclass 107 @param[in] coordList: list of coordinates defining region of interest; if None, search the whole sky 108 @param[in] makeDataRefList: if True, return dataRefList 109 @param[in] selectDataList: List of SelectStruct with dataRefs to consider for selection 110 @return a pipeBase Struct containing: 111 - exposureInfoList: a list of objects derived from ExposureInfo 112 - dataRefList: a list of data references (None if makeDataRefList False) 115 exposureInfoList = self.
run(coordList, **runArgDict).exposureInfoList
117 if len(selectDataList) > 0
and len(exposureInfoList) > 0:
119 ccdKeys, ccdValues = _extractKeyValue(exposureInfoList)
120 inKeys, inValues = _extractKeyValue([s.dataRef
for s
in selectDataList], keys=ccdKeys)
121 inValues = set(inValues)
122 newExposureInfoList = []
123 for info, ccdVal
in zip(exposureInfoList, ccdValues):
124 if ccdVal
in inValues:
125 newExposureInfoList.append(info)
127 self.log.info(
"De-selecting exposure %s: not in selectDataList" % info.dataId)
128 exposureInfoList = newExposureInfoList
131 butler = dataRef.butlerSubset.butler
132 dataRefList = [butler.dataRef(datasetType=
"calexp",
133 dataId=expInfo.dataId,
134 )
for expInfo
in exposureInfoList]
138 return pipeBase.Struct(
139 dataRefList=dataRefList,
140 exposureInfoList=exposureInfoList,
144 def _extractKeyValue(dataList, keys=None):
145 """Extract the keys and values from a list of dataIds 147 The input dataList is a list of objects that have 'dataId' members. 148 This allows it to be used for both a list of data references and a 151 assert len(dataList) > 0
153 keys = sorted(dataList[0].dataId.keys())
156 for data
in dataList:
157 thisKeys = set(data.dataId.keys())
158 if thisKeys != keySet:
159 raise RuntimeError(
"DataId keys inconsistent: %s vs %s" % (keySet, thisKeys))
160 values.append(tuple(data.dataId[k]
for k
in keys))
165 """A container for data to be passed to the WcsSelectImagesTask""" 168 super(SelectStruct, self).
__init__(dataRef=dataRef, wcs=wcs, bbox=bbox)
172 """Select images using their Wcs""" 174 def runDataRef(self, dataRef, coordList, makeDataRefList=True, selectDataList=[]):
175 """Select images in the selectDataList that overlap the patch 177 We use the "convexHull" function in the geom package to define 178 polygons on the celestial sphere, and test the polygon of the 179 patch for overlap with the polygon of the image. 181 We use "convexHull" instead of generating a SphericalConvexPolygon 182 directly because the standard for the inputs to SphericalConvexPolygon 183 are pretty high and we don't want to be responsible for reaching them. 184 If "convexHull" is found to be too slow, we can revise this. 186 @param dataRef: Data reference for coadd/tempExp (with tract, patch) 187 @param coordList: List of ICRS coordinates (lsst.afw.geom.SpherePoint) specifying boundary of patch 188 @param makeDataRefList: Construct a list of data references? 189 @param selectDataList: List of SelectStruct, to consider for selection 194 exposureInfoList = []
196 patchVertices = [coord.getVector()
for coord
in coordList]
197 patchPoly = convexHull(patchVertices)
199 for data
in selectDataList:
200 dataRef = data.dataRef
205 imageCorners = [imageWcs.pixelToSky(pix)
for pix
in afwGeom.Box2D(imageBox).getCorners()]
206 except (pexExceptions.DomainError, pexExceptions.RuntimeError)
as e:
208 self.log.debug(
"WCS error in testing calexp %s (%s): deselecting", dataRef.dataId, e)
211 imagePoly = convexHull([coord.getVector()
for coord
in imageCorners])
212 if imagePoly
is None:
213 self.log.debug(
"Unable to create polygon from image %s: deselecting", dataRef.dataId)
215 if patchPoly.intersects(imagePoly):
216 self.log.info(
"Selecting calexp %s" % dataRef.dataId)
217 dataRefList.append(dataRef)
220 return pipeBase.Struct(
221 dataRefList=dataRefList
if makeDataRefList
else None,
222 exposureInfoList=exposureInfoList,
227 maxEllipResidual = pexConfig.Field(
228 doc=
"Maximum median ellipticity residual",
233 maxSizeScatter = pexConfig.Field(
234 doc=
"Maximum scatter in the size residuals",
238 maxScaledSizeScatter = pexConfig.Field(
239 doc=
"Maximum scatter in the size residuals, scaled by the median size",
244 starSelection = pexConfig.Field(
245 doc=
"select star with this field",
247 default=
'calib_psfUsed' 249 starShape = pexConfig.Field(
250 doc=
"name of star shape",
252 default=
'base_SdssShape' 254 psfShape = pexConfig.Field(
255 doc=
"name of psf shape",
257 default=
'base_SdssShape_psf' 262 "Return median absolute deviation scaled to normally distributed data" 263 return 1.4826*np.median(np.abs(array - np.median(array)))
267 """Select images using their Wcs and cuts on the PSF properties""" 269 ConfigClass = PsfWcsSelectImagesConfig
270 _DefaultName =
"PsfWcsSelectImages" 272 def runDataRef(self, dataRef, coordList, makeDataRefList=True, selectDataList=[]):
273 """Select images in the selectDataList that overlap the patch and satisfy PSF quality critera. 275 The PSF quality criteria are based on the size and ellipticity residuals from the 276 adaptive second moments of the star and the PSF. 279 - the median of the ellipticty residuals 280 - the robust scatter of the size residuals (using the median absolute deviation) 281 - the robust scatter of the size residuals scaled by the square of 284 @param dataRef: Data reference for coadd/tempExp (with tract, patch) 285 @param coordList: List of ICRS coordinates (lsst.afw.geom.SpherePoint) specifying boundary of patch 286 @param makeDataRefList: Construct a list of data references? 287 @param selectDataList: List of SelectStruct, to consider for selection 289 result = super(PsfWcsSelectImagesTask, self).
runDataRef(dataRef, coordList, makeDataRefList,
293 exposureInfoList = []
294 for dataRef, exposureInfo
in zip(result.dataRefList, result.exposureInfoList):
295 butler = dataRef.butlerSubset.butler
296 srcCatalog = butler.get(
'src', dataRef.dataId)
297 mask = srcCatalog[self.config.starSelection]
299 starXX = srcCatalog[self.config.starShape+
'_xx'][mask]
300 starYY = srcCatalog[self.config.starShape+
'_yy'][mask]
301 starXY = srcCatalog[self.config.starShape+
'_xy'][mask]
302 psfXX = srcCatalog[self.config.psfShape+
'_xx'][mask]
303 psfYY = srcCatalog[self.config.psfShape+
'_yy'][mask]
304 psfXY = srcCatalog[self.config.psfShape+
'_xy'][mask]
306 starSize = np.power(starXX*starYY - starXY**2, 0.25)
307 starE1 = (starXX - starYY)/(starXX + starYY)
308 starE2 = 2*starXY/(starXX + starYY)
309 medianSize = np.median(starSize)
311 psfSize = np.power(psfXX*psfYY - psfXY**2, 0.25)
312 psfE1 = (psfXX - psfYY)/(psfXX + psfYY)
313 psfE2 = 2*psfXY/(psfXX + psfYY)
315 medianE1 = np.abs(np.median(starE1 - psfE1))
316 medianE2 = np.abs(np.median(starE2 - psfE2))
317 medianE = np.sqrt(medianE1**2 + medianE2**2)
319 scatterSize =
sigmaMad(starSize - psfSize)
320 scaledScatterSize = scatterSize/medianSize**2
323 if self.config.maxEllipResidual
and medianE > self.config.maxEllipResidual:
324 self.log.info(
"Removing visit %s because median e residual too large: %f vs %f" %
325 (dataRef.dataId, medianE, self.config.maxEllipResidual))
327 elif self.config.maxSizeScatter
and scatterSize > self.config.maxSizeScatter:
328 self.log.info(
"Removing visit %s because size scatter is too large: %f vs %f" %
329 (dataRef.dataId, scatterSize, self.config.maxSizeScatter))
331 elif self.config.maxScaledSizeScatter
and scaledScatterSize > self.config.maxScaledSizeScatter:
332 self.log.info(
"Removing visit %s because scaled size scatter is too large: %f vs %f" %
333 (dataRef.dataId, scaledScatterSize, self.config.maxScaledSizeScatter))
339 dataRefList.append(dataRef)
340 exposureInfoList.append(exposureInfo)
342 return pipeBase.Struct(
343 dataRefList=dataRefList,
344 exposureInfoList=exposureInfoList,
def runDataRef(self, dataRef, coordList, makeDataRefList=True, selectDataList=[])
def _runArgDictFromDataId(self, dataId)
def runDataRef(self, dataRef, coordList, makeDataRefList=True, selectDataList=[])
def runDataRef(self, dataRef, coordList, makeDataRefList=True, selectDataList=[])
def __init__(self, dataRef, wcs, bbox)
def __init__(self, dataId, coordList)