Determine a PCA PSF model for an exposure given a list of PSF candidates.
200 def determinePsf(self, exposure, psfCandidateList, metadata=None, flagKey=None):
201 """!Determine a PCA PSF model for an exposure given a list of PSF candidates
203 \param[in] exposure exposure containing the psf candidates (lsst.afw.image.Exposure)
204 \param[in] psfCandidateList a sequence of PSF candidates (each an lsst.meas.algorithms.PsfCandidate);
205 typically obtained by detecting sources and then running them through a star selector
206 \param[in,out] metadata a home for interesting tidbits of information
207 \param[in] flagKey schema key used to mark sources actually used in PSF determination
210 - psf: the measured PSF, an lsst.meas.algorithms.PcaPsf
211 - cellSet: an lsst.afw.math.SpatialCellSet containing the PSF candidates
216 displayPsfCandidates =
lsstDebug.Info(__name__).displayPsfCandidates
218 displayPsfComponents =
lsstDebug.Info(__name__).displayPsfComponents
221 matchKernelAmplitudes =
lsstDebug.Info(__name__).matchKernelAmplitudes
222 keepMatplotlibPlots =
lsstDebug.Info(__name__).keepMatplotlibPlots
223 displayPsfSpatialModel =
lsstDebug.Info(__name__).displayPsfSpatialModel
231 mi = exposure.getMaskedImage()
233 if len(psfCandidateList) == 0:
234 raise RuntimeError(
"No PSF candidates supplied.")
240 for i, psfCandidate
in enumerate(psfCandidateList):
242 psfCellSet.insertCandidate(psfCandidate)
244 self.debugLog.debug(2,
"Skipping PSF candidate %d of %d: %s" % (i, len(psfCandidateList), e))
246 source = psfCandidate.getSource()
248 quad = afwEll.Quadrupole(source.getIxx(), source.getIyy(), source.getIxy())
249 axes = afwEll.Axes(quad)
250 sizes.append(axes.getA())
252 raise RuntimeError(
"No usable PSF candidates supplied")
253 nEigenComponents = self.config.nEigenComponents
255 if self.config.kernelSize >= 15:
256 self.debugLog.debug(1, \
257 "WARNING: NOT scaling kernelSize by stellar quadrupole moment " +
258 "because config.kernelSize=%s >= 15; using config.kernelSize as as the width, instead" \
259 % (self.config.kernelSize,)
261 actualKernelSize = int(self.config.kernelSize)
263 medSize = numpy.median(sizes)
264 actualKernelSize = 2 * int(self.config.kernelSize * math.sqrt(medSize) + 0.5) + 1
265 if actualKernelSize < self.config.kernelSizeMin:
266 actualKernelSize = self.config.kernelSizeMin
267 if actualKernelSize > self.config.kernelSizeMax:
268 actualKernelSize = self.config.kernelSizeMax
271 print "Median size=%s" % (medSize,)
272 self.debugLog.debug(3,
"Kernel size=%s" % (actualKernelSize,))
275 psfCandidateList[0].setHeight(actualKernelSize)
276 psfCandidateList[0].setWidth(actualKernelSize)
278 if self.config.doRejectBlends:
280 blendedCandidates = []
282 if len(cand.getSource().getFootprint().getPeaks()) > 1:
283 blendedCandidates.append((cell, cand))
286 print "Removing %d blended Psf candidates" % len(blendedCandidates)
287 for cell, cand
in blendedCandidates:
288 cell.removeCandidate(cand)
290 raise RuntimeError(
"All PSF candidates removed as blends")
295 ds9.mtv(exposure, frame=frame, title=
"psf determination")
296 maUtils.showPsfSpatialCells(exposure, psfCellSet, self.config.nStarPerCell,
297 symb=
"o", ctype=ds9.CYAN, ctypeUnused=ds9.YELLOW,
304 for iter
in range(self.config.nIterForPsf):
305 if display
and displayPsfCandidates:
310 for cell
in psfCellSet.getCellList():
311 for cand
in cell.begin(
not showBadCandidates):
312 cand = algorithmsLib.cast_PsfCandidateF(cand)
315 im = cand.getMaskedImage()
317 chi2 = cand.getChi2()
321 stamps.append((im,
"%d%s" %
322 (maUtils.splitId(cand.getSource().getId(),
True)[
"objId"], chi2),
327 mos = displayUtils.Mosaic()
328 for im, label, status
in stamps:
329 im = type(im)(im,
True)
332 except NotImplementedError:
335 mos.append(im, label,
336 ds9.GREEN
if status == afwMath.SpatialCellCandidate.GOOD
else
337 ds9.YELLOW
if status == afwMath.SpatialCellCandidate.UNKNOWN
else ds9.RED)
341 mos.makeMosaic(frame=8, title=
"Psf Candidates")
350 psf, eigenValues, nEigenComponents, fitChi2 = \
351 self.
_fitPsf(exposure, psfCellSet, actualKernelSize, nEigenComponents)
356 for cell
in psfCellSet.getCellList():
358 for cand
in cell.begin(
False):
359 cand = algorithmsLib.cast_PsfCandidateF(cand)
360 cand.setStatus(afwMath.SpatialCellCandidate.UNKNOWN)
361 rchi2 = cand.getChi2()
362 if not numpy.isfinite(rchi2)
or rchi2 <= 0:
364 awfulCandidates.append(cand)
366 self.debugLog.debug(2,
"chi^2=%s; id=%s" %
367 (cand.getChi2(), cand.getSource().getId()))
368 for cand
in awfulCandidates:
370 print "Removing bad candidate: id=%d, chi^2=%f" % \
371 (cand.getSource().getId(), cand.getChi2())
372 cell.removeCandidate(cand)
377 badCandidates = list()
378 for cell
in psfCellSet.getCellList():
379 for cand
in cell.begin(
False):
380 cand = algorithmsLib.cast_PsfCandidateF(cand)
381 rchi2 = cand.getChi2()
383 if rchi2 > self.config.reducedChi2ForPsfCandidates:
384 badCandidates.append(cand)
386 badCandidates.sort(key=
lambda x: x.getChi2(), reverse=
True)
387 numBad = int(len(badCandidates) * (iter + 1) / self.config.nIterForPsf + 0.5)
388 for i, c
in zip(range(numBad), badCandidates):
394 print "Chi^2 clipping %-4d %.2g" % (c.getSource().getId(), chi2)
395 c.setStatus(afwMath.SpatialCellCandidate.BAD)
408 kernel = psf.getKernel()
409 noSpatialKernel = afwMath.cast_LinearCombinationKernel(psf.getKernel())
410 for cell
in psfCellSet.getCellList():
411 for cand
in cell.begin(
False):
412 cand = algorithmsLib.cast_PsfCandidateF(cand)
415 im = cand.getMaskedImage(kernel.getWidth(), kernel.getHeight())
419 fit = algorithmsLib.fitKernelParamsToImage(noSpatialKernel, im, candCenter)
423 for p, k
in zip(params, kernels):
424 amp += p * afwMath.cast_FixedKernel(k).getSum()
426 predict = [kernel.getSpatialFunction(k)(candCenter.getX(), candCenter.getY())
for
427 k
in range(kernel.getNKernelParameters())]
431 residuals.append([a / amp - p
for a, p
in zip(params, predict)])
432 candidates.append(cand)
434 residuals = numpy.array(residuals)
436 for k
in range(kernel.getNKernelParameters()):
439 mean = residuals[:,k].mean()
440 rms = residuals[:,k].
std()
443 sr = numpy.sort(residuals[:,k])
444 mean = sr[int(0.5*len(sr))]
if len(sr) % 2
else \
445 0.5 * (sr[int(0.5*len(sr))] + sr[int(0.5*len(sr))+1])
446 rms = 0.74 * (sr[int(0.75*len(sr))] - sr[int(0.25*len(sr))])
449 mean = stats.getValue(afwMath.MEANCLIP)
450 rms = stats.getValue(afwMath.STDEVCLIP)
452 rms = max(1.0e-4, rms)
455 print "Mean for component %d is %f" % (k, mean)
456 print "RMS for component %d is %f" % (k, rms)
457 badCandidates = list()
458 for i, cand
in enumerate(candidates):
459 if numpy.fabs(residuals[i,k] - mean) > self.config.spatialReject * rms:
460 badCandidates.append(i)
462 badCandidates.sort(key=
lambda x: numpy.fabs(residuals[x,k] - mean), reverse=
True)
464 numBad = int(len(badCandidates) * (iter + 1) / self.config.nIterForPsf + 0.5)
466 for i, c
in zip(range(min(len(badCandidates), numBad)), badCandidates):
469 print "Spatial clipping %d (%f,%f) based on %d: %f vs %f" % \
470 (cand.getSource().getId(), cand.getXCenter(), cand.getYCenter(), k,
471 residuals[badCandidates[i],k], self.config.spatialReject * rms)
472 cand.setStatus(afwMath.SpatialCellCandidate.BAD)
477 if display
and displayIterations:
480 ds9.erase(frame=frame)
481 maUtils.showPsfSpatialCells(exposure, psfCellSet, self.config.nStarPerCell, showChi2=
True,
482 symb=
"o", size=8, frame=frame,
483 ctype=ds9.YELLOW, ctypeBad=ds9.RED, ctypeUnused=ds9.MAGENTA)
484 if self.config.nStarPerCellSpatialFit != self.config.nStarPerCell:
485 maUtils.showPsfSpatialCells(exposure, psfCellSet, self.config.nStarPerCellSpatialFit,
486 symb=
"o", size=10, frame=frame,
487 ctype=ds9.YELLOW, ctypeBad=ds9.RED)
491 maUtils.showPsfCandidates(exposure, psfCellSet, psf=psf, frame=4,
492 normalize=normalizeResiduals,
493 showBadCandidates=showBadCandidates)
494 maUtils.showPsfCandidates(exposure, psfCellSet, psf=psf, frame=5,
495 normalize=normalizeResiduals,
496 showBadCandidates=showBadCandidates,
499 if not showBadCandidates:
500 showBadCandidates =
True
504 if displayPsfComponents:
505 maUtils.showPsf(psf, eigenValues, frame=6)
507 maUtils.showPsfMosaic(exposure, psf, frame=7, showFwhm=
True)
508 ds9.scale(
'linear', 0, 1, frame=7)
509 if displayPsfSpatialModel:
510 maUtils.plotPsfSpatialModel(exposure, psf, psfCellSet, showBadCandidates=
True,
511 matchKernelAmplitudes=matchKernelAmplitudes,
512 keepPlots=keepMatplotlibPlots)
517 reply = raw_input(
"Next iteration? [ynchpqQs] ").strip()
521 reply = reply.split()
523 reply, args = reply[0], reply[1:]
527 if reply
in (
"",
"c",
"h",
"n",
"p",
"q",
"Q",
"s",
"y"):
531 print "c[ontinue without prompting] h[elp] n[o] p[db] q[uit displaying] s[ave fileName] y[es]"
534 import pdb; pdb.set_trace()
540 fileName = args.pop(0)
542 print "Please provide a filename"
545 print "Saving to %s" % fileName
546 maUtils.saveSpatialCellSet(psfCellSet, fileName=fileName)
550 print >> sys.stderr,
"Unrecognised response: %s" % reply
556 psf, eigenValues, nEigenComponents, fitChi2 = \
557 self.
_fitPsf(exposure, psfCellSet, actualKernelSize, nEigenComponents)
562 if display
and reply !=
"n":
564 maUtils.showPsfSpatialCells(exposure, psfCellSet, self.config.nStarPerCell, showChi2=
True,
565 symb=
"o", ctype=ds9.YELLOW, ctypeBad=ds9.RED, size=8, frame=frame)
566 if self.config.nStarPerCellSpatialFit != self.config.nStarPerCell:
567 maUtils.showPsfSpatialCells(exposure, psfCellSet, self.config.nStarPerCellSpatialFit,
568 symb=
"o", ctype=ds9.YELLOW, ctypeBad=ds9.RED,
569 size=10, frame=frame)
571 maUtils.showPsfCandidates(exposure, psfCellSet, psf=psf, frame=4,
572 normalize=normalizeResiduals,
573 showBadCandidates=showBadCandidates)
575 if displayPsfComponents:
576 maUtils.showPsf(psf, eigenValues, frame=6)
579 maUtils.showPsfMosaic(exposure, psf, frame=7, showFwhm=
True)
580 ds9.scale(
"linear", 0, 1, frame=7)
581 if displayPsfSpatialModel:
582 maUtils.plotPsfSpatialModel(exposure, psf, psfCellSet, showBadCandidates=
True,
583 matchKernelAmplitudes=matchKernelAmplitudes,
584 keepPlots=keepMatplotlibPlots)
596 for cell
in psfCellSet.getCellList():
597 for cand
in cell.begin(
False):
600 for cand
in cell.begin(
True):
601 cand = algorithmsLib.cast_PsfCandidateF(cand)
602 src = cand.getSource()
603 if flagKey
is not None:
604 src.set(flagKey,
True)
613 metadata.set(
"spatialFitChi2", fitChi2)
614 metadata.set(
"numGoodStars", numGoodStars)
615 metadata.set(
"numAvailStars", numAvailStars)
616 metadata.set(
"avgX", avgX)
617 metadata.set(
"avgY", avgY)
619 psf = algorithmsLib.PcaPsf(psf.getKernel(),
afwGeom.Point2D(avgX, avgY))
621 return psf, psfCellSet