LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
LSST Data Management Base Package
Classes | Functions
lsst.meas.deblender.plugins Namespace Reference

Classes

class  DeblenderPlugin
 

Functions

def clipFootprintToNonzeroImpl (foot, image)
 
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 1250 of file plugins.py.

1252 getTemplateSum=False):
1253 """Apportion flux to all of the peak templates in each filter
1254
1255 Divide the ``maskedImage`` flux amongst all of the templates based
1256 on the fraction of flux assigned to each ``template``.
1257 Leftover "stray flux" is assigned to peaks based on the other parameters.
1258
1259 Parameters
1260 ----------
1262 Container for the final deblender results.
1263 log: `log.Log`
1264 LSST logger for logging purposes.
1265 assignStrayFlux: `bool`, optional
1266 If True then flux in the parent footprint that is not covered by any
1267 of the template footprints is assigned to templates based on
1268 their 1/(1+r^2) distance.
1269 How the flux is apportioned is determined by ``strayFluxAssignment``.
1270 strayFluxAssignment: `string`, optional
1271 Determines how stray flux is apportioned.
1272
1273 * ``trim``: Trim stray flux and do not include in any footprints
1274 * ``r-to-peak`` (default): Stray flux is assigned based on
1275 (1/(1+r^2) from the peaks
1276 * ``r-to-footprint``: Stray flux is distributed to the footprints
1277 based on 1/(1+r^2) of the minimum distance from the stray flux
1278 to footprint
1279 * ``nearest-footprint``: Stray flux is assigned to the footprint
1280 with lowest L-1 (Manhattan) distance to the stray flux
1281
1282 strayFluxToPointSources: `string`, optional
1283 Determines how stray flux is apportioned to point sources
1284
1285 * ``never``: never apportion stray flux to point sources
1286 * ``necessary`` (default): point sources are included only if there
1287 are no extended sources nearby
1288 * ``always``: point sources are always included in
1289 the 1/(1+r^2) splitting
1290
1291 clipStrayFluxFraction: `float`, optional
1292 Minimum stray-flux portion.
1293 Any stray-flux portion less than ``clipStrayFluxFraction`` is
1294 clipped to zero.
1295 getTemplateSum: `bool`, optional
1296 As part of the flux calculation, the sum of the templates is
1297 calculated. If ``getTemplateSum==True`` then the sum of the
1298 templates is stored in the result (a `DeblendedFootprint`).
1299
1300 Returns
1301 -------
1302 modified: `bool`
1303 Apportion flux always modifies the templates, so ``modified`` is
1304 always ``True``. However, this should likely be the final step and
1305 it is unlikely that any deblender plugins will be re-run.
1306 """
1307 validStrayPtSrc = ['never', 'necessary', 'always']
1308 validStrayAssign = ['r-to-peak', 'r-to-footprint', 'nearest-footprint', 'trim']
1309 if strayFluxToPointSources not in validStrayPtSrc:
1310 raise ValueError((('strayFluxToPointSources: value \"%s\" not in the set of allowed values: ') %
1311 strayFluxToPointSources) + str(validStrayPtSrc))
1312 if strayFluxAssignment not in validStrayAssign:
1313 raise ValueError((('strayFluxAssignment: value \"%s\" not in the set of allowed values: ') %
1314 strayFluxAssignment) + str(validStrayAssign))
1315
1316 for fidx in debResult.filters:
1317 dp = debResult.deblendedParents[fidx]
1318 # Prepare inputs to "apportionFlux" call.
1319 # template maskedImages
1320 tmimgs = []
1321 # template footprints
1322 tfoots = []
1323 # deblended as psf
1324 dpsf = []
1325 # peak x,y
1326 pkx = []
1327 pky = []
1328 # indices of valid templates
1329 ibi = []
1330 bb = dp.fp.getBBox()
1331
1332 for peaki, pkres in enumerate(dp.peaks):
1333 if pkres.skip:
1334 continue
1335 tmimgs.append(pkres.templateImage)
1336 tfoots.append(pkres.templateFootprint)
1337 # for stray flux...
1338 dpsf.append(pkres.deblendedAsPsf)
1339 pk = pkres.peak
1340 pkx.append(pk.getIx())
1341 pky.append(pk.getIy())
1342 ibi.append(pkres.pki)
1343
1344 # Now apportion flux according to the templates
1345 log.trace('Apportioning flux among %i templates', len(tmimgs))
1346 sumimg = afwImage.ImageF(bb)
1347 # .getDimensions())
1348 # sumimg.setXY0(bb.getMinX(), bb.getMinY())
1349
1350 strayopts = 0
1351 if strayFluxAssignment == 'trim':
1352 assignStrayFlux = False
1353 strayopts |= bUtils.STRAYFLUX_TRIM
1354 if assignStrayFlux:
1355 strayopts |= bUtils.ASSIGN_STRAYFLUX
1356 if strayFluxToPointSources == 'necessary':
1357 strayopts |= bUtils.STRAYFLUX_TO_POINT_SOURCES_WHEN_NECESSARY
1358 elif strayFluxToPointSources == 'always':
1359 strayopts |= bUtils.STRAYFLUX_TO_POINT_SOURCES_ALWAYS
1360
1361 if strayFluxAssignment == 'r-to-peak':
1362 # this is the default
1363 pass
1364 elif strayFluxAssignment == 'r-to-footprint':
1365 strayopts |= bUtils.STRAYFLUX_R_TO_FOOTPRINT
1366 elif strayFluxAssignment == 'nearest-footprint':
1367 strayopts |= bUtils.STRAYFLUX_NEAREST_FOOTPRINT
1368
1369 portions, strayflux = bUtils.apportionFlux(dp.maskedImage, dp.fp, tmimgs, tfoots, sumimg, dpsf,
1370 pkx, pky, strayopts, clipStrayFluxFraction)
1371
1372 # Shrink parent to union of children
1373 if strayFluxAssignment == 'trim':
1374 finalSpanSet = afwGeom.SpanSet()
1375 for foot in tfoots:
1376 finalSpanSet = finalSpanSet.union(foot.spans)
1377 dp.fp.setSpans(finalSpanSet)
1378
1379 # Store the template sum in the deblender result
1380 if getTemplateSum:
1381 debResult.setTemplateSums(sumimg, fidx)
1382
1383 # Save the apportioned fluxes
1384 ii = 0
1385 for j, (pk, pkres) in enumerate(zip(dp.fp.getPeaks(), dp.peaks)):
1386 if pkres.skip:
1387 continue
1388 pkres.setFluxPortion(portions[ii])
1389
1390 if assignStrayFlux:
1391 # NOTE that due to a swig bug (https://github.com/swig/swig/issues/59)
1392 # we CANNOT iterate over "strayflux", but must index into it.
1393 stray = strayflux[ii]
1394 else:
1395 stray = None
1396 ii += 1
1397
1398 pkres.setStrayFlux(stray)
1399
1400 # Set child footprints to contain the right number of peaks.
1401 for j, (pk, pkres) in enumerate(zip(dp.fp.getPeaks(), dp.peaks)):
1402 if pkres.skip:
1403 continue
1404
1405 for foot, add in [(pkres.templateFootprint, True), (pkres.origFootprint, True),
1406 (pkres.strayFlux, False)]:
1407 if foot is None:
1408 continue
1409 pks = foot.getPeaks()
1410 pks.clear()
1411 if add:
1412 pks.append(pk)
1413 return True
A compact representation of a collection of pixels.
Definition: SpanSet.h:78

◆ 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 708 of file plugins.py.

708def buildSymmetricTemplates(debResult, log, patchEdges=False, setOrigTemplate=True):
709 """Build a symmetric template for each peak in each filter
710
711 Given ``maskedImageF``, ``footprint``, and a ``DebldendedPeak``, creates
712 a symmetric template (``templateImage`` and ``templateFootprint``) around
713 the peak for all peaks not flagged as ``skip`` or ``deblendedAsPsf``.
714
715 Parameters
716 ----------
718 Container for the final deblender results.
719 log: `log.Log`
720 LSST logger for logging purposes.
721 patchEdges: `bool`, optional
722 If True and if the parent Footprint touches pixels with the
723 ``EDGE`` bit set, then grow the parent Footprint to include
724 all symmetric templates.
725
726 Returns
727 -------
728 modified: `bool`
729 If any peaks are not skipped or marked as point sources,
730 ``modified`` is ``True. Otherwise ``modified`` is ``False``.
731 """
732 modified = False
733 # Create the Templates for each peak in each filter
734 for fidx in debResult.filters:
735 dp = debResult.deblendedParents[fidx]
736 imbb = dp.img.getBBox()
737 log.trace('Creating templates for footprint at x0,y0,W,H = %i, %i, %i, %i)', dp.x0, dp.y0, dp.W, dp.H)
738
739 for peaki, pkres in enumerate(dp.peaks):
740 log.trace('Deblending peak %i of %i', peaki, len(dp.peaks))
741 # TODO: Check debResult to see if the peak is deblended as a point source
742 # when comparing all bands, not just a single band
743 if pkres.skip or pkres.deblendedAsPsf:
744 continue
745 modified = True
746 pk = pkres.peak
747 cx, cy = pk.getIx(), pk.getIy()
748 if not imbb.contains(geom.Point2I(cx, cy)):
749 log.trace('Peak center is not inside image; skipping %i', pkres.pki)
750 pkres.setOutOfBounds()
751 continue
752 log.trace('computing template for peak %i at (%i, %i)', pkres.pki, cx, cy)
753 timg, tfoot, patched = bUtils.buildSymmetricTemplate(dp.maskedImage, dp.fp, pk, dp.avgNoise,
754 True, patchEdges)
755 if timg is None:
756 log.trace('Peak %i at (%i, %i): failed to build symmetric template', pkres.pki, cx, cy)
757 pkres.setFailedSymmetricTemplate()
758 continue
759
760 if patched:
761 pkres.setPatched()
762
763 # possibly save the original symmetric template
764 if setOrigTemplate:
765 pkres.setOrigTemplate(timg, tfoot)
766 pkres.setTemplate(timg, tfoot)
767 return modified
768
769
def buildSymmetricTemplates(debResult, log, patchEdges=False, setOrigTemplate=True)
Definition: plugins.py:708

◆ 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 1041 of file plugins.py.

1041def clipFootprintsToNonzero(debResult, log):
1042 r"""Clip non-zero spans in the template footprints for every peak in each filter.
1043
1044 Peak ``Footprint``\ s are clipped to the region in the image containing
1045 non-zero values by dropping spans that are completely zero and moving
1046 endpoints to non-zero pixels (but does not split spans that have
1047 internal zeros).
1048
1049 Parameters
1050 ----------
1052 Container for the final deblender results.
1053 log: `log.Log`
1054 LSST logger for logging purposes.
1055
1056 Returns
1057 -------
1058 modified: `bool`
1059 Whether or not any templates were modified.
1060 This will be ``True`` as long as there is at least one source that
1061 is not flagged as a PSF.
1062 """
1063 # Loop over all filters
1064 for fidx in debResult.filters:
1065 dp = debResult.deblendedParents[fidx]
1066 for peaki, pkres in enumerate(dp.peaks):
1067 if pkres.skip or pkres.deblendedAsPsf:
1068 continue
1069 timg, tfoot = pkres.templateImage, pkres.templateFootprint
1070 clipFootprintToNonzeroImpl(tfoot, timg)
1071 if not tfoot.getBBox().isEmpty() and tfoot.getBBox() != timg.getBBox(afwImage.PARENT):
1072 timg = timg.Factory(timg, tfoot.getBBox(), afwImage.PARENT, True)
1073 pkres.setTemplate(timg, tfoot)
1074 return False
1075
1076
def clipFootprintToNonzeroImpl(foot, image)
Definition: plugins.py:38
def clipFootprintsToNonzero(debResult, log)
Definition: plugins.py:1041

◆ 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 38 of file plugins.py.

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

◆ 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 164 of file plugins.py.

164def fitPsfs(debResult, log, psfChisqCut1=1.5, psfChisqCut2=1.5, psfChisqCut2b=1.5, tinyFootprintSize=2):
165 """Fit a PSF + smooth background model (linear) to a small region
166 around each peak.
167
168 This function will iterate over all filters in deblender result but does
169 not compare results across filters.
170 DeblendedPeaks that pass the cuts have their templates modified to the
171 PSF + background model and their ``deblendedAsPsf`` property set
172 to ``True``.
173
174 This will likely be replaced in the future with a function that compares
175 the psf chi-squared cuts so that peaks flagged as point sources will be
176 considered point sources in all bands.
177
178 Parameters
179 ----------
181 Container for the final deblender results.
182 log: `log.Log`
183 LSST logger for logging purposes.
184 psfChisqCut*: `float`, optional
185 ``psfChisqCut1`` is the maximum chi-squared-per-degree-of-freedom
186 allowed for a peak to be considered a PSF match without recentering.
187 A fit is also made that includes terms to recenter the PSF.
188 ``psfChisqCut2`` is the same as ``psfChisqCut1`` except it
189 determines the restriction on the fit that includes
190 recentering terms.
191 If the peak is a match for a re-centered PSF, the PSF is
192 repositioned at the new center and
193 the peak footprint is fit again, this time to the new PSF.
194 If the resulting chi-squared-per-degree-of-freedom is less than
195 ``psfChisqCut2b`` then it passes the re-centering algorithm.
196 If the peak passes both the re-centered and fixed position cuts,
197 the better of the two is accepted, but parameters for all three psf
198 fits are stored in the ``DebldendedPeak``.
199 The default for ``psfChisqCut1``, ``psfChisqCut2``, and
200 ``psfChisqCut2b`` is ``1.5``.
201 tinyFootprintSize: `float`, optional
202 The PSF model is shrunk to the size that contains the original
203 footprint. If the bbox of the clipped PSF model for a peak is
204 smaller than ``max(tinyFootprintSize,2)`` then ``tinyFootprint`` for
205 the peak is set to ``True`` and the peak is not fit. The default is 2.
206
207 Returns
208 -------
209 modified: `bool`
210 If any templates have been assigned to PSF point sources then
211 ``modified`` is ``True``, otherwise it is ``False``.
212 """
213 from .baseline import CachingPsf
214 modified = False
215 # Loop over all of the filters to build the PSF
216 for fidx in debResult.filters:
217 dp = debResult.deblendedParents[fidx]
218 peaks = dp.fp.getPeaks()
219 cpsf = CachingPsf(dp.psf)
220
221 # create mask image for pixels within the footprint
222 fmask = afwImage.Mask(dp.bb)
223 fmask.setXY0(dp.bb.getMinX(), dp.bb.getMinY())
224 dp.fp.spans.setMask(fmask, 1)
225
226 # pk.getF() -- retrieving the floating-point location of the peak
227 # -- actually shows up in the profile if we do it in the loop, so
228 # grab them all here.
229 peakF = [pk.getF() for pk in peaks]
230
231 for pki, (pk, pkres, pkF) in enumerate(zip(peaks, dp.peaks, peakF)):
232 log.trace('Filter %s, Peak %i', fidx, pki)
233 ispsf = _fitPsf(dp.fp, fmask, pk, pkF, pkres, dp.bb, peaks, peakF, log, cpsf, dp.psffwhm,
234 dp.img, dp.varimg, psfChisqCut1, psfChisqCut2, psfChisqCut2b, tinyFootprintSize)
235 modified = modified or ispsf
236 return modified
237
238
int max
Represent a 2-dimensional array of bitmask pixels.
Definition: Mask.h:77
def fitPsfs(debResult, log, psfChisqCut1=1.5, psfChisqCut2=1.5, psfChisqCut2b=1.5, tinyFootprintSize=2)
Definition: plugins.py:164

◆ 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 1005 of file plugins.py.

1005def makeTemplatesMonotonic(debResult, log):
1006 """Make the templates monotonic.
1007
1008 The pixels in the templates are modified such that pixels further
1009 from the peak will have values smaller than those closer to the peak.
1010
1011 Parameters
1012 ----------
1014 Container for the final deblender results.
1015 log: `log.Log`
1016 LSST logger for logging purposes.
1017
1018 Returns
1019 -------
1020 modified: `bool`
1021 Whether or not any templates were modified.
1022 This will be ``True`` as long as there is at least one source that
1023 is not flagged as a PSF.
1024 """
1025 modified = False
1026 # Loop over all filters
1027 for fidx in debResult.filters:
1028 dp = debResult.deblendedParents[fidx]
1029 for peaki, pkres in enumerate(dp.peaks):
1030 if pkres.skip or pkres.deblendedAsPsf:
1031 continue
1032 modified = True
1033 timg, tfoot = pkres.templateImage, pkres.templateFootprint
1034 pk = pkres.peak
1035 log.trace('Making template %i monotonic', pkres.pki)
1036 bUtils.makeMonotonic(timg, pk)
1037 pkres.setTemplate(timg, tfoot)
1038 return modified
1039
1040
def makeTemplatesMonotonic(debResult, log)
Definition: plugins.py:1005

◆ 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 956 of file plugins.py.

956def medianSmoothTemplates(debResult, log, medianFilterHalfsize=2):
957 """Applying median smoothing filter to the template images for every
958 peak in every filter.
959
960 Parameters
961 ----------
963 Container for the final deblender results.
964 log: `log.Log`
965 LSST logger for logging purposes.
966 medianFilterHalfSize: `int`, optional
967 Half the box size of the median filter, i.e. a
968 ``medianFilterHalfSize`` of 50 means that each output pixel will
969 be the median of the pixels in a 101 x 101-pixel box in the input
970 image. This parameter is only used when
971 ``medianSmoothTemplate==True``, otherwise it is ignored.
972
973 Returns
974 -------
975 modified: `bool`
976 Whether or not any templates were modified.
977 This will be ``True`` as long as there is at least one source that
978 is not flagged as a PSF.
979 """
980 modified = False
981 # Loop over all filters
982 for fidx in debResult.filters:
983 dp = debResult.deblendedParents[fidx]
984 for peaki, pkres in enumerate(dp.peaks):
985 if pkres.skip or pkres.deblendedAsPsf:
986 continue
987 modified = True
988 timg, tfoot = pkres.templateImage, pkres.templateFootprint
989 filtsize = medianFilterHalfsize*2 + 1
990 if timg.getWidth() >= filtsize and timg.getHeight() >= filtsize:
991 log.trace('Median filtering template %i', pkres.pki)
992 # We want the output to go in "t1", so copy it into
993 # "inimg" for input
994 inimg = timg.Factory(timg, True)
995 bUtils.medianFilter(inimg, timg, medianFilterHalfsize)
996 # possible save this median-filtered template
997 pkres.setMedianFilteredTemplate(timg, tfoot)
998 else:
999 log.trace('Not median-filtering template %i: size %i x %i smaller than required %i x %i',
1000 pkres.pki, timg.getWidth(), timg.getHeight(), filtsize, filtsize)
1001 pkres.setTemplate(timg, tfoot)
1002 return modified
1003
1004
def medianSmoothTemplates(debResult, log, medianFilterHalfsize=2)
Definition: plugins.py:956

◆ 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 ``~afw.detection.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 770 of file plugins.py.

770def rampFluxAtEdge(debResult, log, patchEdges=False):
771 r"""Adjust flux on the edges of the template footprints.
772
773 Using the PSF, a peak ``~afw.detection.Footprint`` with pixels on the edge
774 of ``footprint`` is grown by the ``psffwhm*1.5`` and filled in
775 with ramped pixels. The result is a new symmetric footprint
776 template for the peaks near the edge.
777
778 Parameters
779 ----------
781 Container for the final deblender results.
782 log: `log.Log`
783 LSST logger for logging purposes.
784 patchEdges: `bool`, optional
785 If True and if the parent Footprint touches pixels with the
786 ``EDGE`` bit set, then grow the parent Footprint to include
787 all symmetric templates.
788
789 Returns
790 -------
791 modified: `bool`
792 If any peaks have their templates modified to include flux at the
793 edges, ``modified`` is ``True``.
794 """
795 modified = False
796 # Loop over all filters
797 for fidx in debResult.filters:
798 dp = debResult.deblendedParents[fidx]
799 log.trace('Checking for significant flux at edge: sigma1=%g', dp.avgNoise)
800
801 for peaki, pkres in enumerate(dp.peaks):
802 if pkres.skip or pkres.deblendedAsPsf:
803 continue
804 timg, tfoot = pkres.templateImage, pkres.templateFootprint
805 if bUtils.hasSignificantFluxAtEdge(timg, tfoot, 3*dp.avgNoise):
806 log.trace("Template %i has significant flux at edge: ramping", pkres.pki)
807 try:
808 (timg2, tfoot2, patched) = _handle_flux_at_edge(log, dp.psffwhm, timg, tfoot, dp.fp,
809 dp.maskedImage, dp.x0, dp.x1,
810 dp.y0, dp.y1, dp.psf, pkres.peak,
811 dp.avgNoise, patchEdges)
812 except lsst.pex.exceptions.Exception as exc:
814 and "CoaddPsf" in str(exc)):
815 pkres.setOutOfBounds()
816 continue
817 raise
818 pkres.setRampedTemplate(timg2, tfoot2)
819 if patched:
820 pkres.setPatched()
821 pkres.setTemplate(timg2, tfoot2)
822 modified = True
823 return modified
824
825
Provides consistent interface for LSST exceptions.
Definition: Exception.h:107
Reports invalid arguments.
Definition: Runtime.h:66
def rampFluxAtEdge(debResult, log, patchEdges=False)
Definition: plugins.py:770

◆ 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 1149 of file plugins.py.

1149def reconstructTemplates(debResult, log, maxTempDotProd=0.5):
1150 """Remove "degenerate templates"
1151
1152 If galaxies have substructure, such as face-on spirals, the process of
1153 identifying peaks can "shred" the galaxy into many pieces. The templates
1154 of shredded galaxies are typically quite similar because they represent
1155 the same galaxy, so we try to identify these "degenerate" peaks
1156 by looking at the inner product (in pixel space) of pairs of templates.
1157 If they are nearly parallel, we only keep one of the peaks and reject
1158 the other. If only one of the peaks is a PSF template, the other template
1159 is used, otherwise the one with the maximum template value is kept.
1160
1161 Parameters
1162 ----------
1164 Container for the final deblender results.
1165 log: `log.Log`
1166 LSST logger for logging purposes.
1167 maxTempDotProd: `float`, optional
1168 All dot products between templates greater than ``maxTempDotProd``
1169 will result in one of the templates removed.
1170
1171 Returns
1172 -------
1173 modified: `bool`
1174 If any degenerate templates are found, ``modified`` is ``True``.
1175 """
1176 log.trace('Looking for degnerate templates')
1177
1178 foundReject = False
1179 for fidx in debResult.filters:
1180 dp = debResult.deblendedParents[fidx]
1181 nchild = np.sum([pkres.skip is False for pkres in dp.peaks])
1182 indexes = [pkres.pki for pkres in dp.peaks if pkres.skip is False]
1183
1184 # We build a matrix that stores the dot product between templates.
1185 # We convert the template images to HeavyFootprints because they already have a method
1186 # to compute the dot product.
1187 A = np.zeros((nchild, nchild))
1188 maxTemplate = []
1189 heavies = []
1190 for pkres in dp.peaks:
1191 if pkres.skip:
1192 continue
1193 heavies.append(afwDet.makeHeavyFootprint(pkres.templateFootprint,
1194 afwImage.MaskedImageF(pkres.templateImage)))
1195 maxTemplate.append(np.max(pkres.templateImage.getArray()))
1196
1197 for i in range(nchild):
1198 for j in range(i + 1):
1199 A[i, j] = heavies[i].dot(heavies[j])
1200
1201 # Normalize the dot products to get the cosine of the angle between templates
1202 for i in range(nchild):
1203 for j in range(i):
1204 norm = A[i, i]*A[j, j]
1205 if norm <= 0:
1206 A[i, j] = 0
1207 else:
1208 A[i, j] /= np.sqrt(norm)
1209
1210 # Iterate over pairs of objects and find the maximum non-diagonal element of the matrix.
1211 # Exit the loop once we find a single degenerate pair greater than the threshold.
1212 rejectedIndex = -1
1213 for i in range(nchild):
1214 currentMax = 0.
1215 for j in range(i):
1216 if A[i, j] > currentMax:
1217 currentMax = A[i, j]
1218 if currentMax > maxTempDotProd:
1219 foundReject = True
1220 rejectedIndex = j
1221
1222 if foundReject:
1223 break
1224
1225 del A
1226
1227 # If one of the objects is identified as a PSF keep the other one, otherwise keep the one
1228 # with the maximum template value
1229 if foundReject:
1230 keep = indexes[i]
1231 reject = indexes[rejectedIndex]
1232 if dp.peaks[keep].deblendedAsPsf and dp.peaks[reject].deblendedAsPsf is False:
1233 keep = indexes[rejectedIndex]
1234 reject = indexes[i]
1235 elif dp.peaks[keep].deblendedAsPsf is False and dp.peaks[reject].deblendedAsPsf:
1236 reject = indexes[rejectedIndex]
1237 keep = indexes[i]
1238 else:
1239 if maxTemplate[rejectedIndex] > maxTemplate[i]:
1240 keep = indexes[rejectedIndex]
1241 reject = indexes[i]
1242 log.trace('Removing object with index %d : %f. Degenerate with %d',
1243 reject, currentMax, keep)
1244 dp.peaks[reject].skip = True
1245 dp.peaks[reject].degenerate = True
1246
1247 return foundReject
1248
1249
HeavyFootprint< ImagePixelT, MaskPixelT, VariancePixelT > makeHeavyFootprint(Footprint const &foot, lsst::afw::image::MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > const &img, HeavyFootprintCtrl const *ctrl=nullptr)
Create a HeavyFootprint with footprint defined by the given Footprint and pixel values from the given...
def dot(symb, c, r, frame=None, size=2, ctype=None, origin=afwImage.PARENT, *args, **kwargs)
Definition: ds9.py:100
def reconstructTemplates(debResult, log, maxTempDotProd=0.5)
Definition: plugins.py:1149

◆ 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 1077 of file plugins.py.

1077def weightTemplates(debResult, log):
1078 """Weight the templates to best fit the observed image in each filter
1079
1080 This function re-weights the templates so that their linear combination
1081 best represents the observed image in that filter.
1082 In the future it may be useful to simultaneously weight all of the
1083 filters together.
1084
1085 Parameters
1086 ----------
1088 Container for the final deblender results.
1089 log: `log.Log`
1090 LSST logger for logging purposes.
1091
1092 Returns
1093 -------
1094 modified: `bool`
1095 ``weightTemplates`` does not actually modify the ``Footprint``
1096 templates other than to add a weight to them, so ``modified``
1097 is always ``False``.
1098 """
1099 # Weight the templates by doing a least-squares fit to the image
1100 log.trace('Weighting templates')
1101 for fidx in debResult.filters:
1102 _weightTemplates(debResult.deblendedParents[fidx])
1103 return False
1104
1105
def weightTemplates(debResult, log)
Definition: plugins.py:1077