22 from __future__
import absolute_import, division, print_function
24 from builtins
import zip
35 starSelector = measAlg.starSelectorRegistry.makeField(
"Star selection algorithm", default=
"objectSize")
36 psfDeterminer = measAlg.psfDeterminerRegistry.makeField(
"PSF Determination algorithm", default=
"pca")
37 reserve = pexConfig.ConfigurableField(target=measAlg.ReserveSourcesTask,
38 doc=
"Reserve sources from fitting")
50 \anchor MeasurePsfTask_ 52 \brief Measure the PSF 54 \section pipe_tasks_measurePsf_Contents Contents 56 - \ref pipe_tasks_measurePsf_Purpose 57 - \ref pipe_tasks_measurePsf_Initialize 58 - \ref pipe_tasks_measurePsf_IO 59 - \ref pipe_tasks_measurePsf_Config 60 - \ref pipe_tasks_measurePsf_Debug 61 - \ref pipe_tasks_measurePsf_Example 63 \section pipe_tasks_measurePsf_Purpose Description 65 A task that selects stars from a catalog of sources and uses those to measure the PSF. 67 The star selector is a subclass of 68 \ref lsst.meas.algorithms.starSelector.BaseStarSelectorTask "lsst.meas.algorithms.BaseStarSelectorTask" 69 and the PSF determiner is a sublcass of 70 \ref lsst.meas.algorithms.psfDeterminer.BasePsfDeterminerTask "lsst.meas.algorithms.BasePsfDeterminerTask" 73 There is no establised set of configuration parameters for these algorithms, so once you start modifying 74 parameters (as we do in \ref pipe_tasks_measurePsf_Example) your code is no longer portable. 76 \section pipe_tasks_measurePsf_Initialize Task initialisation 80 \section pipe_tasks_measurePsf_IO Invoking the Task 84 \section pipe_tasks_measurePsf_Config Configuration parameters 86 See \ref MeasurePsfConfig. 88 \section pipe_tasks_measurePsf_Debug Debug variables 90 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a 91 flag \c -d to import \b debug.py from your \c PYTHONPATH; see \ref baseDebug for more about \b debug.py files. 95 <DD> If True, display debugging plots 97 <DD> display the Exposure + spatialCells 98 <DT> displayPsfCandidates 99 <DD> show mosaic of candidates 100 <DT> showBadCandidates 101 <DD> Include bad candidates 102 <DT> displayPsfMosaic 103 <DD> show mosaic of reconstructed PSF(xy) 104 <DT> displayResiduals 106 <DT> normalizeResiduals 107 <DD> Normalise residuals by object amplitude 110 Additionally you can enable any debug outputs that your chosen star selector and psf determiner support. 112 \section pipe_tasks_measurePsf_Example A complete example of using MeasurePsfTask 114 This code is in \link measurePsfTask.py\endlink in the examples directory, and can be run as \em e.g. 116 examples/measurePsfTask.py --ds9 118 \dontinclude measurePsfTask.py 120 The example also runs SourceDetectionTask and SourceMeasurementTask; 121 see \ref meas_algorithms_measurement_Example for more explanation. 123 Import the tasks (there are some other standard imports; read the file to see them all): 125 \skip SourceDetectionTask 126 \until MeasurePsfTask 128 We need to create the tasks before processing any data as the task constructor 129 can add an extra column to the schema, but first we need an almost-empty 132 \skipline makeMinimalSchema 134 We can now call the constructors for the tasks we need to find and characterize candidate 137 \skip SourceDetectionTask.ConfigClass 140 Note that we've chosen a minimal set of measurement plugins: we need the 141 outputs of \c base_SdssCentroid, \c base_SdssShape and \c base_CircularApertureFlux as 142 inputs to the PSF measurement algorithm, while \c base_PixelFlags identifies 143 and flags bad sources (e.g. with pixels too close to the edge) so they can be 146 Now we can create and configure the task that we're interested in: 149 \until measurePsfTask 151 We're now ready to process the data (we could loop over multiple exposures/catalogues using the same 152 task objects). First create the output table: 156 And process the image: 161 We can then unpack and use the results: 166 If you specified \c --ds9 you can see the PSF candidates: 173 To investigate the \ref pipe_tasks_measurePsf_Debug, put something like 177 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively 179 if name == "lsst.pipe.tasks.measurePsf" : 181 di.displayExposure = False # display the Exposure + spatialCells 182 di.displayPsfCandidates = True # show mosaic of candidates 183 di.displayPsfMosaic = True # show mosaic of reconstructed PSF(xy) 184 di.displayResiduals = True # show residuals 185 di.showBadCandidates = True # Include bad candidates 186 di.normalizeResiduals = False # Normalise residuals by object amplitude 190 lsstDebug.Info = DebugInfo 192 into your debug.py file and run measurePsfTask.py with the \c --debug flag. 194 ConfigClass = MeasurePsfConfig
195 _DefaultName =
"measurePsf" 198 """!Create the detection task. Most arguments are simply passed onto pipe.base.Task. 200 \param schema An lsst::afw::table::Schema used to create the output lsst.afw.table.SourceCatalog 201 \param **kwargs Keyword arguments passed to lsst.pipe.base.task.Task.__init__. 203 If schema is not None, 'calib.psf.candidate' and 'calib.psf.used' fields will be added to 204 identify which stars were employed in the PSF estimation. 206 \note This task can add fields to the schema, so any code calling this task must ensure that 207 these fields are indeed present in the input table. 210 pipeBase.Task.__init__(self, **kwargs)
211 if schema
is not None:
213 "calib_psfCandidate", type=
"Flag",
214 doc=(
"Flag set if the source was a candidate for PSF determination, " 215 "as determined by the star selector.")
218 "calib_psfUsed", type=
"Flag",
219 doc=(
"Flag set if the source was actually used for PSF determination, " 220 "as determined by the '%s' PSF determiner.") % self.config.psfDeterminer.name
225 self.makeSubtask(
"starSelector", schema=schema)
226 self.makeSubtask(
"psfDeterminer", schema=schema)
227 self.makeSubtask(
"reserve", columnName=
"calib_psf", schema=schema,
228 doc=
"set if source was reserved from PSF determination")
231 def run(self, exposure, sources, expId=0, matches=None):
234 \param[in,out] exposure Exposure to process; measured PSF will be added. 235 \param[in,out] sources Measured sources on exposure; flag fields will be set marking 236 stars chosen by the star selector and the PSF determiner if a schema 237 was passed to the task constructor. 238 \param[in] expId Exposure id used for generating random seed. 239 \param[in] matches A list of lsst.afw.table.ReferenceMatch objects 240 (\em i.e. of lsst.afw.table.Match 241 with \c first being of type lsst.afw.table.SimpleRecord and \c second 242 type lsst.afw.table.SourceRecord --- the reference object and detected 243 object respectively) as returned by \em e.g. the AstrometryTask. 244 Used by star selectors that choose to refer to an external catalog. 246 \return a pipe.base.Struct with fields: 247 - psf: The measured PSF (also set in the input exposure) 248 - cellSet: an lsst.afw.math.SpatialCellSet containing the PSF candidates 249 as returned by the psf determiner. 251 self.log.info(
"Measuring PSF")
257 displayPsfCandidates =
lsstDebug.Info(__name__).displayPsfCandidates
265 selectionResult = self.starSelector.
run(exposure=exposure, sourceCat=sources, matches=matches)
266 reserveResult = self.reserve.
run(selectionResult.starCat, expId=expId)
267 psfCandidateList = [cand
for cand, use
268 in zip(selectionResult.psfCandidates, reserveResult.use)
if use]
271 for cand
in psfCandidateList:
272 source = cand.getSource()
275 self.log.info(
"PSF star selector found %d candidates" % len(psfCandidateList))
280 ds9.mtv(exposure, frame=frame, title=
"psf determination")
285 psf, cellSet = self.psfDeterminer.determinePsf(exposure, psfCandidateList, self.metadata,
287 self.log.info(
"PSF determination using %d/%d stars." %
288 (self.metadata.get(
"numGoodStars"), self.metadata.get(
"numAvailStars")))
298 if displayPsfCandidates:
304 showBadCandidates=showBadCandidates,
305 normalizeResiduals=normalizeResiduals,
308 maUtils.showPsfMosaic(exposure, psf, frame=frame, showFwhm=
True)
309 ds9.scale(0, 1,
"linear", frame=frame)
312 return pipeBase.Struct(
319 """Return True if this task makes use of the "matches" argument to the run method""" 320 return self.starSelector.usesMatches
328 maUtils.showPsfSpatialCells(exposure, cellSet,
329 symb=
"o", ctype=ds9.CYAN, ctypeUnused=ds9.YELLOW,
331 for cell
in cellSet.getCellList():
332 for cand
in cell.begin(
not showBadCandidates):
333 status = cand.getStatus()
334 ds9.dot(
'+', *cand.getSource().getCentroid(), frame=frame,
335 ctype=ds9.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else 336 ds9.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else ds9.RED)
343 for cell
in cellSet.getCellList():
344 for cand
in cell.begin(
not showBadCandidates):
346 im = cand.getMaskedImage()
348 chi2 = cand.getChi2()
354 stamps.append((im,
"%d%s" %
355 (maUtils.splitId(cand.getSource().getId(),
True)[
"objId"], chi2),
360 mos = displayUtils.Mosaic()
361 for im, label, status
in stamps:
362 im = type(im)(im,
True)
364 im /= afwMath.makeStatistics(im, afwMath.MAX).getValue()
365 except NotImplementedError:
368 mos.append(im, label,
369 ds9.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else 370 ds9.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else ds9.RED)
373 mos.makeMosaic(frame=frame, title=
"Psf Candidates")
376 def plotResiduals(exposure, cellSet, showBadCandidates=False, normalizeResiduals=True, frame=2):
377 psf = exposure.getPsf()
380 maUtils.showPsfCandidates(exposure, cellSet, psf=psf, frame=frame,
381 normalize=normalizeResiduals,
382 showBadCandidates=showBadCandidates)
384 maUtils.showPsfCandidates(exposure, cellSet, psf=psf, frame=frame,
385 normalize=normalizeResiduals,
386 showBadCandidates=showBadCandidates,
390 if not showBadCandidates:
391 showBadCandidates =
True
def run(self, exposure, sources, expId=0, matches=None)
Measure the PSF.
def plotPsfCandidates(cellSet, showBadCandidates=False, frame=1)
def plotResiduals(exposure, cellSet, showBadCandidates=False, normalizeResiduals=True, frame=2)
def showPsfSpatialCells(exposure, cellSet, showBadCandidates, frame=1)
def __init__(self, schema=None, kwargs)
Create the detection task.