28 __all__ = [
"BaseSelectImagesTask",
"BaseExposureInfo",
"WcsSelectImagesTask",
"PsfWcsSelectImagesTask",
29 "DatabaseSelectImagesConfig"]
33 """Base configuration for subclasses of BaseSelectImagesTask that use a database""" 34 host = pexConfig.Field(
35 doc=
"Database server host name",
38 port = pexConfig.Field(
39 doc=
"Database server port",
42 database = pexConfig.Field(
43 doc=
"Name of database",
46 maxExposures = pexConfig.Field(
47 doc=
"maximum exposures to select; intended for debugging; ignored if None",
54 """Data about a selected exposure 58 """Create exposure information that can be used to generate data references 60 The object has the following fields: 61 - dataId: data ID of exposure (a dict) 62 - coordList: ICRS coordinates of the corners of the exposure (list of lsst.afw.geom.SpherePoint) 63 plus any others items that are desired 65 super(BaseExposureInfo, self).
__init__(dataId=dataId, coordList=coordList)
69 """Base task for selecting images suitable for coaddition 71 ConfigClass = pexConfig.Config
72 _DefaultName =
"selectImages" 75 def run(self, coordList):
76 """Select images suitable for coaddition in a particular region 78 @param[in] coordList: list of coordinates defining region of interest; if None then select all images 79 subclasses may add additional keyword arguments, as required 81 @return a pipeBase Struct containing: 82 - exposureInfoList: a list of exposure information objects (subclasses of BaseExposureInfo), 83 which have at least the following fields: 84 - dataId: data ID dictionary 85 - coordList: ICRS coordinates of the corners of the exposure (list of lsst.afw.geom.SpherePoint) 87 raise NotImplementedError()
89 def _runArgDictFromDataId(self, dataId):
90 """Extract keyword arguments for run (other than coordList) from a data ID 92 @return keyword arguments for run (other than coordList), as a dict 94 raise NotImplementedError()
96 def runDataRef(self, dataRef, coordList, makeDataRefList=True, selectDataList=[]):
97 """Run based on a data reference 99 This delegates to run() and _runArgDictFromDataId() to do the actual 100 selection. In the event that the selectDataList is non-empty, this will 101 be used to further restrict the selection, providing the user with 102 additional control over the selection. 104 @param[in] dataRef: data reference; must contain any extra keys needed by the subclass 105 @param[in] coordList: list of coordinates defining region of interest; if None, search the whole sky 106 @param[in] makeDataRefList: if True, return dataRefList 107 @param[in] selectDataList: List of SelectStruct with dataRefs to consider for selection 108 @return a pipeBase Struct containing: 109 - exposureInfoList: a list of objects derived from ExposureInfo 110 - dataRefList: a list of data references (None if makeDataRefList False) 113 exposureInfoList = self.
run(coordList, **runArgDict).exposureInfoList
115 if len(selectDataList) > 0
and len(exposureInfoList) > 0:
117 ccdKeys, ccdValues = _extractKeyValue(exposureInfoList)
118 inKeys, inValues = _extractKeyValue([s.dataRef
for s
in selectDataList], keys=ccdKeys)
119 inValues = set(inValues)
120 newExposureInfoList = []
121 for info, ccdVal
in zip(exposureInfoList, ccdValues):
122 if ccdVal
in inValues:
123 newExposureInfoList.append(info)
125 self.log.info(
"De-selecting exposure %s: not in selectDataList" % info.dataId)
126 exposureInfoList = newExposureInfoList
129 butler = dataRef.butlerSubset.butler
130 dataRefList = [butler.dataRef(datasetType=
"calexp",
131 dataId=expInfo.dataId,
132 )
for expInfo
in exposureInfoList]
136 return pipeBase.Struct(
137 dataRefList=dataRefList,
138 exposureInfoList=exposureInfoList,
142 def _extractKeyValue(dataList, keys=None):
143 """Extract the keys and values from a list of dataIds 145 The input dataList is a list of objects that have 'dataId' members. 146 This allows it to be used for both a list of data references and a 149 assert len(dataList) > 0
151 keys = sorted(dataList[0].dataId.keys())
154 for data
in dataList:
155 thisKeys = set(data.dataId.keys())
156 if thisKeys != keySet:
157 raise RuntimeError(
"DataId keys inconsistent: %s vs %s" % (keySet, thisKeys))
158 values.append(tuple(data.dataId[k]
for k
in keys))
163 """A container for data to be passed to the WcsSelectImagesTask""" 166 super(SelectStruct, self).
__init__(dataRef=dataRef, wcs=wcs, bbox=bbox)
170 """Select images using their Wcs""" 172 def runDataRef(self, dataRef, coordList, makeDataRefList=True, selectDataList=[]):
173 """Select images in the selectDataList that overlap the patch 175 We use the "convexHull" function in the geom package to define 176 polygons on the celestial sphere, and test the polygon of the 177 patch for overlap with the polygon of the image. 179 We use "convexHull" instead of generating a SphericalConvexPolygon 180 directly because the standard for the inputs to SphericalConvexPolygon 181 are pretty high and we don't want to be responsible for reaching them. 182 If "convexHull" is found to be too slow, we can revise this. 184 @param dataRef: Data reference for coadd/tempExp (with tract, patch) 185 @param coordList: List of ICRS coordinates (lsst.afw.geom.SpherePoint) specifying boundary of patch 186 @param makeDataRefList: Construct a list of data references? 187 @param selectDataList: List of SelectStruct, to consider for selection 192 exposureInfoList = []
194 patchVertices = [coord.getVector()
for coord
in coordList]
195 patchPoly = convexHull(patchVertices)
197 for data
in selectDataList:
198 dataRef = data.dataRef
203 imageCorners = [imageWcs.pixelToSky(pix)
for pix
in afwGeom.Box2D(imageBox).getCorners()]
204 except (pexExceptions.DomainError, pexExceptions.RuntimeError)
as e:
206 self.log.debug(
"WCS error in testing calexp %s (%s): deselecting", dataRef.dataId, e)
209 imagePoly = convexHull([coord.getVector()
for coord
in imageCorners])
210 if imagePoly
is None:
211 self.log.debug(
"Unable to create polygon from image %s: deselecting", dataRef.dataId)
213 if patchPoly.intersects(imagePoly):
214 self.log.info(
"Selecting calexp %s" % dataRef.dataId)
215 dataRefList.append(dataRef)
218 return pipeBase.Struct(
219 dataRefList=dataRefList
if makeDataRefList
else None,
220 exposureInfoList=exposureInfoList,
225 maxEllipResidual = pexConfig.Field(
226 doc=
"Maximum median ellipticity residual",
231 maxSizeScatter = pexConfig.Field(
232 doc=
"Maximum scatter in the size residuals",
236 maxScaledSizeScatter = pexConfig.Field(
237 doc=
"Maximum scatter in the size residuals, scaled by the median size",
242 starSelection = pexConfig.Field(
243 doc=
"select star with this field",
245 default=
'calib_psfUsed' 247 starShape = pexConfig.Field(
248 doc=
"name of star shape",
250 default=
'base_SdssShape' 252 psfShape = pexConfig.Field(
253 doc=
"name of psf shape",
255 default=
'base_SdssShape_psf' 260 "Return median absolute deviation scaled to normally distributed data" 261 return 1.4826*np.median(np.abs(array - np.median(array)))
265 """Select images using their Wcs and cuts on the PSF properties""" 267 ConfigClass = PsfWcsSelectImagesConfig
268 _DefaultName =
"PsfWcsSelectImages" 270 def runDataRef(self, dataRef, coordList, makeDataRefList=True, selectDataList=[]):
271 """Select images in the selectDataList that overlap the patch and satisfy PSF quality critera. 273 The PSF quality criteria are based on the size and ellipticity residuals from the 274 adaptive second moments of the star and the PSF. 277 - the median of the ellipticty residuals 278 - the robust scatter of the size residuals (using the median absolute deviation) 279 - the robust scatter of the size residuals scaled by the square of 282 @param dataRef: Data reference for coadd/tempExp (with tract, patch) 283 @param coordList: List of ICRS coordinates (lsst.afw.geom.SpherePoint) specifying boundary of patch 284 @param makeDataRefList: Construct a list of data references? 285 @param selectDataList: List of SelectStruct, to consider for selection 287 result = super(PsfWcsSelectImagesTask, self).
runDataRef(dataRef, coordList, makeDataRefList,
291 exposureInfoList = []
292 for dataRef, exposureInfo
in zip(result.dataRefList, result.exposureInfoList):
293 butler = dataRef.butlerSubset.butler
294 srcCatalog = butler.get(
'src', dataRef.dataId)
295 mask = srcCatalog[self.config.starSelection]
297 starXX = srcCatalog[self.config.starShape+
'_xx'][mask]
298 starYY = srcCatalog[self.config.starShape+
'_yy'][mask]
299 starXY = srcCatalog[self.config.starShape+
'_xy'][mask]
300 psfXX = srcCatalog[self.config.psfShape+
'_xx'][mask]
301 psfYY = srcCatalog[self.config.psfShape+
'_yy'][mask]
302 psfXY = srcCatalog[self.config.psfShape+
'_xy'][mask]
304 starSize = np.power(starXX*starYY - starXY**2, 0.25)
305 starE1 = (starXX - starYY)/(starXX + starYY)
306 starE2 = 2*starXY/(starXX + starYY)
307 medianSize = np.median(starSize)
309 psfSize = np.power(psfXX*psfYY - psfXY**2, 0.25)
310 psfE1 = (psfXX - psfYY)/(psfXX + psfYY)
311 psfE2 = 2*psfXY/(psfXX + psfYY)
313 medianE1 = np.abs(np.median(starE1 - psfE1))
314 medianE2 = np.abs(np.median(starE2 - psfE2))
315 medianE = np.sqrt(medianE1**2 + medianE2**2)
317 scatterSize =
sigmaMad(starSize - psfSize)
318 scaledScatterSize = scatterSize/medianSize**2
321 if self.config.maxEllipResidual
and medianE > self.config.maxEllipResidual:
322 self.log.info(
"Removing visit %s because median e residual too large: %f vs %f" %
323 (dataRef.dataId, medianE, self.config.maxEllipResidual))
325 elif self.config.maxSizeScatter
and scatterSize > self.config.maxSizeScatter:
326 self.log.info(
"Removing visit %s because size scatter is too large: %f vs %f" %
327 (dataRef.dataId, scatterSize, self.config.maxSizeScatter))
329 elif self.config.maxScaledSizeScatter
and scaledScatterSize > self.config.maxScaledSizeScatter:
330 self.log.info(
"Removing visit %s because scaled size scatter is too large: %f vs %f" %
331 (dataRef.dataId, scaledScatterSize, self.config.maxScaledSizeScatter))
337 dataRefList.append(dataRef)
338 exposureInfoList.append(exposureInfo)
340 return pipeBase.Struct(
341 dataRefList=dataRefList,
342 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)