26 import lsst.pex.config
as pexConfig
31 starSelector = measAlg.sourceSelectorRegistry.makeField(
32 "Star selection algorithm",
35 makePsfCandidates = pexConfig.ConfigurableField(
36 target=measAlg.MakePsfCandidatesTask,
37 doc=
"Task to make psf candidates from selected stars.",
39 psfDeterminer = measAlg.psfDeterminerRegistry.makeField(
40 "PSF Determination algorithm",
43 reserve = pexConfig.ConfigurableField(
44 target=measAlg.ReserveSourcesTask,
45 doc=
"Reserve sources from fitting" 58 @anchor MeasurePsfTask_ 60 @brief Measure the PSF 62 @section pipe_tasks_measurePsf_Contents Contents 64 - @ref pipe_tasks_measurePsf_Purpose 65 - @ref pipe_tasks_measurePsf_Initialize 66 - @ref pipe_tasks_measurePsf_IO 67 - @ref pipe_tasks_measurePsf_Config 68 - @ref pipe_tasks_measurePsf_Debug 69 - @ref pipe_tasks_measurePsf_Example 71 @section pipe_tasks_measurePsf_Purpose Description 73 A task that selects stars from a catalog of sources and uses those to measure the PSF. 75 The star selector is a subclass of 76 @ref lsst.meas.algorithms.starSelector.BaseStarSelectorTask "lsst.meas.algorithms.BaseStarSelectorTask" 77 and the PSF determiner is a sublcass of 78 @ref lsst.meas.algorithms.psfDeterminer.BasePsfDeterminerTask "lsst.meas.algorithms.BasePsfDeterminerTask" 81 There is no establised set of configuration parameters for these algorithms, so once you start modifying 82 parameters (as we do in @ref pipe_tasks_measurePsf_Example) your code is no longer portable. 84 @section pipe_tasks_measurePsf_Initialize Task initialisation 88 @section pipe_tasks_measurePsf_IO Invoking the Task 92 @section pipe_tasks_measurePsf_Config Configuration parameters 94 See @ref MeasurePsfConfig. 96 @section pipe_tasks_measurePsf_Debug Debug variables 98 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a 99 flag @c -d to import @b debug.py from your @c PYTHONPATH; see @ref baseDebug for more about @b debug.py files. 103 <DD> If True, display debugging plots 105 <DD> display the Exposure + spatialCells 106 <DT> displayPsfCandidates 107 <DD> show mosaic of candidates 108 <DT> showBadCandidates 109 <DD> Include bad candidates 110 <DT> displayPsfMosaic 111 <DD> show mosaic of reconstructed PSF(xy) 112 <DT> displayResiduals 114 <DT> normalizeResiduals 115 <DD> Normalise residuals by object amplitude 118 Additionally you can enable any debug outputs that your chosen star selector and psf determiner support. 120 @section pipe_tasks_measurePsf_Example A complete example of using MeasurePsfTask 122 This code is in @link measurePsfTask.py@endlink in the examples directory, and can be run as @em e.g. 124 examples/measurePsfTask.py --doDisplay 126 @dontinclude measurePsfTask.py 128 The example also runs SourceDetectionTask and SingleFrameMeasurementTask; 129 see @ref meas_algorithms_measurement_Example for more explanation. 131 Import the tasks (there are some other standard imports; read the file to see them all): 133 @skip SourceDetectionTask 134 @until MeasurePsfTask 136 We need to create the tasks before processing any data as the task constructor 137 can add an extra column to the schema, but first we need an almost-empty 140 @skipline makeMinimalSchema 142 We can now call the constructors for the tasks we need to find and characterize candidate 145 @skip SourceDetectionTask.ConfigClass 148 Note that we've chosen a minimal set of measurement plugins: we need the 149 outputs of @c base_SdssCentroid, @c base_SdssShape and @c base_CircularApertureFlux as 150 inputs to the PSF measurement algorithm, while @c base_PixelFlags identifies 151 and flags bad sources (e.g. with pixels too close to the edge) so they can be 154 Now we can create and configure the task that we're interested in: 157 @until measurePsfTask 159 We're now ready to process the data (we could loop over multiple exposures/catalogues using the same 160 task objects). First create the output table: 164 And process the image: 169 We can then unpack and use the results: 174 If you specified @c --doDisplay you can see the PSF candidates: 181 To investigate the @ref pipe_tasks_measurePsf_Debug, put something like 185 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively 187 if name == "lsst.pipe.tasks.measurePsf" : 189 di.displayExposure = False # display the Exposure + spatialCells 190 di.displayPsfCandidates = True # show mosaic of candidates 191 di.displayPsfMosaic = True # show mosaic of reconstructed PSF(xy) 192 di.displayResiduals = True # show residuals 193 di.showBadCandidates = True # Include bad candidates 194 di.normalizeResiduals = False # Normalise residuals by object amplitude 198 lsstDebug.Info = DebugInfo 200 into your debug.py file and run measurePsfTask.py with the @c --debug flag. 202 ConfigClass = MeasurePsfConfig
203 _DefaultName =
"measurePsf" 206 """!Create the detection task. Most arguments are simply passed onto pipe.base.Task. 208 @param schema An lsst::afw::table::Schema used to create the output lsst.afw.table.SourceCatalog 209 @param **kwargs Keyword arguments passed to lsst.pipe.base.task.Task.__init__. 211 If schema is not None, 'calib_psf_candidate' and 'calib_psf_used' fields will be added to 212 identify which stars were employed in the PSF estimation. 214 @note This task can add fields to the schema, so any code calling this task must ensure that 215 these fields are indeed present in the input table. 218 pipeBase.Task.__init__(self, **kwargs)
219 if schema
is not None:
221 "calib_psf_candidate", type=
"Flag",
222 doc=(
"Flag set if the source was a candidate for PSF determination, " 223 "as determined by the star selector.")
226 "calib_psf_used", type=
"Flag",
227 doc=(
"Flag set if the source was actually used for PSF determination, " 228 "as determined by the '%s' PSF determiner.") % self.config.psfDeterminer.name
233 self.makeSubtask(
"starSelector")
234 self.makeSubtask(
"makePsfCandidates")
235 self.makeSubtask(
"psfDeterminer", schema=schema)
236 self.makeSubtask(
"reserve", columnName=
"calib_psf", schema=schema,
237 doc=
"set if source was reserved from PSF determination")
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
274 stars = self.starSelector.
run(sourceCat=sources, matches=matches, exposure=exposure)
275 selectionResult = self.makePsfCandidates.
run(stars.sourceCat, exposure=exposure)
276 self.log.
info(
"PSF star selector found %d candidates" % len(selectionResult.psfCandidates))
277 reserveResult = self.reserve.
run(selectionResult.goodStarCat, expId=expId)
279 psfDeterminerList = [cand
for cand, use
280 in zip(selectionResult.psfCandidates, reserveResult.use)
if use]
282 if selectionResult.psfCandidates
and self.
candidateKey is not None:
283 for cand
in selectionResult.psfCandidates:
284 source = cand.getSource()
287 self.log.
info(
"Sending %d candidates to PSF determiner" % len(psfDeterminerList))
292 disp = afwDisplay.Display(frame=frame)
293 disp.mtv(exposure, title=
"psf determination")
298 psf, cellSet = self.psfDeterminer.determinePsf(exposure, psfDeterminerList, self.metadata,
300 self.log.
info(
"PSF determination using %d/%d stars." %
301 (self.metadata.getScalar(
"numGoodStars"), self.metadata.getScalar(
"numAvailStars")))
308 disp = afwDisplay.Display(frame=frame)
312 if displayPsfCandidates:
318 showBadCandidates=showBadCandidates,
319 normalizeResiduals=normalizeResiduals,
322 disp = afwDisplay.Display(frame=frame)
323 maUtils.showPsfMosaic(exposure, psf, display=disp, showFwhm=
True)
324 disp.scale(
"linear", 0, 1)
327 return pipeBase.Struct(
334 """Return True if this task makes use of the "matches" argument to the run method""" 335 return self.starSelector.usesMatches
343 disp = afwDisplay.Display(frame=frame)
344 maUtils.showPsfSpatialCells(exposure, cellSet,
345 symb=
"o", ctype=afwDisplay.CYAN, ctypeUnused=afwDisplay.YELLOW,
346 size=4, display=disp)
347 for cell
in cellSet.getCellList():
348 for cand
in cell.begin(
not showBadCandidates):
349 status = cand.getStatus()
350 disp.dot(
'+', *cand.getSource().getCentroid(),
351 ctype=afwDisplay.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else 352 afwDisplay.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else afwDisplay.RED)
357 for cell
in cellSet.getCellList():
358 for cand
in cell.begin(
not showBadCandidates):
360 im = cand.getMaskedImage()
362 chi2 = cand.getChi2()
368 stamps.append((im,
"%d%s" %
369 (maUtils.splitId(cand.getSource().getId(),
True)[
"objId"], chi2),
374 mos = afwDisplay.utils.Mosaic()
375 disp = afwDisplay.Display(frame=frame)
376 for im, label, status
in stamps:
377 im =
type(im)(im,
True)
380 except NotImplementedError:
383 mos.append(im, label,
384 afwDisplay.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else 385 afwDisplay.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else afwDisplay.RED)
388 disp.mtv(mos.makeMosaic(), title=
"Psf Candidates")
391 def plotResiduals(exposure, cellSet, showBadCandidates=False, normalizeResiduals=True, frame=2):
392 psf = exposure.getPsf()
393 disp = afwDisplay.Display(frame=frame)
396 maUtils.showPsfCandidates(exposure, cellSet, psf=psf, display=disp,
397 normalize=normalizeResiduals,
398 showBadCandidates=showBadCandidates)
400 maUtils.showPsfCandidates(exposure, cellSet, psf=psf, display=disp,
401 normalize=normalizeResiduals,
402 showBadCandidates=showBadCandidates,
406 if not showBadCandidates:
407 showBadCandidates =
True
def run(self, exposure, sources, expId=0, matches=None)
Measure the PSF.
def plotPsfCandidates(cellSet, showBadCandidates=False, frame=1)
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
The makeStatistics() overload to handle lsst::afw::math::MaskedVector<>
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.