33 starSelector = measAlg.starSelectorRegistry.makeField(
"Star selection algorithm", default=
"objectSize")
34 psfDeterminer = measAlg.psfDeterminerRegistry.makeField(
"PSF Determination algorithm", default=
"pca")
35 reserveFraction = pexConfig.Field(
37 doc=
"Fraction of PSF candidates to reserve from fitting; none if <= 0",
40 reserveSeed = pexConfig.Field(
42 doc =
"This number will be multiplied by the exposure ID "
43 "to set the random seed for reserving candidates",
57 \anchor MeasurePsfTask_
59 \brief Measure the PSF
61 \section pipe_tasks_measurePsf_Contents Contents
63 - \ref pipe_tasks_measurePsf_Purpose
64 - \ref pipe_tasks_measurePsf_Initialize
65 - \ref pipe_tasks_measurePsf_IO
66 - \ref pipe_tasks_measurePsf_Config
67 - \ref pipe_tasks_measurePsf_Debug
68 - \ref pipe_tasks_measurePsf_Example
70 \section pipe_tasks_measurePsf_Purpose Description
72 A task that selects stars from a catalog of sources and uses those to measure the PSF.
74 The star selector is a subclass of
75 \ref lsst.meas.algorithms.starSelector.BaseStarSelectorTask "lsst.meas.algorithms.BaseStarSelectorTask"
76 and the PSF determiner is a sublcass of
77 \ref lsst.meas.algorithms.psfDeterminer.BasePsfDeterminerTask "lsst.meas.algorithms.BasePsfDeterminerTask"
80 There is no establised set of configuration parameters for these algorithms, so once you start modifying
81 parameters (as we do in \ref pipe_tasks_measurePsf_Example) your code is no longer portable.
83 \section pipe_tasks_measurePsf_Initialize Task initialisation
87 \section pipe_tasks_measurePsf_IO Invoking the Task
91 \section pipe_tasks_measurePsf_Config Configuration parameters
93 See \ref MeasurePsfConfig.
95 \section pipe_tasks_measurePsf_Debug Debug variables
97 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
98 flag \c -d to import \b debug.py from your \c PYTHONPATH; see \ref baseDebug for more about \b debug.py files.
102 <DD> If True, display debugging plots
104 <DD> display the Exposure + spatialCells
105 <DT> displayPsfCandidates
106 <DD> show mosaic of candidates
107 <DT> showBadCandidates
108 <DD> Include bad candidates
109 <DT> displayPsfMosaic
110 <DD> show mosaic of reconstructed PSF(xy)
111 <DT> displayResiduals
113 <DT> normalizeResiduals
114 <DD> Normalise residuals by object amplitude
117 Additionally you can enable any debug outputs that your chosen star selector and psf determiner support.
119 \section pipe_tasks_measurePsf_Example A complete example of using MeasurePsfTask
121 This code is in \link measurePsfTask.py\endlink in the examples directory, and can be run as \em e.g.
123 examples/measurePsfTask.py --ds9
125 \dontinclude measurePsfTask.py
127 The example also runs SourceDetectionTask and SourceMeasurementTask;
128 see \ref meas_algorithms_measurement_Example for more explanation.
130 Import the tasks (there are some other standard imports; read the file to see them all):
132 \skip SourceDetectionTask
133 \until MeasurePsfTask
135 We need to create the tasks before processing any data as the task constructor
136 can add an extra column to the schema, but first we need an almost-empty
139 \skipline makeMinimalSchema
141 We can now call the constructors for the tasks we need to find and characterize candidate
144 \skip SourceDetectionTask.ConfigClass
147 Note that we've chosen a minimal set of measurement plugins: we need the
148 outputs of \c base_SdssCentroid, \c base_SdssShape and \c base_CircularApertureFlux as
149 inputs to the PSF measurement algorithm, while \c base_PixelFlags identifies
150 and flags bad sources (e.g. with pixels too close to the edge) so they can be
153 Now we can create and configure the task that we're interested in:
156 \until measurePsfTask
158 We're now ready to process the data (we could loop over multiple exposures/catalogues using the same
159 task objects). First create the output table:
163 And process the image:
168 We can then unpack and use the results:
173 If you specified \c --ds9 you can see the PSF candidates:
180 To investigate the \ref pipe_tasks_measurePsf_Debug, put something like
184 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively
186 if name == "lsst.pipe.tasks.measurePsf" :
188 di.displayExposure = False # display the Exposure + spatialCells
189 di.displayPsfCandidates = True # show mosaic of candidates
190 di.displayPsfMosaic = True # show mosaic of reconstructed PSF(xy)
191 di.displayResiduals = True # show residuals
192 di.showBadCandidates = True # Include bad candidates
193 di.normalizeResiduals = False # Normalise residuals by object amplitude
197 lsstDebug.Info = DebugInfo
199 into your debug.py file and run measurePsfTask.py with the \c --debug flag.
201 ConfigClass = MeasurePsfConfig
202 _DefaultName =
"measurePsf"
205 """!Create the detection task. Most arguments are simply passed onto pipe.base.Task.
207 \param schema An lsst::afw::table::Schema used to create the output lsst.afw.table.SourceCatalog
208 \param **kwargs Keyword arguments passed to lsst.pipe.base.task.Task.__init__.
210 If schema is not None, 'calib.psf.candidate' and 'calib.psf.used' fields will be added to
211 identify which stars were employed in the PSF estimation.
213 \note This task can add fields to the schema, so any code calling this task must ensure that
214 these fields are indeed present in the input table.
217 pipeBase.Task.__init__(self, **kwargs)
218 if schema
is not None:
220 "calib_psfCandidate", type=
"Flag",
221 doc=(
"Flag set if the source was a candidate for PSF determination, "
222 "as determined by the star selector.")
225 "calib_psfUsed", type=
"Flag",
226 doc=(
"Flag set if the source was actually used for PSF determination, "
227 "as determined by the '%s' PSF determiner.") % self.config.psfDeterminer.name
230 "calib_psfReserved", type=
"Flag",
231 doc=(
"Flag set if the source was selected as a PSF candidate, but was "
232 "reserved from the PSF fitting."))
236 self.makeSubtask(
"starSelector", schema=schema)
237 self.makeSubtask(
"psfDeterminer", schema=schema)
240 def run(self, exposure, sources, expId=0, matches=None):
243 \param[in,out] exposure Exposure to process; measured PSF will be added.
244 \param[in,out] sources Measured sources on exposure; flag fields will be set marking
245 stars chosen by the star selector and the PSF determiner if a schema
246 was passed to the task constructor.
247 \param[in] expId Exposure id used for generating random seed.
248 \param[in] matches A list of lsst.afw.table.ReferenceMatch objects
249 (\em i.e. of lsst.afw.table.Match
250 with \c first being of type lsst.afw.table.SimpleRecord and \c second
251 type lsst.afw.table.SourceRecord --- the reference object and detected
252 object respectively) as returned by \em e.g. the AstrometryTask.
253 Used by star selectors that choose to refer to an external catalog.
255 \return a pipe.base.Struct with fields:
256 - psf: The measured PSF (also set in the input exposure)
257 - cellSet: an lsst.afw.math.SpatialCellSet containing the PSF candidates
258 as returned by the psf determiner.
260 self.log.info(
"Measuring PSF")
266 displayPsfCandidates =
lsstDebug.Info(__name__).displayPsfCandidates
275 psfCandidateList = self.starSelector.run(exposure=exposure, sourceCat=sources,
276 matches=matches).psfCandidates
279 if self.config.reserveFraction > 0:
280 random.seed(self.config.reserveSeed*expId)
281 reserveList = random.sample(psfCandidateList,
282 int((self.config.reserveFraction)*len(psfCandidateList)))
284 for cand
in reserveList:
285 psfCandidateList.remove(cand)
288 for cand
in reserveList:
289 source = cand.getSource()
293 for cand
in psfCandidateList:
294 source = cand.getSource()
297 self.log.info(
"PSF star selector found %d candidates" % len(psfCandidateList))
298 if self.config.reserveFraction > 0:
299 self.log.info(
"Reserved %d candidates from the fitting" % len(reserveList))
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(
344 """Return True if this task makes use of the "matches" argument to the run method"""
345 return self.starSelector.usesMatches
354 maUtils.showPsfSpatialCells(exposure, cellSet,
355 symb=
"o", ctype=ds9.CYAN, ctypeUnused=ds9.YELLOW,
357 for cell
in cellSet.getCellList():
358 for cand
in cell.begin(
not showBadCandidates):
359 cand = measAlg.cast_PsfCandidateF(cand)
360 status = cand.getStatus()
361 ds9.dot(
'+', *cand.getSource().getCentroid(), frame=frame,
362 ctype=ds9.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else
363 ds9.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else ds9.RED)
370 for cell
in cellSet.getCellList():
371 for cand
in cell.begin(
not showBadCandidates):
372 cand = measAlg.cast_PsfCandidateF(cand)
375 im = cand.getMaskedImage()
377 chi2 = cand.getChi2()
383 stamps.append((im,
"%d%s" %
384 (maUtils.splitId(cand.getSource().getId(),
True)[
"objId"], chi2),
389 mos = displayUtils.Mosaic()
390 for im, label, status
in stamps:
391 im = type(im)(im,
True)
394 except NotImplementedError:
397 mos.append(im, label,
398 ds9.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else
399 ds9.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else ds9.RED)
402 mos.makeMosaic(frame=frame, title=
"Psf Candidates")
405 def plotResiduals(exposure, cellSet, showBadCandidates=False, normalizeResiduals=True, frame=2):
406 psf = exposure.getPsf()
409 maUtils.showPsfCandidates(exposure, cellSet, psf=psf, frame=frame,
410 normalize=normalizeResiduals,
411 showBadCandidates=showBadCandidates)
413 maUtils.showPsfCandidates(exposure, cellSet, psf=psf, frame=frame,
414 normalize=normalizeResiduals,
415 showBadCandidates=showBadCandidates,
419 if not showBadCandidates:
420 showBadCandidates =
True
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
def __init__
Create the detection task.
Statistics makeStatistics(afwImage::Mask< afwImage::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl)
Specialization to handle Masks.