LSSTApplications  18.1.0
LSSTDataManagementBasePackage
Classes | Functions
lsst.meas.deblender.plugins Namespace Reference

Classes

class  DeblenderPlugin
 

Functions

def clipFootprintToNonzeroImpl (foot, image)
 
def buildMultibandTemplates (debResult, log, useWeights=False, usePsf=False, sources=None, constraints=None, config=None, maxIter=100, bgScale=0.5, relativeError=1e-2, badMask=None)
 
def fitPsfs (debResult, log, psfChisqCut1=1.5, psfChisqCut2=1.5, psfChisqCut2b=1.5, tinyFootprintSize=2)
 
def buildSymmetricTemplates (debResult, log, patchEdges=False, setOrigTemplate=True)
 
def rampFluxAtEdge (debResult, log, patchEdges=False)
 
def medianSmoothTemplates (debResult, log, medianFilterHalfsize=2)
 
def makeTemplatesMonotonic (debResult, log)
 
def clipFootprintsToNonzero (debResult, log)
 
def weightTemplates (debResult, log)
 
def reconstructTemplates (debResult, log, maxTempDotProd=0.5)
 
def apportionFlux (debResult, log, assignStrayFlux=True, strayFluxAssignment='r-to-peak', strayFluxToPointSources='necessary', clipStrayFluxFraction=0.001, getTemplateSum=False)
 

Function Documentation

◆ apportionFlux()

def lsst.meas.deblender.plugins.apportionFlux (   debResult,
  log,
  assignStrayFlux = True,
  strayFluxAssignment = 'r-to-peak',
  strayFluxToPointSources = 'necessary',
  clipStrayFluxFraction = 0.001,
  getTemplateSum = False 
)
Apportion flux to all of the peak templates in each filter

Divide the ``maskedImage`` flux amongst all of the templates based on the fraction of
flux assigned to each ``template``.
Leftover "stray flux" is assigned to peaks based on the other parameters.

Parameters
----------
debResult: `lsst.meas.deblender.baseline.DeblenderResult`
    Container for the final deblender results.
log: `log.Log`
    LSST logger for logging purposes.
assignStrayFlux: `bool`, optional
    If True then flux in the parent footprint that is not covered by any of the
    template footprints is assigned to templates based on their 1/(1+r^2) distance.
    How the flux is apportioned is determined by ``strayFluxAssignment``.
strayFluxAssignment: `string`, optional
    Determines how stray flux is apportioned.
    * ``trim``: Trim stray flux and do not include in any footprints
    * ``r-to-peak`` (default): Stray flux is assigned based on (1/(1+r^2) from the peaks
    * ``r-to-footprint``: Stray flux is distributed to the footprints based on 1/(1+r^2) of the
      minimum distance from the stray flux to footprint
    * ``nearest-footprint``: Stray flux is assigned to the footprint with lowest L-1 (Manhattan)
      distance to the stray flux
strayFluxToPointSources: `string`, optional
    Determines how stray flux is apportioned to point sources
    * ``never``: never apportion stray flux to point sources
    * ``necessary`` (default): point sources are included only if there are no extended sources nearby
    * ``always``: point sources are always included in the 1/(1+r^2) splitting
clipStrayFluxFraction: `float`, optional
    Minimum stray-flux portion.
    Any stray-flux portion less than ``clipStrayFluxFraction`` is clipped to zero.
getTemplateSum: `bool`, optional
    As part of the flux calculation, the sum of the templates is calculated.
    If ``getTemplateSum==True`` then the sum of the templates is stored in the result
    (a `DeblendedFootprint`).

Returns
-------
modified: `bool`
    Apportion flux always modifies the templates, so ``modified`` is always ``True``.
    However, this should likely be the final step and it is unlikely that
    any deblender plugins will be re-run.

Definition at line 1398 of file plugins.py.

1398  getTemplateSum=False):
1399  """Apportion flux to all of the peak templates in each filter
1400 
1401  Divide the ``maskedImage`` flux amongst all of the templates based on the fraction of
1402  flux assigned to each ``template``.
1403  Leftover "stray flux" is assigned to peaks based on the other parameters.
1404 
1405  Parameters
1406  ----------
1407  debResult: `lsst.meas.deblender.baseline.DeblenderResult`
1408  Container for the final deblender results.
1409  log: `log.Log`
1410  LSST logger for logging purposes.
1411  assignStrayFlux: `bool`, optional
1412  If True then flux in the parent footprint that is not covered by any of the
1413  template footprints is assigned to templates based on their 1/(1+r^2) distance.
1414  How the flux is apportioned is determined by ``strayFluxAssignment``.
1415  strayFluxAssignment: `string`, optional
1416  Determines how stray flux is apportioned.
1417  * ``trim``: Trim stray flux and do not include in any footprints
1418  * ``r-to-peak`` (default): Stray flux is assigned based on (1/(1+r^2) from the peaks
1419  * ``r-to-footprint``: Stray flux is distributed to the footprints based on 1/(1+r^2) of the
1420  minimum distance from the stray flux to footprint
1421  * ``nearest-footprint``: Stray flux is assigned to the footprint with lowest L-1 (Manhattan)
1422  distance to the stray flux
1423  strayFluxToPointSources: `string`, optional
1424  Determines how stray flux is apportioned to point sources
1425  * ``never``: never apportion stray flux to point sources
1426  * ``necessary`` (default): point sources are included only if there are no extended sources nearby
1427  * ``always``: point sources are always included in the 1/(1+r^2) splitting
1428  clipStrayFluxFraction: `float`, optional
1429  Minimum stray-flux portion.
1430  Any stray-flux portion less than ``clipStrayFluxFraction`` is clipped to zero.
1431  getTemplateSum: `bool`, optional
1432  As part of the flux calculation, the sum of the templates is calculated.
1433  If ``getTemplateSum==True`` then the sum of the templates is stored in the result
1434  (a `DeblendedFootprint`).
1435 
1436  Returns
1437  -------
1438  modified: `bool`
1439  Apportion flux always modifies the templates, so ``modified`` is always ``True``.
1440  However, this should likely be the final step and it is unlikely that
1441  any deblender plugins will be re-run.
1442  """
1443  validStrayPtSrc = ['never', 'necessary', 'always']
1444  validStrayAssign = ['r-to-peak', 'r-to-footprint', 'nearest-footprint', 'trim']
1445  if strayFluxToPointSources not in validStrayPtSrc:
1446  raise ValueError((('strayFluxToPointSources: value \"%s\" not in the set of allowed values: ') %
1447  strayFluxToPointSources) + str(validStrayPtSrc))
1448  if strayFluxAssignment not in validStrayAssign:
1449  raise ValueError((('strayFluxAssignment: value \"%s\" not in the set of allowed values: ') %
1450  strayFluxAssignment) + str(validStrayAssign))
1451 
1452  for fidx in debResult.filters:
1453  dp = debResult.deblendedParents[fidx]
1454  # Prepare inputs to "apportionFlux" call.
1455  # template maskedImages
1456  tmimgs = []
1457  # template footprints
1458  tfoots = []
1459  # deblended as psf
1460  dpsf = []
1461  # peak x,y
1462  pkx = []
1463  pky = []
1464  # indices of valid templates
1465  ibi = []
1466  bb = dp.fp.getBBox()
1467 
1468  for peaki, pkres in enumerate(dp.peaks):
1469  if pkres.skip:
1470  continue
1471  tmimgs.append(pkres.templateImage)
1472  tfoots.append(pkres.templateFootprint)
1473  # for stray flux...
1474  dpsf.append(pkres.deblendedAsPsf)
1475  pk = pkres.peak
1476  pkx.append(pk.getIx())
1477  pky.append(pk.getIy())
1478  ibi.append(pkres.pki)
1479 
1480  # Now apportion flux according to the templates
1481  log.trace('Apportioning flux among %i templates', len(tmimgs))
1482  sumimg = afwImage.ImageF(bb)
1483  # .getDimensions())
1484  # sumimg.setXY0(bb.getMinX(), bb.getMinY())
1485 
1486  strayopts = 0
1487  if strayFluxAssignment == 'trim':
1488  assignStrayFlux = False
1489  strayopts |= bUtils.STRAYFLUX_TRIM
1490  if assignStrayFlux:
1491  strayopts |= bUtils.ASSIGN_STRAYFLUX
1492  if strayFluxToPointSources == 'necessary':
1493  strayopts |= bUtils.STRAYFLUX_TO_POINT_SOURCES_WHEN_NECESSARY
1494  elif strayFluxToPointSources == 'always':
1495  strayopts |= bUtils.STRAYFLUX_TO_POINT_SOURCES_ALWAYS
1496 
1497  if strayFluxAssignment == 'r-to-peak':
1498  # this is the default
1499  pass
1500  elif strayFluxAssignment == 'r-to-footprint':
1501  strayopts |= bUtils.STRAYFLUX_R_TO_FOOTPRINT
1502  elif strayFluxAssignment == 'nearest-footprint':
1503  strayopts |= bUtils.STRAYFLUX_NEAREST_FOOTPRINT
1504 
1505  portions, strayflux = bUtils.apportionFlux(dp.maskedImage, dp.fp, tmimgs, tfoots, sumimg, dpsf,
1506  pkx, pky, strayopts, clipStrayFluxFraction)
1507 
1508  # Shrink parent to union of children
1509  if strayFluxAssignment == 'trim':
1510  finalSpanSet = afwGeom.SpanSet()
1511  for foot in tfoots:
1512  finalSpanSet = finalSpanSet.union(foot.spans)
1513  dp.fp.setSpans(finalSpanSet)
1514 
1515  # Store the template sum in the deblender result
1516  if getTemplateSum:
1517  debResult.setTemplateSums(sumimg, fidx)
1518 
1519  # Save the apportioned fluxes
1520  ii = 0
1521  for j, (pk, pkres) in enumerate(zip(dp.fp.getPeaks(), dp.peaks)):
1522  if pkres.skip:
1523  continue
1524  pkres.setFluxPortion(portions[ii])
1525 
1526  if assignStrayFlux:
1527  # NOTE that due to a swig bug (https://github.com/swig/swig/issues/59)
1528  # we CANNOT iterate over "strayflux", but must index into it.
1529  stray = strayflux[ii]
1530  else:
1531  stray = None
1532  ii += 1
1533 
1534  pkres.setStrayFlux(stray)
1535 
1536  # Set child footprints to contain the right number of peaks.
1537  for j, (pk, pkres) in enumerate(zip(dp.fp.getPeaks(), dp.peaks)):
1538  if pkres.skip:
1539  continue
1540 
1541  for foot, add in [(pkres.templateFootprint, True), (pkres.origFootprint, True),
1542  (pkres.strayFlux, False)]:
1543  if foot is None:
1544  continue
1545  pks = foot.getPeaks()
1546  pks.clear()
1547  if add:
1548  pks.append(pk)
1549  return True
1550 
A compact representation of a collection of pixels.
Definition: SpanSet.h:77

◆ buildMultibandTemplates()

def lsst.meas.deblender.plugins.buildMultibandTemplates (   debResult,
  log,
  useWeights = False,
  usePsf = False,
  sources = None,
  constraints = None,
  config = None,
  maxIter = 100,
  bgScale = 0.5,
  relativeError = 1e-2,
  badMask = None 
)
Run the Multiband Deblender to build templates

Parameters
----------
debResult: `lsst.meas.deblender.baseline.DeblenderResult`
    Container for the final deblender results.
log: `log.Log`
    LSST logger for logging purposes.
useWeights: bool, default=False
    Whether or not to use the variance map in each filter for the fit.
usePsf: bool, default=False
    Whether or not to convolve the image with the PSF in each band.
    This is not yet implemented in an optimized algorithm, so it is recommended
    to leave this term off for now
sources: list of `scarlet.source.Source` objects, default=None
    List of sources to use in the blend. By default the
    `scarlet.source.ExtendedSource` class is used, which initializes each
    source as symmetric and monotonic about a peak in the footprint peak catalog.
constraints: `scarlet.constraint.Constraint`, default=None
    Constraint to be applied to each source. If sources require different constraints,
    a list of `sources` must be created instead, which ignores the `constraints` parameter.
    When `constraints` is `None` the default constraints are used.
config: `scarlet.config.Config`, default=None
    Configuration for the blend.
    If `config` is `None` then the default `Config` is used.
maxIter: int, default=100
    Maximum iterations for a single blend.
bgScale: float
    Amount to scale the background RMS to set the floor for deblender model sizes
relativeError: float, default=1e-2
    Relative error to reach for convergence
badMask: list of str, default=`None`
    List of mask plane names to mark bad pixels.
    If `badPixelKeys` is `None`, the default keywords used are
    `["BAD", "CR", "NO_DATA", "SAT", "SUSPECT"]`.

Returns
-------
modified: `bool`
    If any templates have been created then ``modified`` is ``True``,
    otherwise it is ``False`` (meaning all of the peaks were skipped).

Definition at line 157 of file plugins.py.

157  relativeError=1e-2, badMask=None):
158  """Run the Multiband Deblender to build templates
159 
160  Parameters
161  ----------
162  debResult: `lsst.meas.deblender.baseline.DeblenderResult`
163  Container for the final deblender results.
164  log: `log.Log`
165  LSST logger for logging purposes.
166  useWeights: bool, default=False
167  Whether or not to use the variance map in each filter for the fit.
168  usePsf: bool, default=False
169  Whether or not to convolve the image with the PSF in each band.
170  This is not yet implemented in an optimized algorithm, so it is recommended
171  to leave this term off for now
172  sources: list of `scarlet.source.Source` objects, default=None
173  List of sources to use in the blend. By default the
174  `scarlet.source.ExtendedSource` class is used, which initializes each
175  source as symmetric and monotonic about a peak in the footprint peak catalog.
176  constraints: `scarlet.constraint.Constraint`, default=None
177  Constraint to be applied to each source. If sources require different constraints,
178  a list of `sources` must be created instead, which ignores the `constraints` parameter.
179  When `constraints` is `None` the default constraints are used.
180  config: `scarlet.config.Config`, default=None
181  Configuration for the blend.
182  If `config` is `None` then the default `Config` is used.
183  maxIter: int, default=100
184  Maximum iterations for a single blend.
185  bgScale: float
186  Amount to scale the background RMS to set the floor for deblender model sizes
187  relativeError: float, default=1e-2
188  Relative error to reach for convergence
189  badMask: list of str, default=`None`
190  List of mask plane names to mark bad pixels.
191  If `badPixelKeys` is `None`, the default keywords used are
192  `["BAD", "CR", "NO_DATA", "SAT", "SUSPECT"]`.
193 
194  Returns
195  -------
196  modified: `bool`
197  If any templates have been created then ``modified`` is ``True``,
198  otherwise it is ``False`` (meaning all of the peaks were skipped).
199  """
200  # Extract coordinates from each MultiColorPeak
201  bbox = debResult.footprint.getBBox()
202  peakSchema = debResult.footprint.peaks.getSchema()
203  xmin = bbox.getMinX()
204  ymin = bbox.getMinY()
205  peaks = [[pk.y-ymin, pk.x-xmin] for pk in debResult.peaks]
206  xy0 = bbox.getMin()
207 
208  # Create the data array from the masked images
209  mMaskedImage = debResult.mMaskedImage[:, debResult.footprint.getBBox()]
210  data = mMaskedImage.image.array
211 
212  # Use the inverse variance as the weights
213  if useWeights:
214  weights = 1/mMaskedImage.variance.array
215  else:
216  weights = np.ones_like(data)
217 
218  # Use the mask plane to mask bad pixels and
219  # the footprint to mask out pixels outside the footprint
220  if badMask is None:
221  badMask = ["BAD", "CR", "NO_DATA", "SAT", "SUSPECT"]
222  fpMask = afwImage.Mask(bbox)
223  debResult.footprint.spans.setMask(fpMask, 1)
224  fpMask = ~fpMask.getArray().astype(bool)
225  badPixels = mMaskedImage.mask.getPlaneBitMask(badMask)
226  mask = (mMaskedImage.mask.array & badPixels) | fpMask[None, :]
227  weights[mask > 0] = 0
228 
229  # Extract the PSF from each band for PSF convolution
230  if usePsf:
231  psfs = []
232  for psf in debResult.psfs:
233  psfs.append(psf.computeKernelImage().array)
234  psf = np.array(psfs)
235  else:
236  psf = None
237 
238  bg_rms = np.array([debResult.deblendedParents[f].avgNoise for f in debResult.filters])*bgScale
239  if sources is None:
240  # If only a single constraint was given, use it for all of the sources
241  if constraints is None or isinstance(constraints[0], scarlet.constraints.Constraint):
242  constraints = [constraints] * len(peaks)
243  sources = [
244  scarlet.source.ExtendedSource(center=peak,
245  img=data,
246  bg_rms=bg_rms,
247  constraints=constraints[pk],
248  psf=psf,
249  symmetric=True,
250  monotonic=True,
251  thresh=1.0,
252  config=config)
253  for pk, peak in enumerate(peaks)
254  ]
255 
256  # When a footprint includes only non-detections
257  # (peaks in the noise too low to deblend as a source)
258  # the deblender currently fails.
259  try:
260  blend = scarlet.blend.Blend(components=sources)
261  blend.set_data(img=data, weights=weights, bg_rms=bg_rms, config=config)
262  blend.fit(maxIter, e_rel=relativeError)
263  except scarlet.source.SourceInitError as e:
264  log.warn(e.args[0])
265  debResult.failed = True
266  return False
267  except np.linalg.LinAlgError:
268  log.warn("Deblend failed catastrophically, most likely due to no signal in the footprint")
269  debResult.failed = True
270  return False
271  debResult.blend = blend
272 
273  modified = False
274  # Create the Templates for each peak in each filter
275  for pk, source in enumerate(blend.sources):
276  src = source.components[0]
277  _cx = src.Nx >> 1
278  _cy = src.Ny >> 1
279 
280  if debResult.peaks[pk].skip:
281  continue
282  modified = True
283  cx = src.center[1]+xmin
284  cy = src.center[0]+ymin
285  icx = int(np.round(cx))
286  icy = int(np.round(cy))
287  imbb = debResult.deblendedParents[debResult.filters[0]].img.getBBox()
288 
289  # Footprint must be inside the image
290  if not imbb.contains(afwGeom.Point2I(cx, cy)):
291  _setPeakError(debResult, log, pk, cx, cy, debResult.filters,
292  "peak center is not inside image", "setOutOfBounds")
293  continue
294  # Only save templates that have nonzero flux
295  if np.sum(src.morph) == 0:
296  _setPeakError(debResult, log, pk, cx, cy, debResult.filters,
297  "had no flux", "setFailedSymmetricTemplate")
298  continue
299 
300  # Temporary for initial testing: combine multiple components
301  model = blend.get_model(k=pk).astype(np.float32)
302 
303  # The peak in each band will have the same SpanSet
304  mask = afwImage.Mask(np.array(np.sum(model, axis=0) > 0, dtype=np.int32), xy0=xy0)
305  ss = afwGeom.SpanSet.fromMask(mask)
306 
307  if len(ss) == 0:
308  log.warn("No flux in parent footprint")
309  debResult.failed = True
310  return False
311 
312  # Add the template footprint and image to the deblender result for each peak
313  for fidx, f in enumerate(debResult.filters):
314  pkResult = debResult.deblendedParents[f].peaks[pk]
315  tfoot = afwDet.Footprint(ss, peakSchema=peakSchema)
316  # Add the peak with the intensity of the centered model,
317  # which might be slightly larger than the shifted model
318  peakFlux = np.sum(src.sed[fidx]*src.morph[_cy, _cx])
319  tfoot.addPeak(cx, cy, peakFlux)
320  timg = afwImage.ImageF(model[fidx], xy0=xy0)
321  timg = timg[tfoot.getBBox()]
322  pkResult.setOrigTemplate(timg, tfoot)
323  pkResult.setTemplate(timg, tfoot)
324  pkResult.setFluxPortion(afwImage.MaskedImageF(timg))
325  pkResult.multiColorPeak.x = cx
326  pkResult.multiColorPeak.y = cy
327  pkResult.peak.setFx(cx)
328  pkResult.peak.setFy(cy)
329  pkResult.peak.setIx(icx)
330  pkResult.peak.setIy(icy)
331  return modified
332 
333 
Represent a 2-dimensional array of bitmask pixels.
Definition: Mask.h:78
Class to describe the properties of a detected object from an image.
Definition: Footprint.h:62

◆ buildSymmetricTemplates()

def lsst.meas.deblender.plugins.buildSymmetricTemplates (   debResult,
  log,
  patchEdges = False,
  setOrigTemplate = True 
)
Build a symmetric template for each peak in each filter

Given ``maskedImageF``, ``footprint``, and a ``DebldendedPeak``, creates a symmetric template
(``templateImage`` and ``templateFootprint``) around the peak for all peaks not flagged as
``skip`` or ``deblendedAsPsf``.

Parameters
----------
debResult: `lsst.meas.deblender.baseline.DeblenderResult`
    Container for the final deblender results.
log: `log.Log`
    LSST logger for logging purposes.
patchEdges: `bool`, optional
    If True and if the parent Footprint touches pixels with the ``EDGE`` bit set,
    then grow the parent Footprint to include all symmetric templates.

Returns
-------
modified: `bool`
    If any peaks are not skipped or marked as point sources, ``modified`` is ``True.
    Otherwise ``modified`` is ``False``.

Definition at line 869 of file plugins.py.

869 def buildSymmetricTemplates(debResult, log, patchEdges=False, setOrigTemplate=True):
870  """Build a symmetric template for each peak in each filter
871 
872  Given ``maskedImageF``, ``footprint``, and a ``DebldendedPeak``, creates a symmetric template
873  (``templateImage`` and ``templateFootprint``) around the peak for all peaks not flagged as
874  ``skip`` or ``deblendedAsPsf``.
875 
876  Parameters
877  ----------
878  debResult: `lsst.meas.deblender.baseline.DeblenderResult`
879  Container for the final deblender results.
880  log: `log.Log`
881  LSST logger for logging purposes.
882  patchEdges: `bool`, optional
883  If True and if the parent Footprint touches pixels with the ``EDGE`` bit set,
884  then grow the parent Footprint to include all symmetric templates.
885 
886  Returns
887  -------
888  modified: `bool`
889  If any peaks are not skipped or marked as point sources, ``modified`` is ``True.
890  Otherwise ``modified`` is ``False``.
891  """
892  modified = False
893  # Create the Templates for each peak in each filter
894  for fidx in debResult.filters:
895  dp = debResult.deblendedParents[fidx]
896  imbb = dp.img.getBBox()
897  log.trace('Creating templates for footprint at x0,y0,W,H = %i, %i, %i, %i)', dp.x0, dp.y0, dp.W, dp.H)
898 
899  for peaki, pkres in enumerate(dp.peaks):
900  log.trace('Deblending peak %i of %i', peaki, len(dp.peaks))
901  # TODO: Check debResult to see if the peak is deblended as a point source
902  # when comparing all bands, not just a single band
903  if pkres.skip or pkres.deblendedAsPsf:
904  continue
905  modified = True
906  pk = pkres.peak
907  cx, cy = pk.getIx(), pk.getIy()
908  if not imbb.contains(afwGeom.Point2I(cx, cy)):
909  log.trace('Peak center is not inside image; skipping %i', pkres.pki)
910  pkres.setOutOfBounds()
911  continue
912  log.trace('computing template for peak %i at (%i, %i)', pkres.pki, cx, cy)
913  timg, tfoot, patched = bUtils.buildSymmetricTemplate(dp.maskedImage, dp.fp, pk, dp.avgNoise,
914  True, patchEdges)
915  if timg is None:
916  log.trace('Peak %i at (%i, %i): failed to build symmetric template', pkres.pki, cx, cy)
917  pkres.setFailedSymmetricTemplate()
918  continue
919 
920  if patched:
921  pkres.setPatched()
922 
923  # possibly save the original symmetric template
924  if setOrigTemplate:
925  pkres.setOrigTemplate(timg, tfoot)
926  pkres.setTemplate(timg, tfoot)
927  return modified
928 
929 
def buildSymmetricTemplates(debResult, log, patchEdges=False, setOrigTemplate=True)
Definition: plugins.py:869

◆ clipFootprintsToNonzero()

def lsst.meas.deblender.plugins.clipFootprintsToNonzero (   debResult,
  log 
)
Clip non-zero spans in the template footprints for every peak in each filter.

Peak ``Footprint``s are clipped to the region in the image containing non-zero values
by dropping spans that are completely zero and moving endpoints to non-zero pixels
(but does not split spans that have internal zeros).

Parameters
----------
debResult: `lsst.meas.deblender.baseline.DeblenderResult`
    Container for the final deblender results.
log: `log.Log`
    LSST logger for logging purposes.

Returns
-------
modified: `bool`
    Whether or not any templates were modified.
    This will be ``True`` as long as there is at least one source that is not flagged as a PSF.

Definition at line 1193 of file plugins.py.

1193 def clipFootprintsToNonzero(debResult, log):
1194  """Clip non-zero spans in the template footprints for every peak in each filter.
1195 
1196  Peak ``Footprint``s are clipped to the region in the image containing non-zero values
1197  by dropping spans that are completely zero and moving endpoints to non-zero pixels
1198  (but does not split spans that have internal zeros).
1199 
1200  Parameters
1201  ----------
1202  debResult: `lsst.meas.deblender.baseline.DeblenderResult`
1203  Container for the final deblender results.
1204  log: `log.Log`
1205  LSST logger for logging purposes.
1206 
1207  Returns
1208  -------
1209  modified: `bool`
1210  Whether or not any templates were modified.
1211  This will be ``True`` as long as there is at least one source that is not flagged as a PSF.
1212  """
1213  # Loop over all filters
1214  for fidx in debResult.filters:
1215  dp = debResult.deblendedParents[fidx]
1216  for peaki, pkres in enumerate(dp.peaks):
1217  if pkres.skip or pkres.deblendedAsPsf:
1218  continue
1219  timg, tfoot = pkres.templateImage, pkres.templateFootprint
1220  clipFootprintToNonzeroImpl(tfoot, timg)
1221  if not tfoot.getBBox().isEmpty() and tfoot.getBBox() != timg.getBBox(afwImage.PARENT):
1222  timg = timg.Factory(timg, tfoot.getBBox(), afwImage.PARENT, True)
1223  pkres.setTemplate(timg, tfoot)
1224  return False
1225 
1226 
def clipFootprintToNonzeroImpl(foot, image)
Definition: plugins.py:35
def clipFootprintsToNonzero(debResult, log)
Definition: plugins.py:1193

◆ clipFootprintToNonzeroImpl()

def lsst.meas.deblender.plugins.clipFootprintToNonzeroImpl (   foot,
  image 
)
 Clips the given *Footprint* to the region in the *Image*
 containing non-zero values.  The clipping drops spans that are
 totally zero, and moves endpoints to non-zero; it does not
 split spans that have internal zeros.

Definition at line 35 of file plugins.py.

35 def clipFootprintToNonzeroImpl(foot, image):
36  '''
37  Clips the given *Footprint* to the region in the *Image*
38  containing non-zero values. The clipping drops spans that are
39  totally zero, and moves endpoints to non-zero; it does not
40  split spans that have internal zeros.
41  '''
42  x0 = image.getX0()
43  y0 = image.getY0()
44  xImMax = x0 + image.getDimensions().getX()
45  yImMax = y0 + image.getDimensions().getY()
46  newSpans = []
47  arr = image.getArray()
48  for span in foot.spans:
49  y = span.getY()
50  if y < y0 or y > yImMax:
51  continue
52  spanX0 = span.getX0()
53  spanX1 = span.getX1()
54  xMin = spanX0 if spanX0 >= x0 else x0
55  xMax = spanX1 if spanX1 <= xImMax else xImMax
56  xarray = np.arange(xMin, xMax+1)[arr[y-y0, xMin-x0:xMax-x0+1] != 0]
57  if len(xarray) > 0:
58  newSpans.append(afwGeom.Span(y, xarray[0], xarray[-1]))
59  # Time to update the SpanSet
60  foot.setSpans(afwGeom.SpanSet(newSpans, normalize=False))
61  foot.removeOrphanPeaks()
62 
63 
A compact representation of a collection of pixels.
Definition: SpanSet.h:77
A range of pixels within one row of an Image.
Definition: Span.h:47
def clipFootprintToNonzeroImpl(foot, image)
Definition: plugins.py:35

◆ fitPsfs()

def lsst.meas.deblender.plugins.fitPsfs (   debResult,
  log,
  psfChisqCut1 = 1.5,
  psfChisqCut2 = 1.5,
  psfChisqCut2b = 1.5,
  tinyFootprintSize = 2 
)
Fit a PSF + smooth background model (linear) to a small region around each peak

This function will iterate over all filters in deblender result but does not compare
results across filters.
DeblendedPeaks that pass the cuts have their templates modified to the PSF + background model
and their ``deblendedAsPsf`` property set to ``True``.

This will likely be replaced in the future with a function that compares the psf chi-squared cuts
so that peaks flagged as point sources will be considered point sources in all bands.

Parameters
----------
debResult: `lsst.meas.deblender.baseline.DeblenderResult`
    Container for the final deblender results.
log: `log.Log`
    LSST logger for logging purposes.
psfChisqCut*: `float`, optional
    ``psfChisqCut1`` is the maximum chi-squared-per-degree-of-freedom allowed for a peak to
    be considered a PSF match without recentering.
    A fit is also made that includes terms to recenter the PSF.
    ``psfChisqCut2`` is the same as ``psfChisqCut1`` except it determines the restriction on the
    fit that includes recentering terms.
    If the peak is a match for a re-centered PSF, the PSF is repositioned at the new center and
    the peak footprint is fit again, this time to the new PSF.
    If the resulting chi-squared-per-degree-of-freedom is less than ``psfChisqCut2b`` then it
    passes the re-centering algorithm.
    If the peak passes both the re-centered and fixed position cuts, the better of the two is accepted,
    but parameters for all three psf fits are stored in the ``DebldendedPeak``.
    The default for ``psfChisqCut1``, ``psfChisqCut2``, and ``psfChisqCut2b`` is ``1.5``.
tinyFootprintSize: `float`, optional
    The PSF model is shrunk to the size that contains the original footprint.
    If the bbox of the clipped PSF model for a peak is smaller than ``max(tinyFootprintSize,2)``
    then ``tinyFootprint`` for the peak is set to ``True`` and the peak is not fit.
    The default is 2.

Returns
-------
modified: `bool`
    If any templates have been assigned to PSF point sources then ``modified`` is ``True``,
    otherwise it is ``False``.

Definition at line 334 of file plugins.py.

334 def fitPsfs(debResult, log, psfChisqCut1=1.5, psfChisqCut2=1.5, psfChisqCut2b=1.5, tinyFootprintSize=2):
335  """Fit a PSF + smooth background model (linear) to a small region around each peak
336 
337  This function will iterate over all filters in deblender result but does not compare
338  results across filters.
339  DeblendedPeaks that pass the cuts have their templates modified to the PSF + background model
340  and their ``deblendedAsPsf`` property set to ``True``.
341 
342  This will likely be replaced in the future with a function that compares the psf chi-squared cuts
343  so that peaks flagged as point sources will be considered point sources in all bands.
344 
345  Parameters
346  ----------
347  debResult: `lsst.meas.deblender.baseline.DeblenderResult`
348  Container for the final deblender results.
349  log: `log.Log`
350  LSST logger for logging purposes.
351  psfChisqCut*: `float`, optional
352  ``psfChisqCut1`` is the maximum chi-squared-per-degree-of-freedom allowed for a peak to
353  be considered a PSF match without recentering.
354  A fit is also made that includes terms to recenter the PSF.
355  ``psfChisqCut2`` is the same as ``psfChisqCut1`` except it determines the restriction on the
356  fit that includes recentering terms.
357  If the peak is a match for a re-centered PSF, the PSF is repositioned at the new center and
358  the peak footprint is fit again, this time to the new PSF.
359  If the resulting chi-squared-per-degree-of-freedom is less than ``psfChisqCut2b`` then it
360  passes the re-centering algorithm.
361  If the peak passes both the re-centered and fixed position cuts, the better of the two is accepted,
362  but parameters for all three psf fits are stored in the ``DebldendedPeak``.
363  The default for ``psfChisqCut1``, ``psfChisqCut2``, and ``psfChisqCut2b`` is ``1.5``.
364  tinyFootprintSize: `float`, optional
365  The PSF model is shrunk to the size that contains the original footprint.
366  If the bbox of the clipped PSF model for a peak is smaller than ``max(tinyFootprintSize,2)``
367  then ``tinyFootprint`` for the peak is set to ``True`` and the peak is not fit.
368  The default is 2.
369 
370  Returns
371  -------
372  modified: `bool`
373  If any templates have been assigned to PSF point sources then ``modified`` is ``True``,
374  otherwise it is ``False``.
375  """
376  from .baseline import CachingPsf
377  modified = False
378  # Loop over all of the filters to build the PSF
379  for fidx in debResult.filters:
380  dp = debResult.deblendedParents[fidx]
381  peaks = dp.fp.getPeaks()
382  cpsf = CachingPsf(dp.psf)
383 
384  # create mask image for pixels within the footprint
385  fmask = afwImage.Mask(dp.bb)
386  fmask.setXY0(dp.bb.getMinX(), dp.bb.getMinY())
387  dp.fp.spans.setMask(fmask, 1)
388 
389  # pk.getF() -- retrieving the floating-point location of the peak
390  # -- actually shows up in the profile if we do it in the loop, so
391  # grab them all here.
392  peakF = [pk.getF() for pk in peaks]
393 
394  for pki, (pk, pkres, pkF) in enumerate(zip(peaks, dp.peaks, peakF)):
395  log.trace('Filter %s, Peak %i', fidx, pki)
396  ispsf = _fitPsf(dp.fp, fmask, pk, pkF, pkres, dp.bb, peaks, peakF, log, cpsf, dp.psffwhm,
397  dp.img, dp.varimg, psfChisqCut1, psfChisqCut2, psfChisqCut2b, tinyFootprintSize)
398  modified = modified or ispsf
399  return modified
400 
401 
def fitPsfs(debResult, log, psfChisqCut1=1.5, psfChisqCut2=1.5, psfChisqCut2b=1.5, tinyFootprintSize=2)
Definition: plugins.py:334
Represent a 2-dimensional array of bitmask pixels.
Definition: Mask.h:78

◆ makeTemplatesMonotonic()

def lsst.meas.deblender.plugins.makeTemplatesMonotonic (   debResult,
  log 
)
Make the templates monotonic.

The pixels in the templates are modified such that pixels further from the peak will
have values smaller than those closer to the peak.

Parameters
----------
debResult: `lsst.meas.deblender.baseline.DeblenderResult`
    Container for the final deblender results.
log: `log.Log`
    LSST logger for logging purposes.

Returns
-------
modified: `bool`
    Whether or not any templates were modified.
    This will be ``True`` as long as there is at least one source that is not flagged as a PSF.

Definition at line 1158 of file plugins.py.

1158 def makeTemplatesMonotonic(debResult, log):
1159  """Make the templates monotonic.
1160 
1161  The pixels in the templates are modified such that pixels further from the peak will
1162  have values smaller than those closer to the peak.
1163 
1164  Parameters
1165  ----------
1166  debResult: `lsst.meas.deblender.baseline.DeblenderResult`
1167  Container for the final deblender results.
1168  log: `log.Log`
1169  LSST logger for logging purposes.
1170 
1171  Returns
1172  -------
1173  modified: `bool`
1174  Whether or not any templates were modified.
1175  This will be ``True`` as long as there is at least one source that is not flagged as a PSF.
1176  """
1177  modified = False
1178  # Loop over all filters
1179  for fidx in debResult.filters:
1180  dp = debResult.deblendedParents[fidx]
1181  for peaki, pkres in enumerate(dp.peaks):
1182  if pkres.skip or pkres.deblendedAsPsf:
1183  continue
1184  modified = True
1185  timg, tfoot = pkres.templateImage, pkres.templateFootprint
1186  pk = pkres.peak
1187  log.trace('Making template %i monotonic', pkres.pki)
1188  bUtils.makeMonotonic(timg, pk)
1189  pkres.setTemplate(timg, tfoot)
1190  return modified
1191 
1192 
def makeTemplatesMonotonic(debResult, log)
Definition: plugins.py:1158

◆ medianSmoothTemplates()

def lsst.meas.deblender.plugins.medianSmoothTemplates (   debResult,
  log,
  medianFilterHalfsize = 2 
)
Applying median smoothing filter to the template images for every peak in every filter.

Parameters
----------
debResult: `lsst.meas.deblender.baseline.DeblenderResult`
    Container for the final deblender results.
log: `log.Log`
    LSST logger for logging purposes.
medianFilterHalfSize: `int`, optional
    Half the box size of the median filter, i.e. a ``medianFilterHalfSize`` of 50 means that
    each output pixel will be the median of  the pixels in a 101 x 101-pixel box in the input image.
    This parameter is only used when ``medianSmoothTemplate==True``, otherwise it is ignored.

Returns
-------
modified: `bool`
    Whether or not any templates were modified.
    This will be ``True`` as long as there is at least one source that is not flagged as a PSF.

Definition at line 1113 of file plugins.py.

1113 def medianSmoothTemplates(debResult, log, medianFilterHalfsize=2):
1114  """Applying median smoothing filter to the template images for every peak in every filter.
1115 
1116  Parameters
1117  ----------
1118  debResult: `lsst.meas.deblender.baseline.DeblenderResult`
1119  Container for the final deblender results.
1120  log: `log.Log`
1121  LSST logger for logging purposes.
1122  medianFilterHalfSize: `int`, optional
1123  Half the box size of the median filter, i.e. a ``medianFilterHalfSize`` of 50 means that
1124  each output pixel will be the median of the pixels in a 101 x 101-pixel box in the input image.
1125  This parameter is only used when ``medianSmoothTemplate==True``, otherwise it is ignored.
1126 
1127  Returns
1128  -------
1129  modified: `bool`
1130  Whether or not any templates were modified.
1131  This will be ``True`` as long as there is at least one source that is not flagged as a PSF.
1132  """
1133  modified = False
1134  # Loop over all filters
1135  for fidx in debResult.filters:
1136  dp = debResult.deblendedParents[fidx]
1137  for peaki, pkres in enumerate(dp.peaks):
1138  if pkres.skip or pkres.deblendedAsPsf:
1139  continue
1140  modified = True
1141  timg, tfoot = pkres.templateImage, pkres.templateFootprint
1142  filtsize = medianFilterHalfsize*2 + 1
1143  if timg.getWidth() >= filtsize and timg.getHeight() >= filtsize:
1144  log.trace('Median filtering template %i', pkres.pki)
1145  # We want the output to go in "t1", so copy it into
1146  # "inimg" for input
1147  inimg = timg.Factory(timg, True)
1148  bUtils.medianFilter(inimg, timg, medianFilterHalfsize)
1149  # possible save this median-filtered template
1150  pkres.setMedianFilteredTemplate(timg, tfoot)
1151  else:
1152  log.trace('Not median-filtering template %i: size %i x %i smaller than required %i x %i',
1153  pkres.pki, timg.getWidth(), timg.getHeight(), filtsize, filtsize)
1154  pkres.setTemplate(timg, tfoot)
1155  return modified
1156 
1157 
def medianSmoothTemplates(debResult, log, medianFilterHalfsize=2)
Definition: plugins.py:1113

◆ rampFluxAtEdge()

def lsst.meas.deblender.plugins.rampFluxAtEdge (   debResult,
  log,
  patchEdges = False 
)
Adjust flux on the edges of the template footprints.

Using the PSF, a peak ``Footprint`` with pixels on the edge of ``footprint``
is grown by the ``psffwhm``*1.5 and filled in with ramped pixels.
The result is a new symmetric footprint template for the peaks near the edge.

Parameters
----------
debResult: `lsst.meas.deblender.baseline.DeblenderResult`
    Container for the final deblender results.
log: `log.Log`
    LSST logger for logging purposes.
patchEdges: `bool`, optional
    If True and if the parent Footprint touches pixels with the ``EDGE`` bit set,
    then grow the parent Footprint to include all symmetric templates.

Returns
-------
modified: `bool`
    If any peaks have their templates modified to include flux at the edges,
    ``modified`` is ``True``.

Definition at line 930 of file plugins.py.

930 def rampFluxAtEdge(debResult, log, patchEdges=False):
931  """Adjust flux on the edges of the template footprints.
932 
933  Using the PSF, a peak ``Footprint`` with pixels on the edge of ``footprint``
934  is grown by the ``psffwhm``*1.5 and filled in with ramped pixels.
935  The result is a new symmetric footprint template for the peaks near the edge.
936 
937  Parameters
938  ----------
939  debResult: `lsst.meas.deblender.baseline.DeblenderResult`
940  Container for the final deblender results.
941  log: `log.Log`
942  LSST logger for logging purposes.
943  patchEdges: `bool`, optional
944  If True and if the parent Footprint touches pixels with the ``EDGE`` bit set,
945  then grow the parent Footprint to include all symmetric templates.
946 
947  Returns
948  -------
949  modified: `bool`
950  If any peaks have their templates modified to include flux at the edges,
951  ``modified`` is ``True``.
952  """
953  modified = False
954  # Loop over all filters
955  for fidx in debResult.filters:
956  dp = debResult.deblendedParents[fidx]
957  log.trace('Checking for significant flux at edge: sigma1=%g', dp.avgNoise)
958 
959  for peaki, pkres in enumerate(dp.peaks):
960  if pkres.skip or pkres.deblendedAsPsf:
961  continue
962  timg, tfoot = pkres.templateImage, pkres.templateFootprint
963  if bUtils.hasSignificantFluxAtEdge(timg, tfoot, 3*dp.avgNoise):
964  log.trace("Template %i has significant flux at edge: ramping", pkres.pki)
965  try:
966  (timg2, tfoot2, patched) = _handle_flux_at_edge(log, dp.psffwhm, timg, tfoot, dp.fp,
967  dp.maskedImage, dp.x0, dp.x1,
968  dp.y0, dp.y1, dp.psf, pkres.peak,
969  dp.avgNoise, patchEdges)
970  except lsst.pex.exceptions.Exception as exc:
971  if (isinstance(exc, lsst.pex.exceptions.InvalidParameterError) and
972  "CoaddPsf" in str(exc)):
973  pkres.setOutOfBounds()
974  continue
975  raise
976  pkres.setRampedTemplate(timg2, tfoot2)
977  if patched:
978  pkres.setPatched()
979  pkres.setTemplate(timg2, tfoot2)
980  modified = True
981  return modified
982 
983 
Provides consistent interface for LSST exceptions.
Definition: Exception.h:107
def rampFluxAtEdge(debResult, log, patchEdges=False)
Definition: plugins.py:930
Reports invalid arguments.
Definition: Runtime.h:66

◆ reconstructTemplates()

def lsst.meas.deblender.plugins.reconstructTemplates (   debResult,
  log,
  maxTempDotProd = 0.5 
)
Remove "degenerate templates"

If galaxies have substructure, such as face-on spirals, the process of identifying peaks can
"shred" the galaxy into many pieces. The templates of shredded galaxies are typically quite
similar because they represent the same galaxy, so we try to identify these "degenerate" peaks
by looking at the inner product (in pixel space) of pairs of templates.
If they are nearly parallel, we only keep one of the peaks and reject the other.
If only one of the peaks is a PSF template, the other template is used,
otherwise the one with the maximum template value is kept.

Parameters
----------
debResult: `lsst.meas.deblender.baseline.DeblenderResult`
    Container for the final deblender results.
log: `log.Log`
    LSST logger for logging purposes.
maxTempDotProd: `float`, optional
    All dot products between templates greater than ``maxTempDotProd`` will result in one
    of the templates removed.

Returns
-------
modified: `bool`
    If any degenerate templates are found, ``modified`` is ``True``.

Definition at line 1296 of file plugins.py.

1296 def reconstructTemplates(debResult, log, maxTempDotProd=0.5):
1297  """Remove "degenerate templates"
1298 
1299  If galaxies have substructure, such as face-on spirals, the process of identifying peaks can
1300  "shred" the galaxy into many pieces. The templates of shredded galaxies are typically quite
1301  similar because they represent the same galaxy, so we try to identify these "degenerate" peaks
1302  by looking at the inner product (in pixel space) of pairs of templates.
1303  If they are nearly parallel, we only keep one of the peaks and reject the other.
1304  If only one of the peaks is a PSF template, the other template is used,
1305  otherwise the one with the maximum template value is kept.
1306 
1307  Parameters
1308  ----------
1309  debResult: `lsst.meas.deblender.baseline.DeblenderResult`
1310  Container for the final deblender results.
1311  log: `log.Log`
1312  LSST logger for logging purposes.
1313  maxTempDotProd: `float`, optional
1314  All dot products between templates greater than ``maxTempDotProd`` will result in one
1315  of the templates removed.
1316 
1317  Returns
1318  -------
1319  modified: `bool`
1320  If any degenerate templates are found, ``modified`` is ``True``.
1321  """
1322  log.trace('Looking for degnerate templates')
1323 
1324  foundReject = False
1325  for fidx in debResult.filters:
1326  dp = debResult.deblendedParents[fidx]
1327  nchild = np.sum([pkres.skip is False for pkres in dp.peaks])
1328  indexes = [pkres.pki for pkres in dp.peaks if pkres.skip is False]
1329 
1330  # We build a matrix that stores the dot product between templates.
1331  # We convert the template images to HeavyFootprints because they already have a method
1332  # to compute the dot product.
1333  A = np.zeros((nchild, nchild))
1334  maxTemplate = []
1335  heavies = []
1336  for pkres in dp.peaks:
1337  if pkres.skip:
1338  continue
1339  heavies.append(afwDet.makeHeavyFootprint(pkres.templateFootprint,
1340  afwImage.MaskedImageF(pkres.templateImage)))
1341  maxTemplate.append(np.max(pkres.templateImage.getArray()))
1342 
1343  for i in range(nchild):
1344  for j in range(i + 1):
1345  A[i, j] = heavies[i].dot(heavies[j])
1346 
1347  # Normalize the dot products to get the cosine of the angle between templates
1348  for i in range(nchild):
1349  for j in range(i):
1350  norm = A[i, i]*A[j, j]
1351  if norm <= 0:
1352  A[i, j] = 0
1353  else:
1354  A[i, j] /= np.sqrt(norm)
1355 
1356  # Iterate over pairs of objects and find the maximum non-diagonal element of the matrix.
1357  # Exit the loop once we find a single degenerate pair greater than the threshold.
1358  rejectedIndex = -1
1359  for i in range(nchild):
1360  currentMax = 0.
1361  for j in range(i):
1362  if A[i, j] > currentMax:
1363  currentMax = A[i, j]
1364  if currentMax > maxTempDotProd:
1365  foundReject = True
1366  rejectedIndex = j
1367 
1368  if foundReject:
1369  break
1370 
1371  del A
1372 
1373  # If one of the objects is identified as a PSF keep the other one, otherwise keep the one
1374  # with the maximum template value
1375  if foundReject:
1376  keep = indexes[i]
1377  reject = indexes[rejectedIndex]
1378  if dp.peaks[keep].deblendedAsPsf and dp.peaks[reject].deblendedAsPsf is False:
1379  keep = indexes[rejectedIndex]
1380  reject = indexes[i]
1381  elif dp.peaks[keep].deblendedAsPsf is False and dp.peaks[reject].deblendedAsPsf:
1382  reject = indexes[rejectedIndex]
1383  keep = indexes[i]
1384  else:
1385  if maxTemplate[rejectedIndex] > maxTemplate[i]:
1386  keep = indexes[rejectedIndex]
1387  reject = indexes[i]
1388  log.trace('Removing object with index %d : %f. Degenerate with %d' % (reject, currentMax,
1389  keep))
1390  dp.peaks[reject].skip = True
1391  dp.peaks[reject].degenerate = True
1392 
1393  return foundReject
1394 
1395 
def dot(symb, c, r, frame=None, size=2, ctype=None, origin=afwImage.PARENT, args, kwargs)
Definition: ds9.py:101
def reconstructTemplates(debResult, log, maxTempDotProd=0.5)
Definition: plugins.py:1296
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...

◆ weightTemplates()

def lsst.meas.deblender.plugins.weightTemplates (   debResult,
  log 
)
Weight the templates to best fit the observed image in each filter

This function re-weights the templates so that their linear combination best represents
the observed image in that filter.
In the future it may be useful to simultaneously weight all of the filters together.

Parameters
----------
debResult: `lsst.meas.deblender.baseline.DeblenderResult`
    Container for the final deblender results.
log: `log.Log`
    LSST logger for logging purposes.

Returns
-------
modified: `bool`
    ``weightTemplates`` does not actually modify the ``Footprint`` templates other than
    to add a weight to them, so ``modified`` is always ``False``.

Definition at line 1227 of file plugins.py.

1227 def weightTemplates(debResult, log):
1228  """Weight the templates to best fit the observed image in each filter
1229 
1230  This function re-weights the templates so that their linear combination best represents
1231  the observed image in that filter.
1232  In the future it may be useful to simultaneously weight all of the filters together.
1233 
1234  Parameters
1235  ----------
1236  debResult: `lsst.meas.deblender.baseline.DeblenderResult`
1237  Container for the final deblender results.
1238  log: `log.Log`
1239  LSST logger for logging purposes.
1240 
1241  Returns
1242  -------
1243  modified: `bool`
1244  ``weightTemplates`` does not actually modify the ``Footprint`` templates other than
1245  to add a weight to them, so ``modified`` is always ``False``.
1246  """
1247  # Weight the templates by doing a least-squares fit to the image
1248  log.trace('Weighting templates')
1249  for fidx in debResult.filters:
1250  _weightTemplates(debResult.deblendedParents[fidx])
1251  return False
1252 
1253 
def weightTemplates(debResult, log)
Definition: plugins.py:1227