LSSTApplications  19.0.0-13-g16625d3+11,20.0.0+1,20.0.0+22,20.0.0+23,20.0.0+25,20.0.0+28,20.0.0+3,20.0.0+4,20.0.0+5,20.0.0+7,20.0.0+8,20.0.0-1-g10df615+22,20.0.0-1-g253301a+6,20.0.0-1-g596936a+26,20.0.0-1-g8a53f90+2,20.0.0-1-gc96f8cb+27,20.0.0-1-gd1c87d7+2,20.0.0-1-gdb27ee5+6,20.0.0-11-gda4966f+10,20.0.0-13-g198ee8df,20.0.0-2-gec03fae+4,20.0.0-24-g0eb5a41+1,20.0.0-3-gd2e950e,20.0.0-4-g4a2362f,20.0.0-4-gde602ef96+5,20.0.0-4-ge48a6ca+21,20.0.0-5-gc4485221+1,20.0.0-6-g9bcb941+3,20.0.0-7-g3c4151b+3,20.0.0-7-gb92c176+1,w.2020.30
LSSTDataManagementBasePackage
Public Member Functions | Public Attributes | Static Public Attributes | List of all members
lsst.meas.deblender.deblend.SourceDeblendTask Class Reference

Split blended sources into individual sources. More...

Inheritance diagram for lsst.meas.deblender.deblend.SourceDeblendTask:

Public Member Functions

def __init__ (self, schema, peakSchema=None, **kwargs)
 Create the task, adding necessary fields to the given schema. More...
 
def addSchemaKeys (self, schema)
 
def run (self, exposure, sources)
 Get the psf from the provided exposure and then run deblend(). More...
 
def deblend (self, exposure, srcs, psf)
 Deblend. More...
 
def preSingleDeblendHook (self, exposure, srcs, i, fp, psf, psf_fwhm, sigma1)
 
def postSingleDeblendHook (self, exposure, srcs, i, npre, kids, fp, psf, psf_fwhm, sigma1, res)
 
def isLargeFootprint (self, footprint)
 
def isMasked (self, footprint, mask)
 
def skipParent (self, source, mask)
 

Public Attributes

 schema
 
 toCopyFromParent
 
 peakSchemaMapper
 
 nChildKey
 
 psfKey
 
 psfCenterKey
 
 psfFluxKey
 
 tooManyPeaksKey
 
 tooBigKey
 
 maskedKey
 
 deblendFailedKey
 
 deblendSkippedKey
 
 deblendRampedTemplateKey
 
 deblendPatchedTemplateKey
 
 hasStrayFluxKey
 

Static Public Attributes

 ConfigClass = SourceDeblendConfig
 

Detailed Description

Split blended sources into individual sources.

This task has no return value; it only modifies the SourceCatalog in-place.

Definition at line 150 of file deblend.py.

Constructor & Destructor Documentation

◆ __init__()

def lsst.meas.deblender.deblend.SourceDeblendTask.__init__ (   self,
  schema,
  peakSchema = None,
**  kwargs 
)

Create the task, adding necessary fields to the given schema.

Parameters
[in,out]schemaSchema object for measurement fields; will be modified in-place.
[in]peakSchemaSchema of Footprint Peaks that will be passed to the deblender. Any fields beyond the PeakTable minimal schema will be transferred to the main source Schema. If None, no fields will be transferred from the Peaks.
[in]**kwargsPassed to Task.__init__.

Definition at line 161 of file deblend.py.

161  def __init__(self, schema, peakSchema=None, **kwargs):
162  """!
163  Create the task, adding necessary fields to the given schema.
164 
165  @param[in,out] schema Schema object for measurement fields; will be modified in-place.
166  @param[in] peakSchema Schema of Footprint Peaks that will be passed to the deblender.
167  Any fields beyond the PeakTable minimal schema will be transferred
168  to the main source Schema. If None, no fields will be transferred
169  from the Peaks.
170  @param[in] **kwargs Passed to Task.__init__.
171  """
172  pipeBase.Task.__init__(self, **kwargs)
173  self.schema = schema
174  self.toCopyFromParent = [item.key for item in self.schema
175  if item.field.getName().startswith("merge_footprint")]
176  peakMinimalSchema = afwDet.PeakTable.makeMinimalSchema()
177  if peakSchema is None:
178  # In this case, the peakSchemaMapper will transfer nothing, but we'll still have one
179  # to simplify downstream code
180  self.peakSchemaMapper = afwTable.SchemaMapper(peakMinimalSchema, schema)
181  else:
182  self.peakSchemaMapper = afwTable.SchemaMapper(peakSchema, schema)
183  for item in peakSchema:
184  if item.key not in peakMinimalSchema:
185  self.peakSchemaMapper.addMapping(item.key, item.field)
186  # Because SchemaMapper makes a copy of the output schema you give its ctor, it isn't
187  # updating this Schema in place. That's probably a design flaw, but in the meantime,
188  # we'll keep that schema in sync with the peakSchemaMapper.getOutputSchema() manually,
189  # by adding the same fields to both.
190  schema.addField(item.field)
191  assert schema == self.peakSchemaMapper.getOutputSchema(), "Logic bug mapping schemas"
192  self.addSchemaKeys(schema)
193 

Member Function Documentation

◆ addSchemaKeys()

def lsst.meas.deblender.deblend.SourceDeblendTask.addSchemaKeys (   self,
  schema 
)

Definition at line 194 of file deblend.py.

194  def addSchemaKeys(self, schema):
195  self.nChildKey = schema.addField('deblend_nChild', type=np.int32,
196  doc='Number of children this object has (defaults to 0)')
197  self.psfKey = schema.addField('deblend_deblendedAsPsf', type='Flag',
198  doc='Deblender thought this source looked like a PSF')
199  self.psfCenterKey = afwTable.Point2DKey.addFields(schema, 'deblend_psfCenter',
200  'If deblended-as-psf, the PSF centroid', "pixel")
201  self.psfFluxKey = schema.addField('deblend_psf_instFlux', type='D',
202  doc='If deblended-as-psf, the instrumental PSF flux', units='count')
203  self.tooManyPeaksKey = schema.addField('deblend_tooManyPeaks', type='Flag',
204  doc='Source had too many peaks; '
205  'only the brightest were included')
206  self.tooBigKey = schema.addField('deblend_parentTooBig', type='Flag',
207  doc='Parent footprint covered too many pixels')
208  self.maskedKey = schema.addField('deblend_masked', type='Flag',
209  doc='Parent footprint was predominantly masked')
210 
211  if self.config.catchFailures:
212  self.deblendFailedKey = schema.addField('deblend_failed', type='Flag',
213  doc="Deblending failed on source")
214 
215  self.deblendSkippedKey = schema.addField('deblend_skipped', type='Flag',
216  doc="Deblender skipped this source")
217 
218  self.deblendRampedTemplateKey = schema.addField(
219  'deblend_rampedTemplate', type='Flag',
220  doc=('This source was near an image edge and the deblender used '
221  '"ramp" edge-handling.'))
222 
223  self.deblendPatchedTemplateKey = schema.addField(
224  'deblend_patchedTemplate', type='Flag',
225  doc=('This source was near an image edge and the deblender used '
226  '"patched" edge-handling.'))
227 
228  self.hasStrayFluxKey = schema.addField(
229  'deblend_hasStrayFlux', type='Flag',
230  doc=('This source was assigned some stray flux'))
231 
232  self.log.trace('Added keys to schema: %s', ", ".join(str(x) for x in (
233  self.nChildKey, self.psfKey, self.psfCenterKey, self.psfFluxKey,
234  self.tooManyPeaksKey, self.tooBigKey)))
235 

◆ deblend()

def lsst.meas.deblender.deblend.SourceDeblendTask.deblend (   self,
  exposure,
  srcs,
  psf 
)

Deblend.

Parameters
[in]exposureExposure to process
[in,out]srcsSourceCatalog containing sources detected on this exposure.
[in]psfPSF
Returns
None

Definition at line 256 of file deblend.py.

256  def deblend(self, exposure, srcs, psf):
257  """!
258  Deblend.
259 
260  @param[in] exposure Exposure to process
261  @param[in,out] srcs SourceCatalog containing sources detected on this exposure.
262  @param[in] psf PSF
263 
264  @return None
265  """
266  self.log.info("Deblending %d sources" % len(srcs))
267 
268  from lsst.meas.deblender.baseline import deblend
269 
270  # find the median stdev in the image...
271  mi = exposure.getMaskedImage()
272  statsCtrl = afwMath.StatisticsControl()
273  statsCtrl.setAndMask(mi.getMask().getPlaneBitMask(self.config.maskPlanes))
274  stats = afwMath.makeStatistics(mi.getVariance(), mi.getMask(), afwMath.MEDIAN, statsCtrl)
275  sigma1 = math.sqrt(stats.getValue(afwMath.MEDIAN))
276  self.log.trace('sigma1: %g', sigma1)
277 
278  n0 = len(srcs)
279  nparents = 0
280  for i, src in enumerate(srcs):
281  # t0 = time.clock()
282 
283  fp = src.getFootprint()
284  pks = fp.getPeaks()
285 
286  # Since we use the first peak for the parent object, we should propagate its flags
287  # to the parent source.
288  src.assign(pks[0], self.peakSchemaMapper)
289 
290  if len(pks) < 2:
291  continue
292 
293  if self.isLargeFootprint(fp):
294  src.set(self.tooBigKey, True)
295  self.skipParent(src, mi.getMask())
296  self.log.warn('Parent %i: skipping large footprint (area: %i)',
297  int(src.getId()), int(fp.getArea()))
298  continue
299  if self.isMasked(fp, exposure.getMaskedImage().getMask()):
300  src.set(self.maskedKey, True)
301  self.skipParent(src, mi.getMask())
302  self.log.warn('Parent %i: skipping masked footprint (area: %i)',
303  int(src.getId()), int(fp.getArea()))
304  continue
305 
306  nparents += 1
307  bb = fp.getBBox()
308  psf_fwhm = self._getPsfFwhm(psf, bb)
309 
310  self.log.trace('Parent %i: deblending %i peaks', int(src.getId()), len(pks))
311 
312  self.preSingleDeblendHook(exposure, srcs, i, fp, psf, psf_fwhm, sigma1)
313  npre = len(srcs)
314 
315  # This should really be set in deblend, but deblend doesn't have access to the src
316  src.set(self.tooManyPeaksKey, len(fp.getPeaks()) > self.config.maxNumberOfPeaks)
317 
318  try:
319  res = deblend(
320  fp, mi, psf, psf_fwhm, sigma1=sigma1,
321  psfChisqCut1=self.config.psfChisq1,
322  psfChisqCut2=self.config.psfChisq2,
323  psfChisqCut2b=self.config.psfChisq2b,
324  maxNumberOfPeaks=self.config.maxNumberOfPeaks,
325  strayFluxToPointSources=self.config.strayFluxToPointSources,
326  assignStrayFlux=self.config.assignStrayFlux,
327  strayFluxAssignment=self.config.strayFluxRule,
328  rampFluxAtEdge=(self.config.edgeHandling == 'ramp'),
329  patchEdges=(self.config.edgeHandling == 'noclip'),
330  tinyFootprintSize=self.config.tinyFootprintSize,
331  clipStrayFluxFraction=self.config.clipStrayFluxFraction,
332  weightTemplates=self.config.weightTemplates,
333  removeDegenerateTemplates=self.config.removeDegenerateTemplates,
334  maxTempDotProd=self.config.maxTempDotProd,
335  medianSmoothTemplate=self.config.medianSmoothTemplate
336  )
337  if self.config.catchFailures:
338  src.set(self.deblendFailedKey, False)
339  except Exception as e:
340  if self.config.catchFailures:
341  self.log.warn("Unable to deblend source %d: %s" % (src.getId(), e))
342  src.set(self.deblendFailedKey, True)
343  import traceback
344  traceback.print_exc()
345  continue
346  else:
347  raise
348 
349  kids = []
350  nchild = 0
351  for j, peak in enumerate(res.deblendedParents[0].peaks):
352  heavy = peak.getFluxPortion()
353  if heavy is None or peak.skip:
354  src.set(self.deblendSkippedKey, True)
355  if not self.config.propagateAllPeaks:
356  # Don't care
357  continue
358  # We need to preserve the peak: make sure we have enough info to create a minimal
359  # child src
360  self.log.trace("Peak at (%i,%i) failed. Using minimal default info for child.",
361  pks[j].getIx(), pks[j].getIy())
362  if heavy is None:
363  # copy the full footprint and strip out extra peaks
364  foot = afwDet.Footprint(src.getFootprint())
365  peakList = foot.getPeaks()
366  peakList.clear()
367  peakList.append(peak.peak)
368  zeroMimg = afwImage.MaskedImageF(foot.getBBox())
369  heavy = afwDet.makeHeavyFootprint(foot, zeroMimg)
370  if peak.deblendedAsPsf:
371  if peak.psfFitFlux is None:
372  peak.psfFitFlux = 0.0
373  if peak.psfFitCenter is None:
374  peak.psfFitCenter = (peak.peak.getIx(), peak.peak.getIy())
375 
376  assert(len(heavy.getPeaks()) == 1)
377 
378  src.set(self.deblendSkippedKey, False)
379  child = srcs.addNew()
380  nchild += 1
381  for key in self.toCopyFromParent:
382  child.set(key, src.get(key))
383  child.assign(heavy.getPeaks()[0], self.peakSchemaMapper)
384  child.setParent(src.getId())
385  child.setFootprint(heavy)
386  child.set(self.psfKey, peak.deblendedAsPsf)
387  child.set(self.hasStrayFluxKey, peak.strayFlux is not None)
388  if peak.deblendedAsPsf:
389  (cx, cy) = peak.psfFitCenter
390  child.set(self.psfCenterKey, geom.Point2D(cx, cy))
391  child.set(self.psfFluxKey, peak.psfFitFlux)
392  child.set(self.deblendRampedTemplateKey, peak.hasRampedTemplate)
393  child.set(self.deblendPatchedTemplateKey, peak.patched)
394  kids.append(child)
395 
396  # Child footprints may extend beyond the full extent of their parent's which
397  # results in a failure of the replace-by-noise code to reinstate these pixels
398  # to their original values. The following updates the parent footprint
399  # in-place to ensure it contains the full union of itself and all of its
400  # children's footprints.
401  spans = src.getFootprint().spans
402  for child in kids:
403  spans = spans.union(child.getFootprint().spans)
404  src.getFootprint().setSpans(spans)
405 
406  src.set(self.nChildKey, nchild)
407 
408  self.postSingleDeblendHook(exposure, srcs, i, npre, kids, fp, psf, psf_fwhm, sigma1, res)
409  # print('Deblending parent id', src.getId(), 'took', time.clock() - t0)
410 
411  n1 = len(srcs)
412  self.log.info('Deblended: of %i sources, %i were deblended, creating %i children, total %i sources'
413  % (n0, nparents, n1-n0, n1))
414 

◆ isLargeFootprint()

def lsst.meas.deblender.deblend.SourceDeblendTask.isLargeFootprint (   self,
  footprint 
)
Returns whether a Footprint is large

'Large' is defined by thresholds on the area, size and axis ratio.
These may be disabled independently by configuring them to be non-positive.

This is principally intended to get rid of satellite streaks, which the
deblender or other downstream processing can have trouble dealing with
(e.g., multiple large HeavyFootprints can chew up memory).

Definition at line 421 of file deblend.py.

421  def isLargeFootprint(self, footprint):
422  """Returns whether a Footprint is large
423 
424  'Large' is defined by thresholds on the area, size and axis ratio.
425  These may be disabled independently by configuring them to be non-positive.
426 
427  This is principally intended to get rid of satellite streaks, which the
428  deblender or other downstream processing can have trouble dealing with
429  (e.g., multiple large HeavyFootprints can chew up memory).
430  """
431  if self.config.maxFootprintArea > 0 and footprint.getArea() > self.config.maxFootprintArea:
432  return True
433  if self.config.maxFootprintSize > 0:
434  bbox = footprint.getBBox()
435  if max(bbox.getWidth(), bbox.getHeight()) > self.config.maxFootprintSize:
436  return True
437  if self.config.minFootprintAxisRatio > 0:
438  axes = afwEll.Axes(footprint.getShape())
439  if axes.getB() < self.config.minFootprintAxisRatio*axes.getA():
440  return True
441  return False
442 

◆ isMasked()

def lsst.meas.deblender.deblend.SourceDeblendTask.isMasked (   self,
  footprint,
  mask 
)
Returns whether the footprint violates the mask limits

Definition at line 443 of file deblend.py.

443  def isMasked(self, footprint, mask):
444  """Returns whether the footprint violates the mask limits"""
445  size = float(footprint.getArea())
446  for maskName, limit in self.config.maskLimits.items():
447  maskVal = mask.getPlaneBitMask(maskName)
448  unmaskedSpan = footprint.spans.intersectNot(mask, maskVal) # spanset of unmasked pixels
449  if (size - unmaskedSpan.getArea())/size > limit:
450  return True
451  return False
452 

◆ postSingleDeblendHook()

def lsst.meas.deblender.deblend.SourceDeblendTask.postSingleDeblendHook (   self,
  exposure,
  srcs,
  i,
  npre,
  kids,
  fp,
  psf,
  psf_fwhm,
  sigma1,
  res 
)

Definition at line 418 of file deblend.py.

418  def postSingleDeblendHook(self, exposure, srcs, i, npre, kids, fp, psf, psf_fwhm, sigma1, res):
419  pass
420 

◆ preSingleDeblendHook()

def lsst.meas.deblender.deblend.SourceDeblendTask.preSingleDeblendHook (   self,
  exposure,
  srcs,
  i,
  fp,
  psf,
  psf_fwhm,
  sigma1 
)

Definition at line 415 of file deblend.py.

415  def preSingleDeblendHook(self, exposure, srcs, i, fp, psf, psf_fwhm, sigma1):
416  pass
417 

◆ run()

def lsst.meas.deblender.deblend.SourceDeblendTask.run (   self,
  exposure,
  sources 
)

Get the psf from the provided exposure and then run deblend().

Parameters
[in]exposureExposure to process
[in,out]sourcesSourceCatalog containing sources detected on this exposure.
Returns
None

Definition at line 237 of file deblend.py.

237  def run(self, exposure, sources):
238  """!
239  Get the psf from the provided exposure and then run deblend().
240 
241  @param[in] exposure Exposure to process
242  @param[in,out] sources SourceCatalog containing sources detected on this exposure.
243 
244  @return None
245  """
246  psf = exposure.getPsf()
247  assert sources.getSchema() == self.schema
248  self.deblend(exposure, sources, psf)
249 

◆ skipParent()

def lsst.meas.deblender.deblend.SourceDeblendTask.skipParent (   self,
  source,
  mask 
)
Indicate that the parent source is not being deblended

We set the appropriate flags and mask.

@param source  The source to flag as skipped
@param mask  The mask to update

Definition at line 453 of file deblend.py.

453  def skipParent(self, source, mask):
454  """Indicate that the parent source is not being deblended
455 
456  We set the appropriate flags and mask.
457 
458  @param source The source to flag as skipped
459  @param mask The mask to update
460  """
461  fp = source.getFootprint()
462  source.set(self.deblendSkippedKey, True)
463  source.set(self.nChildKey, len(fp.getPeaks())) # It would have this many if we deblended them all
464  if self.config.notDeblendedMask:
465  mask.addMaskPlane(self.config.notDeblendedMask)
466  fp.spans.setMask(mask, mask.getPlaneBitMask(self.config.notDeblendedMask))

Member Data Documentation

◆ ConfigClass

lsst.meas.deblender.deblend.SourceDeblendTask.ConfigClass = SourceDeblendConfig
static

Definition at line 158 of file deblend.py.

◆ deblendFailedKey

lsst.meas.deblender.deblend.SourceDeblendTask.deblendFailedKey

Definition at line 212 of file deblend.py.

◆ deblendPatchedTemplateKey

lsst.meas.deblender.deblend.SourceDeblendTask.deblendPatchedTemplateKey

Definition at line 223 of file deblend.py.

◆ deblendRampedTemplateKey

lsst.meas.deblender.deblend.SourceDeblendTask.deblendRampedTemplateKey

Definition at line 218 of file deblend.py.

◆ deblendSkippedKey

lsst.meas.deblender.deblend.SourceDeblendTask.deblendSkippedKey

Definition at line 215 of file deblend.py.

◆ hasStrayFluxKey

lsst.meas.deblender.deblend.SourceDeblendTask.hasStrayFluxKey

Definition at line 228 of file deblend.py.

◆ maskedKey

lsst.meas.deblender.deblend.SourceDeblendTask.maskedKey

Definition at line 208 of file deblend.py.

◆ nChildKey

lsst.meas.deblender.deblend.SourceDeblendTask.nChildKey

Definition at line 195 of file deblend.py.

◆ peakSchemaMapper

lsst.meas.deblender.deblend.SourceDeblendTask.peakSchemaMapper

Definition at line 180 of file deblend.py.

◆ psfCenterKey

lsst.meas.deblender.deblend.SourceDeblendTask.psfCenterKey

Definition at line 199 of file deblend.py.

◆ psfFluxKey

lsst.meas.deblender.deblend.SourceDeblendTask.psfFluxKey

Definition at line 201 of file deblend.py.

◆ psfKey

lsst.meas.deblender.deblend.SourceDeblendTask.psfKey

Definition at line 197 of file deblend.py.

◆ schema

lsst.meas.deblender.deblend.SourceDeblendTask.schema

Definition at line 173 of file deblend.py.

◆ toCopyFromParent

lsst.meas.deblender.deblend.SourceDeblendTask.toCopyFromParent

Definition at line 174 of file deblend.py.

◆ tooBigKey

lsst.meas.deblender.deblend.SourceDeblendTask.tooBigKey

Definition at line 206 of file deblend.py.

◆ tooManyPeaksKey

lsst.meas.deblender.deblend.SourceDeblendTask.tooManyPeaksKey

Definition at line 203 of file deblend.py.


The documentation for this class was generated from the following file:
lsst::log.log.logContinued.warn
def warn(fmt, *args)
Definition: logContinued.py:205
lsst::afw::detection::makeHeavyFootprint
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...
Definition: HeavyFootprint.h:148
lsst::log.log.logContinued.info
def info(fmt, *args)
Definition: logContinued.py:201
lsst::meas::deblender.baseline.deblend
def deblend(footprint, maskedImage, psf, psffwhm, psfChisqCut1=1.5, psfChisqCut2=1.5, psfChisqCut2b=1.5, fitPsfs=True, medianSmoothTemplate=True, medianFilterHalfsize=2, monotonicTemplate=True, weightTemplates=False, log=None, verbose=False, sigma1=None, maxNumberOfPeaks=0, assignStrayFlux=True, strayFluxToPointSources='necessary', strayFluxAssignment='r-to-peak', rampFluxAtEdge=False, patchEdges=False, tinyFootprintSize=2, getTemplateSum=False, clipStrayFluxFraction=0.001, clipFootprintToNonzero=True, removeDegenerateTemplates=False, maxTempDotProd=0.5)
Definition: baseline.py:448
lsst::afw::math::makeStatistics
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<>
Definition: Statistics.h:520
lsst.pipe.tasks.assembleCoadd.run
def run(self, skyInfo, tempExpRefList, imageScalerList, weightList, altMaskList=None, mask=None, supplementaryData=None)
Definition: assembleCoadd.py:712
lsst::afw::table::SchemaMapper
A mapping between the keys of two Schemas, used to copy data between them.
Definition: SchemaMapper.h:21
lsst::meas::deblender.baseline
Definition: baseline.py:1
max
int max
Definition: BoundedField.cc:104
lsst::meas::deblender
Definition: BaselineUtils.h:17
lsst::afw::math::StatisticsControl
Pass parameters to a Statistics object.
Definition: Statistics.h:93
lsst::log.log.logContinued.trace
def trace(fmt, *args)
Definition: logContinued.py:193
lsst::geom::Point< double, 2 >
lsst::afw::detection::Footprint
Class to describe the properties of a detected object from an image.
Definition: Footprint.h:63