32 starSelector = measAlg.starSelectorRegistry.makeField(
"Star selection algorithm", default=
"objectSize")
33 psfDeterminer = measAlg.psfDeterminerRegistry.makeField(
"PSF Determination algorithm", default=
"pca")
34 reserveFraction = pexConfig.Field(
36 doc =
"Fraction of PSF candidates to reserve from fitting; none if <= 0",
39 reserveSeed = pexConfig.Field(
41 doc =
"This number will be multiplied by the exposure ID "
42 "to set the random seed for reserving candidates",
55 \anchor MeasurePsfTask_
57 \brief Measure the PSF
59 \section pipe_tasks_measurePsf_Contents Contents
61 - \ref pipe_tasks_measurePsf_Purpose
62 - \ref pipe_tasks_measurePsf_Initialize
63 - \ref pipe_tasks_measurePsf_IO
64 - \ref pipe_tasks_measurePsf_Config
65 - \ref pipe_tasks_measurePsf_Debug
66 - \ref pipe_tasks_measurePsf_Example
68 \section pipe_tasks_measurePsf_Purpose Description
70 A task that selects stars from a catalog of sources and uses those to measure the PSF.
72 The star selector is a subclass of
73 \ref lsst.meas.algorithms.starSelector.BaseStarSelectorTask "lsst.meas.algorithms.BaseStarSelectorTask"
74 and the PSF determiner is a sublcass of
75 \ref lsst.meas.algorithms.psfDeterminer.BasePsfDeterminerTask "lsst.meas.algorithms.BasePsfDeterminerTask"
78 There is no establised set of configuration parameters for these algorithms, so once you start modifying
79 parameters (as we do in \ref pipe_tasks_measurePsf_Example) your code is no longer portable.
81 \section pipe_tasks_measurePsf_Initialize Task initialisation
85 \section pipe_tasks_measurePsf_IO Invoking the Task
89 \section pipe_tasks_measurePsf_Config Configuration parameters
91 See \ref MeasurePsfConfig.
93 \section pipe_tasks_measurePsf_Debug Debug variables
95 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a
96 flag \c -d to import \b debug.py from your \c PYTHONPATH; see \ref baseDebug for more about \b debug.py files.
100 <DD> If True, display debugging plots
102 <DD> display the Exposure + spatialCells
103 <DT> displayPsfCandidates
104 <DD> show mosaic of candidates
105 <DT> showBadCandidates
106 <DD> Include bad candidates
107 <DT> displayPsfMosaic
108 <DD> show mosaic of reconstructed PSF(xy)
109 <DT> displayResiduals
111 <DT> normalizeResiduals
112 <DD> Normalise residuals by object amplitude
115 Additionally you can enable any debug outputs that your chosen star selector and psf determiner support.
117 \section pipe_tasks_measurePsf_Example A complete example of using MeasurePsfTask
119 This code is in \link measurePsfTask.py\endlink in the examples directory, and can be run as \em e.g.
121 examples/measurePsfTask.py --ds9
123 \dontinclude measurePsfTask.py
125 The example also runs SourceDetectionTask and SourceMeasurementTask;
126 see \ref meas_algorithms_measurement_Example for more explanation.
128 Import the tasks (there are some other standard imports; read the file to see them all):
130 \skip SourceDetectionTask
131 \until MeasurePsfTask
133 We need to create the tasks before processing any data as the task constructor
134 can add an extra column to the schema, but first we need an almost-empty
137 \skipline makeMinimalSchema
139 We can now call the constructors for the tasks we need to find and characterize candidate
142 \skip SourceDetectionTask.ConfigClass
145 Note that we've chosen a minimal set of measurement plugins: we need the
146 outputs of \c base_SdssCentroid, \c base_SdssShape and \c base_CircularApertureFlux as
147 inputs to the PSF measurement algorithm, while \c base_PixelFlags identifies
148 and flags bad sources (e.g. with pixels too close to the edge) so they can be
151 Now we can create and configure the task that we're interested in:
154 \until measurePsfTask
156 We're now ready to process the data (we could loop over multiple exposures/catalogues using the same
157 task objects). First create the output table:
161 And process the image:
166 We can then unpack and use the results:
171 If you specified \c --ds9 you can see the PSF candidates:
178 To investigate the \ref pipe_tasks_measurePsf_Debug, put something like
182 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively
184 if name == "lsst.pipe.tasks.measurePsf" :
186 di.displayExposure = False # display the Exposure + spatialCells
187 di.displayPsfCandidates = True # show mosaic of candidates
188 di.displayPsfMosaic = True # show mosaic of reconstructed PSF(xy)
189 di.displayResiduals = True # show residuals
190 di.showBadCandidates = True # Include bad candidates
191 di.normalizeResiduals = False # Normalise residuals by object amplitude
195 lsstDebug.Info = DebugInfo
197 into your debug.py file and run measurePsfTask.py with the \c --debug flag.
199 ConfigClass = MeasurePsfConfig
200 _DefaultName =
"measurePsf"
203 """!Create the detection task. Most arguments are simply passed onto pipe.base.Task.
205 \param schema An lsst::afw::table::Schema used to create the output lsst.afw.table.SourceCatalog
206 \param **kwargs Keyword arguments passed to lsst.pipe.base.task.Task.__init__.
208 If schema is not None, 'calib.psf.candidate' and 'calib.psf.used' fields will be added to
209 identify which stars were employed in the PSF estimation.
211 \note This task can add fields to the schema, so any code calling this task must ensure that
212 these fields are indeed present in the input table.
215 pipeBase.Task.__init__(self, **kwargs)
216 if schema
is not None:
218 "calib_psfCandidate", type=
"Flag",
219 doc=(
"Flag set if the source was a candidate for PSF determination, "
220 "as determined by the star selector.")
223 "calib_psfUsed", type=
"Flag",
224 doc=(
"Flag set if the source was actually used for PSF determination, "
225 "as determined by the '%s' PSF determiner.") % self.config.psfDeterminer.name
228 "calib_psfReserved", type=
"Flag",
229 doc=(
"Flag set if the source was selected as a PSF candidate, but was "
230 "reserved from the PSF fitting."))
234 self.makeSubtask(
"starSelector", schema=schema)
235 self.makeSubtask(
"psfDeterminer", schema=schema)
238 def run(self, exposure, sources, expId=0, matches=None):
241 \param[in,out] exposure Exposure to process; measured PSF will be added.
242 \param[in,out] sources Measured sources on exposure; flag fields will be set marking
243 stars chosen by the star selector and the PSF determiner if a schema
244 was passed to the task constructor.
245 \param[in] expId Exposure id used for generating random seed.
246 \param[in] matches A list of lsst.afw.table.ReferenceMatch objects
247 (\em i.e. of lsst.afw.table.Match
248 with \c first being of type lsst.afw.table.SimpleRecord and \c second
249 type lsst.afw.table.SourceRecord --- the reference object and detected
250 object respectively) as returned by \em e.g. the AstrometryTask.
251 Used by star selectors that choose to refer to an external catalog.
253 \return a pipe.base.Struct with fields:
254 - psf: The measured PSF (also set in the input exposure)
255 - cellSet: an lsst.afw.math.SpatialCellSet containing the PSF candidates
256 as returned by the psf determiner.
258 self.log.info(
"Measuring PSF")
264 displayPsfCandidates =
lsstDebug.Info(__name__).displayPsfCandidates
273 psfCandidateList = self.starSelector.run(exposure=exposure, sourceCat=sources,
274 matches=matches).psfCandidates
277 if self.config.reserveFraction > 0 :
278 random.seed(self.config.reserveSeed*expId)
279 reserveList = random.sample(psfCandidateList,
280 int((self.config.reserveFraction)*len(psfCandidateList)))
282 for cand
in reserveList:
283 psfCandidateList.remove(cand)
286 for cand
in reserveList:
287 source = cand.getSource()
291 for cand
in psfCandidateList:
292 source = cand.getSource()
295 self.log.info(
"PSF star selector found %d candidates" % len(psfCandidateList))
296 if self.config.reserveFraction > 0 :
297 self.log.info(
"Reserved %d candidates from the fitting" % len(reserveList))
302 ds9.mtv(exposure, frame=frame, title=
"psf determination")
308 psf, cellSet = self.psfDeterminer.determinePsf(exposure, psfCandidateList, self.metadata,
310 self.log.info(
"PSF determination using %d/%d stars." %
311 (self.metadata.get(
"numGoodStars"), self.metadata.get(
"numAvailStars")))
321 if displayPsfCandidates:
327 showBadCandidates=showBadCandidates,
328 normalizeResiduals=normalizeResiduals,
331 maUtils.showPsfMosaic(exposure, psf, frame=frame, showFwhm=
True)
332 ds9.scale(0, 1,
"linear", frame=frame)
335 return pipeBase.Struct(
342 """Return True if this task makes use of the "matches" argument to the run method"""
343 return self.starSelector.usesMatches
350 maUtils.showPsfSpatialCells(exposure, cellSet,
351 symb=
"o", ctype=ds9.CYAN, ctypeUnused=ds9.YELLOW,
353 for cell
in cellSet.getCellList():
354 for cand
in cell.begin(
not showBadCandidates):
355 cand = measAlg.cast_PsfCandidateF(cand)
356 status = cand.getStatus()
357 ds9.dot(
'+', *cand.getSource().getCentroid(), frame=frame,
358 ctype=ds9.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else
359 ds9.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else ds9.RED)
365 for cell
in cellSet.getCellList():
366 for cand
in cell.begin(
not showBadCandidates):
367 cand = measAlg.cast_PsfCandidateF(cand)
370 im = cand.getMaskedImage()
372 chi2 = cand.getChi2()
378 stamps.append((im,
"%d%s" %
379 (maUtils.splitId(cand.getSource().getId(),
True)[
"objId"], chi2),
384 mos = displayUtils.Mosaic()
385 for im, label, status
in stamps:
386 im = type(im)(im,
True)
389 except NotImplementedError:
392 mos.append(im, label,
393 ds9.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else
394 ds9.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else ds9.RED)
397 mos.makeMosaic(frame=frame, title=
"Psf Candidates")
399 def plotResiduals(exposure, cellSet, showBadCandidates=False, normalizeResiduals=True, frame=2):
400 psf = exposure.getPsf()
403 maUtils.showPsfCandidates(exposure, cellSet, psf=psf, frame=frame,
404 normalize=normalizeResiduals,
405 showBadCandidates=showBadCandidates)
407 maUtils.showPsfCandidates(exposure, cellSet, psf=psf, frame=frame,
408 normalize=normalizeResiduals,
409 showBadCandidates=showBadCandidates,
413 if not showBadCandidates:
414 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.