285 def deblend(self, exposure, srcs, psf):
286 """Deblend.
287
288 Parameters
289 ----------
290 exposure : `lsst.afw.image.Exposure`
291 Exposure to be processed
292 srcs : `lsst.afw.table.SourceCatalog`
293 SourceCatalog containing sources detected on this exposure
294 psf : `lsst.afw.detection.Psf`
295 Point source function
296
297 Returns
298 -------
299 None
300 """
301
302 if self.config.useCiLimits:
303 self.log.info(f"Using CI catalog limits, "
304 f"the original number of sources to deblend was {len(srcs)}.")
305
306
307 minChildren, maxChildren = self.config.ciDeblendChildRange
308 nPeaks = np.array([len(src.getFootprint().peaks) for src in srcs])
309 childrenInRange = np.where((nPeaks >= minChildren) & (nPeaks <= maxChildren))[0]
310 if len(childrenInRange) < self.config.ciNumParentsToDeblend:
311 raise ValueError("Fewer than ciNumParentsToDeblend children were contained in the range "
312 "indicated by ciDeblendChildRange. Adjust this range to include more "
313 "parents.")
314
315
316 parents = nPeaks == 1
317 children = np.zeros((len(srcs),), dtype=bool)
318 children[childrenInRange[:self.config.ciNumParentsToDeblend]] = True
319 srcs = srcs[parents | children]
320
321
322 idFactory = srcs.getIdFactory()
323 maxId = np.max(srcs["id"])
324 idFactory.notify(maxId)
325
326 self.log.info("Deblending %d sources", len(srcs))
327
329
330
331 mi = exposure.getMaskedImage()
333 statsCtrl.setAndMask(mi.getMask().getPlaneBitMask(self.config.maskPlanes))
335 sigma1 = math.sqrt(stats.getValue(afwMath.MEDIAN))
336 self.log.trace('sigma1: %g', sigma1)
337
338 n0 = len(srcs)
339 nparents = 0
340 for i, src in enumerate(srcs):
341
342
343 fp = src.getFootprint()
344 pks = fp.getPeaks()
345
346
347
348 src.assign(pks[0], self.peakSchemaMapper)
349
350 if len(pks) < 2:
351 continue
352
353 if self.isLargeFootprint(fp):
354 src.set(self.tooBigKey, True)
355 self.skipParent(src, mi.getMask())
356 self.log.debug('Parent %i: skipping large footprint (area: %i)',
357 int(src.getId()), int(fp.getArea()))
358 continue
359 if self.isMasked(fp, exposure.getMaskedImage().getMask()):
360 src.set(self.maskedKey, True)
361 self.skipParent(src, mi.getMask())
362 self.log.debug('Parent %i: skipping masked footprint (area: %i)',
363 int(src.getId()), int(fp.getArea()))
364 continue
365
366 nparents += 1
367 center = fp.getCentroid()
368 psf_fwhm = self._getPsfFwhm(psf, center)
369
370 if not (psf_fwhm > 0):
371 if self.config.catchFailures:
372 self.log.warning("Unable to deblend source %d: because PSF FWHM=%f is invalid.",
373 src.getId(), psf_fwhm)
374 src.set(self.deblendFailedKey, True)
375 continue
376 else:
377 raise ValueError(f"PSF at {center} has an invalid FWHM value of {psf_fwhm}")
378
379 self.log.trace('Parent %i: deblending %i peaks', int(src.getId()), len(pks))
380
381 self.preSingleDeblendHook(exposure, srcs, i, fp, psf, psf_fwhm, sigma1)
382 npre = len(srcs)
383
384
385 src.set(self.tooManyPeaksKey, len(fp.getPeaks()) > self.config.maxNumberOfPeaks)
386
387 try:
389 fp, mi, psf, psf_fwhm, sigma1=sigma1,
390 psfChisqCut1=self.config.psfChisq1,
391 psfChisqCut2=self.config.psfChisq2,
392 psfChisqCut2b=self.config.psfChisq2b,
393 maxNumberOfPeaks=self.config.maxNumberOfPeaks,
394 strayFluxToPointSources=self.config.strayFluxToPointSources,
395 assignStrayFlux=self.config.assignStrayFlux,
396 strayFluxAssignment=self.config.strayFluxRule,
397 rampFluxAtEdge=(self.config.edgeHandling == 'ramp'),
398 patchEdges=(self.config.edgeHandling == 'noclip'),
399 tinyFootprintSize=self.config.tinyFootprintSize,
400 clipStrayFluxFraction=self.config.clipStrayFluxFraction,
401 weightTemplates=self.config.weightTemplates,
402 removeDegenerateTemplates=self.config.removeDegenerateTemplates,
403 maxTempDotProd=self.config.maxTempDotProd,
404 medianSmoothTemplate=self.config.medianSmoothTemplate
405 )
406 if self.config.catchFailures:
407 src.set(self.deblendFailedKey, False)
408 except Exception as e:
409 if self.config.catchFailures:
410 self.log.warning("Unable to deblend source %d: %s", src.getId(), e)
411 src.set(self.deblendFailedKey, True)
412 import traceback
413 traceback.print_exc()
414 continue
415 else:
416 raise
417
418 kids = []
419 nchild = 0
420 for j, peak in enumerate(res.deblendedParents[0].peaks):
421 heavy = peak.getFluxPortion()
422 if heavy is None or peak.skip:
423 src.set(self.deblendSkippedKey, True)
424 if not self.config.propagateAllPeaks:
425
426 continue
427
428
429 self.log.trace("Peak at (%i,%i) failed. Using minimal default info for child.",
430 pks[j].getIx(), pks[j].getIy())
431 if heavy is None:
432
434 peakList = foot.getPeaks()
435 peakList.clear()
436 peakList.append(peak.peak)
437 zeroMimg = afwImage.MaskedImageF(foot.getBBox())
439 if peak.deblendedAsPsf:
440 if peak.psfFitFlux is None:
441 peak.psfFitFlux = 0.0
442 if peak.psfFitCenter is None:
443 peak.psfFitCenter = (peak.peak.getIx(), peak.peak.getIy())
444
445 assert len(heavy.getPeaks()) == 1
446
447 src.set(self.deblendSkippedKey, False)
448 child = srcs.addNew()
449 nchild += 1
450 for key in self.toCopyFromParent:
451 child.set(key, src.get(key))
452 child.assign(heavy.getPeaks()[0], self.peakSchemaMapper)
453 child.setParent(src.getId())
454 child.setFootprint(heavy)
455 child.set(self.psfKey, peak.deblendedAsPsf)
456 child.set(self.hasStrayFluxKey, peak.strayFlux is not None)
457 if peak.deblendedAsPsf:
458 (cx, cy) = peak.psfFitCenter
460 child.set(self.psfFluxKey, peak.psfFitFlux)
461 child.set(self.deblendRampedTemplateKey, peak.hasRampedTemplate)
462 child.set(self.deblendPatchedTemplateKey, peak.patched)
463
464
465
466
467
468
469 child.set(self.peakCenter,
geom.Point2I(pks[j].getIx(), pks[j].getIy()))
470 child.set(self.peakIdKey, pks[j].getId())
471
472
473 child.set(self.nPeaksKey, 1)
474
475 child.set(self.parentNPeaksKey, len(pks))
476
477 kids.append(child)
478
479
480
481
482
483
484 spans = src.getFootprint().spans
485 for child in kids:
486 spans = spans.union(child.getFootprint().spans)
487 src.getFootprint().setSpans(spans)
488
489 src.set(self.nChildKey, nchild)
490
491 self.postSingleDeblendHook(exposure, srcs, i, npre, kids, fp, psf, psf_fwhm, sigma1, res)
492
493
494 n1 = len(srcs)
495 self.log.info('Deblended: of %i sources, %i were deblended, creating %i children, total %i sources',
496 n0, nparents, n1-n0, n1)
497
Pass parameters to a Statistics object.
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...
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Handle a watered-down front-end to the constructor (no variance)