29 from lsst.utils.timer 
import timeMethod
 
   33     starSelector = measAlg.sourceSelectorRegistry.makeField(
 
   34         "Star selection algorithm",
 
   37     makePsfCandidates = pexConfig.ConfigurableField(
 
   38         target=measAlg.MakePsfCandidatesTask,
 
   39         doc=
"Task to make psf candidates from selected stars.",
 
   41     psfDeterminer = measAlg.psfDeterminerRegistry.makeField(
 
   42         "PSF Determination algorithm",
 
   45     reserve = pexConfig.ConfigurableField(
 
   46         target=measAlg.ReserveSourcesTask,
 
   47         doc=
"Reserve sources from fitting" 
   60 @anchor MeasurePsfTask_ 
   62 @brief Measure the PSF 
   64 @section pipe_tasks_measurePsf_Contents Contents 
   66  - @ref pipe_tasks_measurePsf_Purpose 
   67  - @ref pipe_tasks_measurePsf_Initialize 
   68  - @ref pipe_tasks_measurePsf_IO 
   69  - @ref pipe_tasks_measurePsf_Config 
   70  - @ref pipe_tasks_measurePsf_Debug 
   71  - @ref pipe_tasks_measurePsf_Example 
   73 @section pipe_tasks_measurePsf_Purpose  Description 
   75 A task that selects stars from a catalog of sources and uses those to measure the PSF. 
   77 The star selector is a subclass of 
   78 @ref lsst.meas.algorithms.starSelector.BaseStarSelectorTask "lsst.meas.algorithms.BaseStarSelectorTask" 
   79 and the PSF determiner is a sublcass of 
   80 @ref lsst.meas.algorithms.psfDeterminer.BasePsfDeterminerTask "lsst.meas.algorithms.BasePsfDeterminerTask" 
   83 There is no establised set of configuration parameters for these algorithms, so once you start modifying 
   84 parameters (as we do in @ref pipe_tasks_measurePsf_Example) your code is no longer portable. 
   86 @section pipe_tasks_measurePsf_Initialize       Task initialisation 
   90 @section pipe_tasks_measurePsf_IO               Invoking the Task 
   94 @section pipe_tasks_measurePsf_Config       Configuration parameters 
   96 See @ref MeasurePsfConfig. 
   98 @section pipe_tasks_measurePsf_Debug            Debug variables 
  100 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a 
  101 flag @c -d to import @b debug.py from your @c PYTHONPATH; see @ref baseDebug for more about @b debug.py files. 
  105   <DD> If True, display debugging plots 
  107   <DD> display the Exposure + spatialCells 
  108   <DT> displayPsfCandidates 
  109   <DD> show mosaic of candidates 
  110   <DT> showBadCandidates 
  111   <DD> Include bad candidates 
  112   <DT> displayPsfMosaic 
  113   <DD> show mosaic of reconstructed PSF(xy) 
  114   <DT> displayResiduals 
  116   <DT> normalizeResiduals 
  117   <DD> Normalise residuals by object amplitude 
  120 Additionally you can enable any debug outputs that your chosen star selector and psf determiner support. 
  122 @section pipe_tasks_measurePsf_Example  A complete example of using MeasurePsfTask 
  124 This code is in @link measurePsfTask.py@endlink in the examples directory, and can be run as @em e.g. 
  126 examples/measurePsfTask.py --doDisplay 
  128 @dontinclude measurePsfTask.py 
  130 The example also runs SourceDetectionTask and SingleFrameMeasurementTask; 
  131 see @ref meas_algorithms_measurement_Example for more explanation. 
  133 Import the tasks (there are some other standard imports; read the file to see them all): 
  135 @skip SourceDetectionTask 
  136 @until MeasurePsfTask 
  138 We need to create the tasks before processing any data as the task constructor 
  139 can add an extra column to the schema, but first we need an almost-empty 
  142 @skipline makeMinimalSchema 
  144 We can now call the constructors for the tasks we need to find and characterize candidate 
  147 @skip SourceDetectionTask.ConfigClass 
  150 Note that we've chosen a minimal set of measurement plugins: we need the 
  151 outputs of @c base_SdssCentroid, @c base_SdssShape and @c base_CircularApertureFlux as 
  152 inputs to the PSF measurement algorithm, while @c base_PixelFlags identifies 
  153 and flags bad sources (e.g. with pixels too close to the edge) so they can be 
  156 Now we can create and configure the task that we're interested in: 
  159 @until measurePsfTask 
  161 We're now ready to process the data (we could loop over multiple exposures/catalogues using the same 
  162 task objects).  First create the output table: 
  166 And process the image: 
  171 We can then unpack and use the results: 
  176 If you specified @c  --doDisplay you can see the PSF candidates: 
  183 To investigate the @ref pipe_tasks_measurePsf_Debug, put something like 
  187         di = lsstDebug.getInfo(name)        # N.b. lsstDebug.Info(name) would call us recursively 
  189         if name == "lsst.pipe.tasks.measurePsf" : 
  191             di.displayExposure = False          # display the Exposure + spatialCells 
  192             di.displayPsfCandidates = True      # show mosaic of candidates 
  193             di.displayPsfMosaic = True          # show mosaic of reconstructed PSF(xy) 
  194             di.displayResiduals = True          # show residuals 
  195             di.showBadCandidates = True         # Include bad candidates 
  196             di.normalizeResiduals = False       # Normalise residuals by object amplitude 
  200     lsstDebug.Info = DebugInfo 
  202 into your debug.py file and run measurePsfTask.py with the @c --debug flag. 
  204     ConfigClass = MeasurePsfConfig
 
  205     _DefaultName = 
"measurePsf" 
  208         """!Create the detection task.  Most arguments are simply passed onto pipe.base.Task. 
  210         @param schema An lsst::afw::table::Schema used to create the output lsst.afw.table.SourceCatalog 
  211         @param **kwargs Keyword arguments passed to lsst.pipe.base.task.Task.__init__. 
  213         If schema is not None, 'calib_psf_candidate' and 'calib_psf_used' fields will be added to 
  214         identify which stars were employed in the PSF estimation. 
  216         @note This task can add fields to the schema, so any code calling this task must ensure that 
  217         these fields are indeed present in the input table. 
  220         pipeBase.Task.__init__(self, **kwargs)
 
  221         if schema 
is not None:
 
  223                 "calib_psf_candidate", type=
"Flag",
 
  224                 doc=(
"Flag set if the source was a candidate for PSF determination, " 
  225                      "as determined by the star selector.")
 
  228                 "calib_psf_used", type=
"Flag",
 
  229                 doc=(
"Flag set if the source was actually used for PSF determination, " 
  230                      "as determined by the '%s' PSF determiner.") % self.config.psfDeterminer.name
 
  235         self.makeSubtask(
"starSelector")
 
  236         self.makeSubtask(
"makePsfCandidates")
 
  237         self.makeSubtask(
"psfDeterminer", schema=schema)
 
  238         self.makeSubtask(
"reserve", columnName=
"calib_psf", schema=schema,
 
  239                          doc=
"set if source was reserved from PSF determination")
 
  242     def run(self, exposure, sources, expId=0, matches=None):
 
  245         @param[in,out]   exposure   Exposure to process; measured PSF will be added. 
  246         @param[in,out]   sources    Measured sources on exposure; flag fields will be set marking 
  247                                     stars chosen by the star selector and the PSF determiner if a schema 
  248                                     was passed to the task constructor. 
  249         @param[in]       expId      Exposure id used for generating random seed. 
  250         @param[in]  matches         A list of lsst.afw.table.ReferenceMatch objects 
  251                                     (@em i.e. of lsst.afw.table.Match 
  252                                     with @c first being of type lsst.afw.table.SimpleRecord and @c second 
  253                                     type lsst.afw.table.SourceRecord --- the reference object and detected 
  254                                     object respectively) as returned by @em e.g. the AstrometryTask. 
  255                                     Used by star selectors that choose to refer to an external catalog. 
  257         @return a pipe.base.Struct with fields: 
  258          - psf: The measured PSF (also set in the input exposure) 
  259          - cellSet: an lsst.afw.math.SpatialCellSet containing the PSF candidates 
  260             as returned by the psf determiner. 
  262         self.log.
info(
"Measuring PSF")
 
  268         displayPsfCandidates = 
lsstDebug.Info(__name__).displayPsfCandidates  
 
  276         stars = self.starSelector.
run(sourceCat=sources, matches=matches, exposure=exposure)
 
  277         selectionResult = self.makePsfCandidates.
run(stars.sourceCat, exposure=exposure)
 
  278         self.log.
info(
"PSF star selector found %d candidates", len(selectionResult.psfCandidates))
 
  279         reserveResult = self.reserve.
run(selectionResult.goodStarCat, expId=expId)
 
  281         psfDeterminerList = [cand 
for cand, use
 
  282                              in zip(selectionResult.psfCandidates, reserveResult.use) 
if use]
 
  284         if selectionResult.psfCandidates 
and self.
candidateKeycandidateKey 
is not None:
 
  285             for cand 
in selectionResult.psfCandidates:
 
  286                 source = cand.getSource()
 
  289         self.log.
info(
"Sending %d candidates to PSF determiner", len(psfDeterminerList))
 
  294                 disp = afwDisplay.Display(frame=frame)
 
  295                 disp.mtv(exposure, title=
"psf determination")
 
  300         psf, cellSet = self.psfDeterminer.determinePsf(exposure, psfDeterminerList, self.metadata,
 
  302         self.log.
info(
"PSF determination using %d/%d stars.",
 
  303                       self.metadata.getScalar(
"numGoodStars"), self.metadata.getScalar(
"numAvailStars"))
 
  310                 disp = afwDisplay.Display(frame=frame)
 
  314             if displayPsfCandidates:    
 
  320                                       showBadCandidates=showBadCandidates,
 
  321                                       normalizeResiduals=normalizeResiduals,
 
  324                 disp = afwDisplay.Display(frame=frame)
 
  325                 maUtils.showPsfMosaic(exposure, psf, display=disp, showFwhm=
True)
 
  326                 disp.scale(
"linear", 0, 1)
 
  329         return pipeBase.Struct(
 
  336         """Return True if this task makes use of the "matches" argument to the run method""" 
  337         return self.starSelector.usesMatches
 
  345     disp = afwDisplay.Display(frame=frame)
 
  346     maUtils.showPsfSpatialCells(exposure, cellSet,
 
  347                                 symb=
"o", ctype=afwDisplay.CYAN, ctypeUnused=afwDisplay.YELLOW,
 
  348                                 size=4, display=disp)
 
  349     for cell 
in cellSet.getCellList():
 
  350         for cand 
in cell.begin(
not showBadCandidates):  
 
  351             status = cand.getStatus()
 
  352             disp.dot(
'+', *cand.getSource().getCentroid(),
 
  353                      ctype=afwDisplay.GREEN 
if status == afwMath.SpatialCellCandidate.GOOD 
else 
  354                      afwDisplay.YELLOW 
if status == afwMath.SpatialCellCandidate.UNKNOWN 
else afwDisplay.RED)
 
  359     for cell 
in cellSet.getCellList():
 
  360         for cand 
in cell.begin(
not showBadCandidates):  
 
  362                 im = cand.getMaskedImage()
 
  364                 chi2 = cand.getChi2()
 
  370                 stamps.append((im, 
"%d%s" %
 
  371                                (maUtils.splitId(cand.getSource().getId(), 
True)[
"objId"], chi2),
 
  376     mos = afwDisplay.utils.Mosaic()
 
  377     disp = afwDisplay.Display(frame=frame)
 
  378     for im, label, status 
in stamps:
 
  379         im = 
type(im)(im, 
True)
 
  382         except NotImplementedError:
 
  385         mos.append(im, label,
 
  386                    afwDisplay.GREEN 
if status == afwMath.SpatialCellCandidate.GOOD 
else 
  387                    afwDisplay.YELLOW 
if status == afwMath.SpatialCellCandidate.UNKNOWN 
else afwDisplay.RED)
 
  390         disp.mtv(mos.makeMosaic(), title=
"Psf Candidates")
 
  393 def plotResiduals(exposure, cellSet, showBadCandidates=False, normalizeResiduals=True, frame=2):
 
  394     psf = exposure.getPsf()
 
  395     disp = afwDisplay.Display(frame=frame)
 
  398             maUtils.showPsfCandidates(exposure, cellSet, psf=psf, display=disp,
 
  399                                       normalize=normalizeResiduals,
 
  400                                       showBadCandidates=showBadCandidates)
 
  402             maUtils.showPsfCandidates(exposure, cellSet, psf=psf, display=disp,
 
  403                                       normalize=normalizeResiduals,
 
  404                                       showBadCandidates=showBadCandidates,
 
  408             if not showBadCandidates:
 
  409                 showBadCandidates = 
True 
def run(self, exposure, sources, expId=0, matches=None)
Measure the PSF.
def __init__(self, schema=None, **kwargs)
Create the detection task.
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Handle a watered-down front-end to the constructor (no variance)
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations.
def plotPsfCandidates(cellSet, showBadCandidates=False, frame=1)
def plotResiduals(exposure, cellSet, showBadCandidates=False, normalizeResiduals=True, frame=2)
def showPsfSpatialCells(exposure, cellSet, showBadCandidates, frame=1)