40 __all__ = 
'SourceDeblendConfig', 
'SourceDeblendTask', 
'MultibandDeblendConfig', 
'MultibandDeblendTask'    45     edgeHandling = pexConfig.ChoiceField(
    46         doc=
'What to do when a peak to be deblended is close to the edge of the image',
    47         dtype=str, default=
'ramp',
    49             'clip': 
'Clip the template at the edge AND the mirror of the edge.',
    50             'ramp': 
'Ramp down flux at the image edge by the PSF',
    51             'noclip': 
'Ignore the edge when building the symmetric template.',
    55     strayFluxToPointSources = pexConfig.ChoiceField(
    56         doc=
'When the deblender should attribute stray flux to point sources',
    57         dtype=str, default=
'necessary',
    59             'necessary': 
'When there is not an extended object in the footprint',
    61             'never': (
'Never; stray flux will not be attributed to any deblended child '    62                       'if the deblender thinks all peaks look like point sources'),
    66     assignStrayFlux = pexConfig.Field(dtype=bool, default=
True,
    67                                       doc=
'Assign stray flux (not claimed by any child in the deblender) '    68                                           'to deblend children.')
    70     strayFluxRule = pexConfig.ChoiceField(
    71         doc=
'How to split flux among peaks',
    72         dtype=str, default=
'trim',
    74             'r-to-peak': 
'~ 1/(1+R^2) to the peak',
    75             'r-to-footprint': (
'~ 1/(1+R^2) to the closest pixel in the footprint.  '    76                                'CAUTION: this can be computationally expensive on large footprints!'),
    77             'nearest-footprint': (
'Assign 100% to the nearest footprint (using L-1 norm aka '    78                                   'Manhattan distance)'),
    79             'trim': (
'Shrink the parent footprint to pixels that are not assigned to children')
    83     clipStrayFluxFraction = pexConfig.Field(dtype=float, default=0.001,
    84                                             doc=(
'When splitting stray flux, clip fractions below '    85                                                  'this value to zero.'))
    86     psfChisq1 = pexConfig.Field(dtype=float, default=1.5, optional=
False,
    87                                 doc=(
'Chi-squared per DOF cut for deciding a source is '    88                                      'a PSF during deblending (un-shifted PSF model)'))
    89     psfChisq2 = pexConfig.Field(dtype=float, default=1.5, optional=
False,
    90                                 doc=(
'Chi-squared per DOF cut for deciding a source is '    91                                      'PSF during deblending (shifted PSF model)'))
    92     psfChisq2b = pexConfig.Field(dtype=float, default=1.5, optional=
False,
    93                                  doc=(
'Chi-squared per DOF cut for deciding a source is '    94                                       'a PSF during deblending (shifted PSF model #2)'))
    95     maxNumberOfPeaks = pexConfig.Field(dtype=int, default=0,
    96                                        doc=(
"Only deblend the brightest maxNumberOfPeaks peaks in the parent"    97                                             " (<= 0: unlimited)"))
    98     maxFootprintArea = pexConfig.Field(dtype=int, default=1000000,
    99                                        doc=(
"Maximum area for footprints before they are ignored as large; "   100                                             "non-positive means no threshold applied"))
   101     maxFootprintSize = pexConfig.Field(dtype=int, default=0,
   102                                        doc=(
"Maximum linear dimension for footprints before they are ignored "   103                                             "as large; non-positive means no threshold applied"))
   104     minFootprintAxisRatio = pexConfig.Field(dtype=float, default=0.0,
   105                                             doc=(
"Minimum axis ratio for footprints before they are ignored "   106                                                  "as large; non-positive means no threshold applied"))
   107     notDeblendedMask = pexConfig.Field(dtype=str, default=
"NOT_DEBLENDED", optional=
True,
   108                                        doc=
"Mask name for footprints not deblended, or None")
   110     tinyFootprintSize = pexConfig.RangeField(dtype=int, default=2, min=2, inclusiveMin=
True,
   111                                              doc=(
'Footprints smaller in width or height than this value '   112                                                   'will be ignored; minimum of 2 due to PSF gradient '   115     propagateAllPeaks = pexConfig.Field(dtype=bool, default=
False,
   116                                         doc=(
'Guarantee that all peaks produce a child source.'))
   117     catchFailures = pexConfig.Field(
   118         dtype=bool, default=
False,
   119         doc=(
"If True, catch exceptions thrown by the deblender, log them, "   120              "and set a flag on the parent, instead of letting them propagate up"))
   121     maskPlanes = pexConfig.ListField(dtype=str, default=[
"SAT", 
"INTRP", 
"NO_DATA"],
   122                                      doc=
"Mask planes to ignore when performing statistics")
   123     maskLimits = pexConfig.DictField(
   127         doc=(
"Mask planes with the corresponding limit on the fraction of masked pixels. "   128              "Sources violating this limit will not be deblended."),
   130     weightTemplates = pexConfig.Field(
   131         dtype=bool, default=
False,
   132         doc=(
"If true, a least-squares fit of the templates will be done to the "   133              "full image. The templates will be re-weighted based on this fit."))
   134     removeDegenerateTemplates = pexConfig.Field(dtype=bool, default=
False,
   135                                                 doc=(
"Try to remove similar templates?"))
   136     maxTempDotProd = pexConfig.Field(
   137         dtype=float, default=0.5,
   138         doc=(
"If the dot product between two templates is larger than this value, we consider them to be "   139              "describing the same object (i.e. they are degenerate).  If one of the objects has been "   140              "labeled as a PSF it will be removed, otherwise the template with the lowest value will "   142     medianSmoothTemplate = pexConfig.Field(dtype=bool, default=
True,
   143                                            doc=
"Apply a smoothing filter to all of the template images")
   155     \anchor SourceDeblendTask_   157     \brief Split blended sources into individual sources.   159     This task has no return value; it only modifies the SourceCatalog in-place.   161     ConfigClass = SourceDeblendConfig
   162     _DefaultName = 
"sourceDeblend"   164     def __init__(self, schema, peakSchema=None, **kwargs):
   166         Create the task, adding necessary fields to the given schema.   168         @param[in,out] schema        Schema object for measurement fields; will be modified in-place.   169         @param[in]     peakSchema    Schema of Footprint Peaks that will be passed to the deblender.   170                                      Any fields beyond the PeakTable minimal schema will be transferred   171                                      to the main source Schema.  If None, no fields will be transferred   173         @param[in]     **kwargs      Passed to Task.__init__.   175         pipeBase.Task.__init__(self, **kwargs)
   178                                  if item.field.getName().startswith(
"merge_footprint")]
   179         peakMinimalSchema = afwDet.PeakTable.makeMinimalSchema()
   180         if peakSchema 
is None:
   186             for item 
in peakSchema:
   187                 if item.key 
not in peakMinimalSchema:
   193                     schema.addField(item.field)
   194             assert schema == self.
peakSchemaMapper.getOutputSchema(), 
"Logic bug mapping schemas"   198         self.
nChildKey = schema.addField(
'deblend_nChild', type=np.int32,
   199                                          doc=
'Number of children this object has (defaults to 0)')
   200         self.
psfKey = schema.addField(
'deblend_deblendedAsPsf', type=
'Flag',
   201                                       doc=
'Deblender thought this source looked like a PSF')
   202         self.
psfCenterKey = afwTable.Point2DKey.addFields(schema, 
'deblend_psfCenter',
   203                                                           'If deblended-as-psf, the PSF centroid', 
"pixel")
   204         self.
psfFluxKey = schema.addField(
'deblend_psf_instFlux', type=
'D',
   205                                           doc=
'If deblended-as-psf, the instrumental PSF flux', units=
'count')
   207                                                doc=
'Source had too many peaks; '   208                                                'only the brightest were included')
   209         self.
tooBigKey = schema.addField(
'deblend_parentTooBig', type=
'Flag',
   210                                          doc=
'Parent footprint covered too many pixels')
   211         self.
maskedKey = schema.addField(
'deblend_masked', type=
'Flag',
   212                                          doc=
'Parent footprint was predominantly masked')
   214         if self.config.catchFailures:
   216                                                     doc=
"Deblending failed on source")
   219                                                  doc=
"Deblender skipped this source")
   222             'deblend_rampedTemplate', type=
'Flag',
   223             doc=(
'This source was near an image edge and the deblender used '   224                  '"ramp" edge-handling.'))
   227             'deblend_patchedTemplate', type=
'Flag',
   228             doc=(
'This source was near an image edge and the deblender used '   229                  '"patched" edge-handling.'))
   232             'deblend_hasStrayFlux', type=
'Flag',
   233             doc=(
'This source was assigned some stray flux'))
   235         self.log.
trace(
'Added keys to schema: %s', 
", ".join(
str(x) 
for x 
in (
   240     def run(self, exposure, sources):
   242         Get the psf from the provided exposure and then run deblend().   244         @param[in]     exposure Exposure to process   245         @param[in,out] sources  SourceCatalog containing sources detected on this exposure.   249         psf = exposure.getPsf()
   250         assert sources.getSchema() == self.
schema   251         self.
deblend(exposure, sources, psf)
   253     def _getPsfFwhm(self, psf, bbox):
   256         return psf.computeShape().getDeterminantRadius() * 2.35
   263         @param[in]     exposure Exposure to process   264         @param[in,out] srcs     SourceCatalog containing sources detected on this exposure.   269         self.log.
info(
"Deblending %d sources" % len(srcs))
   274         mi = exposure.getMaskedImage()
   276         statsCtrl.setAndMask(mi.getMask().getPlaneBitMask(self.config.maskPlanes))
   278         sigma1 = math.sqrt(stats.getValue(afwMath.MEDIAN))
   279         self.log.
trace(
'sigma1: %g', sigma1)
   283         for i, src 
in enumerate(srcs):
   286             fp = src.getFootprint()
   299                 self.log.
trace(
'Parent %i: skipping large footprint', 
int(src.getId()))
   301             if self.
isMasked(fp, exposure.getMaskedImage().getMask()):
   304                 self.log.
trace(
'Parent %i: skipping masked footprint', 
int(src.getId()))
   311             self.log.
trace(
'Parent %i: deblending %i peaks', 
int(src.getId()), len(pks))
   317             src.set(self.
tooManyPeaksKey, len(fp.getPeaks()) > self.config.maxNumberOfPeaks)
   321                     fp, mi, psf, psf_fwhm, sigma1=sigma1,
   322                     psfChisqCut1=self.config.psfChisq1,
   323                     psfChisqCut2=self.config.psfChisq2,
   324                     psfChisqCut2b=self.config.psfChisq2b,
   325                     maxNumberOfPeaks=self.config.maxNumberOfPeaks,
   326                     strayFluxToPointSources=self.config.strayFluxToPointSources,
   327                     assignStrayFlux=self.config.assignStrayFlux,
   328                     strayFluxAssignment=self.config.strayFluxRule,
   329                     rampFluxAtEdge=(self.config.edgeHandling == 
'ramp'),
   330                     patchEdges=(self.config.edgeHandling == 
'noclip'),
   331                     tinyFootprintSize=self.config.tinyFootprintSize,
   332                     clipStrayFluxFraction=self.config.clipStrayFluxFraction,
   333                     weightTemplates=self.config.weightTemplates,
   334                     removeDegenerateTemplates=self.config.removeDegenerateTemplates,
   335                     maxTempDotProd=self.config.maxTempDotProd,
   336                     medianSmoothTemplate=self.config.medianSmoothTemplate
   338                 if self.config.catchFailures:
   340             except Exception 
as e:
   341                 if self.config.catchFailures:
   342                     self.log.
warn(
"Unable to deblend source %d: %s" % (src.getId(), e))
   345                     traceback.print_exc()
   352             for j, peak 
in enumerate(res.deblendedParents[0].peaks):
   353                 heavy = peak.getFluxPortion()
   354                 if heavy 
is None or peak.skip:
   356                     if not self.config.propagateAllPeaks:
   361                     self.log.
trace(
"Peak at (%i,%i) failed.  Using minimal default info for child.",
   362                                    pks[j].getIx(), pks[j].getIy())
   366                         peakList = foot.getPeaks()
   368                         peakList.append(peak.peak)
   369                         zeroMimg = afwImage.MaskedImageF(foot.getBBox())
   371                     if peak.deblendedAsPsf:
   372                         if peak.psfFitFlux 
is None:
   373                             peak.psfFitFlux = 0.0
   374                         if peak.psfFitCenter 
is None:
   375                             peak.psfFitCenter = (peak.peak.getIx(), peak.peak.getIy())
   377                 assert(len(heavy.getPeaks()) == 1)
   380                 child = srcs.addNew()
   383                     child.set(key, src.get(key))
   385                 child.setParent(src.getId())
   386                 child.setFootprint(heavy)
   387                 child.set(self.
psfKey, peak.deblendedAsPsf)
   389                 if peak.deblendedAsPsf:
   390                     (cx, cy) = peak.psfFitCenter
   402             spans = src.getFootprint().spans
   404                 spans = spans.union(child.getFootprint().spans)
   405             src.getFootprint().setSpans(spans)
   413         self.log.
info(
'Deblended: of %i sources, %i were deblended, creating %i children, total %i sources'   414                       % (n0, nparents, n1-n0, n1))
   419     def postSingleDeblendHook(self, exposure, srcs, i, npre, kids, fp, psf, psf_fwhm, sigma1, res):
   423         """Returns whether a Footprint is large   425         'Large' is defined by thresholds on the area, size and axis ratio.   426         These may be disabled independently by configuring them to be non-positive.   428         This is principally intended to get rid of satellite streaks, which the   429         deblender or other downstream processing can have trouble dealing with   430         (e.g., multiple large HeavyFootprints can chew up memory).   432         if self.config.maxFootprintArea > 0 
and footprint.getArea() > self.config.maxFootprintArea:
   434         if self.config.maxFootprintSize > 0:
   435             bbox = footprint.getBBox()
   436             if max(bbox.getWidth(), bbox.getHeight()) > self.config.maxFootprintSize:
   438         if self.config.minFootprintAxisRatio > 0:
   439             axes = afwEll.Axes(footprint.getShape())
   440             if axes.getB() < self.config.minFootprintAxisRatio*axes.getA():
   445         """Returns whether the footprint violates the mask limits"""   446         size = 
float(footprint.getArea())
   447         for maskName, limit 
in self.config.maskLimits.items():
   448             maskVal = mask.getPlaneBitMask(maskName)
   449             unmaskedSpan = footprint.spans.intersectNot(mask, maskVal)  
   450             if (size - unmaskedSpan.getArea())/size > limit:
   455         """Indicate that the parent source is not being deblended   457         We set the appropriate flags and mask.   459         @param source  The source to flag as skipped   460         @param mask  The mask to update   462         fp = source.getFootprint()
   464         source.set(self.
nChildKey, len(fp.getPeaks()))  
   465         if self.config.notDeblendedMask:
   466             mask.addMaskPlane(self.config.notDeblendedMask)
   467             fp.spans.setMask(mask, mask.getPlaneBitMask(self.config.notDeblendedMask))
   471     """MultibandDeblendConfig   473     Configuration for the multiband deblender.   474     The parameters are organized by the parameter types, which are   475     - Stopping Criteria: Used to determine if the fit has converged   476     - Position Fitting Criteria: Used to fit the positions of the peaks   477     - Constraints: Used to apply constraints to the peaks and their components   478     - Other: Parameters that don't fit into the above categories   481     maxIter = pexConfig.Field(dtype=int, default=200,
   482                               doc=(
"Maximum number of iterations to deblend a single parent"))
   483     relativeError = pexConfig.Field(dtype=float, default=1e-3,
   484                                     doc=(
"Relative error to use when determining stopping criteria"))
   487     minTranslation = pexConfig.Field(dtype=float, default=1e-3,
   488                                      doc=(
"A peak must be updated by at least 'minTranslation' (pixels)"   489                                           "or no update is performed."   490                                           "This field is ignored if fitPositions is False."))
   491     refinementSkip = pexConfig.Field(dtype=int, default=10,
   492                                      doc=(
"If fitPositions is True, the positions and box sizes are"   493                                           "updated on every 'refinementSkip' iterations."))
   494     translationMethod = pexConfig.Field(dtype=str, default=
"default",
   495                                         doc=(
"Method to use for fitting translations."   496                                              "Currently 'default' is the only available option,"   497                                              "which performs a linear fit, but it is possible that we"   498                                              "will use galsim or some other method as a future option"))
   499     edgeFluxThresh = pexConfig.Field(dtype=float, default=1.0,
   500                                      doc=(
"Boxes are resized when the flux at an edge is "   501                                           "> edgeFluxThresh * background RMS"))
   502     exactLipschitz = pexConfig.Field(dtype=bool, default=
False,
   503                                      doc=(
"Calculate exact Lipschitz constant in every step"   504                                           "(True) or only calculate the approximate"   505                                           "Lipschitz constant with significant changes in A,S"   507     stepSlack = pexConfig.Field(dtype=float, default=0.2,
   508                                 doc=(
"A fractional measure of how much a value (like the exactLipschitz)"   509                                      "can change before it needs to be recalculated."   510                                      "This must be between 0 and 1."))
   513     constraints = pexConfig.Field(dtype=str, default=
"1,+,S,M",
   514                                   doc=(
"List of constraints to use for each object"   515                                        "(order does not matter)"   516                                        "Current options are all used by default:\n"   519                                        "1: normalized SED to unity"   520                                        "+: non-negative morphology"))
   521     symmetryThresh = pexConfig.Field(dtype=float, default=1.0,
   522                                      doc=(
"Strictness of symmetry, from"   523                                           "0 (no symmetry enforced) to"   524                                           "1 (perfect symmetry required)."   525                                           "If 'S' is not in `constraints`, this argument is ignored"))
   526     l0Thresh = pexConfig.Field(dtype=float, default=np.nan,
   527                                doc=(
"L0 threshold. NaN results in no L0 penalty."))
   528     l1Thresh = pexConfig.Field(dtype=float, default=np.nan,
   529                                doc=(
"L1 threshold. NaN results in no L1 penalty."))
   530     tvxThresh = pexConfig.Field(dtype=float, default=np.nan,
   531                                 doc=(
"Threshold for TV (total variation) constraint in the x-direction."   532                                      "NaN results in no TVx penalty."))
   533     tvyThresh = pexConfig.Field(dtype=float, default=np.nan,
   534                                 doc=(
"Threshold for TV (total variation) constraint in the y-direction."   535                                      "NaN results in no TVy penalty."))
   538     useWeights = pexConfig.Field(dtype=bool, default=
False, doc=
"Use inverse variance as deblender weights")
   539     bgScale = pexConfig.Field(
   540         dtype=float, default=0.5,
   541         doc=(
"Fraction of background RMS level to use as a"   542              "cutoff for defining the background of the image"   543              "This is used to initialize the model for each source"   544              "and to set the size of the bounding box for each source"   545              "every `refinementSkip` iteration."))
   546     usePsfConvolution = pexConfig.Field(
   547         dtype=bool, default=
True,
   548         doc=(
"Whether or not to convolve the morphology with the"   549              "PSF in each band or use the same morphology in all bands"))
   550     saveTemplates = pexConfig.Field(
   551         dtype=bool, default=
True,
   552         doc=
"Whether or not to save the SEDs and templates")
   553     processSingles = pexConfig.Field(
   554         dtype=bool, default=
False,
   555         doc=
"Whether or not to process isolated sources in the deblender")
   556     badMask = pexConfig.Field(
   557         dtype=str, default=
"BAD,CR,NO_DATA,SAT,SUSPECT",
   558         doc=
"Whether or not to process isolated sources in the deblender")
   561     maxNumberOfPeaks = pexConfig.Field(
   562         dtype=int, default=0,
   563         doc=(
"Only deblend the brightest maxNumberOfPeaks peaks in the parent"   564              " (<= 0: unlimited)"))
   565     maxFootprintArea = pexConfig.Field(
   566         dtype=int, default=1000000,
   567         doc=(
"Maximum area for footprints before they are ignored as large; "   568              "non-positive means no threshold applied"))
   569     maxFootprintSize = pexConfig.Field(
   570         dtype=int, default=0,
   571         doc=(
"Maximum linear dimension for footprints before they are ignored "   572              "as large; non-positive means no threshold applied"))
   573     minFootprintAxisRatio = pexConfig.Field(
   574         dtype=float, default=0.0,
   575         doc=(
"Minimum axis ratio for footprints before they are ignored "   576              "as large; non-positive means no threshold applied"))
   577     notDeblendedMask = pexConfig.Field(
   578         dtype=str, default=
"NOT_DEBLENDED", optional=
True,
   579         doc=
"Mask name for footprints not deblended, or None")
   581     tinyFootprintSize = pexConfig.RangeField(
   582         dtype=int, default=2, min=2, inclusiveMin=
True,
   583         doc=(
'Footprints smaller in width or height than this value will '   584              'be ignored; minimum of 2 due to PSF gradient calculation.'))
   585     catchFailures = pexConfig.Field(
   586         dtype=bool, default=
False,
   587         doc=(
"If True, catch exceptions thrown by the deblender, log them, "   588              "and set a flag on the parent, instead of letting them propagate up"))
   589     propagateAllPeaks = pexConfig.Field(dtype=bool, default=
False,
   590                                         doc=(
'Guarantee that all peaks produce a child source.'))
   591     maskPlanes = pexConfig.ListField(dtype=str, default=[
"SAT", 
"INTRP", 
"NO_DATA"],
   592                                      doc=
"Mask planes to ignore when performing statistics")
   593     maskLimits = pexConfig.DictField(
   597         doc=(
"Mask planes with the corresponding limit on the fraction of masked pixels. "   598              "Sources violating this limit will not be deblended."),
   601     edgeHandling = pexConfig.ChoiceField(
   602         doc=
'What to do when a peak to be deblended is close to the edge of the image',
   603         dtype=str, default=
'noclip',
   605             'clip': 
'Clip the template at the edge AND the mirror of the edge.',
   606             'ramp': 
'Ramp down flux at the image edge by the PSF',
   607             'noclip': 
'Ignore the edge when building the symmetric template.',
   611     medianSmoothTemplate = pexConfig.Field(dtype=bool, default=
False,
   612                                            doc=
"Apply a smoothing filter to all of the template images")
   613     medianFilterHalfsize = pexConfig.Field(dtype=float, default=2,
   614                                            doc=(
'Half size of the median smoothing filter'))
   615     clipFootprintToNonzero = pexConfig.Field(dtype=bool, default=
False,
   616                                              doc=(
"Clip non-zero spans in the footprints"))
   618     conserveFlux = pexConfig.Field(dtype=bool, default=
True,
   619                                    doc=(
"Reapportion flux to the footprints so that flux is conserved"))
   620     weightTemplates = pexConfig.Field(
   621         dtype=bool, default=
False,
   622         doc=(
"If true, a least-squares fit of the templates will be done to the "   623              "full image. The templates will be re-weighted based on this fit."))
   624     strayFluxToPointSources = pexConfig.ChoiceField(
   625         doc=
'When the deblender should attribute stray flux to point sources',
   626         dtype=str, default=
'necessary',
   628             'necessary': 
'When there is not an extended object in the footprint',
   630             'never': (
'Never; stray flux will not be attributed to any deblended child '   631                       'if the deblender thinks all peaks look like point sources'),
   635     assignStrayFlux = pexConfig.Field(dtype=bool, default=
True,
   636                                       doc=
'Assign stray flux (not claimed by any child in the deblender) '   637                                           'to deblend children.')
   639     strayFluxRule = pexConfig.ChoiceField(
   640         doc=
'How to split flux among peaks',
   641         dtype=str, default=
'trim',
   643             'r-to-peak': 
'~ 1/(1+R^2) to the peak',
   644             'r-to-footprint': (
'~ 1/(1+R^2) to the closest pixel in the footprint.  '   645                                'CAUTION: this can be computationally expensive on large footprints!'),
   646             'nearest-footprint': (
'Assign 100% to the nearest footprint (using L-1 norm aka '   647                                   'Manhattan distance)'),
   648             'trim': (
'Shrink the parent footprint to pixels that are not assigned to children')
   652     clipStrayFluxFraction = pexConfig.Field(dtype=float, default=0.001,
   653                                             doc=(
'When splitting stray flux, clip fractions below '   654                                                  'this value to zero.'))
   655     getTemplateSum = pexConfig.Field(dtype=bool, default=
False,
   656                                      doc=(
"As part of the flux calculation, the sum of the templates is"   657                                           "calculated. If 'getTemplateSum==True' then the sum of the"   658                                           "templates is stored in the result (a 'PerFootprint')."))
   662     """MultibandDeblendTask   664     Split blended sources into individual sources.   666     This task has no return value; it only modifies the SourceCatalog in-place.   668     ConfigClass = MultibandDeblendConfig
   669     _DefaultName = 
"multibandDeblend"   671     def __init__(self, schema, peakSchema=None, **kwargs):
   672         """Create the task, adding necessary fields to the given schema.   676         schema: `lsst.afw.table.schema.schema.Schema`   677             Schema object for measurement fields; will be modified in-place.   678         peakSchema: `lsst.afw.table.schema.schema.Schema`   679             Schema of Footprint Peaks that will be passed to the deblender.   680             Any fields beyond the PeakTable minimal schema will be transferred   681             to the main source Schema.  If None, no fields will be transferred   684             Names of the filters used for the eposures. This is needed to store the SED as a field   686             Passed to Task.__init__.   690         pipeBase.Task.__init__(self, **kwargs)
   691         if not self.config.conserveFlux 
and not self.config.saveTemplates:
   692             raise ValueError(
"Either `conserveFlux` or `saveTemplates` must be True")
   694         peakMinimalSchema = afwDet.PeakTable.makeMinimalSchema()
   695         if peakSchema 
is None:
   701             for item 
in peakSchema:
   702                 if item.key 
not in peakMinimalSchema:
   708                     schema.addField(item.field)
   709             assert schema == self.
peakSchemaMapper.getOutputSchema(), 
"Logic bug mapping schemas"   716         config = scarlet.config.Config(
   717             center_min_dist=self.config.minTranslation,
   718             edge_flux_thresh=self.config.edgeFluxThresh,
   719             exact_lipschitz=self.config.exactLipschitz,
   720             refine_skip=self.config.refinementSkip,
   721             slack=self.config.stepSlack,
   723         if self.config.translationMethod != 
"default":
   724             err = 
"Currently the only supported translationMethod is 'default', you entered '{0}'"   725             raise NotImplementedError(err.format(self.config.translationMethod))
   730         _constraints = self.config.constraints.split(
",")
   731         if (sorted(_constraints) != [
'+', 
'1', 
'M', 
'S'] 
or   732                 ~np.isnan(self.config.l0Thresh) 
or   733                 ~np.isnan(self.config.l1Thresh)):
   735                 "+": scarlet.constraint.PositivityConstraint,
   736                 "1": scarlet.constraint.SimpleConstraint,
   737                 "M": scarlet.constraint.DirectMonotonicityConstraint(use_nearest=
False),
   738                 "S": scarlet.constraint.DirectSymmetryConstraint(sigma=self.config.symmetryThresh)
   740             for c 
in _constraints:
   741                 if constraints 
is None:
   742                     constraints = [constraintDict[c]]
   744                     constraints += [constraintDict[c]]
   745             if constraints 
is None:
   746                 constraints = scarlet.constraint.MinimalConstraint()
   747             if ~np.isnan(self.config.l0Thresh):
   748                 constraints += [scarlet.constraint.L0Constraint(self.config.l0Thresh)]
   749             if ~np.isnan(self.config.l1Thresh):
   750                 constraints += [scarlet.constraint.L1Constraint(self.config.l1Thresh)]
   751             if ~np.isnan(self.config.tvxThresh):
   752                 constraints += [scarlet.constraint.TVxConstraint(self.config.tvxThresh)]
   753             if ~np.isnan(self.config.tvyThresh):
   754                 constraints += [scarlet.constraint.TVyConstraint(self.config.tvyThresh)]
   757             plugins.buildMultibandTemplates,
   758             useWeights=self.config.useWeights,
   759             usePsf=self.config.usePsfConvolution,
   760             constraints=constraints,
   762             maxIter=self.config.maxIter,
   763             bgScale=self.config.bgScale,
   764             relativeError=self.config.relativeError,
   765             badMask=self.config.badMask.split(
","),
   771         if self.config.edgeHandling == 
'ramp':
   773         if self.config.medianSmoothTemplate:
   775                 plugins.medianSmoothTemplates,
   776                 medianFilterHalfsize=self.config.medianFilterHalfsize))
   777         if self.config.clipFootprintToNonzero:
   779         if self.config.conserveFlux:
   780             if self.config.weightTemplates:
   783                 plugins.apportionFlux,
   784                 clipStrayFluxFraction=self.config.clipStrayFluxFraction,
   785                 assignStrayFlux=self.config.assignStrayFlux,
   786                 strayFluxAssignment=self.config.strayFluxRule,
   787                 strayFluxToPointSources=self.config.strayFluxToPointSources,
   788                 getTemplateSum=self.config.getTemplateSum))
   790     def _addSchemaKeys(self, schema):
   791         """Add deblender specific keys to the schema   793         self.
runtimeKey = schema.addField(
'runtime', type=np.float32, doc=
'runtime in ms')
   795         self.
nChildKey = schema.addField(
'deblend_nChild', type=np.int32,
   796                                          doc=
'Number of children this object has (defaults to 0)')
   797         self.
psfKey = schema.addField(
'deblend_deblendedAsPsf', type=
'Flag',
   798                                       doc=
'Deblender thought this source looked like a PSF')
   800                                                doc=
'Source had too many peaks; '   801                                                'only the brightest were included')
   802         self.
tooBigKey = schema.addField(
'deblend_parentTooBig', type=
'Flag',
   803                                          doc=
'Parent footprint covered too many pixels')
   804         self.
maskedKey = schema.addField(
'deblend_masked', type=
'Flag',
   805                                          doc=
'Parent footprint was predominantly masked')
   807                                                 doc=
"Deblending failed on source")
   810                                                  doc=
"Deblender skipped this source")
   814         self.
psfCenterKey = afwTable.Point2DKey.addFields(schema, 
'deblend_psfCenter',
   815                                                           'If deblended-as-psf, the PSF centroid', 
"pixel")
   816         self.
psfFluxKey = schema.addField(
'deblend_psf_instFlux', type=
'D',
   817                                           doc=
'If deblended-as-psf, the instrumental PSF flux', units=
'count')
   819             'deblend_rampedTemplate', type=
'Flag',
   820             doc=(
'This source was near an image edge and the deblender used '   821                  '"ramp" edge-handling.'))
   824             'deblend_patchedTemplate', type=
'Flag',
   825             doc=(
'This source was near an image edge and the deblender used '   826                  '"patched" edge-handling.'))
   829             'deblend_hasStrayFlux', type=
'Flag',
   830             doc=(
'This source was assigned some stray flux'))
   832         self.log.
trace(
'Added keys to schema: %s', 
", ".join(
str(x) 
for x 
in (
   837     def run(self, mExposure, mergedSources):
   838         """Get the psf from each exposure and then run deblend().   842         mExposure: `MultibandExposure`   843             The exposures should be co-added images of the same   844             shape and region of the sky.   845         mergedSources: `SourceCatalog`   846             The merged `SourceCatalog` that contains parent footprints   847             to (potentially) deblend.   851         fluxCatalogs: dict or None   852             Keys are the names of the filters and the values are   853             `lsst.afw.table.source.source.SourceCatalog`'s.   854             These are the flux-conserved catalogs with heavy footprints with   855             the image data weighted by the multiband templates.   856             If `self.config.conserveFlux` is `False`, then this item will be None   857         templateCatalogs: dict or None   858             Keys are the names of the filters and the values are   859             `lsst.afw.table.source.source.SourceCatalog`'s.   860             These are catalogs with heavy footprints that are the templates   861             created by the multiband templates.   862             If `self.config.saveTemplates` is `False`, then this item will be None   864         psfs = {f: mExposure[f].getPsf() 
for f 
in mExposure.filters}
   865         return self.
deblend(mExposure, mergedSources, psfs)
   867     def _getPsfFwhm(self, psf, bbox):
   868         return psf.computeShape().getDeterminantRadius() * 2.35
   870     def _addChild(self, parentId, peak, sources, heavy):
   871         """Add a child to a catalog   873         This creates a new child in the source catalog,   874         assigning it a parent id, adding a footprint,   875         and setting all appropriate flags based on the   878         assert len(heavy.getPeaks()) == 1
   879         src = sources.addNew()
   881         src.setParent(parentId)
   882         src.setFootprint(heavy)
   883         src.set(self.
psfKey, peak.deblendedAsPsf)
   892         """Deblend a data cube of multiband images   896         mExposure: `MultibandExposure`   897             The exposures should be co-added images of the same   898             shape and region of the sky.   899         sources: `SourceCatalog`   900             The merged `SourceCatalog` that contains parent footprints   901             to (potentially) deblend.   903             Keys are the names of the filters   904             (should be the same as `mExposure.filters`)   905             and the values are the PSFs in each band.   909         fluxCatalogs: dict or None   910             Keys are the names of the filters and the values are   911             `lsst.afw.table.source.source.SourceCatalog`'s.   912             These are the flux-conserved catalogs with heavy footprints with   913             the image data weighted by the multiband templates.   914             If `self.config.conserveFlux` is `False`, then this item will be None   915         templateCatalogs: dict or None   916             Keys are the names of the filters and the values are   917             `lsst.afw.table.source.source.SourceCatalog`'s.   918             These are catalogs with heavy footprints that are the templates   919             created by the multiband templates.   920             If `self.config.saveTemplates` is `False`, then this item will be None   924         if tuple(psfs.keys()) != mExposure.filters:
   925             msg = 
"PSF keys must be the same as mExposure.filters ({0}), got {1}"   926             raise ValueError(msg.format(mExposure.filters, psfs.keys()))
   928         filters = mExposure.filters
   930                                                      mask=mExposure.mask, variance=mExposure.variance)
   931         self.log.
info(
"Deblending {0} sources in {1} exposures".
format(len(sources), len(mExposure)))
   936             exposure = mExposure[f]
   937             mi = exposure.getMaskedImage()
   939             statsCtrl.setAndMask(mi.getMask().getPlaneBitMask(self.config.maskPlanes))
   941             sigma1 = math.sqrt(stats.getValue(afwMath.MEDIAN))
   942             self.log.
trace(
'Exposure {0}, sigma1: {1}'.
format(f, sigma1))
   946         if self.config.conserveFlux:
   950                 _catalog.extend(sources)
   951                 fluxCatalogs[f] = _catalog
   954         if self.config.saveTemplates:
   955             templateCatalogs = {}
   958                 _catalog.extend(sources)
   959                 templateCatalogs[f] = _catalog
   961             templateCatalogs = 
None   965         for pk, src 
in enumerate(sources):
   966             foot = src.getFootprint()
   967             logger.info(
"id: {0}".
format(src[
"id"]))
   968             peaks = foot.getPeaks()
   975             if len(peaks) < 2 
and not self.config.processSingles:
   977                     if self.config.saveTemplates:
   979                     if self.config.conserveFlux:
   984                 self.
skipParent(src, [mi.getMask() 
for mi 
in mMaskedImage])
   985                 self.log.
trace(
'Parent %i: skipping large footprint', 
int(src.getId()))
   987             if self.
isMasked(foot, exposure.getMaskedImage().getMask()):
   990                 self.log.
trace(
'Parent %i: skipping masked footprint', 
int(src.getId()))
   992             if len(peaks) > self.config.maxNumberOfPeaks:
   994                 msg = 
'Parent {0}: Too many peaks, using the first {1} peaks'   995                 self.log.
trace(msg.format(
int(src.getId()), self.config.maxNumberOfPeaks))
   998             bbox = foot.getBBox()
   999             psf_fwhms = {f: self.
_getPsfFwhm(psf, bbox) 
for f, psf 
in psfs.items()}
  1000             self.log.
trace(
'Parent %i: deblending %i peaks', 
int(src.getId()), len(peaks))
  1007                 images = mMaskedImage[:, bbox]
  1008                 psf_list = [psfs[f] 
for f 
in filters]
  1009                 fwhm_list = [psf_fwhms[f] 
for f 
in filters]
  1010                 avgNoise = [sigmas[f] 
for f 
in filters]
  1014                                     mMaskedImage=images,
  1018                                     maxNumberOfPeaks=self.config.maxNumberOfPeaks)
  1020                 runtime = (tf-t0)*1000
  1025             except Exception 
as e:
  1026                 if self.config.catchFailures:
  1027                     self.log.
warn(
"Unable to deblend source %d: %s" % (src.getId(), e))
  1031                     traceback.print_exc()
  1037             templateParents = {}
  1039             parentId = src.getId()
  1041                 if self.config.saveTemplates:
  1042                     templateParents[f] = templateCatalogs[f][pk]
  1044                 if self.config.conserveFlux:
  1045                     fluxParents[f] = fluxCatalogs[f][pk]
  1052             for j, multiPeak 
in enumerate(result.peaks):
  1053                 heavy = {f: peak.getFluxPortion() 
for f, peak 
in multiPeak.deblendedPeaks.items()}
  1054                 no_flux = 
all([v 
is None for v 
in heavy.values()])
  1055                 skip_peak = 
all([peak.skip 
for peak 
in multiPeak.deblendedPeaks.values()])
  1056                 if no_flux 
or skip_peak:
  1058                     if not self.config.propagateAllPeaks:
  1063                     msg = 
"Peak at {0} failed deblending.  Using minimal default info for child."  1064                     self.log.
trace(msg.format(multiPeak.x, multiPeak.y))
  1068                     peakList = pfoot.getPeaks()
  1070                     pfoot.addPeak(multiPeak.x, multiPeak.y, 0)
  1071                     zeroMimg = afwImage.MaskedImageF(pfoot.getBBox())
  1079                     if len(heavy[f].getPeaks()) != 1:
  1080                         err = 
"Heavy footprint should have a single peak, got {0}"  1081                         raise ValueError(err.format(len(heavy[f].getPeaks())))
  1082                     peak = multiPeak.deblendedPeaks[f]
  1083                     if self.config.saveTemplates:
  1084                         cat = templateCatalogs[f]
  1085                         tfoot = peak.templateFootprint
  1086                         timg = afwImage.MaskedImageF(peak.templateImage)
  1088                         child = self.
_addChild(parentId, peak, cat, tHeavy)
  1090                             child.setId(src.getId())
  1093                             templateSpans[f] = templateSpans[f].union(tHeavy.getSpans())
  1094                     if self.config.conserveFlux:
  1095                         cat = fluxCatalogs[f]
  1096                         child = self.
_addChild(parentId, peak, cat, heavy[f])
  1098                             child.setId(src.getId())
  1101                             fluxSpans[f] = fluxSpans[f].union(heavy[f].getSpans())
  1110                 if self.config.saveTemplates:
  1112                     templateParents[f].getFootprint().setSpans(templateSpans[f])
  1113                 if self.config.conserveFlux:
  1115                     fluxParents[f].getFootprint().setSpans(fluxSpans[f])
  1118                                        pk, npre, foot, psfs, psf_fwhms, sigmas, result)
  1120         if fluxCatalogs 
is not None:
  1121             n1 = len(
list(fluxCatalogs.values())[0])
  1123             n1 = len(
list(templateCatalogs.values())[0])
  1124         self.log.
info(
'Deblended: of %i sources, %i were deblended, creating %i children, total %i sources'  1125                       % (n0, nparents, n1-n0, n1))
  1126         return fluxCatalogs, templateCatalogs
  1132                               pk, npre, fp, psfs, psf_fwhms, sigmas, result):
  1136         """Returns whether a Footprint is large  1138         'Large' is defined by thresholds on the area, size and axis ratio.  1139         These may be disabled independently by configuring them to be non-positive.  1141         This is principally intended to get rid of satellite streaks, which the  1142         deblender or other downstream processing can have trouble dealing with  1143         (e.g., multiple large HeavyFootprints can chew up memory).  1145         if self.config.maxFootprintArea > 0 
and footprint.getArea() > self.config.maxFootprintArea:
  1147         if self.config.maxFootprintSize > 0:
  1148             bbox = footprint.getBBox()
  1149             if max(bbox.getWidth(), bbox.getHeight()) > self.config.maxFootprintSize:
  1151         if self.config.minFootprintAxisRatio > 0:
  1152             axes = afwEll.Axes(footprint.getShape())
  1153             if axes.getB() < self.config.minFootprintAxisRatio*axes.getA():
  1158         """Returns whether the footprint violates the mask limits"""  1159         size = 
float(footprint.getArea())
  1160         for maskName, limit 
in self.config.maskLimits.items():
  1161             maskVal = mask.getPlaneBitMask(maskName)
  1162             unmaskedSpan = footprint.spans.intersectNot(mask, maskVal)  
  1163             if (size - unmaskedSpan.getArea())/size > limit:
  1168         """Indicate that the parent source is not being deblended  1170         We set the appropriate flags and masks for each exposure.  1174         source: `lsst.afw.table.source.source.SourceRecord`  1175             The source to flag as skipped  1176         masks: list of `lsst.afw.image.MaskX`  1177             The mask in each band to update with the non-detection  1179         fp = source.getFootprint()
  1181         source.set(self.
nChildKey, len(fp.getPeaks()))  
  1182         if self.config.notDeblendedMask:
  1184                 mask.addMaskPlane(self.config.notDeblendedMask)
  1185                 fp.spans.setMask(mask, mask.getPlaneBitMask(self.config.notDeblendedMask))
 
def newDeblend(debPlugins, footprint, mMaskedImage, psfs, psfFwhms, log=None, verbose=False, avgNoise=None, maxNumberOfPeaks=0)
def _addChild(self, parentId, peak, sources, heavy)
def isLargeFootprint(self, footprint)
def isLargeFootprint(self, footprint)
deblendPatchedTemplateKey
def postSingleDeblendHook(self, exposures, fluxCatalogs, templateCatalogs, pk, npre, fp, psfs, psf_fwhms, sigmas, result)
def postSingleDeblendHook(self, exposure, srcs, i, npre, kids, fp, psf, psf_fwhm, sigma1, res)
A compact representation of a collection of pixels. 
A mapping between the keys of two Schemas, used to copy data between them. 
def _getPsfFwhm(self, psf, bbox)
def preSingleDeblendHook(self, exposure, srcs, i, fp, psf, psf_fwhm, sigma1)
def run(self, exposure, sources)
Get the psf from the provided exposure and then run deblend(). 
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series. 
daf::base::PropertySet * set
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 __init__(self, schema, peakSchema=None, kwargs)
def _getPsfFwhm(self, psf, bbox)
bool all(CoordinateExpr< N > const &expr) noexcept
Return true if all elements are true. 
def _addSchemaKeys(self, schema)
Pass parameters to a Statistics object. 
def preSingleDeblendHook(self, exposures, sources, pk, fp, psfs, psf_fwhms, sigmas)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
def addSchemaKeys(self, schema)
Split blended sources into individual sources. 
def skipParent(self, source, mask)
def deblend(self, exposure, srcs, psf)
Deblend. 
static Log getLogger(Log const &logger)
def skipParent(self, source, masks)
def __init__(self, schema, peakSchema=None, kwargs)
Create the task, adding necessary fields to the given schema. 
def deblend(self, mExposure, sources, psfs)
def isMasked(self, footprint, mask)
deblendPatchedTemplateKey
def isMasked(self, footprint, mask)
daf::base::PropertyList * list
def run(self, mExposure, mergedSources)
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...