28import lsst.meas.extensions.psfex.psfexPsfDeterminer
32 starSelector = measAlg.sourceSelectorRegistry.makeField(
33 "Star selection algorithm",
36 makePsfCandidates = pexConfig.ConfigurableField(
37 target=measAlg.MakePsfCandidatesTask,
38 doc=
"Task to make psf candidates from selected stars.",
40 psfDeterminer = measAlg.psfDeterminerRegistry.makeField(
41 "PSF Determination algorithm",
44 reserve = pexConfig.ConfigurableField(
45 target=measAlg.ReserveSourcesTask,
46 doc=
"Reserve sources from fitting"
59@anchor MeasurePsfTask_
63@section pipe_tasks_measurePsf_Contents Contents
65 - @ref pipe_tasks_measurePsf_Purpose
66 - @ref pipe_tasks_measurePsf_Initialize
67 - @ref pipe_tasks_measurePsf_IO
68 - @ref pipe_tasks_measurePsf_Config
69 - @ref pipe_tasks_measurePsf_Debug
70 - @ref pipe_tasks_measurePsf_Example
72@section pipe_tasks_measurePsf_Purpose Description
74A task that selects stars from a catalog of sources and uses those to measure the PSF.
76The star selector is a subclass of
77@ref lsst.meas.algorithms.starSelector.BaseStarSelectorTask "lsst.meas.algorithms.BaseStarSelectorTask"
78and the PSF determiner is a sublcass of
79@ref lsst.meas.algorithms.psfDeterminer.BasePsfDeterminerTask "lsst.meas.algorithms.BasePsfDeterminerTask"
82There is no establised set of configuration parameters for these algorithms, so once you start modifying
83parameters (as we do in @ref pipe_tasks_measurePsf_Example) your code is no longer portable.
85@section pipe_tasks_measurePsf_Initialize Task initialisation
89@section pipe_tasks_measurePsf_IO Invoking the Task
93@section pipe_tasks_measurePsf_Config Configuration parameters
95See
@ref MeasurePsfConfig.
97@section pipe_tasks_measurePsf_Debug Debug variables
99The
@link lsst.pipe.base.cmdLineTask.CmdLineTask command line task
@endlink interface supports a
100flag
@c -d to
import @b debug.py
from your
@c PYTHONPATH; see
@ref baseDebug
for more about
@b debug.py files.
104 <DD> If
True, display debugging plots
106 <DD> display the Exposure + spatialCells
107 <DT> displayPsfCandidates
108 <DD> show mosaic of candidates
109 <DT> showBadCandidates
110 <DD> Include bad candidates
111 <DT> displayPsfMosaic
112 <DD> show mosaic of reconstructed PSF(xy)
113 <DT> displayResiduals
115 <DT> normalizeResiduals
116 <DD> Normalise residuals by object amplitude
119Additionally you can enable any debug outputs that your chosen star selector
and psf determiner support.
121@section pipe_tasks_measurePsf_Example A complete example of using MeasurePsfTask
123This code
is in @link measurePsfTask.py
@endlink in the examples directory,
and can be run
as @em e.g.
125examples/measurePsfTask.py --doDisplay
127@dontinclude measurePsfTask.py
129The example also runs SourceDetectionTask
and SingleFrameMeasurementTask;
130see
@ref meas_algorithms_measurement_Example
for more explanation.
132Import the tasks (there are some other standard imports; read the file to see them all):
134@skip SourceDetectionTask
137We need to create the tasks before processing any data
as the task constructor
138can add an extra column to the schema, but first we need an almost-empty
141@skipline makeMinimalSchema
143We can now call the constructors
for the tasks we need to find
and characterize candidate
146@skip SourceDetectionTask.ConfigClass
149Note that we
've chosen a minimal set of measurement plugins: we need the
150outputs of @c base_SdssCentroid,
@c base_SdssShape
and @c base_CircularApertureFlux
as
151inputs to the PSF measurement algorithm,
while @c base_PixelFlags identifies
152and flags bad sources (e.g.
with pixels too close to the edge) so they can be
155Now we can create
and configure the task that we
're interested in:
160We
're now ready to process the data (we could loop over multiple exposures/catalogues using the same
161task objects). First create the output table:
165And process the image:
170We can then unpack
and use the results:
175If you specified
@c --doDisplay you can see the PSF candidates:
182To investigate the
@ref pipe_tasks_measurePsf_Debug, put something like
188 if name ==
"lsst.pipe.tasks.measurePsf" :
190 di.displayExposure =
False
191 di.displayPsfCandidates =
True
192 di.displayPsfMosaic =
True
193 di.displayResiduals =
True
194 di.showBadCandidates =
True
195 di.normalizeResiduals =
False
201into your debug.py file
and run measurePsfTask.py
with the
@c --debug flag.
203 ConfigClass = MeasurePsfConfig
204 _DefaultName = "measurePsf"
207 """!Create the detection task. Most arguments are simply passed onto pipe.base.Task.
210 @param **kwargs Keyword arguments passed to lsst.pipe.base.task.Task.__init__.
212 If schema
is not None,
'calib_psf_candidate' and 'calib_psf_used' fields will be added to
213 identify which stars were employed
in the PSF estimation.
215 @note This task can add fields to the schema, so any code calling this task must ensure that
216 these fields are indeed present
in the input table.
219 pipeBase.Task.__init__(self, **kwargs)
220 if schema
is not None:
222 "calib_psf_candidate", type=
"Flag",
223 doc=(
"Flag set if the source was a candidate for PSF determination, "
224 "as determined by the star selector.")
227 "calib_psf_used", type=
"Flag",
228 doc=(
"Flag set if the source was actually used for PSF determination, "
229 "as determined by the '%s' PSF determiner.") % self.config.psfDeterminer.name
234 self.makeSubtask(
"starSelector")
235 self.makeSubtask(
"makePsfCandidates")
236 self.makeSubtask(
"psfDeterminer", schema=schema)
237 self.makeSubtask(
"reserve", columnName=
"calib_psf", schema=schema,
238 doc=
"set if source was reserved from PSF determination")
241 def run(self, exposure, sources, expId=0, matches=None):
244 @param[
in,out] exposure Exposure to process; measured PSF will be added.
245 @param[
in,out] sources Measured sources on exposure; flag fields will be set marking
246 stars chosen by the star selector
and the PSF determiner
if a schema
247 was passed to the task constructor.
248 @param[
in] expId Exposure id used
for generating random seed.
253 object respectively)
as returned by
@em e.g. the AstrometryTask.
254 Used by star selectors that choose to refer to an external catalog.
256 @return a pipe.base.Struct
with fields:
257 - psf: The measured PSF (also set
in the input exposure)
259 as returned by the psf determiner.
261 self.log.info("Measuring PSF")
267 displayPsfCandidates =
lsstDebug.Info(__name__).displayPsfCandidates
275 stars = self.starSelector.
run(sourceCat=sources, matches=matches, exposure=exposure)
276 selectionResult = self.makePsfCandidates.
run(stars.sourceCat, exposure=exposure)
277 self.log.info(
"PSF star selector found %d candidates", len(selectionResult.psfCandidates))
278 reserveResult = self.reserve.
run(selectionResult.goodStarCat, expId=expId)
280 psfDeterminerList = [cand
for cand, use
281 in zip(selectionResult.psfCandidates, reserveResult.use)
if use]
283 if selectionResult.psfCandidates
and self.
candidateKeycandidateKey
is not None:
284 for cand
in selectionResult.psfCandidates:
285 source = cand.getSource()
288 self.log.info(
"Sending %d candidates to PSF determiner", len(psfDeterminerList))
293 disp = afwDisplay.Display(frame=frame)
294 disp.mtv(exposure, title=
"psf determination")
299 psf, cellSet = self.psfDeterminer.determinePsf(exposure, psfDeterminerList, self.metadata,
301 self.log.info(
"PSF determination using %d/%d stars.",
302 self.metadata.getScalar(
"numGoodStars"), self.metadata.getScalar(
"numAvailStars"))
309 disp = afwDisplay.Display(frame=frame)
313 if displayPsfCandidates:
319 showBadCandidates=showBadCandidates,
320 normalizeResiduals=normalizeResiduals,
323 disp = afwDisplay.Display(frame=frame)
324 maUtils.showPsfMosaic(exposure, psf, display=disp, showFwhm=
True)
325 disp.scale(
"linear", 0, 1)
328 return pipeBase.Struct(
335 """Return True if this task makes use of the "matches" argument to the run method"""
336 return self.starSelector.usesMatches
344 disp = afwDisplay.Display(frame=frame)
345 maUtils.showPsfSpatialCells(exposure, cellSet,
346 symb=
"o", ctype=afwDisplay.CYAN, ctypeUnused=afwDisplay.YELLOW,
347 size=4, display=disp)
348 for cell
in cellSet.getCellList():
349 for cand
in cell.begin(
not showBadCandidates):
350 status = cand.getStatus()
351 disp.dot(
'+', *cand.getSource().getCentroid(),
352 ctype=afwDisplay.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else
353 afwDisplay.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else afwDisplay.RED)
358 for cell
in cellSet.getCellList():
359 for cand
in cell.begin(
not showBadCandidates):
361 im = cand.getMaskedImage()
363 chi2 = cand.getChi2()
369 stamps.append((im,
"%d%s" %
370 (maUtils.splitId(cand.getSource().getId(),
True)[
"objId"], chi2),
375 mos = afwDisplay.utils.Mosaic()
376 disp = afwDisplay.Display(frame=frame)
377 for im, label, status
in stamps:
378 im = type(im)(im,
True)
380 im /= afwMath.makeStatistics(im, afwMath.MAX).getValue()
381 except NotImplementedError:
384 mos.append(im, label,
385 afwDisplay.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else
386 afwDisplay.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else afwDisplay.RED)
389 disp.mtv(mos.makeMosaic(), title=
"Psf Candidates")
392def plotResiduals(exposure, cellSet, showBadCandidates=False, normalizeResiduals=True, frame=2):
393 psf = exposure.getPsf()
394 disp = afwDisplay.Display(frame=frame)
397 maUtils.showPsfCandidates(exposure, cellSet, psf=psf, display=disp,
398 normalize=normalizeResiduals,
399 showBadCandidates=showBadCandidates)
401 maUtils.showPsfCandidates(exposure, cellSet, psf=psf, display=disp,
402 normalize=normalizeResiduals,
403 showBadCandidates=showBadCandidates,
407 if not showBadCandidates:
408 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)