22 from __future__
import absolute_import, division, print_function
24 from builtins
import zip
35 starSelector = measAlg.starSelectorRegistry.makeField(
"Star selection algorithm", default=
"objectSize")
36 psfDeterminer = measAlg.psfDeterminerRegistry.makeField(
"PSF Determination algorithm", default=
"pca")
37 reserve = pexConfig.ConfigurableField(target=measAlg.ReserveSourcesTask,
38 doc=
"Reserve sources from fitting")
50 \anchor MeasurePsfTask_ 52 \brief Measure the PSF 54 \section pipe_tasks_measurePsf_Contents Contents 56 - \ref pipe_tasks_measurePsf_Purpose 57 - \ref pipe_tasks_measurePsf_Initialize 58 - \ref pipe_tasks_measurePsf_IO 59 - \ref pipe_tasks_measurePsf_Config 60 - \ref pipe_tasks_measurePsf_Debug 61 - \ref pipe_tasks_measurePsf_Example 63 \section pipe_tasks_measurePsf_Purpose Description 65 A task that selects stars from a catalog of sources and uses those to measure the PSF. 67 The star selector is a subclass of 68 \ref lsst.meas.algorithms.starSelector.BaseStarSelectorTask "lsst.meas.algorithms.BaseStarSelectorTask" 69 and the PSF determiner is a sublcass of 70 \ref lsst.meas.algorithms.psfDeterminer.BasePsfDeterminerTask "lsst.meas.algorithms.BasePsfDeterminerTask" 73 There is no establised set of configuration parameters for these algorithms, so once you start modifying 74 parameters (as we do in \ref pipe_tasks_measurePsf_Example) your code is no longer portable. 76 \section pipe_tasks_measurePsf_Initialize Task initialisation 80 \section pipe_tasks_measurePsf_IO Invoking the Task 84 \section pipe_tasks_measurePsf_Config Configuration parameters 86 See \ref MeasurePsfConfig. 88 \section pipe_tasks_measurePsf_Debug Debug variables 90 The \link lsst.pipe.base.cmdLineTask.CmdLineTask command line task\endlink interface supports a 91 flag \c -d to import \b debug.py from your \c PYTHONPATH; see \ref baseDebug for more about \b debug.py files. 95 <DD> If True, display debugging plots 97 <DD> display the Exposure + spatialCells 98 <DT> displayPsfCandidates 99 <DD> show mosaic of candidates 100 <DT> showBadCandidates 101 <DD> Include bad candidates 102 <DT> displayPsfMosaic 103 <DD> show mosaic of reconstructed PSF(xy) 104 <DT> displayResiduals 106 <DT> normalizeResiduals 107 <DD> Normalise residuals by object amplitude 110 Additionally you can enable any debug outputs that your chosen star selector and psf determiner support. 112 \section pipe_tasks_measurePsf_Example A complete example of using MeasurePsfTask 114 This code is in \link measurePsfTask.py\endlink in the examples directory, and can be run as \em e.g. 116 examples/measurePsfTask.py --ds9 118 \dontinclude measurePsfTask.py 120 The example also runs SourceDetectionTask and SourceMeasurementTask; 121 see \ref meas_algorithms_measurement_Example for more explanation. 123 Import the tasks (there are some other standard imports; read the file to see them all): 125 \skip SourceDetectionTask 126 \until MeasurePsfTask 128 We need to create the tasks before processing any data as the task constructor 129 can add an extra column to the schema, but first we need an almost-empty 132 \skipline makeMinimalSchema 134 We can now call the constructors for the tasks we need to find and characterize candidate 137 \skip SourceDetectionTask.ConfigClass 140 Note that we've chosen a minimal set of measurement plugins: we need the 141 outputs of \c base_SdssCentroid, \c base_SdssShape and \c base_CircularApertureFlux as 142 inputs to the PSF measurement algorithm, while \c base_PixelFlags identifies 143 and flags bad sources (e.g. with pixels too close to the edge) so they can be 146 Now we can create and configure the task that we're interested in: 149 \until measurePsfTask 151 We're now ready to process the data (we could loop over multiple exposures/catalogues using the same 152 task objects). First create the output table: 156 And process the image: 161 We can then unpack and use the results: 166 If you specified \c --ds9 you can see the PSF candidates: 173 To investigate the \ref pipe_tasks_measurePsf_Debug, put something like 177 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively 179 if name == "lsst.pipe.tasks.measurePsf" : 181 di.displayExposure = False # display the Exposure + spatialCells 182 di.displayPsfCandidates = True # show mosaic of candidates 183 di.displayPsfMosaic = True # show mosaic of reconstructed PSF(xy) 184 di.displayResiduals = True # show residuals 185 di.showBadCandidates = True # Include bad candidates 186 di.normalizeResiduals = False # Normalise residuals by object amplitude 190 lsstDebug.Info = DebugInfo 192 into your debug.py file and run measurePsfTask.py with the \c --debug flag. 194 ConfigClass = MeasurePsfConfig
195 _DefaultName =
"measurePsf" 198 """!Create the detection task. Most arguments are simply passed onto pipe.base.Task. 200 \param schema An lsst::afw::table::Schema used to create the output lsst.afw.table.SourceCatalog 201 \param **kwargs Keyword arguments passed to lsst.pipe.base.task.Task.__init__. 203 If schema is not None, 'calib.psf.candidate' and 'calib.psf.used' fields will be added to 204 identify which stars were employed in the PSF estimation. 206 \note This task can add fields to the schema, so any code calling this task must ensure that 207 these fields are indeed present in the input table. 210 pipeBase.Task.__init__(self, **kwargs)
211 if schema
is not None:
213 "calib_psfCandidate", type=
"Flag",
214 doc=(
"Flag set if the source was a candidate for PSF determination, " 215 "as determined by the star selector.")
218 "calib_psfUsed", type=
"Flag",
219 doc=(
"Flag set if the source was actually used for PSF determination, " 220 "as determined by the '%s' PSF determiner.") % self.config.psfDeterminer.name
225 self.makeSubtask(
"starSelector", schema=schema)
226 self.makeSubtask(
"psfDeterminer", schema=schema)
227 self.makeSubtask(
"reserve", columnName=
"calib_psf", schema=schema,
228 doc=
"set if source was reserved from PSF determination")
231 def run(self, exposure, sources, expId=0, matches=None):
234 \param[in,out] exposure Exposure to process; measured PSF will be added. 235 \param[in,out] sources Measured sources on exposure; flag fields will be set marking 236 stars chosen by the star selector and the PSF determiner if a schema 237 was passed to the task constructor. 238 \param[in] expId Exposure id used for generating random seed. 239 \param[in] matches A list of lsst.afw.table.ReferenceMatch objects 240 (\em i.e. of lsst.afw.table.Match 241 with \c first being of type lsst.afw.table.SimpleRecord and \c second 242 type lsst.afw.table.SourceRecord --- the reference object and detected 243 object respectively) as returned by \em e.g. the AstrometryTask. 244 Used by star selectors that choose to refer to an external catalog. 246 \return a pipe.base.Struct with fields: 247 - psf: The measured PSF (also set in the input exposure) 248 - cellSet: an lsst.afw.math.SpatialCellSet containing the PSF candidates 249 as returned by the psf determiner. 251 self.log.info(
"Measuring PSF")
257 displayPsfCandidates =
lsstDebug.Info(__name__).displayPsfCandidates
266 selectionResult = self.starSelector.
run(exposure=exposure, sourceCat=sources, matches=matches)
267 reserveResult = self.reserve.
run(selectionResult.starCat, expId=expId)
268 psfCandidateList = [cand
for cand, use
269 in zip(selectionResult.psfCandidates, reserveResult.use)
if use]
272 for cand
in psfCandidateList:
273 source = cand.getSource()
276 self.log.info(
"PSF star selector found %d candidates" % len(psfCandidateList))
281 ds9.mtv(exposure, frame=frame, title=
"psf determination")
287 psf, cellSet = self.psfDeterminer.determinePsf(exposure, psfCandidateList, self.metadata,
289 self.log.info(
"PSF determination using %d/%d stars." %
290 (self.metadata.get(
"numGoodStars"), self.metadata.get(
"numAvailStars")))
300 if displayPsfCandidates:
306 showBadCandidates=showBadCandidates,
307 normalizeResiduals=normalizeResiduals,
310 maUtils.showPsfMosaic(exposure, psf, frame=frame, showFwhm=
True)
311 ds9.scale(0, 1,
"linear", frame=frame)
314 return pipeBase.Struct(
321 """Return True if this task makes use of the "matches" argument to the run method""" 322 return self.starSelector.usesMatches
331 maUtils.showPsfSpatialCells(exposure, cellSet,
332 symb=
"o", ctype=ds9.CYAN, ctypeUnused=ds9.YELLOW,
334 for cell
in cellSet.getCellList():
335 for cand
in cell.begin(
not showBadCandidates):
336 status = cand.getStatus()
337 ds9.dot(
'+', *cand.getSource().getCentroid(), frame=frame,
338 ctype=ds9.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else 339 ds9.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else ds9.RED)
346 for cell
in cellSet.getCellList():
347 for cand
in cell.begin(
not showBadCandidates):
349 im = cand.getMaskedImage()
351 chi2 = cand.getChi2()
357 stamps.append((im,
"%d%s" %
358 (maUtils.splitId(cand.getSource().getId(),
True)[
"objId"], chi2),
363 mos = displayUtils.Mosaic()
364 for im, label, status
in stamps:
365 im = type(im)(im,
True)
367 im /= afwMath.makeStatistics(im, afwMath.MAX).getValue()
368 except NotImplementedError:
371 mos.append(im, label,
372 ds9.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else 373 ds9.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else ds9.RED)
376 mos.makeMosaic(frame=frame, title=
"Psf Candidates")
379 def plotResiduals(exposure, cellSet, showBadCandidates=False, normalizeResiduals=True, frame=2):
380 psf = exposure.getPsf()
383 maUtils.showPsfCandidates(exposure, cellSet, psf=psf, frame=frame,
384 normalize=normalizeResiduals,
385 showBadCandidates=showBadCandidates)
387 maUtils.showPsfCandidates(exposure, cellSet, psf=psf, frame=frame,
388 normalize=normalizeResiduals,
389 showBadCandidates=showBadCandidates,
393 if not showBadCandidates:
394 showBadCandidates =
True
def run(self, exposure, sources, expId=0, matches=None)
Measure the PSF.
def plotPsfCandidates(cellSet, showBadCandidates=False, frame=1)
def plotResiduals(exposure, cellSet, showBadCandidates=False, normalizeResiduals=True, frame=2)
def showPsfSpatialCells(exposure, cellSet, showBadCandidates, frame=1)
def __init__(self, schema=None, kwargs)
Create the detection task.