LSST Applications  21.0.0-147-g0e635eb1+1acddb5be5,22.0.0+052faf71bd,22.0.0+1ea9a8b2b2,22.0.0+6312710a6c,22.0.0+729191ecac,22.0.0+7589c3a021,22.0.0+9f079a9461,22.0.1-1-g7d6de66+b8044ec9de,22.0.1-1-g87000a6+536b1ee016,22.0.1-1-g8e32f31+6312710a6c,22.0.1-10-gd060f87+016f7cdc03,22.0.1-12-g9c3108e+df145f6f68,22.0.1-16-g314fa6d+c825727ab8,22.0.1-19-g93a5c75+d23f2fb6d8,22.0.1-19-gb93eaa13+aab3ef7709,22.0.1-2-g8ef0a89+b8044ec9de,22.0.1-2-g92698f7+9f079a9461,22.0.1-2-ga9b0f51+052faf71bd,22.0.1-2-gac51dbf+052faf71bd,22.0.1-2-gb66926d+6312710a6c,22.0.1-2-gcb770ba+09e3807989,22.0.1-20-g32debb5+b8044ec9de,22.0.1-23-gc2439a9a+fb0756638e,22.0.1-3-g496fd5d+09117f784f,22.0.1-3-g59f966b+1e6ba2c031,22.0.1-3-g849a1b8+f8b568069f,22.0.1-3-gaaec9c0+c5c846a8b1,22.0.1-32-g5ddfab5d3+60ce4897b0,22.0.1-4-g037fbe1+64e601228d,22.0.1-4-g8623105+b8044ec9de,22.0.1-5-g096abc9+d18c45d440,22.0.1-5-g15c806e+57f5c03693,22.0.1-7-gba73697+57f5c03693,master-g6e05de7fdc+c1283a92b8,master-g72cdda8301+729191ecac,w.2021.39
LSST Data Management Base Package
Functions | Variables
lsst.meas.algorithms.utils Namespace Reference

Functions

def splitId (oid, asDict=True)
 
def showSourceSet (sSet, xy0=(0, 0), display=None, ctype=afwDisplay.GREEN, symb="+", size=2)
 
def showPsfSpatialCells (exposure, psfCellSet, nMaxPerCell=-1, showChi2=False, showMoments=False, symb=None, ctype=None, ctypeUnused=None, ctypeBad=None, size=2, display=None)
 
def showPsfCandidates (exposure, psfCellSet, psf=None, display=None, normalize=True, showBadCandidates=True, fitBasisComponents=False, variance=None, chi=None)
 
def makeSubplots (fig, nx=2, ny=2, Nx=1, Ny=1, plottingArea=(0.1, 0.1, 0.85, 0.80), pxgutter=0.05, pygutter=0.05, xgutter=0.04, ygutter=0.04, headroom=0.0, panelBorderWeight=0, panelColor='black')
 
def plotPsfSpatialModel (exposure, psf, psfCellSet, showBadCandidates=True, numSample=128, matchKernelAmplitudes=False, keepPlots=True)
 
def showPsf (psf, eigenValues=None, XY=None, normalize=True, display=None)
 
def showPsfMosaic (exposure, psf=None, nx=7, ny=None, showCenter=True, showEllipticity=False, showFwhm=False, stampSize=0, display=None, title=None)
 
def showPsfResiduals (exposure, sourceSet, magType="psf", scale=10, display=None)
 
def saveSpatialCellSet (psfCellSet, fileName="foo.fits", display=None)
 

Variables

bool keptPlots = False
 

Function Documentation

◆ makeSubplots()

def lsst.meas.algorithms.utils.makeSubplots (   fig,
  nx = 2,
  ny = 2,
  Nx = 1,
  Ny = 1,
  plottingArea = (0.1, 0.1, 0.85, 0.80),
  pxgutter = 0.05,
  pygutter = 0.05,
  xgutter = 0.04,
  ygutter = 0.04,
  headroom = 0.0,
  panelBorderWeight = 0,
  panelColor = 'black' 
)
Return a generator of a set of subplots, a set of Nx*Ny panels of nx*ny plots.  Each panel is fully
filled by row (starting in the bottom left) before the next panel is started.  If panelBorderWidth is
greater than zero a border is drawn around each panel, adjusted to enclose the axis labels.

E.g.
subplots = makeSubplots(fig, 2, 2, Nx=1, Ny=1, panelColor='k')
ax = subplots.next(); ax.text(0.3, 0.5, '[0, 0] (0,0)')
ax = subplots.next(); ax.text(0.3, 0.5, '[0, 0] (1,0)')
ax = subplots.next(); ax.text(0.3, 0.5, '[0, 0] (0,1)')
ax = subplots.next(); ax.text(0.3, 0.5, '[0, 0] (1,1)')
fig.show()

Parameters
----------
fig : `matplotlib.pyplot.figure`
    The matplotlib figure to draw
nx : `int`
    The number of plots in each row of each panel
ny : `int`
    The number of plots in each column of each panel
Nx : `int`
    The number of panels in each row of the figure
Ny : `int`
    The number of panels in each column of the figure
plottingArea : `tuple`
    (x0, y0, x1, y1) for the part of the figure containing all the panels
pxgutter : `float`
    Spacing between columns of panels in units of (x1 - x0)
pygutter : `float`
    Spacing between rows of panels in units of (y1 - y0)
xgutter : `float`
    Spacing between columns of plots within a panel in units of (x1 - x0)
ygutter : `float`
    Spacing between rows of plots within a panel in units of (y1 - y0)
headroom : `float`
    Extra spacing above each plot for e.g. a title
panelBorderWeight : `int`
    Width of border drawn around panels
panelColor : `str`
    Colour of border around panels

Definition at line 340 of file utils.py.

342  headroom=0.0, panelBorderWeight=0, panelColor='black'):
343  """Return a generator of a set of subplots, a set of Nx*Ny panels of nx*ny plots. Each panel is fully
344  filled by row (starting in the bottom left) before the next panel is started. If panelBorderWidth is
345  greater than zero a border is drawn around each panel, adjusted to enclose the axis labels.
346 
347  E.g.
348  subplots = makeSubplots(fig, 2, 2, Nx=1, Ny=1, panelColor='k')
349  ax = subplots.next(); ax.text(0.3, 0.5, '[0, 0] (0,0)')
350  ax = subplots.next(); ax.text(0.3, 0.5, '[0, 0] (1,0)')
351  ax = subplots.next(); ax.text(0.3, 0.5, '[0, 0] (0,1)')
352  ax = subplots.next(); ax.text(0.3, 0.5, '[0, 0] (1,1)')
353  fig.show()
354 
355  Parameters
356  ----------
357  fig : `matplotlib.pyplot.figure`
358  The matplotlib figure to draw
359  nx : `int`
360  The number of plots in each row of each panel
361  ny : `int`
362  The number of plots in each column of each panel
363  Nx : `int`
364  The number of panels in each row of the figure
365  Ny : `int`
366  The number of panels in each column of the figure
367  plottingArea : `tuple`
368  (x0, y0, x1, y1) for the part of the figure containing all the panels
369  pxgutter : `float`
370  Spacing between columns of panels in units of (x1 - x0)
371  pygutter : `float`
372  Spacing between rows of panels in units of (y1 - y0)
373  xgutter : `float`
374  Spacing between columns of plots within a panel in units of (x1 - x0)
375  ygutter : `float`
376  Spacing between rows of plots within a panel in units of (y1 - y0)
377  headroom : `float`
378  Extra spacing above each plot for e.g. a title
379  panelBorderWeight : `int`
380  Width of border drawn around panels
381  panelColor : `str`
382  Colour of border around panels
383  """
384 
385  log = lsst.log.Log.getLogger("utils.makeSubplots")
386  try:
387  import matplotlib.pyplot as plt
388  except ImportError as e:
389  log.warning("Unable to import matplotlib: %s", e)
390  return
391 
392  # Make show() call canvas.draw() too so that we know how large the axis labels are. Sigh
393  try:
394  fig.__show
395  except AttributeError:
396  fig.__show = fig.show
397 
398  def myShow(fig):
399  fig.__show()
400  fig.canvas.draw()
401 
402  import types
403  fig.show = types.MethodType(myShow, fig)
404  #
405  # We can't get the axis sizes until after draw()'s been called, so use a callback Sigh^2
406  #
407  axes = {} # all axes in all the panels we're drawing: axes[panel][0] etc.
408  #
409 
410  def on_draw(event):
411  """
412  Callback to draw the panel borders when the plots are drawn to the canvas
413  """
414  if panelBorderWeight <= 0:
415  return False
416 
417  for p in axes.keys():
418  bboxes = []
419  for ax in axes[p]:
420  bboxes.append(ax.bbox.union([label.get_window_extent() for label in
421  ax.get_xticklabels() + ax.get_yticklabels()]))
422 
423  ax = axes[p][0]
424 
425  # this is the bbox that bounds all the bboxes, again in relative
426  # figure coords
427 
428  bbox = ax.bbox.union(bboxes)
429 
430  xy0, xy1 = ax.transData.inverted().transform(bbox)
431  x0, y0 = xy0
432  x1, y1 = xy1
433  w, h = x1 - x0, y1 - y0
434  # allow a little space around BBox
435  x0 -= 0.02*w
436  w += 0.04*w
437  y0 -= 0.02*h
438  h += 0.04*h
439  h += h*headroom
440  # draw BBox
441  ax.patches = [] # remove old ones
442  rec = ax.add_patch(plt.Rectangle((x0, y0), w, h, fill=False,
443  lw=panelBorderWeight, edgecolor=panelColor))
444  rec.set_clip_on(False)
445 
446  return False
447 
448  fig.canvas.mpl_connect('draw_event', on_draw)
449  #
450  # Choose the plotting areas for each subplot
451  #
452  x0, y0 = plottingArea[0:2]
453  W, H = plottingArea[2:4]
454  w = (W - (Nx - 1)*pxgutter - (nx*Nx - 1)*xgutter)/float(nx*Nx)
455  h = (H - (Ny - 1)*pygutter - (ny*Ny - 1)*ygutter)/float(ny*Ny)
456  #
457  # OK! Time to create the subplots
458  #
459  for panel in range(Nx*Ny):
460  axes[panel] = []
461  px = panel%Nx
462  py = panel//Nx
463  for window in range(nx*ny):
464  x = nx*px + window%nx
465  y = ny*py + window//nx
466  ax = fig.add_axes((x0 + xgutter + pxgutter + x*w + (px - 1)*pxgutter + (x - 1)*xgutter,
467  y0 + ygutter + pygutter + y*h + (py - 1)*pygutter + (y - 1)*ygutter,
468  w, h), frame_on=True, facecolor='w')
469  axes[panel].append(ax)
470  yield ax
471 
472 
static Log getLogger(Log const &logger)
Definition: Log.h:772
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
Definition: functional.cc:33

◆ plotPsfSpatialModel()

def lsst.meas.algorithms.utils.plotPsfSpatialModel (   exposure,
  psf,
  psfCellSet,
  showBadCandidates = True,
  numSample = 128,
  matchKernelAmplitudes = False,
  keepPlots = True 
)
Plot the PSF spatial model.

Definition at line 473 of file utils.py.

474  matchKernelAmplitudes=False, keepPlots=True):
475  """Plot the PSF spatial model."""
476 
477  log = lsst.log.Log.getLogger("utils.plotPsfSpatialModel")
478  try:
479  import matplotlib.pyplot as plt
480  import matplotlib as mpl
481  except ImportError as e:
482  log.warning("Unable to import matplotlib: %s", e)
483  return
484 
485  noSpatialKernel = psf.getKernel()
486  candPos = list()
487  candFits = list()
488  badPos = list()
489  badFits = list()
490  candAmps = list()
491  badAmps = list()
492  for cell in psfCellSet.getCellList():
493  for cand in cell.begin(False):
494  if not showBadCandidates and cand.isBad():
495  continue
496  candCenter = lsst.geom.PointD(cand.getXCenter(), cand.getYCenter())
497  try:
498  im = cand.getMaskedImage()
499  except Exception:
500  continue
501 
502  fit = fitKernelParamsToImage(noSpatialKernel, im, candCenter)
503  params = fit[0]
504  kernels = fit[1]
505  amp = 0.0
506  for p, k in zip(params, kernels):
507  amp += p * k.getSum()
508 
509  targetFits = badFits if cand.isBad() else candFits
510  targetPos = badPos if cand.isBad() else candPos
511  targetAmps = badAmps if cand.isBad() else candAmps
512 
513  targetFits.append([x / amp for x in params])
514  targetPos.append(candCenter)
515  targetAmps.append(amp)
516 
517  xGood = numpy.array([pos.getX() for pos in candPos]) - exposure.getX0()
518  yGood = numpy.array([pos.getY() for pos in candPos]) - exposure.getY0()
519  zGood = numpy.array(candFits)
520 
521  xBad = numpy.array([pos.getX() for pos in badPos]) - exposure.getX0()
522  yBad = numpy.array([pos.getY() for pos in badPos]) - exposure.getY0()
523  zBad = numpy.array(badFits)
524  numBad = len(badPos)
525 
526  xRange = numpy.linspace(0, exposure.getWidth(), num=numSample)
527  yRange = numpy.linspace(0, exposure.getHeight(), num=numSample)
528 
529  kernel = psf.getKernel()
530  nKernelComponents = kernel.getNKernelParameters()
531  #
532  # Figure out how many panels we'll need
533  #
534  nPanelX = int(math.sqrt(nKernelComponents))
535  nPanelY = nKernelComponents//nPanelX
536  while nPanelY*nPanelX < nKernelComponents:
537  nPanelX += 1
538 
539  fig = plt.figure(1)
540  fig.clf()
541  try:
542  fig.canvas._tkcanvas._root().lift() # == Tk's raise, but raise is a python reserved word
543  except Exception: # protect against API changes
544  pass
545  #
546  # Generator for axes arranged in panels
547  #
548  mpl.rcParams["figure.titlesize"] = "x-small"
549  subplots = makeSubplots(fig, 2, 2, Nx=nPanelX, Ny=nPanelY, xgutter=0.06, ygutter=0.06, pygutter=0.04)
550 
551  for k in range(nKernelComponents):
552  func = kernel.getSpatialFunction(k)
553  dfGood = zGood[:, k] - numpy.array([func(pos.getX(), pos.getY()) for pos in candPos])
554  yMin = dfGood.min()
555  yMax = dfGood.max()
556  if numBad > 0:
557  dfBad = zBad[:, k] - numpy.array([func(pos.getX(), pos.getY()) for pos in badPos])
558  yMin = min([yMin, dfBad.min()])
559  yMax = max([yMax, dfBad.max()])
560  yMin -= 0.05 * (yMax - yMin)
561  yMax += 0.05 * (yMax - yMin)
562 
563  yMin = -0.01
564  yMax = 0.01
565 
566  fRange = numpy.ndarray((len(xRange), len(yRange)))
567  for j, yVal in enumerate(yRange):
568  for i, xVal in enumerate(xRange):
569  fRange[j][i] = func(xVal, yVal)
570 
571  ax = next(subplots)
572 
573  ax.set_autoscale_on(False)
574  ax.set_xbound(lower=0, upper=exposure.getHeight())
575  ax.set_ybound(lower=yMin, upper=yMax)
576  ax.plot(yGood, dfGood, 'b+')
577  if numBad > 0:
578  ax.plot(yBad, dfBad, 'r+')
579  ax.axhline(0.0)
580  ax.set_title('Residuals(y)')
581 
582  ax = next(subplots)
583 
584  if matchKernelAmplitudes and k == 0:
585  vmin = 0.0
586  vmax = 1.1
587  else:
588  vmin = fRange.min()
589  vmax = fRange.max()
590 
591  norm = mpl.colors.Normalize(vmin=vmin, vmax=vmax)
592  im = ax.imshow(fRange, aspect='auto', origin="lower", norm=norm,
593  extent=[0, exposure.getWidth()-1, 0, exposure.getHeight()-1])
594  ax.set_title('Spatial poly')
595  plt.colorbar(im, orientation='horizontal', ticks=[vmin, vmax])
596 
597  ax = next(subplots)
598  ax.set_autoscale_on(False)
599  ax.set_xbound(lower=0, upper=exposure.getWidth())
600  ax.set_ybound(lower=yMin, upper=yMax)
601  ax.plot(xGood, dfGood, 'b+')
602  if numBad > 0:
603  ax.plot(xBad, dfBad, 'r+')
604  ax.axhline(0.0)
605  ax.set_title('K%d Residuals(x)' % k)
606 
607  ax = next(subplots)
608 
609  photoCalib = exposure.getPhotoCalib()
610  # If there is no calibration factor, use 1.0.
611  if photoCalib.getCalibrationMean() <= 0:
612  photoCalib = afwImage.PhotoCalib(1.0)
613 
614  ampMag = [photoCalib.instFluxToMagnitude(candAmp) for candAmp in candAmps]
615  ax.plot(ampMag, zGood[:, k], 'b+')
616  if numBad > 0:
617  badAmpMag = [photoCalib.instFluxToMagnitude(badAmp) for badAmp in badAmps]
618  ax.plot(badAmpMag, zBad[:, k], 'r+')
619 
620  ax.set_title('Flux variation')
621 
622  fig.show()
623 
624  global keptPlots
625  if keepPlots and not keptPlots:
626  # Keep plots open when done
627  def show():
628  print("%s: Please close plots when done." % __name__)
629  try:
630  plt.show()
631  except Exception:
632  pass
633  print("Plots closed, exiting...")
634  import atexit
635  atexit.register(show)
636  keptPlots = True
637 
638 
int min
int max
The photometric calibration of an exposure.
Definition: PhotoCalib.h:114
daf::base::PropertyList * list
Definition: fits.cc:913
def show(frame=None)
Definition: ds9.py:88
def makeSubplots(fig, nx=2, ny=2, Nx=1, Ny=1, plottingArea=(0.1, 0.1, 0.85, 0.80), pxgutter=0.05, pygutter=0.05, xgutter=0.04, ygutter=0.04, headroom=0.0, panelBorderWeight=0, panelColor='black')
Definition: utils.py:342
std::pair< std::vector< double >, afw::math::KernelList > fitKernelParamsToImage(afw::math::LinearCombinationKernel const &kernel, Image const &image, geom::Point2D const &pos)
Fit a LinearCombinationKernel to an Image, allowing the coefficients of the components to vary.

◆ saveSpatialCellSet()

def lsst.meas.algorithms.utils.saveSpatialCellSet (   psfCellSet,
  fileName = "foo.fits",
  display = None 
)
Write the contents of a SpatialCellSet to a many-MEF fits file

Definition at line 844 of file utils.py.

844 def saveSpatialCellSet(psfCellSet, fileName="foo.fits", display=None):
845  """Write the contents of a SpatialCellSet to a many-MEF fits file"""
846 
847  mode = "w"
848  for cell in psfCellSet.getCellList():
849  for cand in cell.begin(False): # include bad candidates
850  dx = afwImage.positionToIndex(cand.getXCenter(), True)[1]
851  dy = afwImage.positionToIndex(cand.getYCenter(), True)[1]
852  im = afwMath.offsetImage(cand.getMaskedImage(), -dx, -dy, "lanczos5")
853 
854  md = dafBase.PropertySet()
855  md.set("CELL", cell.getLabel())
856  md.set("ID", cand.getId())
857  md.set("XCENTER", cand.getXCenter())
858  md.set("YCENTER", cand.getYCenter())
859  md.set("BAD", cand.isBad())
860  md.set("AMPL", cand.getAmplitude())
861  md.set("FLUX", cand.getSource().getPsfInstFlux())
862  md.set("CHI2", cand.getSource().getChi2())
863 
864  im.writeFits(fileName, md, mode)
865  mode = "a"
866 
867  if display:
868  display.mtv(im, title="saveSpatialCellSet: image")
Class for storing generic metadata.
Definition: PropertySet.h:66
int positionToIndex(double pos)
Convert image position to nearest integer index.
Definition: ImageUtils.h:69
std::shared_ptr< ImageT > offsetImage(ImageT const &image, float dx, float dy, std::string const &algorithmName="lanczos5", unsigned int buffer=0)
Return an image offset by (dx, dy) using the specified algorithm.
Definition: offsetImage.cc:41
def saveSpatialCellSet(psfCellSet, fileName="foo.fits", display=None)
Definition: utils.py:844

◆ showPsf()

def lsst.meas.algorithms.utils.showPsf (   psf,
  eigenValues = None,
  XY = None,
  normalize = True,
  display = None 
)
Display a PSF's eigen images

If normalize is True, set the largest absolute value of each eigenimage to 1.0 (n.b. sum == 0.0 for i > 0)

Definition at line 639 of file utils.py.

639 def showPsf(psf, eigenValues=None, XY=None, normalize=True, display=None):
640  """Display a PSF's eigen images
641 
642  If normalize is True, set the largest absolute value of each eigenimage to 1.0 (n.b. sum == 0.0 for i > 0)
643  """
644 
645  if eigenValues:
646  coeffs = eigenValues
647  elif XY is not None:
648  coeffs = psf.getLocalKernel(lsst.geom.PointD(XY[0], XY[1])).getKernelParameters()
649  else:
650  coeffs = None
651 
652  mos = displayUtils.Mosaic(gutter=2, background=-0.1)
653  for i, k in enumerate(psf.getKernel().getKernelList()):
654  im = afwImage.ImageD(k.getDimensions())
655  k.computeImage(im, False)
656  if normalize:
657  im /= numpy.max(numpy.abs(im.getArray()))
658 
659  if coeffs:
660  mos.append(im, "%g" % (coeffs[i]/coeffs[0]))
661  else:
662  mos.append(im)
663 
664  if not display:
665  display = afwDisplay.Display()
666  mos.makeMosaic(display=display, title="Kernel Basis Functions")
667 
668  return mos
669 
670 
def showPsf(psf, eigenValues=None, XY=None, normalize=True, display=None)
Definition: utils.py:639

◆ showPsfCandidates()

def lsst.meas.algorithms.utils.showPsfCandidates (   exposure,
  psfCellSet,
  psf = None,
  display = None,
  normalize = True,
  showBadCandidates = True,
  fitBasisComponents = False,
  variance = None,
  chi = None 
)
Display the PSF candidates.

If psf is provided include PSF model and residuals;  if normalize is true normalize the PSFs
(and residuals)

If chi is True, generate a plot of residuals/sqrt(variance), i.e. chi

If fitBasisComponents is true, also find the best linear combination of the PSF's components
(if they exist)

Definition at line 135 of file utils.py.

136  fitBasisComponents=False, variance=None, chi=None):
137  """Display the PSF candidates.
138 
139  If psf is provided include PSF model and residuals; if normalize is true normalize the PSFs
140  (and residuals)
141 
142  If chi is True, generate a plot of residuals/sqrt(variance), i.e. chi
143 
144  If fitBasisComponents is true, also find the best linear combination of the PSF's components
145  (if they exist)
146  """
147  if not display:
148  display = afwDisplay.Display()
149 
150  if chi is None:
151  if variance is not None: # old name for chi
152  chi = variance
153  #
154  # Show us the ccandidates
155  #
156  mos = displayUtils.Mosaic()
157  #
158  candidateCenters = []
159  candidateCentersBad = []
160  candidateIndex = 0
161 
162  for cell in psfCellSet.getCellList():
163  for cand in cell.begin(False): # include bad candidates
164  rchi2 = cand.getChi2()
165  if rchi2 > 1e100:
166  rchi2 = numpy.nan
167 
168  if not showBadCandidates and cand.isBad():
169  continue
170 
171  if psf:
172  im_resid = displayUtils.Mosaic(gutter=0, background=-5, mode="x")
173 
174  try:
175  im = cand.getMaskedImage() # copy of this object's image
176  xc, yc = cand.getXCenter(), cand.getYCenter()
177 
178  margin = 0 if True else 5
179  w, h = im.getDimensions()
180  bbox = lsst.geom.BoxI(lsst.geom.PointI(margin, margin), im.getDimensions())
181 
182  if margin > 0:
183  bim = im.Factory(w + 2*margin, h + 2*margin)
184 
185  stdev = numpy.sqrt(afwMath.makeStatistics(im.getVariance(), afwMath.MEAN).getValue())
187  bim.getVariance().set(stdev**2)
188 
189  bim.assign(im, bbox)
190  im = bim
191  xc += margin
192  yc += margin
193 
194  im = im.Factory(im, True)
195  im.setXY0(cand.getMaskedImage().getXY0())
196  except Exception:
197  continue
198 
199  if not variance:
200  im_resid.append(im.Factory(im, True))
201 
202  if True: # tweak up centroids
203  mi = im
204  psfIm = mi.getImage()
205  config = measBase.SingleFrameMeasurementTask.ConfigClass()
206  config.slots.centroid = "base_SdssCentroid"
207 
208  schema = afwTable.SourceTable.makeMinimalSchema()
209  measureSources = measBase.SingleFrameMeasurementTask(schema, config=config)
210  catalog = afwTable.SourceCatalog(schema)
211 
212  extra = 10 # enough margin to run the sdss centroider
213  miBig = mi.Factory(im.getWidth() + 2*extra, im.getHeight() + 2*extra)
214  miBig[extra:-extra, extra:-extra, afwImage.LOCAL] = mi
215  miBig.setXY0(mi.getX0() - extra, mi.getY0() - extra)
216  mi = miBig
217  del miBig
218 
219  exp = afwImage.makeExposure(mi)
220  exp.setPsf(psf)
221 
222  footprintSet = afwDet.FootprintSet(mi,
223  afwDet.Threshold(0.5*numpy.max(psfIm.getArray())),
224  "DETECTED")
225  footprintSet.makeSources(catalog)
226 
227  if len(catalog) == 0:
228  raise RuntimeError("Failed to detect any objects")
229 
230  measureSources.run(catalog, exp)
231  if len(catalog) == 1:
232  source = catalog[0]
233  else: # more than one source; find the once closest to (xc, yc)
234  dmin = None # an invalid value to catch logic errors
235  for i, s in enumerate(catalog):
236  d = numpy.hypot(xc - s.getX(), yc - s.getY())
237  if i == 0 or d < dmin:
238  source, dmin = s, d
239  xc, yc = source.getCentroid()
240 
241  # residuals using spatial model
242  try:
243  subtractPsf(psf, im, xc, yc)
244  except Exception:
245  continue
246 
247  resid = im
248  if variance:
249  resid = resid.getImage()
250  var = im.getVariance()
251  var = var.Factory(var, True)
252  numpy.sqrt(var.getArray(), var.getArray()) # inplace sqrt
253  resid /= var
254 
255  im_resid.append(resid)
256 
257  # Fit the PSF components directly to the data (i.e. ignoring the spatial model)
258  if fitBasisComponents:
259  im = cand.getMaskedImage()
260 
261  im = im.Factory(im, True)
262  im.setXY0(cand.getMaskedImage().getXY0())
263 
264  try:
265  noSpatialKernel = psf.getKernel()
266  except Exception:
267  noSpatialKernel = None
268 
269  if noSpatialKernel:
270  candCenter = lsst.geom.PointD(cand.getXCenter(), cand.getYCenter())
271  fit = fitKernelParamsToImage(noSpatialKernel, im, candCenter)
272  params = fit[0]
273  kernels = afwMath.KernelList(fit[1])
274  outputKernel = afwMath.LinearCombinationKernel(kernels, params)
275 
276  outImage = afwImage.ImageD(outputKernel.getDimensions())
277  outputKernel.computeImage(outImage, False)
278 
279  im -= outImage.convertF()
280  resid = im
281 
282  if margin > 0:
283  bim = im.Factory(w + 2*margin, h + 2*margin)
285  bim *= stdev
286 
287  bim.assign(resid, bbox)
288  resid = bim
289 
290  if variance:
291  resid = resid.getImage()
292  resid /= var
293 
294  im_resid.append(resid)
295 
296  im = im_resid.makeMosaic()
297  else:
298  im = cand.getMaskedImage()
299 
300  if normalize:
301  im /= afwMath.makeStatistics(im, afwMath.MAX).getValue()
302 
303  objId = splitId(cand.getSource().getId(), True)["objId"]
304  if psf:
305  lab = "%d chi^2 %.1f" % (objId, rchi2)
306  ctype = afwDisplay.RED if cand.isBad() else afwDisplay.GREEN
307  else:
308  lab = "%d flux %8.3g" % (objId, cand.getSource().getPsfInstFlux())
309  ctype = afwDisplay.GREEN
310 
311  mos.append(im, lab, ctype)
312 
313  if False and numpy.isnan(rchi2):
314  display.mtv(cand.getMaskedImage().getImage(), title="showPsfCandidates: candidate")
315  print("amp", cand.getAmplitude())
316 
317  im = cand.getMaskedImage()
318  center = (candidateIndex, xc - im.getX0(), yc - im.getY0())
319  candidateIndex += 1
320  if cand.isBad():
321  candidateCentersBad.append(center)
322  else:
323  candidateCenters.append(center)
324 
325  if variance:
326  title = "chi(Psf fit)"
327  else:
328  title = "Stars & residuals"
329  mosaicImage = mos.makeMosaic(display=display, title=title)
330 
331  with display.Buffering():
332  for centers, color in ((candidateCenters, afwDisplay.GREEN), (candidateCentersBad, afwDisplay.RED)):
333  for cen in centers:
334  bbox = mos.getBBox(cen[0])
335  display.dot("+", cen[1] + bbox.getMinX(), cen[2] + bbox.getMinY(), ctype=color)
336 
337  return mosaicImage
338 
339 
A set of Footprints, associated with a MaskedImage.
Definition: FootprintSet.h:53
A Threshold is used to pass a threshold value to detection algorithms.
Definition: Threshold.h:43
A kernel that is a linear combination of fixed basis kernels.
Definition: Kernel.h:704
A class that can be used to generate sequences of random numbers according to a number of different a...
Definition: Random.h:57
An integer coordinate rectangle.
Definition: Box.h:55
daf::base::PropertySet * set
Definition: fits.cc:912
std::shared_ptr< Exposure< ImagePixelT, MaskPixelT, VariancePixelT > > makeExposure(MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > &mimage, std::shared_ptr< geom::SkyWcs const > wcs=std::shared_ptr< geom::SkyWcs const >())
A function to return an Exposure of the correct type (cf.
Definition: Exposure.h:462
void randomGaussianImage(ImageT *image, Random &rand)
Set image to random numbers with a gaussian N(0, 1) distribution.
Definition: RandomImage.cc:130
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)
Definition: Statistics.h:359
def splitId(oid, asDict=True)
Definition: utils.py:50
double subtractPsf(afw::detection::Psf const &psf, ImageT *data, double x, double y, double psfFlux=std::numeric_limits< double >::quiet_NaN())

◆ showPsfMosaic()

def lsst.meas.algorithms.utils.showPsfMosaic (   exposure,
  psf = None,
  nx = 7,
  ny = None,
  showCenter = True,
  showEllipticity = False,
  showFwhm = False,
  stampSize = 0,
  display = None,
  title = None 
)
Show a mosaic of Psf images.  exposure may be an Exposure (optionally with PSF),
or a tuple (width, height)

If stampSize is > 0, the psf images will be trimmed to stampSize*stampSize

Definition at line 671 of file utils.py.

672  showFwhm=False, stampSize=0, display=None, title=None):
673  """Show a mosaic of Psf images. exposure may be an Exposure (optionally with PSF),
674  or a tuple (width, height)
675 
676  If stampSize is > 0, the psf images will be trimmed to stampSize*stampSize
677  """
678 
679  scale = 1.0
680  if showFwhm:
681  showEllipticity = True
682  scale = 2*math.log(2) # convert sigma^2 to HWHM^2 for a Gaussian
683 
684  mos = displayUtils.Mosaic()
685 
686  try: # maybe it's a real Exposure
687  width, height = exposure.getWidth(), exposure.getHeight()
688  x0, y0 = exposure.getXY0()
689  if not psf:
690  psf = exposure.getPsf()
691  except AttributeError:
692  try: # OK, maybe a list [width, height]
693  width, height = exposure[0], exposure[1]
694  x0, y0 = 0, 0
695  except TypeError: # I guess not
696  raise RuntimeError("Unable to extract width/height from object of type %s" % type(exposure))
697 
698  if not ny:
699  ny = int(nx*float(height)/width + 0.5)
700  if not ny:
701  ny = 1
702 
703  centroidName = "SdssCentroid"
704  shapeName = "base_SdssShape"
705 
706  schema = afwTable.SourceTable.makeMinimalSchema()
707  schema.getAliasMap().set("slot_Centroid", centroidName)
708  schema.getAliasMap().set("slot_Centroid_flag", centroidName+"_flag")
709 
710  control = measBase.SdssCentroidControl()
711  centroider = measBase.SdssCentroidAlgorithm(control, centroidName, schema)
712 
713  sdssShape = measBase.SdssShapeControl()
714  shaper = measBase.SdssShapeAlgorithm(sdssShape, shapeName, schema)
715  table = afwTable.SourceTable.make(schema)
716 
717  table.defineCentroid(centroidName)
718  table.defineShape(shapeName)
719 
720  bbox = None
721  if stampSize > 0:
722  w, h = psf.computeImage(lsst.geom.PointD(0, 0)).getDimensions()
723  if stampSize <= w and stampSize <= h:
724  bbox = lsst.geom.BoxI(lsst.geom.PointI((w - stampSize)//2, (h - stampSize)//2),
725  lsst.geom.ExtentI(stampSize, stampSize))
726 
727  centers = []
728  shapes = []
729  for iy in range(ny):
730  for ix in range(nx):
731  x = int(ix*(width-1)/(nx-1)) + x0
732  y = int(iy*(height-1)/(ny-1)) + y0
733 
734  im = psf.computeImage(lsst.geom.PointD(x, y)).convertF()
735  imPeak = psf.computePeak(lsst.geom.PointD(x, y))
736  im /= imPeak
737  if bbox:
738  im = im.Factory(im, bbox)
739  lab = "PSF(%d,%d)" % (x, y) if False else ""
740  mos.append(im, lab)
741 
743  exp.setPsf(psf)
744  w, h = im.getWidth(), im.getHeight()
745  centerX = im.getX0() + w//2
746  centerY = im.getY0() + h//2
747  src = table.makeRecord()
748  spans = afwGeom.SpanSet(exp.getBBox())
749  foot = afwDet.Footprint(spans)
750  foot.addPeak(centerX, centerY, 1)
751  src.setFootprint(foot)
752 
753  try:
754  centroider.measure(src, exp)
755  centers.append((src.getX() - im.getX0(), src.getY() - im.getY0()))
756 
757  shaper.measure(src, exp)
758  shapes.append((src.getIxx(), src.getIxy(), src.getIyy()))
759  except Exception:
760  pass
761 
762  if not display:
763  display = afwDisplay.Display()
764  mos.makeMosaic(display=display, title=title if title else "Model Psf", mode=nx)
765 
766  if centers and display:
767  with display.Buffering():
768  for i, (cen, shape) in enumerate(zip(centers, shapes)):
769  bbox = mos.getBBox(i)
770  xc, yc = cen[0] + bbox.getMinX(), cen[1] + bbox.getMinY()
771  if showCenter:
772  display.dot("+", xc, yc, ctype=afwDisplay.BLUE)
773 
774  if showEllipticity:
775  ixx, ixy, iyy = shape
776  ixx *= scale
777  ixy *= scale
778  iyy *= scale
779  display.dot("@:%g,%g,%g" % (ixx, ixy, iyy), xc, yc, ctype=afwDisplay.RED)
780 
781  return mos
782 
783 
table::Key< int > type
Definition: Detector.cc:163
Class to describe the properties of a detected object from an image.
Definition: Footprint.h:63
A compact representation of a collection of pixels.
Definition: SpanSet.h:78
MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > * makeMaskedImage(typename std::shared_ptr< Image< ImagePixelT >> image, typename std::shared_ptr< Mask< MaskPixelT >> mask=Mask< MaskPixelT >(), typename std::shared_ptr< Image< VariancePixelT >> variance=Image< VariancePixelT >())
A function to return a MaskedImage of the correct type (cf.
Definition: MaskedImage.h:1240

◆ showPsfResiduals()

def lsst.meas.algorithms.utils.showPsfResiduals (   exposure,
  sourceSet,
  magType = "psf",
  scale = 10,
  display = None 
)

Definition at line 784 of file utils.py.

784 def showPsfResiduals(exposure, sourceSet, magType="psf", scale=10, display=None):
785  mimIn = exposure.getMaskedImage()
786  mimIn = mimIn.Factory(mimIn, True) # make a copy to subtract from
787 
788  psf = exposure.getPsf()
789  psfWidth, psfHeight = psf.getLocalKernel().getDimensions()
790  #
791  # Make the image that we'll paste our residuals into. N.b. they can overlap the edges
792  #
793  w, h = int(mimIn.getWidth()/scale), int(mimIn.getHeight()/scale)
794 
795  im = mimIn.Factory(w + psfWidth, h + psfHeight)
796 
797  cenPos = []
798  for s in sourceSet:
799  x, y = s.getX(), s.getY()
800 
801  sx, sy = int(x/scale + 0.5), int(y/scale + 0.5)
802 
803  smim = im.Factory(im, lsst.geom.BoxI(lsst.geom.PointI(sx, sy),
804  lsst.geom.ExtentI(psfWidth, psfHeight)))
805  sim = smim.getImage()
806 
807  try:
808  if magType == "ap":
809  flux = s.getApInstFlux()
810  elif magType == "model":
811  flux = s.getModelInstFlux()
812  elif magType == "psf":
813  flux = s.getPsfInstFlux()
814  else:
815  raise RuntimeError("Unknown flux type %s" % magType)
816 
817  subtractPsf(psf, mimIn, x, y, flux)
818  except Exception as e:
819  print(e)
820 
821  try:
822  expIm = mimIn.getImage().Factory(mimIn.getImage(),
823  lsst.geom.BoxI(lsst.geom.PointI(int(x) - psfWidth//2,
824  int(y) - psfHeight//2),
825  lsst.geom.ExtentI(psfWidth, psfHeight)),
826  )
827  except pexExcept.Exception:
828  continue
829 
830  cenPos.append([x - expIm.getX0() + sx, y - expIm.getY0() + sy])
831 
832  sim += expIm
833 
834  if display:
835  display = afwDisplay.Display()
836  display.mtv(im, title="showPsfResiduals: image")
837  with display.Buffering():
838  for x, y in cenPos:
839  display.dot("+", x, y)
840 
841  return im
842 
843 
Provides consistent interface for LSST exceptions.
Definition: Exception.h:107
def showPsfResiduals(exposure, sourceSet, magType="psf", scale=10, display=None)
Definition: utils.py:784

◆ showPsfSpatialCells()

def lsst.meas.algorithms.utils.showPsfSpatialCells (   exposure,
  psfCellSet,
  nMaxPerCell = -1,
  showChi2 = False,
  showMoments = False,
  symb = None,
  ctype = None,
  ctypeUnused = None,
  ctypeBad = None,
  size = 2,
  display = None 
)
Show the SpatialCells.

If symb is something that afwDisplay.Display.dot() understands (e.g. "o"),
the top nMaxPerCell candidates will be indicated with that symbol, using
ctype and size.

Definition at line 79 of file utils.py.

80  symb=None, ctype=None, ctypeUnused=None, ctypeBad=None, size=2, display=None):
81  """Show the SpatialCells.
82 
83  If symb is something that afwDisplay.Display.dot() understands (e.g. "o"),
84  the top nMaxPerCell candidates will be indicated with that symbol, using
85  ctype and size.
86  """
87 
88  if not display:
89  display = afwDisplay.Display()
90  with display.Buffering():
91  origin = [-exposure.getMaskedImage().getX0(), -exposure.getMaskedImage().getY0()]
92  for cell in psfCellSet.getCellList():
93  displayUtils.drawBBox(cell.getBBox(), origin=origin, display=display)
94 
95  if nMaxPerCell < 0:
96  nMaxPerCell = 0
97 
98  i = 0
99  goodies = ctypeBad is None
100  for cand in cell.begin(goodies):
101  if nMaxPerCell > 0:
102  i += 1
103 
104  xc, yc = cand.getXCenter() + origin[0], cand.getYCenter() + origin[1]
105 
106  if i > nMaxPerCell:
107  if not ctypeUnused:
108  continue
109 
110  color = ctypeBad if cand.isBad() else ctype
111 
112  if symb:
113  if i > nMaxPerCell:
114  ct = ctypeUnused
115  else:
116  ct = ctype
117 
118  display.dot(symb, xc, yc, ctype=ct, size=size)
119 
120  source = cand.getSource()
121 
122  if showChi2:
123  rchi2 = cand.getChi2()
124  if rchi2 > 1e100:
125  rchi2 = numpy.nan
126  display.dot("%d %.1f" % (splitId(source.getId(), True)["objId"], rchi2),
127  xc - size, yc - size - 4, ctype=color, size=2)
128 
129  if showMoments:
130  display.dot("%.2f %.2f %.2f" % (source.getIxx(), source.getIxy(), source.getIyy()),
131  xc-size, yc + size + 4, ctype=color, size=size)
132  return display
133 
134 

◆ showSourceSet()

def lsst.meas.algorithms.utils.showSourceSet (   sSet,
  xy0 = (0, 0),
  display = None,
  ctype = afwDisplay.GREEN,
  symb = "+",
  size = 2 
)
Draw the (XAstrom, YAstrom) positions of a set of Sources.  Image has the given XY0

Definition at line 60 of file utils.py.

60 def showSourceSet(sSet, xy0=(0, 0), display=None, ctype=afwDisplay.GREEN, symb="+", size=2):
61  """Draw the (XAstrom, YAstrom) positions of a set of Sources. Image has the given XY0"""
62 
63  if not display:
64  display = afwDisplay.Display()
65  with display.Buffering():
66  for s in sSet:
67  xc, yc = s.getXAstrom() - xy0[0], s.getYAstrom() - xy0[1]
68 
69  if symb == "id":
70  display.dot(str(splitId(s.getId(), True)["objId"]), xc, yc, ctype=ctype, size=size)
71  else:
72  display.dot(symb, xc, yc, ctype=ctype, size=size)
73 
74 #
75 # PSF display utilities
76 #
77 
78 
def showSourceSet(sSet, xy0=(0, 0), display=None, ctype=afwDisplay.GREEN, symb="+", size=2)
Definition: utils.py:60

◆ splitId()

def lsst.meas.algorithms.utils.splitId (   oid,
  asDict = True 
)

Definition at line 50 of file utils.py.

50 def splitId(oid, asDict=True):
51 
52  objId = int((oid & 0xffff) - 1) # Should be the value set by apps code
53 
54  if asDict:
55  return dict(objId=objId)
56  else:
57  return [objId]
58 
59 

Variable Documentation

◆ keptPlots

bool lsst.meas.algorithms.utils.keptPlots = False

Definition at line 45 of file utils.py.