22 from collections
import OrderedDict
45 """Collection of objects in multiple bands for a single parent footprint 48 def __init__(self, footprint, mMaskedImage, psfs, psffwhms, log,
49 maxNumberOfPeaks=0, avgNoise=None):
50 """ Initialize a DeblededParent 54 footprint: `afw.detection.Footprint` 55 Parent footprint to deblend. While `maskedImages`, `psfs`, 56 and `psffwhms` are lists of objects, one for each band, 57 `footprint` is a single parent footprint (from a `mergedDet`) 58 this is used for all bands. 59 mMaskedImage: `MaskedImage`s or `MultibandMaskedImage` 60 Masked image containing the ``footprint`` in each band. 61 psfs: list of `afw.detection.Psf`s 62 Psf of the ``maskedImage`` for each band. 63 psffwhm: list of `float`s 64 FWHM of the ``maskedImage``'s ``psf`` in each band. 65 maxNumberOfPeaks: `int`, optional 66 If positive, the maximum number of peaks to deblend. 67 If the total number of peaks is greater than ``maxNumberOfPeaks``, 68 then only the first ``maxNumberOfPeaks`` sources are deblended. 69 The default is 0, which deblends all of the peaks. 70 avgNoise: `float`or list of `float`s, optional 71 Average noise level in each ``maskedImage``. 72 The default is ``None``, which estimates the noise from the median value of the 73 variance plane of ``maskedImage`` for each filter. 80 mMaskedImage = [mMaskedImage]
83 self.
filters = mMaskedImage.filters
96 avgNoise = [
None]*len(psfs)
100 if any([len(self.
filters) != len(p)
for p
in [psfs, psffwhms, avgNoise]]):
101 raise ValueError(
"To use the multi-color deblender, " 102 "'maskedImage', 'psf', 'psffwhm', 'avgNoise'" 103 "must have the same length, but instead have lengths: " 104 "{0}".
format([len(p)
for p
in [mMaskedImage,
115 if maxNumberOfPeaks > 0
and maxNumberOfPeaks < self.
peakCount:
120 for n, f
in enumerate(self.
filters):
123 psffwhms[n], avgNoise[n], maxNumberOfPeaks, self)
138 """Get the footprint in each filter""" 143 self.templateSums[fidx] = templateSums
145 for f, templateSum
in templateSums.items():
150 """Deblender result of a single parent footprint, in a single band 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. 155 def __init__(self, filterName, footprint, maskedImage, psf, psffwhm, avgNoise,
156 maxNumberOfPeaks, debResult):
157 """Create a DeblendedParent to store a deblender result 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``. 188 self.
img = maskedImage.getImage()
191 self.
mask = maskedImage.getMask()
201 avgNoise = np.sqrt(stats.getValue(afwMath.MEDIAN))
202 debResult.log.trace(
'Estimated avgNoise for filter %s = %f', self.
filter, avgNoise)
207 peaks = self.
fp.getPeaks()
213 """Update the bounding box of the parent footprint 215 If the parent Footprint is resized it will be necessary to update the bounding box of the footprint. 220 raise ValueError((
'Footprint bounding-box %s extends outside image bounding-box %s') %
222 self.W, self.
H = self.
bb.getWidth(), self.
bb.getHeight()
223 self.x0, self.
y0 = self.
bb.getMinX(), self.
bb.getMinY()
224 self.x1, self.
y1 = self.
bb.getMaxX(), self.
bb.getMaxY()
228 """Collection of single peak deblender results in multiple bands. 230 There is one of these objects for each Peak in the footprint. 234 """Create a collection for deblender results in each band. 238 peaks: `dict` of `afw.detection.PeakRecord` 239 Each entry in ``peaks`` is a peak record for the same peak in a different band 241 Index of the peak in `parent.peaks` 242 parent: `DeblendedParent` 243 DeblendedParent object that contains the peak as a child. 254 peak.multiColorPeak = self
267 """Result of deblending a single Peak within a parent Footprint. 269 There is one of these objects for each Peak in the Footprint. 272 def __init__(self, peak, pki, parent, multiColorPeak=None):
273 """Initialize a new deblended peak in a single filter band 277 peak: `afw.detection.PeakRecord` 278 Peak object in a single band from a peak record 280 Index of the peak in `multiColorPeak.parent.peaks` 281 parent: `DeblendedParent` 282 Parent in the same filter that contains the peak 283 multiColorPeak: `MultiColorPeak` 284 Object containing the same peak in multiple bands 362 return ((
'deblend result: outOfBounds: %s, deblendedAsPsf: %s') %
377 Return a HeavyFootprint containing the flux apportioned to this peak. 379 @param[in] strayFlux include stray flux also? 448 def deblend(footprint, maskedImage, psf, psffwhm,
449 psfChisqCut1=1.5, psfChisqCut2=1.5, psfChisqCut2b=1.5, fitPsfs=True,
450 medianSmoothTemplate=True, medianFilterHalfsize=2,
451 monotonicTemplate=True, weightTemplates=False,
452 log=None, verbose=False, sigma1=None, maxNumberOfPeaks=0,
453 assignStrayFlux=True, strayFluxToPointSources='necessary', strayFluxAssignment='r-to-peak',
454 rampFluxAtEdge=False, patchEdges=False, tinyFootprintSize=2,
455 getTemplateSum=False, clipStrayFluxFraction=0.001, clipFootprintToNonzero=True,
456 removeDegenerateTemplates=False, maxTempDotProd=0.5
458 """Deblend a parent ``Footprint`` in a ``MaskedImageF``. 460 Deblending assumes that ``footprint`` has multiple peaks, as it will still create a 461 `PerFootprint` object with a list of peaks even if there is only one peak in the list. 462 It is recommended to first check that ``footprint`` has more than one peak, similar to the 463 execution of `lsst.meas.deblender.deblend.SourceDeblendTask`. 466 This is the API for the old deblender, however the function builds the plugins necessary 467 to use the new deblender to perform identically to the old deblender. 468 To test out newer functionality use ``newDeblend`` instead. 470 Deblending involves several mandatory and optional steps: 471 # Optional: If ``fitPsfs`` is True, find all peaks that are well-fit by a PSF + background model 472 * Peaks that pass the cuts have their footprints modified to the PSF + background model 473 and their ``deblendedAsPsf`` property set to ``True``. 474 * Relevant parameters: ``psfChisqCut1``, ``psfChisqCut2``, ``psfChisqCut2b``, 475 ``tinyFootprintSize``. 476 * See the parameter descriptions for more. 477 # Build a symmetric template for each peak not well-fit by the PSF model 478 * Given ``maskedImageF``, ``footprint``, and a ``DeblendedPeak``, creates a symmetric 479 template (``templateImage`` and ``templateFootprint``) around the peak 480 for all peaks not flagged as ``skip`` or ``deblendedAsPsf``. 481 * If ``patchEdges=True`` and if ``footprint`` touches pixels with the 482 ``EDGE`` bit set, then ``footprint`` is grown to include spans whose 483 symmetric mirror is outside of the image. 484 * Relevant parameters: ``sigma1`` and ``patchEdges``. 485 # Optional: If ``rampFluxAtEdge`` is True, adjust flux on the edges of the template footprints 486 * Using the PSF, a peak ``Footprint`` with pixels on the edge of of ``footprint`` 487 is grown by the psffwhm*1.5 and filled in with zeros. 488 * The result is a new symmetric footprint template for the peaks near the edge. 489 * Relevant parameter: ``patchEdges``. 490 # Optionally (``medianSmoothTemplate=True``) filter the template images 491 * Apply a median smoothing filter to all of the template images. 492 * Relevant parameters: ``medianFilterHalfSize`` 493 # Optional: If ``monotonicTemplate`` is True, make the templates monotonic. 494 * The pixels in the templates are modified such that pixels 495 further from the peak will have values smaller than those closer to the peak. 496 # Optional: If ``clipFootprintToNonzero`` is True, clip non-zero spans in the template footprints 497 * Peak ``Footprint``s are clipped to the region in the image containing non-zero values 498 by dropping spans that are completely zero and moving endpoints to non-zero pixels 499 (but does not split spans that have internal zeros). 500 # Optional: If ``weightTemplates`` is True, weight the templates to best fit the observed image 501 * Re-weight the templates so that their linear combination 502 best represents the observed ``maskedImage`` 503 # Optional: If ``removeDegenerateTempaltes`` is True, reconstruct shredded galaxies 504 * If galaxies have substructure, such as face-on spirals, the process of identifying peaks can 505 "shred" the galaxy into many pieces. The templates of shredded galaxies are typically quite 506 similar because they represent the same galaxy, so we try to identify these "degenerate" peaks 507 by looking at the inner product (in pixel space) of pairs of templates. 508 * If they are nearly parallel, we only keep one of the peaks and reject the other. 509 * If only one of the peaks is a PSF template, the other template is used, 510 otherwise the one with the maximum template value is kept. 511 * Relevant parameters: ``maxTempDotProduct`` 512 # Apportion flux to all of the peak templates 513 * Divide the ``maskedImage`` flux amongst all of the templates based on the fraction of 514 flux assigned to each ``tempalteFootprint``. 515 * Leftover "stray flux" is assigned to peaks based on the other parameters. 516 * Relevant parameters: ``clipStrayFluxFraction``, ``strayFluxAssignment``, 517 ``strayFluxToPointSources``, ``assignStrayFlux`` 521 footprint: `afw.detection.Footprint` 522 Parent footprint to deblend 523 maskedImage: `afw.image.MaskedImageF` 524 Masked image containing the ``footprint`` 525 psf: `afw.detection.Psf` 526 Psf of the ``maskedImage`` 528 FWHM of the ``maskedImage``'s ``psf`` 529 psfChisqCut*: `float`, optional 530 If ``fitPsfs==True``, all of the peaks are fit to the image PSF. 531 ``psfChisqCut1`` is the maximum chi-squared-per-degree-of-freedom allowed for a peak to 532 be considered a PSF match without recentering. 533 A fit is also made that includes terms to recenter the PSF. 534 ``psfChisqCut2`` is the same as ``psfChisqCut1`` except it determines the restriction on the 535 fit that includes recentering terms. 536 If the peak is a match for a re-centered PSF, the PSF is repositioned at the new center and 537 the peak footprint is fit again, this time to the new PSF. 538 If the resulting chi-squared-per-degree-of-freedom is less than ``psfChisqCut2b`` then it 539 passes the re-centering algorithm. 540 If the peak passes both the re-centered and fixed position cuts, the better of the two is accepted, 541 but parameters for all three psf fits are stored in the ``DeblendedPeak``. 542 The default for ``psfChisqCut1``, ``psfChisqCut2``, and ``psfChisqCut2b`` is ``1.5``. 543 fitPsfs: `bool`, optional 544 If True then all of the peaks will be compared to the image PSF to 545 distinguish stars from galaxies. 546 medianSmoothTemplate: ``bool``, optional 547 If ``medianSmoothTemplate==True`` it a median smoothing filter is applied to the ``maskedImage``. 548 The default is ``True``. 549 medianFilterHalfSize: `int`, optional 550 Half the box size of the median filter, ie a ``medianFilterHalfSize`` of 50 means that 551 each output pixel will be the median of the pixels in a 101 x 101-pixel box in the input image. 552 This parameter is only used when ``medianSmoothTemplate==True``, otherwise it is ignored. 553 The default value is 2. 554 monotonicTempalte: `bool`, optional 555 If True then make the template monotonic. 557 weightTemplates: `bool`, optional 558 If True, re-weight the templates so that their linear combination best represents 559 the observed ``maskedImage``. 560 The default is False. 561 log: `log.Log`, optional 562 LSST logger for logging purposes. 563 The default is ``None`` (no logging). 564 verbose: `bool`, optional 565 Whether or not to show a more verbose output. 566 The default is ``False``. 567 sigma1: `float`, optional 568 Average noise level in ``maskedImage``. 569 The default is ``None``, which estimates the noise from the median value of ``maskedImage``. 570 maxNumberOfPeaks: `int`, optional 571 If nonzero, the maximum number of peaks to deblend. 572 If the total number of peaks is greater than ``maxNumberOfPeaks``, 573 then only the first ``maxNumberOfPeaks`` sources are deblended. 574 The default is 0, which deblends all of the peaks. 575 assignStrayFlux: `bool`, optional 576 If True then flux in the parent footprint that is not covered by any of the 577 template footprints is assigned to templates based on their 1/(1+r^2) distance. 578 How the flux is apportioned is determined by ``strayFluxAssignment``. 580 strayFluxToPointSources: `string` 581 Determines how stray flux is apportioned to point sources 582 * ``never``: never apportion stray flux to point sources 583 * ``necessary`` (default): point sources are included only if there are no extended sources nearby 584 * ``always``: point sources are always included in the 1/(1+r^2) splitting 585 strayFluxAssignment: `string`, optional 586 Determines how stray flux is apportioned. 587 * ``trim``: Trim stray flux and do not include in any footprints 588 * ``r-to-peak`` (default): Stray flux is assigned based on (1/(1+r^2) from the peaks 589 * ``r-to-footprint``: Stray flux is distributed to the footprints based on 1/(1+r^2) of the 590 minimum distance from the stray flux to footprint 591 * ``nearest-footprint``: Stray flux is assigned to the footprint with lowest L-1 (Manhattan) 592 distance to the stray flux 593 rampFluxAtEdge: `bool`, optional 594 If True then extend footprints with excessive flux on the edges as described above. 595 The default is False. 596 patchEdges: `bool`, optional 597 If True and if the footprint touches pixels with the ``EDGE`` bit set, 598 then grow the footprint to include all symmetric templates. 599 The default is ``False``. 600 tinyFootprintSize: `float`, optional 601 The PSF model is shrunk to the size that contains the original footprint. 602 If the bbox of the clipped PSF model for a peak is smaller than ``max(tinyFootprintSize,2)`` 603 then ``tinyFootprint`` for the peak is set to ``True`` and the peak is not fit. 605 getTemplateSum: `bool`, optional 606 As part of the flux calculation, the sum of the templates is calculated. 607 If ``getTemplateSum==True`` then the sum of the templates is stored in the result (a `PerFootprint`). 608 The default is False. 609 clipStrayFluxFraction: `float`, optional 610 Minimum stray-flux portion. 611 Any stray-flux portion less than ``clipStrayFluxFraction`` is clipped to zero. 612 The default is 0.001. 613 clipFootprintToNonzero: `bool`, optional 614 If True then clip non-zero spans in the template footprints. See above for more. 616 removeDegenerateTemplates: `bool`, optional 617 If True then we try to identify "degenerate" peaks by looking at the inner product 618 (in pixel space) of pairs of templates. 619 The default is False. 620 maxTempDotProduct: `float`, optional 621 All dot products between templates greater than ``maxTempDotProduct`` will result in one 622 of the templates removed. This parameter is only used when ``removeDegenerateTempaltes==True``. 628 Deblender result that contains a list of ``DeblendedPeak``s for each peak and (optionally) 638 psfChisqCut1=psfChisqCut1,
639 psfChisqCut2=psfChisqCut2,
640 psfChisqCut2b=psfChisqCut2b,
641 tinyFootprintSize=tinyFootprintSize))
645 if medianSmoothTemplate:
647 medianFilterHalfsize=medianFilterHalfsize))
648 if monotonicTemplate:
650 if clipFootprintToNonzero:
654 if removeDegenerateTemplates:
656 onReset = len(debPlugins)-1
658 onReset = len(debPlugins)
661 maxTempDotProd=maxTempDotProd))
663 clipStrayFluxFraction=clipStrayFluxFraction,
664 assignStrayFlux=assignStrayFlux,
665 strayFluxAssignment=strayFluxAssignment,
666 strayFluxToPointSources=strayFluxToPointSources,
667 getTemplateSum=getTemplateSum))
669 debResult =
newDeblend(debPlugins, footprint, maskedImage, psf, psffwhm, log, verbose, avgNoise)
674 def newDeblend(debPlugins, footprint, mMaskedImage, psfs, psfFwhms,
675 log=None, verbose=False, avgNoise=None, maxNumberOfPeaks=0):
676 """Deblend a parent ``Footprint`` in a ``MaskedImageF``. 678 Deblending assumes that ``footprint`` has multiple peaks, as it will still create a 679 `PerFootprint` object with a list of peaks even if there is only one peak in the list. 680 It is recommended to first check that ``footprint`` has more than one peak, similar to the 681 execution of `lsst.meas.deblender.deblend.SourceDeblendTask`. 683 This version of the deblender accepts a list of plugins to execute, with the option to re-run parts 684 of the deblender if templates are changed during any of the steps. 688 debPlugins: list of `meas.deblender.plugins.DeblenderPlugins` 689 Plugins to execute (in order of execution) for the deblender. 690 footprint: `afw.detection.Footprint` or list of Footprints 691 Parent footprint to deblend. 692 mMaskedImage: `MultibandMaskedImage` or `MaskedImage` 693 Masked image in each band. 694 psfs: `afw.detection.Psf` or list of Psfs 695 Psf of the ``maskedImage``. 696 psfFwhms: `float` or list of floats 697 FWHM of the ``maskedImage``'s ``psf``. 698 log: `log.Log`, optional 699 LSST logger for logging purposes. 700 The default is ``None`` (no logging). 701 verbose: `bool`, optional 702 Whether or not to show a more verbose output. 703 The default is ``False``. 704 avgNoise: `float`or list of `float`s, optional 705 Average noise level in each ``maskedImage``. 706 The default is ``None``, which estimates the noise from the median value of the 707 variance plane of ``maskedImage`` for each filter. 708 maxNumberOfPeaks: `int`, optional 709 If nonzero, the maximum number of peaks to deblend. 710 If the total number of peaks is greater than ``maxNumberOfPeaks``, 711 then only the first ``maxNumberOfPeaks`` sources are deblended. 715 debResult: `DeblendedParent` 716 Deblender result that contains a list of ``MultiColorPeak``s for each peak and 717 information that describes the footprint in all filters. 724 component =
'meas_deblender.baseline' 725 log = lsstLog.Log.getLogger(component)
728 log.setLevel(lsstLog.Log.TRACE)
731 debResult =
DeblenderResult(footprint, mMaskedImage, psfs, psfFwhms, log,
732 maxNumberOfPeaks=maxNumberOfPeaks, avgNoise=avgNoise)
735 while step < len(debPlugins):
739 if not debResult.failed:
740 reset = debPlugins[step].
run(debResult, log)
742 log.warn(
"Skipping steps {0}".
format(debPlugins[step:]))
745 step = debPlugins[step].onReset
753 """Cache the PSF models 755 In the PSF fitting code, we request PSF models for all peaks near 756 the one being fit. This was turning out to be quite expensive in 757 some cases. Here, we cache the PSF models to bring the cost down 758 closer to O(N) rather than O(N^2). 766 im = self.
cache.get((cx, cy),
None)
773 self.
cache[(cx, cy)] = im
def setDeblendedAsPsf(self)
def deblend(footprint, maskedImage, psf, psffwhm, psfChisqCut1=1.5, psfChisqCut2=1.5, psfChisqCut2b=1.5, fitPsfs=True, medianSmoothTemplate=True, medianFilterHalfsize=2, monotonicTemplate=True, weightTemplates=False, log=None, verbose=False, sigma1=None, maxNumberOfPeaks=0, assignStrayFlux=True, strayFluxToPointSources='necessary', strayFluxAssignment='r-to-peak', rampFluxAtEdge=False, patchEdges=False, tinyFootprintSize=2, getTemplateSum=False, clipStrayFluxFraction=0.001, clipFootprintToNonzero=True, removeDegenerateTemplates=False, maxTempDotProd=0.5)
def newDeblend(debPlugins, footprint, mMaskedImage, psfs, psfFwhms, log=None, verbose=False, avgNoise=None, maxNumberOfPeaks=0)
def setOrigTemplate(self, t, tfoot)
bool contains(VertexIterator const begin, VertexIterator const end, UnitVector3d const &v)
def setTemplate(self, image, footprint)
def setFailedSymmetricTemplate(self)
def setPsfFitFailed(self)
def setStrayFlux(self, stray)
def setNoValidPixels(self)
def setFluxPortion(self, mimg)
Provides consistent interface for LSST exceptions.
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
def __init__(self, footprint, mMaskedImage, psfs, psffwhms, log, maxNumberOfPeaks=0, avgNoise=None)
bool any(CoordinateExpr< N > const &expr) noexcept
Return true if any elements are true.
def setPsfTemplate(self, tim, tfoot)
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 setMedianFilteredTemplate(self, t, tfoot)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
def __init__(self, peak, pki, parent, multiColorPeak=None)
def updateFootprintBbox(self)
def __init__(self, filterName, footprint, maskedImage, psf, psffwhm, avgNoise, maxNumberOfPeaks, debResult)
def computeImage(self, cx, cy)
def __init__(self, peaks, pki, parent)
std::shared_ptr< HeavyFootprint< ImagePixelT, MaskPixelT, VariancePixelT > > mergeHeavyFootprints(HeavyFootprint< ImagePixelT, MaskPixelT, VariancePixelT > const &h1, HeavyFootprint< ImagePixelT, MaskPixelT, VariancePixelT > const &h2)
Sum the two given HeavyFootprints h1 and h2, returning a HeavyFootprint with the union footprint...
def setTemplateSums(self, templateSums, fidx=None)
def setTemplateWeight(self, w)
def setTinyFootprint(self)
def getFluxPortion(self, strayFlux=True)
Return a HeavyFootprint containing the flux apportioned to this peak.
def setRampedTemplate(self, t, tfoot)
std::vector< SchemaItem< Flag > > * items
def getParentProperty(self, propertyName)
daf::base::PropertyList * list
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...