31 starSelector = measAlg.starSelectorRegistry.makeField(
"Star selection algorithm", default=
"secondMoment")
32 psfDeterminer = measAlg.psfDeterminerRegistry.makeField(
"PSF Determination algorithm", default=
"pca")
43 \anchor MeasurePsfTask_
45 \brief Measure the PSF
47 \section pipe_tasks_measurePsf_Contents Contents
49 - \ref pipe_tasks_measurePsf_Purpose
50 - \ref pipe_tasks_measurePsf_Initialize
51 - \ref pipe_tasks_measurePsf_IO
52 - \ref pipe_tasks_measurePsf_Config
53 - \ref pipe_tasks_measurePsf_Debug
54 - \ref pipe_tasks_measurePsf_Example
56 \section pipe_tasks_measurePsf_Purpose Description
58 \copybrief MeasurePsfTask
61 A task that wraps two algorithms set via a pair of registries specified in the task's
62 \ref pipe_tasks_measurePsf_Config.
63 Both algorithms are classes with a constructor taking a pex.config.Config object (\em e.g.
64 lsst.meas.algorithms.objectSizeStarSelector.ObjectSizeStarSelector.__init__).
67 - a star selector with API:
69 selectStars(self, exposure, catalog, matches=None)
71 which returns a list of lsst.meas.algorithms.PsfCandidate (\em e.g.
72 lsst.meas.algorithms.objectSizeStarSelector.ObjectSizeStarSelector.selectStars)
74 - a psf estimator with API:
76 determinePsf(exposure, psfCandidateList, metadata=None, flagKey=None)
78 which returns an lsst.afw.detection.Psf and lsst.afw.math.SpatialCellSet (\em e.g.
79 lsst.meas.algorithms.pcaPsfDeterminer.PcaPsfDeterminer.determinePsf).
80 MeasurePsfTask calls determinePsf with \c flagKey set to
81 "calib.psf.used" if a schema is passed to its constructor (see \ref pipe_tasks_measurePsf_Initialize).
83 See also lsst.meas.algorithms.starSelectorRegistry.starSelectorRegistry and
84 lsst.meas.algorithms.psfDeterminerRegistry.psfDeterminerRegistry.
87 There is no establised set of configuration parameters for these algorithms, so once you start modifying
88 parameters (as we do in \ref pipe_tasks_measurePsf_Example) your code is no longer portable.
90 \section pipe_tasks_measurePsf_Initialize Task initialisation
94 \section pipe_tasks_measurePsf_IO Invoking the Task
98 \section pipe_tasks_measurePsf_Config Configuration parameters
100 See \ref MeasurePsfConfig
103 The star selector and psf determiner registries should be modified to return a class
104 which has a ConfigClass attribute and can be instantiated with a config. Until then, there's no
105 obvious way to get a registry algorithm's Config from another Config.
107 \section pipe_tasks_measurePsf_Debug Debug variables
109 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
110 flag \c -d to import \b debug.py from your \c PYTHONPATH; see \ref baseDebug for more about \b debug.py files.
114 <DD> If True, display debugging plots
116 <DD> display the Exposure + spatialCells
117 <DT> displayPsfCandidates
118 <DD> show mosaic of candidates
119 <DT> showBadCandidates
120 <DD> Include bad candidates
121 <DT> displayPsfMosaic
122 <DD> show mosaic of reconstructed PSF(xy)
123 <DT> displayResiduals
125 <DT> normalizeResiduals
126 <DD> Normalise residuals by object amplitude
129 Additionally you can enable any debug outputs that your chosen star selector and psf determiner support.
131 \section pipe_tasks_measurePsf_Example A complete example of using MeasurePsfTask
133 This code is in \link measurePsfTask.py\endlink in the examples directory, and can be run as \em e.g.
135 examples/measurePsfTask.py --ds9
137 \dontinclude measurePsfTask.py
138 The example also runs SourceDetectionTask and SourceMeasurementTask; see \ref meas_algorithms_measurement_Example for more explanation.
140 Import the tasks (there are some other standard imports; read the file to see them all)
141 \skip SourceDetectionTask
142 \until MeasurePsfTask
144 We need to create the tasks before processing any data as the task constructor
145 can add an extra column to the schema, but first we need an almost-empty Schema
146 \skipline makeMinimalSchema
147 after which we can call the constructors for the tasks we need to find and characterize candidate
149 \skip SourceDetectionTask.ConfigClass
151 Now the task that we're interested in:
152 \skipline MeasurePsfTask.ConfigClass
153 \skipline MeasurePsfTask
155 Unfortunately that won't quite work as we didn't run the standard bright star measuring code
156 which sets fields such as "initial.flags.pixel.edge" rather than "flags.pixel.edge". One option
157 would have been to modify the SourceMeasurementTask configuration with
159 config.prefix = "initial."
160 config.algorithms.names -= ["correctfluxes"]
162 (you need the second line due to a bug present in the aperture correction code as of 2014-07-01);
163 or we can set \c badFlags
164 \dontinclude measurePsfTask.py
166 \until flags.pixel.saturated.center
169 We can set psfDeterminer options too:
171 \until nEigenComponents
173 before creating our task
174 \skipline MeasurePsfTask
176 We're now ready to process the data (we could loop over multiple exposures/catalogues using the same
177 task objects). First create the output table:
180 And process the image
184 We can then unpack and use the results:
187 If you specified \c --ds9 you can see the PSF candidates:
192 To investigate the \ref pipe_tasks_measurePsf_Debug, put something like
196 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively
198 if name == "lsst.pipe.tasks.measurePsf" :
200 di.displayExposure = False # display the Exposure + spatialCells
201 di.displayPsfCandidates = True # show mosaic of candidates
202 di.displayPsfMosaic = True # show mosaic of reconstructed PSF(xy)
203 di.displayResiduals = True # show residuals
204 di.showBadCandidates = True # Include bad candidates
205 di.normalizeResiduals = False # Normalise residuals by object amplitude
209 lsstDebug.Info = DebugInfo
211 into your debug.py file and run measurePsfTask.py with the \c --debug flag.
213 ConfigClass = MeasurePsfConfig
214 _DefaultName =
"measurePsf"
217 """!Create the detection task. Most arguments are simply passed onto pipe.base.Task.
219 \param schema An lsst::afw::table::Schema used to create the output lsst.afw.table.SourceCatalog
220 \param **kwargs Keyword arguments passed to lsst.pipe.base.task.Task.__init__.
222 If schema is not None, 'calib.psf.candidate' and 'calib.psf.used' fields will be added to
223 identify which stars were employed in the PSF estimation.
225 \note This task can add fields to the schema, so any code calling this task must ensure that
226 these fields are indeed present in the input table.
229 pipeBase.Task.__init__(self, **kwargs)
230 if schema
is not None:
231 if schema.getVersion() == 0:
233 "calib.psf.candidate", type=
"Flag",
234 doc=(
"Flag set if the source was a candidate for PSF determination, "
235 "as determined by the '%s' star selector.") % self.config.starSelector.name
238 "calib.psf.used", type=
"Flag",
239 doc=(
"Flag set if the source was actually used for PSF determination, "
240 "as determined by the '%s' PSF determiner.") % self.config.psfDeterminer.name
244 "calib_psfCandidate", type=
"Flag",
245 doc=(
"Flag set if the source was a candidate for PSF determination, "
246 "as determined by the '%s' star selector.") % self.config.starSelector.name
248 self.
usedKey = schema.addField(
249 "calib_psfUsed", type=
"Flag",
250 doc=(
"Flag set if the source was actually used for PSF determination, "
251 "as determined by the '%s' PSF determiner.") % self.config.psfDeterminer.name
260 def run(self, exposure, sources, matches=None):
263 \param[in,out] exposure Exposure to process; measured PSF will be added.
264 \param[in,out] sources Measured sources on exposure; flag fields will be set marking
265 stars chosen by the star selector and the PSF determiner if a schema
266 was passed to the task constructor.
268 \param[in] matches a list of lsst.afw.table.ReferenceMatch objects (\em i.e. of lsst.afw.table.Match
269 with \c first being of type lsst.afw.table.SimpleRecord and \c second
270 type lsst.afw.table.SourceRecord --- the reference object and detected
271 object respectively) as returned by \em e.g. the AstrometryTask.
272 Used by star selectors that choose to refer to an external catalog.
274 \return a pipe.base.Struct with fields:
275 - psf: The measured PSF (also set in the input exposure)
276 - cellSet: an lsst.afw.math.SpatialCellSet containing the PSF candidates as returned by the psf determiner.
278 self.log.info(
"Measuring PSF")
284 displayPsfCandidates =
lsstDebug.Info(__name__).displayPsfCandidates
293 psfCandidateList = self.starSelector.selectStars(exposure, sources, matches=matches)
295 for cand
in psfCandidateList:
296 source = cand.getSource()
299 self.log.info(
"PSF star selector found %d candidates" % len(psfCandidateList))
304 ds9.mtv(exposure, frame=frame, title=
"psf determination")
310 psf, cellSet = self.psfDeterminer.determinePsf(exposure, psfCandidateList, self.metadata,
312 self.log.info(
"PSF determination using %d/%d stars." %
313 (self.metadata.get(
"numGoodStars"), self.metadata.get(
"numAvailStars")))
323 if displayPsfCandidates:
329 showBadCandidates=showBadCandidates,
330 normalizeResiduals=normalizeResiduals,
333 maUtils.showPsfMosaic(exposure, psf, frame=frame, showFwhm=
True)
334 ds9.scale(0, 1,
"linear", frame=frame)
337 return pipeBase.Struct(
347 maUtils.showPsfSpatialCells(exposure, cellSet,
348 symb=
"o", ctype=ds9.CYAN, ctypeUnused=ds9.YELLOW,
350 for cell
in cellSet.getCellList():
351 for cand
in cell.begin(
not showBadCandidates):
352 cand = measAlg.cast_PsfCandidateF(cand)
353 status = cand.getStatus()
354 ds9.dot(
'+', *cand.getSource().getCentroid(), frame=frame,
355 ctype=ds9.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else
356 ds9.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else ds9.RED)
362 for cell
in cellSet.getCellList():
363 for cand
in cell.begin(
not showBadCandidates):
364 cand = measAlg.cast_PsfCandidateF(cand)
367 im = cand.getMaskedImage()
369 chi2 = cand.getChi2()
375 stamps.append((im,
"%d%s" %
376 (maUtils.splitId(cand.getSource().getId(),
True)[
"objId"], chi2),
381 mos = displayUtils.Mosaic()
382 for im, label, status
in stamps:
383 im = type(im)(im,
True)
386 except NotImplementedError:
389 mos.append(im, label,
390 ds9.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else
391 ds9.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else ds9.RED)
394 mos.makeMosaic(frame=frame, title=
"Psf Candidates")
396 def plotResiduals(exposure, cellSet, showBadCandidates=False, normalizeResiduals=True, frame=2):
397 psf = exposure.getPsf()
400 maUtils.showPsfCandidates(exposure, cellSet, psf=psf, frame=frame,
401 normalize=normalizeResiduals,
402 showBadCandidates=showBadCandidates)
404 maUtils.showPsfCandidates(exposure, cellSet, psf=psf, frame=frame,
405 normalize=normalizeResiduals,
406 showBadCandidates=showBadCandidates,
409 except Exception
as e:
410 if not showBadCandidates:
411 showBadCandidates =
True
def __init__
Create the detection task.
Statistics makeStatistics(afwImage::Mask< afwImage::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl)
Specialization to handle Masks.