28import lsst.meas.extensions.psfex.psfexPsfDeterminer
29from lsst.utils.timer
import timeMethod
33 starSelector = measAlg.sourceSelectorRegistry.makeField(
34 "Star selection algorithm",
37 makePsfCandidates = pexConfig.ConfigurableField(
38 target=measAlg.MakePsfCandidatesTask,
39 doc=
"Task to make psf candidates from selected stars.",
41 psfDeterminer = measAlg.psfDeterminerRegistry.makeField(
42 "PSF Determination algorithm",
45 reserve = pexConfig.ConfigurableField(
46 target=measAlg.ReserveSourcesTask,
47 doc=
"Reserve sources from fitting"
60@anchor MeasurePsfTask_
64@section pipe_tasks_measurePsf_Contents Contents
66 - @ref pipe_tasks_measurePsf_Purpose
67 - @ref pipe_tasks_measurePsf_Initialize
68 - @ref pipe_tasks_measurePsf_IO
69 - @ref pipe_tasks_measurePsf_Config
70 - @ref pipe_tasks_measurePsf_Debug
71 - @ref pipe_tasks_measurePsf_Example
73@section pipe_tasks_measurePsf_Purpose Description
75A task that selects stars from a catalog of sources and uses those to measure the PSF.
77The star selector is a subclass of
78@ref lsst.meas.algorithms.starSelector.BaseStarSelectorTask "lsst.meas.algorithms.BaseStarSelectorTask"
79and the PSF determiner is a sublcass of
80@ref lsst.meas.algorithms.psfDeterminer.BasePsfDeterminerTask "lsst.meas.algorithms.BasePsfDeterminerTask"
83There is no establised set of configuration parameters for these algorithms, so once you start modifying
84parameters (as we do in @ref pipe_tasks_measurePsf_Example) your code is no longer portable.
86@section pipe_tasks_measurePsf_Initialize Task initialisation
90@section pipe_tasks_measurePsf_IO Invoking the Task
94@section pipe_tasks_measurePsf_Config Configuration parameters
96See
@ref MeasurePsfConfig.
98@section pipe_tasks_measurePsf_Debug Debug variables
100The
@link lsst.pipe.base.cmdLineTask.CmdLineTask command line task
@endlink interface supports a
101flag
@c -d to
import @b debug.py
from your
@c PYTHONPATH; see
@ref baseDebug
for more about
@b debug.py files.
105 <DD> If
True, display debugging plots
107 <DD> display the Exposure + spatialCells
108 <DT> displayPsfCandidates
109 <DD> show mosaic of candidates
110 <DT> showBadCandidates
111 <DD> Include bad candidates
112 <DT> displayPsfMosaic
113 <DD> show mosaic of reconstructed PSF(xy)
114 <DT> displayResiduals
116 <DT> normalizeResiduals
117 <DD> Normalise residuals by object amplitude
120Additionally you can enable any debug outputs that your chosen star selector
and psf determiner support.
122@section pipe_tasks_measurePsf_Example A complete example of using MeasurePsfTask
124This code
is in @link measurePsfTask.py
@endlink in the examples directory,
and can be run
as @em e.g.
126examples/measurePsfTask.py --doDisplay
128@dontinclude measurePsfTask.py
130The example also runs SourceDetectionTask
and SingleFrameMeasurementTask;
131see
@ref meas_algorithms_measurement_Example
for more explanation.
133Import the tasks (there are some other standard imports; read the file to see them all):
135@skip SourceDetectionTask
138We need to create the tasks before processing any data
as the task constructor
139can add an extra column to the schema, but first we need an almost-empty
142@skipline makeMinimalSchema
144We can now call the constructors
for the tasks we need to find
and characterize candidate
147@skip SourceDetectionTask.ConfigClass
150Note that we
've chosen a minimal set of measurement plugins: we need the
151outputs of @c base_SdssCentroid,
@c base_SdssShape
and @c base_CircularApertureFlux
as
152inputs to the PSF measurement algorithm,
while @c base_PixelFlags identifies
153and flags bad sources (e.g.
with pixels too close to the edge) so they can be
156Now we can create
and configure the task that we
're interested in:
161We
're now ready to process the data (we could loop over multiple exposures/catalogues using the same
162task objects). First create the output table:
166And process the image:
171We can then unpack
and use the results:
176If you specified
@c --doDisplay you can see the PSF candidates:
183To investigate the
@ref pipe_tasks_measurePsf_Debug, put something like
189 if name ==
"lsst.pipe.tasks.measurePsf" :
191 di.displayExposure =
False
192 di.displayPsfCandidates =
True
193 di.displayPsfMosaic =
True
194 di.displayResiduals =
True
195 di.showBadCandidates =
True
196 di.normalizeResiduals =
False
202into your debug.py file
and run measurePsfTask.py
with the
@c --debug flag.
204 ConfigClass = MeasurePsfConfig
205 _DefaultName = "measurePsf"
208 """!Create the detection task. Most arguments are simply passed onto pipe.base.Task.
211 @param **kwargs Keyword arguments passed to lsst.pipe.base.task.Task.__init__.
213 If schema
is not None,
'calib_psf_candidate' and 'calib_psf_used' fields will be added to
214 identify which stars were employed
in the PSF estimation.
216 @note This task can add fields to the schema, so any code calling this task must ensure that
217 these fields are indeed present
in the input table.
220 pipeBase.Task.__init__(self, **kwargs)
221 if schema
is not None:
223 "calib_psf_candidate", type=
"Flag",
224 doc=(
"Flag set if the source was a candidate for PSF determination, "
225 "as determined by the star selector.")
228 "calib_psf_used", type=
"Flag",
229 doc=(
"Flag set if the source was actually used for PSF determination, "
230 "as determined by the '%s' PSF determiner.") % self.config.psfDeterminer.name
235 self.makeSubtask(
"starSelector")
236 self.makeSubtask(
"makePsfCandidates")
237 self.makeSubtask(
"psfDeterminer", schema=schema)
238 self.makeSubtask(
"reserve", columnName=
"calib_psf", schema=schema,
239 doc=
"set if source was reserved from PSF determination")
242 def run(self, exposure, sources, expId=0, matches=None):
245 @param[
in,out] exposure Exposure to process; measured PSF will be added.
246 @param[
in,out] sources Measured sources on exposure; flag fields will be set marking
247 stars chosen by the star selector
and the PSF determiner
if a schema
248 was passed to the task constructor.
249 @param[
in] expId Exposure id used
for generating random seed.
254 object respectively)
as returned by
@em e.g. the AstrometryTask.
255 Used by star selectors that choose to refer to an external catalog.
257 @return a pipe.base.Struct
with fields:
258 - psf: The measured PSF (also set
in the input exposure)
260 as returned by the psf determiner.
262 self.log.info("Measuring PSF")
268 displayPsfCandidates =
lsstDebug.Info(__name__).displayPsfCandidates
276 stars = self.starSelector.
run(sourceCat=sources, matches=matches, exposure=exposure)
277 selectionResult = self.makePsfCandidates.
run(stars.sourceCat, exposure=exposure)
278 self.log.info(
"PSF star selector found %d candidates", len(selectionResult.psfCandidates))
279 reserveResult = self.reserve.
run(selectionResult.goodStarCat, expId=expId)
281 psfDeterminerList = [cand
for cand, use
282 in zip(selectionResult.psfCandidates, reserveResult.use)
if use]
284 if selectionResult.psfCandidates
and self.
candidateKeycandidateKey
is not None:
285 for cand
in selectionResult.psfCandidates:
286 source = cand.getSource()
289 self.log.info(
"Sending %d candidates to PSF determiner", len(psfDeterminerList))
294 disp = afwDisplay.Display(frame=frame)
295 disp.mtv(exposure, title=
"psf determination")
300 psf, cellSet = self.psfDeterminer.determinePsf(exposure, psfDeterminerList, self.metadata,
302 self.log.info(
"PSF determination using %d/%d stars.",
303 self.metadata.getScalar(
"numGoodStars"), self.metadata.getScalar(
"numAvailStars"))
310 disp = afwDisplay.Display(frame=frame)
314 if displayPsfCandidates:
320 showBadCandidates=showBadCandidates,
321 normalizeResiduals=normalizeResiduals,
324 disp = afwDisplay.Display(frame=frame)
325 maUtils.showPsfMosaic(exposure, psf, display=disp, showFwhm=
True)
326 disp.scale(
"linear", 0, 1)
329 return pipeBase.Struct(
336 """Return True if this task makes use of the "matches" argument to the run method"""
337 return self.starSelector.usesMatches
345 disp = afwDisplay.Display(frame=frame)
346 maUtils.showPsfSpatialCells(exposure, cellSet,
347 symb=
"o", ctype=afwDisplay.CYAN, ctypeUnused=afwDisplay.YELLOW,
348 size=4, display=disp)
349 for cell
in cellSet.getCellList():
350 for cand
in cell.begin(
not showBadCandidates):
351 status = cand.getStatus()
352 disp.dot(
'+', *cand.getSource().getCentroid(),
353 ctype=afwDisplay.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else
354 afwDisplay.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else afwDisplay.RED)
359 for cell
in cellSet.getCellList():
360 for cand
in cell.begin(
not showBadCandidates):
362 im = cand.getMaskedImage()
364 chi2 = cand.getChi2()
370 stamps.append((im,
"%d%s" %
371 (maUtils.splitId(cand.getSource().getId(),
True)[
"objId"], chi2),
376 mos = afwDisplay.utils.Mosaic()
377 disp = afwDisplay.Display(frame=frame)
378 for im, label, status
in stamps:
379 im = type(im)(im,
True)
381 im /= afwMath.makeStatistics(im, afwMath.MAX).getValue()
382 except NotImplementedError:
385 mos.append(im, label,
386 afwDisplay.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else
387 afwDisplay.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else afwDisplay.RED)
390 disp.mtv(mos.makeMosaic(), title=
"Psf Candidates")
393def plotResiduals(exposure, cellSet, showBadCandidates=False, normalizeResiduals=True, frame=2):
394 psf = exposure.getPsf()
395 disp = afwDisplay.Display(frame=frame)
398 maUtils.showPsfCandidates(exposure, cellSet, psf=psf, display=disp,
399 normalize=normalizeResiduals,
400 showBadCandidates=showBadCandidates)
402 maUtils.showPsfCandidates(exposure, cellSet, psf=psf, display=disp,
403 normalize=normalizeResiduals,
404 showBadCandidates=showBadCandidates,
408 if not showBadCandidates:
409 showBadCandidates =
True
def run(self, exposure, sources, expId=0, matches=None)
Measure the PSF.
def __init__(self, schema=None, **kwargs)
Create the detection task.
def plotPsfCandidates(cellSet, showBadCandidates=False, frame=1)
def plotResiduals(exposure, cellSet, showBadCandidates=False, normalizeResiduals=True, frame=2)
def showPsfSpatialCells(exposure, cellSet, showBadCandidates, frame=1)