LSSTApplications  11.0-13-gbb96280,12.1+18,12.1+7,12.1-1-g14f38d3+72,12.1-1-g16c0db7+5,12.1-1-g5961e7a+84,12.1-1-ge22e12b+23,12.1-11-g06625e2+4,12.1-11-g0d7f63b+4,12.1-19-gd507bfc,12.1-2-g7dda0ab+38,12.1-2-gc0bc6ab+81,12.1-21-g6ffe579+2,12.1-21-gbdb6c2a+4,12.1-24-g941c398+5,12.1-3-g57f6835+7,12.1-3-gf0736f3,12.1-37-g3ddd237,12.1-4-gf46015e+5,12.1-5-g06c326c+20,12.1-5-g648ee80+3,12.1-5-gc2189d7+4,12.1-6-ga608fc0+1,12.1-7-g3349e2a+5,12.1-7-gfd75620+9,12.1-9-g577b946+5,12.1-9-gc4df26a+10
LSSTDataManagementBasePackage
baseline.py
Go to the documentation of this file.
1 from builtins import zip
2 from builtins import str
3 from builtins import range
4 from builtins import object
5 #!/usr/bin/env python
6 #
7 # LSST Data Management System
8 # Copyright 2008-2016 AURA/LSST.
9 #
10 # This product includes software developed by the
11 # LSST Project (http://www.lsst.org/).
12 #
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
17 #
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
22 #
23 # You should have received a copy of the LSST License Statement and
24 # the GNU General Public License along with this program. If not,
25 # see <https://www.lsstcorp.org/LegalNotices/>.
26 #
27 import math # FIXME: remove this line and replace all uses of math
28 from collections import OrderedDict
29 import numpy as np
30 
32 import lsst.afw.image as afwImage
33 import lsst.afw.detection as afwDet
34 import lsst.afw.geom as afwGeom
35 import lsst.afw.math as afwMath
36 
37 from . import plugins
38 
39 DEFAULT_PLUGINS = [
40  plugins.DeblenderPlugin(plugins.fitPsfs),
41  plugins.DeblenderPlugin(plugins.buildSymmetricTemplates),
42  plugins.DeblenderPlugin(plugins.medianSmoothTemplates),
43  plugins.DeblenderPlugin(plugins.makeTemplatesMonotonic),
44  plugins.DeblenderPlugin(plugins.clipFootprintsToNonzero),
45  plugins.DeblenderPlugin(plugins.reconstructTemplates, onReset=5),
46  plugins.DeblenderPlugin(plugins.apportionFlux),
47 ]
48 
49 class DeblenderResult(object):
50  """Collection of objects in multiple bands for a single parent footprint
51  """
52 
53  def __init__(self, footprints, maskedImages, psfs, psffwhms, log, filters=None,
54  maxNumberOfPeaks=0, avgNoise=None):
55  """ Initialize a DeblededParent
56 
57  Parameters
58  ----------
59  footprint: list of `afw.detection.Footprint`s
60  Parent footprint to deblend in each band.
61  maskedImages: list of `afw.image.MaskedImageF`s
62  Masked image containing the ``footprint`` in each band.
63  psf: list of `afw.detection.Psf`s
64  Psf of the ``maskedImage`` for each band.
65  psffwhm: list of `float`s
66  FWHM of the ``maskedImage``'s ``psf`` in each band.
67  filters: list of `string`, optional
68  Names of the filters. This should be the same length as the other lists
69  maxNumberOfPeaks: `int`, optional
70  If positive, the maximum number of peaks to deblend.
71  If the total number of peaks is greater than ``maxNumberOfPeaks``,
72  then only the first ``maxNumberOfPeaks`` sources are deblended.
73  The default is 0, which deblends all of the peaks.
74  avgNoise: `float`or list of `float`s, optional
75  Average noise level in each ``maskedImage``.
76  The default is ``None``, which estimates the noise from the median value of the
77  variance plane of ``maskedImage`` for each filter.
78  Returns
79  -------
80  None
81  """
82  # Check if this is collection of footprints in multiple bands or a single footprint
83  try:
84  len(footprints)
85  except TypeError:
86  footprints = [footprints]
87  try:
88  len(maskedImages)
89  except TypeError:
90  maskedImages = [maskedImages]
91  try:
92  len(psfs)
93  except TypeError:
94  psfs = [psfs]
95  try:
96  len(psffwhms)
97  except TypeError:
98  psffwhms = [psffwhms]
99  try:
100  len(avgNoise)
101  except TypeError:
102  avgNoise = [avgNoise]
103  # Now check that all of the parameters have the same number of entries
104  if any([len(footprints)!=len(p) for p in [maskedImages, psfs, psffwhms, avgNoise]]):
105  raise ValueError("To use the multi-color deblender, "
106  "'footprint', 'maskedImage', 'psf', 'psffwhm', 'avgNoise'"
107  "must have the same length, but instead have lengths: "
108  "{0}".format([len(p) for p in [footprints,
109  maskedImages,
110  psfs,
111  psffwhms,
112  avgNoise]]))
113 
114  self.log = log
115  self.filterCount = len(footprints)
116 
117  self.peakCount = len(footprints[0].getPeaks())
118  if maxNumberOfPeaks>0 and maxNumberOfPeaks<self.peakCount:
119  self.peakCount = maxNumberOfPeaks
120 
121  if filters is None:
122  filters = range(len(footprints))
123  self.filters = filters
124 
125  # Create a DeblendedParent for the Footprint in every filter
126  self.deblendedParents = OrderedDict([])
127  for n in range(self.filterCount):
128  f = self.filters[n]
129  dp = DeblendedParent(f, footprints[n], maskedImages[n], psfs[n],
130  psffwhms[n], avgNoise[n], maxNumberOfPeaks, self)
131  self.deblendedParents[self.filters[n]] = dp
132 
133  # Group the peaks in each color
134  for idx in range(self.peakCount):
135  peakDict = {f: dp.peaks[idx] for f,dp in self.deblendedParents.items()}
136  multiPeak = MultiColorPeak(peakDict, idx, self)
137 
138  def getParentProperty(self, propertyName):
139  """Get the footprint in each filter"""
140  return [getattr(fp, propertyName) for dp in self.deblendedParents]
141 
142  def setTemplateSums(self, templateSums, fidx=None):
143  if fidx is not None:
144  self.templateSums[fidx] = templateSums
145  else:
146  for f, templateSum in templateSums.items():
147  self.deblendedParents[f].templateSum = templateSum
148 
149 class DeblendedParent(object):
150  """Deblender result of a single parent footprint, in a single band
151 
152  Convenience class to store useful objects used by the deblender for a single band,
153  such as the maskedImage, psf, etc as well as the results from the deblender.
154  """
155  def __init__(self, filterName, footprint, maskedImage, psf, psffwhm, avgNoise,
156  maxNumberOfPeaks, debResult):
157  """Create a DeblendedParent to store a deblender result
158 
159  Parameters
160  ----------
161  filterName: `str`
162  Name of the filter used for `maskedImage`
163  footprint: list of `afw.detection.Footprint`s
164  Parent footprint to deblend in each band.
165  maskedImages: list of `afw.image.MaskedImageF`s
166  Masked image containing the ``footprint`` in each band.
167  psf: list of `afw.detection.Psf`s
168  Psf of the ``maskedImage`` for each band.
169  psffwhm: list of `float`s
170  FWHM of the ``maskedImage``'s ``psf`` in each band.
171  avgNoise: `float`or list of `float`s, optional
172  Average noise level in each ``maskedImage``.
173  The default is ``None``, which estimates the noise from the median value of the
174  variance plane of ``maskedImage`` for each filter.
175  maxNumberOfPeaks: `int`, optional
176  If positive, the maximum number of peaks to deblend.
177  If the total number of peaks is greater than ``maxNumberOfPeaks``,
178  then only the first ``maxNumberOfPeaks`` sources are deblended.
179  The default is 0, which deblends all of the peaks.
180  debResult: `DeblenderResult`
181  The ``DeblenderResult`` that contains this ``DeblendedParent``.
182  """
183  self.filter = filterName
184  self.fp = footprint
185  self.fp.normalize()
186  self.maskedImage = maskedImage
187  self.psf = psf
188  self.psffwhm = psffwhm
189  self.img = maskedImage.getImage()
190  self.imbb = self.img.getBBox()
191  self.varimg = maskedImage.getVariance()
192  self.mask = maskedImage.getMask()
193  self.avgNoise = avgNoise
194  self.updateFootprintBbox()
195  self.debResult = debResult
196  self.peakCount = debResult.peakCount
197  self.templateSum = None
198 
199  # avgNoise is an estiamte of the average noise level for the image in this filter
200  if avgNoise is None:
201  stats = afwMath.makeStatistics(self.varimg, self.mask, afwMath.MEDIAN)
202  avgNoise = math.sqrt(stats.getValue(afwMath.MEDIAN))
203  debResult.log.trace('Estimated avgNoise for filter %s = %f', self.filter, avgNoise)
204  self.avgNoise = avgNoise
205 
206  # Store all of the peak information in a single object
207  self.peaks = []
208  peaks = self.fp.getPeaks()
209  for idx in range(self.peakCount):
210  deblendedPeak = DeblendedPeak(peaks[idx], idx, self)
211  self.peaks.append(deblendedPeak)
212 
214  """Update the bounding box of the parent footprint
215 
216  If the parent Footprint is resized it will be necessary to update the bounding box of the footprint.
217  """
218  # Pull out the image bounds of the parent Footprint
219  self.bb = self.fp.getBBox()
220  if not self.imbb.contains(self.bb):
221  raise ValueError(('Footprint bounding-box %s extends outside image bounding-box %s') %
222  (str(self.bb), str(self.imbb)))
223  self.W, self.H = self.bb.getWidth(), self.bb.getHeight()
224  self.x0, self.y0 = self.bb.getMinX(), self.bb.getMinY()
225  self.x1, self.y1 = self.bb.getMaxX(), self.bb.getMaxY()
226 
227 class MultiColorPeak(object):
228  """Collection of single peak deblender results in multiple bands.
229 
230  There is one of these objects for each Peak in the footprint.
231  """
232 
233  def __init__(self, peaks, pki, parent):
234  """Create a collection for deblender results in each band.
235 
236  Parameters
237  ----------
238  peaks: `dict` of `afw.detection.PeakRecord`
239  Each entry in ``peaks`` is a peak record for the same peak in a different band
240  pki: int
241  Index of the peak in `parent.peaks`
242  parent: `DeblendedParent`
243  DeblendedParent object that contains the peak as a child.
244 
245  Returns
246  -------
247  None
248 
249  """
250  self.filters = list(peaks.keys())
251  self.deblendedPeaks = peaks
252  self.parent = parent
253  for pki, peak in self.deblendedPeaks.items():
254  peak.multiColorPeak = self
255 
256  # Fields common to the peak in all bands that will be set by the deblender
257  # In the future this is likely to contain information about the probability of the peak
258  # being a point source, color-information about templateFootprints, etc.
259  self.pki = pki
260  self.skip = False
261  self.deblendedAsPsf = False
262 
263 class DeblendedPeak(object):
264  """Result of deblending a single Peak within a parent Footprint.
265 
266  There is one of these objects for each Peak in the Footprint.
267  """
268 
269  def __init__(self, peak, pki, parent, multiColorPeak=None):
270  """Initialize a new deblended peak in a single filter band
271 
272  Parameters
273  ----------
274  peak: `afw.detection.PeakRecord`
275  Peak object in a single band from a peak record
276  pki: `int`
277  Index of the peak in `multiColorPeak.parent.peaks`
278  parent: `DeblendedParent`
279  Parent in the same filter that contains the peak
280  multiColorPeak: `MultiColorPeak`
281  Object containing the same peak in multiple bands
282 
283  Returns
284  -------
285  None
286  """
287  # Peak object
288  self.peak = peak
289  # int, peak index number
290  self.pki = pki
291  self.parent = parent
292  self.multiColorPeak = multiColorPeak
293  # union of all the ways of failing...
294  self.skip = False
295 
296  self.outOfBounds = False
297  self.tinyFootprint = False
298  self.noValidPixels = False
299  self.deblendedAsPsf = False
300  self.degenerate = False
301 
302  # Field set during _fitPsf:
303  self.psfFitFailed = False
304  self.psfFitBadDof = False
305  # (chisq, dof) for PSF fit without decenter
306  self.psfFit1 = None
307  # (chisq, dof) for PSF fit with decenter
308  self.psfFit2 = None
309  # (chisq, dof) for PSF fit after applying decenter
310  self.psfFit3 = None
311  # decentered PSF fit wanted to move the center too much
312  self.psfFitBigDecenter = False
313  # was the fit with decenter better?
314  self.psfFitWithDecenter = False
315  #
316  self.psfFitR0 = None
317  self.psfFitR1 = None
318  self.psfFitStampExtent = None
319  self.psfFitCenter = None
320  self.psfFitBest = None
321  self.psfFitParams = None
322  self.psfFitFlux = None
323  self.psfFitNOthers = None
324 
325  # Things only set in _fitPsf when debugging is turned on:
326  self.psfFitDebugPsf0Img = None
327  self.psfFitDebugPsfImg = None
330 
332 
333  # The actual template Image and Footprint
334  self.templateImage = None
335  self.templateFootprint = None
336 
337  # The flux assigned to this template -- a MaskedImage
338  self.fluxPortion = None
339 
340  # The stray flux assigned to this template (may be None), a HeavyFootprint
341  self.strayFlux = None
342 
343  self.hasRampedTemplate = False
344 
345  self.patched = False
346 
347  # debug -- a copy of the original symmetric template
348  self.origTemplate = None
349  self.origFootprint = None
350  # MaskedImage
351  self.rampedTemplate = None
352  # MaskedImage
354 
355  # when least-squares fitting templates, the template weight.
356  self.templateWeight = 1.0
357 
358  def __str__(self):
359  return (('deblend result: outOfBounds: %s, deblendedAsPsf: %s') %
360  (self.outOfBounds, self.deblendedAsPsf))
361 
362  @property
363  def psfFitChisq(self):
364  chisq, dof = self.psfFitBest
365  return chisq
366 
367  @property
368  def psfFitDof(self):
369  chisq, dof = self.psfFitBest
370  return dof
371 
372  def getFluxPortion(self, strayFlux=True):
373  """!
374  Return a HeavyFootprint containing the flux apportioned to this peak.
375 
376  @param[in] strayFlux include stray flux also?
377  """
378  if self.templateFootprint is None or self.fluxPortion is None:
379  return None
381  if strayFlux:
382  if self.strayFlux is not None:
383  heavy.normalize()
384  self.strayFlux.normalize()
385  heavy = afwDet.mergeHeavyFootprintsF(heavy, self.strayFlux)
386 
387  return heavy
388 
389  def setStrayFlux(self, stray):
390  self.strayFlux = stray
391 
392  def setFluxPortion(self, mimg):
393  self.fluxPortion = mimg
394 
395  def setTemplateWeight(self, w):
396  self.templateWeight = w
397 
398  def setPatched(self):
399  self.patched = True
400 
401  # DEBUG
402  def setOrigTemplate(self, t, tfoot):
403  self.origTemplate = t.Factory(t, True)
404  self.origFootprint = tfoot
405 
406  def setRampedTemplate(self, t, tfoot):
407  self.hasRampedTemplate = True
408  self.rampedTemplate = t.Factory(t, True)
409 
410  def setMedianFilteredTemplate(self, t, tfoot):
411  self.medianFilteredTemplate = t.Factory(t, True)
412 
413  def setPsfTemplate(self, tim, tfoot):
415  self.psfTemplate = tim.Factory(tim, True)
416 
417  def setOutOfBounds(self):
418  self.outOfBounds = True
419  self.skip = True
420 
421  def setTinyFootprint(self):
422  self.tinyFootprint = True
423  self.skip = True
424 
425  def setNoValidPixels(self):
426  self.noValidPixels = True
427  self.skip = True
428 
429  def setPsfFitFailed(self):
430  self.psfFitFailed = True
431 
432  def setBadPsfDof(self):
433  self.psfFitBadDof = True
434 
435  def setDeblendedAsPsf(self):
436  self.deblendedAsPsf = True
437 
439  self.failedSymmetricTemplate = True
440  self.skip = True
441 
442  def setTemplate(self, image, footprint):
443  self.templateImage = image
444  self.templateFootprint = footprint
445 
446 def deblend(footprint, maskedImage, psf, psffwhm, filters=None,
447  psfChisqCut1=1.5, psfChisqCut2=1.5, psfChisqCut2b=1.5, fitPsfs=True,
448  medianSmoothTemplate=True, medianFilterHalfsize=2,
449  monotonicTemplate=True, weightTemplates=False,
450  log=None, verbose=False, sigma1=None, maxNumberOfPeaks=0,
451  assignStrayFlux=True, strayFluxToPointSources='necessary', strayFluxAssignment='r-to-peak',
452  rampFluxAtEdge=False, patchEdges=False, tinyFootprintSize=2,
453  getTemplateSum=False, clipStrayFluxFraction=0.001, clipFootprintToNonzero=True,
454  removeDegenerateTemplates=False, maxTempDotProd=0.5
455  ):
456  """Deblend a parent ``Footprint`` in a ``MaskedImageF``.
457 
458  Deblending assumes that ``footprint`` has multiple peaks, as it will still create a
459  `PerFootprint` object with a list of peaks even if there is only one peak in the list.
460  It is recommended to first check that ``footprint`` has more than one peak, similar to the
461  execution of `lsst.meas.deblender.deblend.SourceDeblendTask`.
462 
463  .. note::
464  This is the API for the old deblender, however the function builds the plugins necessary
465  to use the new deblender to perform identically to the old deblender.
466  To test out newer functionality use ``newDeblend`` instead.
467 
468  Deblending involves several mandatory and optional steps:
469  # Optional: If ``fitPsfs`` is True, find all peaks that are well-fit by a PSF + background model
470  * Peaks that pass the cuts have their footprints modified to the PSF + background model
471  and their ``deblendedAsPsf`` property set to ``True``.
472  * Relevant parameters: ``psfChisqCut1``, ``psfChisqCut2``, ``psfChisqCut2b``,
473  ``tinyFootprintSize``.
474  * See the parameter descriptions for more.
475  # Build a symmetric template for each peak not well-fit by the PSF model
476  * Given ``maskedImageF``, ``footprint``, and a ``DeblendedPeak``, creates a symmetric
477  template (``templateImage`` and ``templateFootprint``) around the peak
478  for all peaks not flagged as ``skip`` or ``deblendedAsPsf``.
479  * If ``patchEdges=True`` and if ``footprint`` touches pixels with the
480  ``EDGE`` bit set, then ``footprint`` is grown to include spans whose
481  symmetric mirror is outside of the image.
482  * Relevant parameters: ``sigma1`` and ``patchEdges``.
483  # Optional: If ``rampFluxAtEdge`` is True, adjust flux on the edges of the template footprints
484  * Using the PSF, a peak ``Footprint`` with pixels on the edge of of ``footprint``
485  is grown by the psffwhm*1.5 and filled in with zeros.
486  * The result is a new symmetric footprint template for the peaks near the edge.
487  * Relevant parameter: ``patchEdges``.
488  # Optionally (``medianSmoothTemplate=True``) filter the template images
489  * Apply a median smoothing filter to all of the template images.
490  * Relevant parameters: ``medianFilterHalfSize``
491  # Optional: If ``monotonicTemplate`` is True, make the templates monotonic.
492  * The pixels in the templates are modified such that pixels
493  further from the peak will have values smaller than those closer to the peak.
494  # Optional: If ``clipFootprintToNonzero`` is True, clip non-zero spans in the template footprints
495  * Peak ``Footprint``s are clipped to the region in the image containing non-zero values
496  by dropping spans that are completely zero and moving endpoints to non-zero pixels
497  (but does not split spans that have internal zeros).
498  # Optional: If ``weightTemplates`` is True, weight the templates to best fit the observed image
499  * Re-weight the templates so that their linear combination
500  best represents the observed ``maskedImage``
501  # Optional: If ``removeDegenerateTempaltes`` is True, reconstruct shredded galaxies
502  * If galaxies have substructure, such as face-on spirals, the process of identifying peaks can
503  "shred" the galaxy into many pieces. The templates of shredded galaxies are typically quite
504  similar because they represent the same galaxy, so we try to identify these "degenerate" peaks
505  by looking at the inner product (in pixel space) of pairs of templates.
506  * If they are nearly parallel, we only keep one of the peaks and reject the other.
507  * If only one of the peaks is a PSF template, the other template is used,
508  otherwise the one with the maximum template value is kept.
509  * Relevant parameters: ``maxTempDotProduct``
510  # Apportion flux to all of the peak templates
511  * Divide the ``maskedImage`` flux amongst all of the templates based on the fraction of
512  flux assigned to each ``tempalteFootprint``.
513  * Leftover "stray flux" is assigned to peaks based on the other parameters.
514  * Relevant parameters: ``clipStrayFluxFraction``, ``strayFluxAssignment``,
515  ``strayFluxToPointSources``, ``assignStrayFlux``
516 
517  Parameters
518  ----------
519  footprint: `afw.detection.Footprint`
520  Parent footprint to deblend
521  maskedImage: `afw.image.MaskedImageF`
522  Masked image containing the ``footprint``
523  psf: `afw.detection.Psf`
524  Psf of the ``maskedImage``
525  psffwhm: `float`
526  FWHM of the ``maskedImage``'s ``psf``
527  psfChisqCut*: `float`, optional
528  If ``fitPsfs==True``, all of the peaks are fit to the image PSF.
529  ``psfChisqCut1`` is the maximum chi-squared-per-degree-of-freedom allowed for a peak to
530  be considered a PSF match without recentering.
531  A fit is also made that includes terms to recenter the PSF.
532  ``psfChisqCut2`` is the same as ``psfChisqCut1`` except it determines the restriction on the
533  fit that includes recentering terms.
534  If the peak is a match for a re-centered PSF, the PSF is repositioned at the new center and
535  the peak footprint is fit again, this time to the new PSF.
536  If the resulting chi-squared-per-degree-of-freedom is less than ``psfChisqCut2b`` then it
537  passes the re-centering algorithm.
538  If the peak passes both the re-centered and fixed position cuts, the better of the two is accepted,
539  but parameters for all three psf fits are stored in the ``DeblendedPeak``.
540  The default for ``psfChisqCut1``, ``psfChisqCut2``, and ``psfChisqCut2b`` is ``1.5``.
541  fitPsfs: `bool`, optional
542  If True then all of the peaks will be compared to the image PSF to
543  distinguish stars from galaxies.
544  medianSmoothTemplate: ``bool``, optional
545  If ``medianSmoothTemplate==True`` it a median smoothing filter is applied to the ``maskedImage``.
546  The default is ``True``.
547  medianFilterHalfSize: `int`, optional
548  Half the box size of the median filter, ie a ``medianFilterHalfSize`` of 50 means that
549  each output pixel will be the median of the pixels in a 101 x 101-pixel box in the input image.
550  This parameter is only used when ``medianSmoothTemplate==True``, otherwise it is ignored.
551  The default value is 2.
552  monotonicTempalte: `bool`, optional
553  If True then make the template monotonic.
554  The default is True.
555  weightTemplates: `bool`, optional
556  If True, re-weight the templates so that their linear combination best represents
557  the observed ``maskedImage``.
558  The default is False.
559  log: `log.Log`, optional
560  LSST logger for logging purposes.
561  The default is ``None`` (no logging).
562  verbose: `bool`, optional
563  Whether or not to show a more verbose output.
564  The default is ``False``.
565  sigma1: `float`, optional
566  Average noise level in ``maskedImage``.
567  The default is ``None``, which estimates the noise from the median value of ``maskedImage``.
568  maxNumberOfPeaks: `int`, optional
569  If nonzero, the maximum number of peaks to deblend.
570  If the total number of peaks is greater than ``maxNumberOfPeaks``,
571  then only the first ``maxNumberOfPeaks`` sources are deblended.
572  The default is 0, which deblends all of the peaks.
573  assignStrayFlux: `bool`, optional
574  If True then flux in the parent footprint that is not covered by any of the
575  template footprints is assigned to templates based on their 1/(1+r^2) distance.
576  How the flux is apportioned is determined by ``strayFluxAssignment``.
577  The default is True.
578  strayFluxToPointSources: `string`
579  Determines how stray flux is apportioned to point sources
580  * ``never``: never apportion stray flux to point sources
581  * ``necessary`` (default): point sources are included only if there are no extended sources nearby
582  * ``always``: point sources are always included in the 1/(1+r^2) splitting
583  strayFluxAssignment: `string`, optional
584  Determines how stray flux is apportioned.
585  * ``trim``: Trim stray flux and do not include in any footprints
586  * ``r-to-peak`` (default): Stray flux is assigned based on (1/(1+r^2) from the peaks
587  * ``r-to-footprint``: Stray flux is distributed to the footprints based on 1/(1+r^2) of the
588  minimum distance from the stray flux to footprint
589  * ``nearest-footprint``: Stray flux is assigned to the footprint with lowest L-1 (Manhattan)
590  distance to the stray flux
591  rampFluxAtEdge: `bool`, optional
592  If True then extend footprints with excessive flux on the edges as described above.
593  The default is False.
594  patchEdges: `bool`, optional
595  If True and if the footprint touches pixels with the ``EDGE`` bit set,
596  then grow the footprint to include all symmetric templates.
597  The default is ``False``.
598  tinyFootprintSize: `float`, optional
599  The PSF model is shrunk to the size that contains the original footprint.
600  If the bbox of the clipped PSF model for a peak is smaller than ``max(tinyFootprintSize,2)``
601  then ``tinyFootprint`` for the peak is set to ``True`` and the peak is not fit.
602  The default is 2.
603  getTemplateSum: `bool`, optional
604  As part of the flux calculation, the sum of the templates is calculated.
605  If ``getTemplateSum==True`` then the sum of the templates is stored in the result (a `PerFootprint`).
606  The default is False.
607  clipStrayFluxFraction: `float`, optional
608  Minimum stray-flux portion.
609  Any stray-flux portion less than ``clipStrayFluxFraction`` is clipped to zero.
610  The default is 0.001.
611  clipFootprintToNonzero: `bool`, optional
612  If True then clip non-zero spans in the template footprints. See above for more.
613  The default is True.
614  removeDegenerateTemplates: `bool`, optional
615  If True then we try to identify "degenerate" peaks by looking at the inner product
616  (in pixel space) of pairs of templates.
617  The default is False.
618  maxTempDotProduct: `float`, optional
619  All dot products between templates greater than ``maxTempDotProduct`` will result in one
620  of the templates removed. This parameter is only used when ``removeDegenerateTempaltes==True``.
621  The default is 0.5.
622 
623  Returns
624  -------
625  res: `PerFootprint`
626  Deblender result that contains a list of ``DeblendedPeak``s for each peak and (optionally)
627  the template sum.
628  """
629  avgNoise = sigma1
630 
631  debPlugins = []
632 
633  # Add activated deblender plugins
634  if fitPsfs:
635  debPlugins.append(plugins.DeblenderPlugin(plugins.fitPsfs,
636  psfChisqCut1=psfChisqCut1,
637  psfChisqCut2=psfChisqCut2,
638  psfChisqCut2b=psfChisqCut2b,
639  tinyFootprintSize=tinyFootprintSize))
640  debPlugins.append(plugins.DeblenderPlugin(plugins.buildSymmetricTemplates, patchEdges=patchEdges))
641  if rampFluxAtEdge:
642  debPlugins.append(plugins.DeblenderPlugin(plugins.rampFluxAtEdge, patchEdges=patchEdges))
643  if medianSmoothTemplate:
644  debPlugins.append(plugins.DeblenderPlugin(plugins.medianSmoothTemplates,
645  medianFilterHalfsize=medianFilterHalfsize))
646  if monotonicTemplate:
647  debPlugins.append(plugins.DeblenderPlugin(plugins.makeTemplatesMonotonic))
648  if clipFootprintToNonzero:
649  debPlugins.append(plugins.DeblenderPlugin(plugins.clipFootprintsToNonzero))
650  if weightTemplates:
651  debPlugins.append(plugins.DeblenderPlugin(plugins.weightTemplates))
652  if removeDegenerateTemplates:
653  if weightTemplates:
654  onReset = len(debPlugins)-1
655  else:
656  onReset = len(debPlugins)
657  debPlugins.append(plugins.DeblenderPlugin(plugins.reconstructTemplates,
658  onReset=onReset,
659  maxTempDotProd=maxTempDotProd))
660  debPlugins.append(plugins.DeblenderPlugin(plugins.apportionFlux,
661  clipStrayFluxFraction=clipStrayFluxFraction,
662  assignStrayFlux=assignStrayFlux,
663  strayFluxAssignment=strayFluxAssignment,
664  strayFluxToPointSources=strayFluxToPointSources,
665  getTemplateSum=getTemplateSum))
666 
667  debResult = newDeblend(debPlugins, footprint, maskedImage, psf, psffwhm, filters, log, verbose, avgNoise)
668 
669  return debResult
670 
671 def newDeblend(debPlugins, footprint, maskedImage, psf, psffwhm, filters=None,
672  log=None, verbose=False, avgNoise=None, maxNumberOfPeaks=0):
673  """Deblend a parent ``Footprint`` in a ``MaskedImageF``.
674 
675  Deblending assumes that ``footprint`` has multiple peaks, as it will still create a
676  `PerFootprint` object with a list of peaks even if there is only one peak in the list.
677  It is recommended to first check that ``footprint`` has more than one peak, similar to the
678  execution of `lsst.meas.deblender.deblend.SourceDeblendTask`.
679 
680  This version of the deblender accepts a list of plugins to execute, with the option to re-run parts
681  of the deblender if templates are changed during any of the steps.
682 
683  Parameters
684  ----------
685  debPlugins: list of `meas.deblender.plugins.DeblenderPlugins`
686  Plugins to execute (in order of execution) for the deblender.
687  footprint: `afw.detection.Footprint` or list of Footprints
688  Parent footprint to deblend.
689  maskedImage: `afw.image.MaskedImageF` or list of MaskedImages
690  Masked image containing the ``footprint``.
691  psf: `afw.detection.Psf` or list of Psfs
692  Psf of the ``maskedImage``.
693  psffwhm: `float` or list of floats
694  FWHM of the ``maskedImage``'s ``psf``.
695  filters: list of `string`s, optional
696  Names of the filters when ``footprint`` is a list instead of a single ``footprint``.
697  This is used to index the deblender results by filter.
698  The default is None, which will numerically index lists of objects.
699  log: `log.Log`, optional
700  LSST logger for logging purposes.
701  The default is ``None`` (no logging).
702  verbose: `bool`, optional
703  Whether or not to show a more verbose output.
704  The default is ``False``.
705  avgNoise: `float`or list of `float`s, optional
706  Average noise level in each ``maskedImage``.
707  The default is ``None``, which estimates the noise from the median value of the
708  variance plane of ``maskedImage`` for each filter.
709  maxNumberOfPeaks: `int`, optional
710  If nonzero, the maximum number of peaks to deblend.
711  If the total number of peaks is greater than ``maxNumberOfPeaks``,
712  then only the first ``maxNumberOfPeaks`` sources are deblended.
713 
714  Returns
715  -------
716  debResult: `DeblendedParent`
717  Deblender result that contains a list of ``MultiColorPeak``s for each peak and
718  information that describes the footprint in all filters.
719  """
720  # Import C++ routines
721  import lsst.meas.deblender as deb
722  butils = deb.BaselineUtilsF
723 
724  if log is None:
725  import lsst.log as lsstLog
726 
727  component = 'meas_deblender.baseline'
728  log = lsstLog.Log.getLogger(component)
729 
730  if verbose:
731  log.setLevel(lsstLog.Log.TRACE)
732 
733  # get object that will hold our results
734  debResult = DeblenderResult(footprint, maskedImage, psf, psffwhm, log, filters=filters,
735  maxNumberOfPeaks=maxNumberOfPeaks, avgNoise=avgNoise)
736 
737  step = 0
738  while step < len(debPlugins):
739  reset = debPlugins[step].run(debResult, log)
740  if reset:
741  step = debPlugins[step].onReset
742  else:
743  step+=1
744 
745  return debResult
746 
747 
748 class CachingPsf(object):
749  """Cache the PSF models
750 
751  In the PSF fitting code, we request PSF models for all peaks near
752  the one being fit. This was turning out to be quite expensive in
753  some cases. Here, we cache the PSF models to bring the cost down
754  closer to O(N) rather than O(N^2).
755  """
756 
757  def __init__(self, psf):
758  self.cache = {}
759  self.psf = psf
760 
761  def computeImage(self, cx, cy):
762  im = self.cache.get((cx, cy), None)
763  if im is not None:
764  return im
765  try:
766  im = self.psf.computeImage(afwGeom.Point2D(cx, cy))
768  im = self.psf.computeImage()
769  self.cache[(cx, cy)] = im
770  return im
bool any(CoordinateExpr< N > const &expr)
Return true if any elements are true.
def getFluxPortion
Return a HeavyFootprint containing the flux apportioned to this peak.
Definition: baseline.py:372
A set of pixels in an Image.
Definition: Footprint.h:62
def run
Exit with the status code resulting from running the provided test suite.
Definition: tests.py:77
Statistics makeStatistics(afwImage::Mask< afwImage::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl)
Specialization to handle Masks.
Definition: Statistics.cc:1107
HeavyFootprint< ImagePixelT, MaskPixelT, VariancePixelT > makeHeavyFootprint(Footprint const &foot, lsst::afw::image::MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > const &img, HeavyFootprintCtrl const *ctrl=NULL)
Create a HeavyFootprint with footprint defined by the given Footprint and pixel values from the given...