24__all__ = (
"SourceDetectionConfig",
"SourceDetectionTask",
"addExposures")
26from contextlib
import contextmanager
39from lsst.utils.timer
import timeMethod
40from .subtractBackground
import SubtractBackgroundTask
44 """Configuration parameters for the SourceDetectionTask
46 minPixels = pexConfig.RangeField(
47 doc="detected sources with fewer than the specified number of pixels will be ignored",
48 dtype=int, optional=
False, default=1, min=0,
50 isotropicGrow = pexConfig.Field(
51 doc=
"Pixels should be grown as isotropically as possible (slower)",
52 dtype=bool, optional=
False, default=
False,
54 combinedGrow = pexConfig.Field(
55 doc=
"Grow all footprints at the same time? This allows disconnected footprints to merge.",
56 dtype=bool, default=
True,
58 nSigmaToGrow = pexConfig.Field(
59 doc=
"Grow detections by nSigmaToGrow * [PSF RMS width]; if 0 then do not grow",
60 dtype=float, default=2.4,
62 returnOriginalFootprints = pexConfig.Field(
63 doc=
"Grow detections to set the image mask bits, but return the original (not-grown) footprints",
64 dtype=bool, optional=
False, default=
False,
66 thresholdValue = pexConfig.RangeField(
67 doc=
"Threshold for footprints; exact meaning and units depend on thresholdType.",
68 dtype=float, optional=
False, default=5.0, min=0.0,
70 includeThresholdMultiplier = pexConfig.RangeField(
71 doc=
"Include threshold relative to thresholdValue",
72 dtype=float, default=1.0, min=0.0,
74 thresholdType = pexConfig.ChoiceField(
75 doc=
"specifies the desired flavor of Threshold",
76 dtype=str, optional=
False, default=
"stdev",
78 "variance":
"threshold applied to image variance",
79 "stdev":
"threshold applied to image std deviation",
80 "value":
"threshold applied to image value",
81 "pixel_stdev":
"threshold applied to per-pixel std deviation",
84 thresholdPolarity = pexConfig.ChoiceField(
85 doc=
"specifies whether to detect positive, or negative sources, or both",
86 dtype=str, optional=
False, default=
"positive",
88 "positive":
"detect only positive sources",
89 "negative":
"detect only negative sources",
90 "both":
"detect both positive and negative sources",
93 adjustBackground = pexConfig.Field(
95 doc=
"Fiddle factor to add to the background; debugging only",
98 reEstimateBackground = pexConfig.Field(
100 doc=
"Estimate the background again after final source detection?",
101 default=
True, optional=
False,
103 background = pexConfig.ConfigurableField(
104 doc=
"Background re-estimation; ignored if reEstimateBackground false",
105 target=SubtractBackgroundTask,
107 tempLocalBackground = pexConfig.ConfigurableField(
108 doc=(
"A local (small-scale), temporary background estimation step run between "
109 "detecting above-threshold regions and detecting the peaks within "
110 "them; used to avoid detecting spuerious peaks in the wings."),
111 target=SubtractBackgroundTask,
113 doTempLocalBackground = pexConfig.Field(
115 doc=
"Enable temporary local background subtraction? (see tempLocalBackground)",
118 tempWideBackground = pexConfig.ConfigurableField(
119 doc=(
"A wide (large-scale) background estimation and removal before footprint and peak detection. "
120 "It is added back into the image after detection. The purpose is to suppress very large "
121 "footprints (e.g., from large artifacts) that the deblender may choke on."),
122 target=SubtractBackgroundTask,
124 doTempWideBackground = pexConfig.Field(
126 doc=
"Do temporary wide (large-scale) background subtraction before footprint detection?",
129 nPeaksMaxSimple = pexConfig.Field(
131 doc=(
"The maximum number of peaks in a Footprint before trying to "
132 "replace its peaks using the temporary local background"),
135 nSigmaForKernel = pexConfig.Field(
137 doc=(
"Multiple of PSF RMS size to use for convolution kernel bounding box size; "
138 "note that this is not a half-size. The size will be rounded up to the nearest odd integer"),
141 statsMask = pexConfig.ListField(
143 doc=
"Mask planes to ignore when calculating statistics of image (for thresholdType=stdev)",
144 default=[
'BAD',
'SAT',
'EDGE',
'NO_DATA'],
157 for maskPlane
in (
"DETECTED",
"DETECTED_NEGATIVE"):
163 """Detect peaks and footprints of sources in an image.
165 This task convolves the image with a Gaussian approximation to the PSF,
166 matched to the sigma of the input exposure, because this
is separable
and
167 fast. The PSF would have to be very non-Gaussian
or non-circular
for this
168 approximation to have a significant impact on the signal-to-noise of the
176 Keyword arguments passed to `lsst.pipe.base.Task.__init__`
178 If schema
is not None and configured
for 'both' detections,
179 a
'flags.negative' field will be added to label detections made
with a
184 This task can add fields to the schema, so any code calling this task must
185 ensure that these columns are indeed present
in the input match list.
187 ConfigClass = SourceDetectionConfig
188 _DefaultName = "sourceDetection"
191 pipeBase.Task.__init__(self, **kwds)
192 if schema
is not None and self.config.thresholdPolarity ==
"both":
194 "flags_negative", type=
"Flag",
195 doc=
"set if source was detected as significantly negative"
198 if self.config.thresholdPolarity ==
"both":
199 self.log.warning(
"Detection polarity set to 'both', but no flag will be "
200 "set to distinguish between positive and negative detections")
202 if self.config.reEstimateBackground:
203 self.makeSubtask(
"background")
204 if self.config.doTempLocalBackground:
205 self.makeSubtask(
"tempLocalBackground")
206 if self.config.doTempWideBackground:
207 self.makeSubtask(
"tempWideBackground")
210 def run(self, table, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None):
211 r"""Detect sources and return catalog(s) of detections.
216 Table object that will be used to create the SourceCatalog.
218 Exposure to process; DETECTED mask plane will be set in-place.
220 If
True, smooth the image before detection using a Gaussian of width
221 ``sigma``,
or the measured PSF width. Set to
False when running on
222 e.g. a pre-convolved image,
or a mask plane.
224 Sigma of PSF (pixels); used
for smoothing
and to grow detections;
225 if None then measure the sigma of the PSF of the exposure
227 Clear DETECTED{,_NEGATIVE} planes before running detection.
229 Exposure identifier; unused by this implementation, but used
for
230 RNG seed by subclasses.
234 result : `lsst.pipe.base.Struct`
235 The `~lsst.pipe.base.Struct` contains:
238 Detected sources on the exposure.
241 Positive polarity footprints.
244 Negative polarity footprints.
247 Number of footprints
in positive
or 0
if detection polarity was
250 Number of footprints
in negative
or 0
if detection polarity was
253 Re-estimated background. `
None`
if
254 ``reEstimateBackground==
False``.
255 (`lsst.afw.math.BackgroundList`)
257 Multiplication factor applied to the configured detection
263 Raised
if flags.negative
is needed, but isn
't in table's schema.
264 lsst.pipe.base.TaskError
265 Raised
if sigma=
None, doSmooth=
True and the exposure has no PSF.
269 If you want to avoid dealing
with Sources
and Tables, you can use
274 raise ValueError(
"Table has incorrect Schema")
275 results = self.
detectFootprints(exposure=exposure, doSmooth=doSmooth, sigma=sigma,
276 clearMask=clearMask, expId=expId)
277 sources = afwTable.SourceCatalog(table)
278 sources.reserve(results.numPos + results.numNeg)
280 results.negative.makeSources(sources)
282 for record
in sources:
285 results.positive.makeSources(sources)
286 results.sources = sources
289 def display(self, exposure, results, convolvedImage=None):
290 """Display detections if so configured
292 Displays the ``exposure`` in frame 0, overlays the detection peaks.
294 Requires that ``lsstDebug`` has been set up correctly, so that
295 ``
lsstDebug.Info(
"lsst.meas.algorithms.detection")`` evaluates `
True`.
297 If the ``convolvedImage``
is non-`
None`
and
299 ``convolvedImage`` will be displayed
in frame 1.
304 Exposure to display, on which will be plotted the detections.
305 results : `lsst.pipe.base.Struct`
306 Results of the
'detectFootprints' method, containing positive
and
307 negative footprints (which contain the peak positions that we will
308 plot). This
is a `Struct`
with ``positive``
and ``negative``
311 Convolved image used
for thresholding.
324 afwDisplay.setDefaultMaskTransparency(75)
326 disp0 = afwDisplay.Display(frame=0)
327 disp0.mtv(exposure, title=
"detection")
329 def plotPeaks(fps, ctype):
332 with disp0.Buffering():
333 for fp
in fps.getFootprints():
334 for pp
in fp.getPeaks():
335 disp0.dot(
"+", pp.getFx(), pp.getFy(), ctype=ctype)
336 plotPeaks(results.positive,
"yellow")
337 plotPeaks(results.negative,
"red")
339 if convolvedImage
and display > 1:
340 disp1 = afwDisplay.Display(frame=1)
341 disp1.mtv(convolvedImage, title=
"PSF smoothed")
343 disp2 = afwDisplay.Display(frame=2)
344 disp2.mtv(afwImage.ImageF(np.sqrt(exposure.variance.array)), title=
"stddev")
347 """Apply a temporary local background subtraction
349 This temporary local background serves to suppress noise fluctuations
350 in the wings of bright objects.
352 Peaks
in the footprints will be updated.
357 Exposure
for which to fit local background.
359 Convolved image on which detection will be performed
360 (typically smaller than ``exposure`` because the
361 half-kernel has been removed around the edges).
362 results : `lsst.pipe.base.Struct`
363 Results of the
'detectFootprints' method, containing positive
and
364 negative footprints (which contain the peak positions that we will
365 plot). This
is a `Struct`
with ``positive``
and ``negative``
371 bg = self.tempLocalBackground.fitBackground(exposure.getMaskedImage())
372 bgImage = bg.getImageF(self.tempLocalBackground.config.algorithm,
373 self.tempLocalBackground.config.undersampleStyle)
374 middle -= bgImage.Factory(bgImage, middle.getBBox())
375 if self.config.thresholdPolarity !=
"negative":
376 results.positiveThreshold = self.
makeThreshold(middle,
"positive")
377 self.
updatePeaks(results.positive, middle, results.positiveThreshold)
378 if self.config.thresholdPolarity !=
"positive":
379 results.negativeThreshold = self.
makeThreshold(middle,
"negative")
380 self.
updatePeaks(results.negative, middle, results.negativeThreshold)
383 """Clear the DETECTED and DETECTED_NEGATIVE mask planes.
385 Removes any previous detection mask in preparation
for a new
393 mask &= ~(mask.getPlaneBitMask("DETECTED") | mask.getPlaneBitMask(
"DETECTED_NEGATIVE"))
396 """Calculate the size of the smoothing kernel.
398 Uses the ``nSigmaForKernel`` configuration parameter. Note
399 that that is the full width of the kernel bounding box
400 (so a value of 7 means 3.5 sigma on either side of center).
401 The value will be rounded up to the nearest odd integer.
406 Gaussian sigma of smoothing kernel.
411 Size of the smoothing kernel.
413 return (int(sigma * self.config.nSigmaForKernel + 0.5)//2)*2 + 1
416 """Create a single Gaussian PSF for an exposure.
419 with that, otherwise use the sigma
from the psf of the ``exposure`` to
425 Exposure
from which to retrieve the PSF.
426 sigma : `float`, optional
427 Gaussian sigma to use
if provided.
432 PSF to use
for detection.
437 Raised
if ``sigma``
is not provided
and ``exposure`` does
not
438 contain a ``Psf`` object.
441 psf = exposure.getPsf()
443 raise RuntimeError(
"Unable to determine PSF to use for detection: no sigma provided")
444 sigma = psf.computeShape(psf.getAveragePosition()).getDeterminantRadius()
446 psf = afwDet.GaussianPsf(size, size, sigma)
450 """Convolve the image with the PSF.
452 We convolve the image with a Gaussian approximation to the PSF,
453 because this
is separable
and therefore fast. It
's technically a
454 correlation rather than a convolution, but since we use a symmetric
455 Gaussian there's no difference.
457 The convolution can be disabled with ``doSmooth=
False``. If we do
458 convolve, we mask the edges
as ``EDGE``
and return the convolved image
459 with the edges removed. This
is because we can
't convolve the edges
460 because the kernel would extend off the image.
467 PSF to convolve with (actually
with a Gaussian approximation
470 Actually do the convolution? Set to
False when running on
471 e.g. a pre-convolved image,
or a mask plane.
475 results : `lsst.pipe.base.Struct`
476 The `~lsst.pipe.base.Struct` contains:
481 Gaussian sigma used
for the convolution. (`float`)
483 self.metadata["doSmooth"] = doSmooth
484 sigma = psf.computeShape(psf.getAveragePosition()).getDeterminantRadius()
485 self.metadata[
"sigma"] = sigma
488 middle = maskedImage.Factory(maskedImage, deep=
True)
489 return pipeBase.Struct(middle=middle, sigma=sigma)
494 self.metadata[
"smoothingKernelWidth"] = kWidth
495 gaussFunc = afwMath.GaussianFunction1D(sigma)
496 gaussKernel = afwMath.SeparableKernel(kWidth, kWidth, gaussFunc, gaussFunc)
498 convolvedImage = maskedImage.Factory(maskedImage.getBBox())
500 afwMath.convolve(convolvedImage, maskedImage, gaussKernel, afwMath.ConvolutionControl())
503 goodBBox = gaussKernel.shrinkBBox(convolvedImage.getBBox())
504 middle = convolvedImage.Factory(convolvedImage, goodBBox, afwImage.PARENT,
False)
507 self.
setEdgeBits(maskedImage, goodBBox, maskedImage.getMask().getPlaneBitMask(
"EDGE"))
509 return pipeBase.Struct(middle=middle, sigma=sigma)
512 r"""Apply thresholds to the convolved image
515 The threshold can be modified by the provided multiplication
521 Convolved image to threshold.
523 Bounding box of unconvolved image.
525 Multiplier
for the configured threshold.
526 factorNeg : `float`
or `
None`
527 Multiplier
for the configured threshold
for negative detection polarity.
528 If `
None`, will be set equal to ``factor`` (i.e. equal to the factor used
529 for positive detection polarity).
533 results : `lsst.pipe.base.Struct`
534 The `~lsst.pipe.base.Struct` contains:
537 Positive detection footprints,
if configured.
540 Negative detection footprints,
if configured.
543 Multiplier
for the configured threshold.
546 Multiplier
for the configured threshold
for negative detection polarity.
549 if factorNeg
is None:
551 self.log.info(
"Setting factor for negative detections equal to that for positive "
552 "detections: %f", factor)
553 results = pipeBase.Struct(positive=
None, negative=
None, factor=factor, factorNeg=factorNeg,
554 positiveThreshold=
None, negativeThreshold=
None)
556 if self.config.reEstimateBackground
or self.config.thresholdPolarity !=
"negative":
557 results.positiveThreshold = self.
makeThreshold(middle,
"positive", factor=factor)
558 results.positive = afwDet.FootprintSet(
560 results.positiveThreshold,
562 self.config.minPixels
564 results.positive.setRegion(bbox)
565 if self.config.reEstimateBackground
or self.config.thresholdPolarity !=
"positive":
566 results.negativeThreshold = self.
makeThreshold(middle,
"negative", factor=factorNeg)
567 results.negative = afwDet.FootprintSet(
569 results.negativeThreshold,
571 self.config.minPixels
573 results.negative.setRegion(bbox)
578 """Finalize the detected footprints.
580 Grow the footprints, set the ``DETECTED`` and ``DETECTED_NEGATIVE``
581 mask planes,
and log the results.
583 ``numPos`` (number of positive footprints), ``numPosPeaks`` (number
584 of positive peaks), ``numNeg`` (number of negative footprints),
585 ``numNegPeaks`` (number of negative peaks) entries are added to the
591 Mask image on which to flag detected pixels.
592 results : `lsst.pipe.base.Struct`
593 Struct of detection results, including ``positive``
and
594 ``negative`` entries; modified.
596 Gaussian sigma of PSF.
598 Multiplier
for the configured threshold. Note that this
is only
599 used here
for logging purposes.
600 factorNeg : `float`
or `
None`
601 Multiplier used
for the negative detection polarity threshold.
602 If `
None`, a factor equal to ``factor`` (i.e. equal to the one used
603 for positive detection polarity)
is assumed. Note that this
is only
604 used here
for logging purposes.
606 factorNeg = factor if factorNeg
is None else factorNeg
607 for polarity, maskName
in ((
"positive",
"DETECTED"), (
"negative",
"DETECTED_NEGATIVE")):
608 fpSet = getattr(results, polarity)
611 if self.config.nSigmaToGrow > 0:
612 nGrow = int((self.config.nSigmaToGrow * sigma) + 0.5)
613 self.metadata[
"nGrow"] = nGrow
614 if self.config.combinedGrow:
615 fpSet = afwDet.FootprintSet(fpSet, nGrow, self.config.isotropicGrow)
617 stencil = (afwGeom.Stencil.CIRCLE
if self.config.isotropicGrow
else
618 afwGeom.Stencil.MANHATTAN)
620 fp.dilate(nGrow, stencil)
621 fpSet.setMask(mask, maskName)
622 if not self.config.returnOriginalFootprints:
623 setattr(results, polarity, fpSet)
626 results.numPosPeaks = 0
628 results.numNegPeaks = 0
632 if results.positive
is not None:
633 results.numPos = len(results.positive.getFootprints())
634 results.numPosPeaks = sum(len(fp.getPeaks())
for fp
in results.positive.getFootprints())
635 positive =
" %d positive peaks in %d footprints" % (results.numPosPeaks, results.numPos)
636 if results.negative
is not None:
637 results.numNeg = len(results.negative.getFootprints())
638 results.numNegPeaks = sum(len(fp.getPeaks())
for fp
in results.negative.getFootprints())
639 negative =
" %d negative peaks in %d footprints" % (results.numNegPeaks, results.numNeg)
641 self.log.info(
"Detected%s%s%s to %g +ve and %g -ve %s",
642 positive,
" and" if positive
and negative
else "", negative,
643 self.config.thresholdValue*self.config.includeThresholdMultiplier*factor,
644 self.config.thresholdValue*self.config.includeThresholdMultiplier*factorNeg,
645 "DN" if self.config.thresholdType ==
"value" else "sigma")
648 """Estimate the background after detection
653 Image on which to estimate the background.
654 backgrounds : `lsst.afw.math.BackgroundList`
655 List of backgrounds; modified.
659 bg : `lsst.afw.math.backgroundMI`
660 Empirical background model.
662 bg = self.background.fitBackground(maskedImage)
663 if self.config.adjustBackground:
664 self.log.warning(
"Fiddling the background by %g", self.config.adjustBackground)
665 bg += self.config.adjustBackground
666 self.log.info(
"Resubtracting the background after object detection")
667 maskedImage -= bg.getImageF(self.background.config.algorithm,
668 self.background.config.undersampleStyle)
670 actrl = bg.getBackgroundControl().getApproximateControl()
671 backgrounds.append((bg, getattr(afwMath.Interpolate, self.background.config.algorithm),
672 bg.getAsUsedUndersampleStyle(), actrl.getStyle(), actrl.getOrderX(),
673 actrl.getOrderY(), actrl.getWeighting()))
677 """Clear unwanted results from the Struct of results
679 If we specifically want only positive or only negative detections,
680 drop the ones we don
't want, and its associated mask plane.
686 results : `lsst.pipe.base.Struct`
687 Detection results, with ``positive``
and ``negative`` elements;
690 if self.config.thresholdPolarity ==
"positive":
691 if self.config.reEstimateBackground:
692 mask &= ~mask.getPlaneBitMask(
"DETECTED_NEGATIVE")
693 results.negative =
None
694 elif self.config.thresholdPolarity ==
"negative":
695 if self.config.reEstimateBackground:
696 mask &= ~mask.getPlaneBitMask(
"DETECTED")
697 results.positive =
None
700 def detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None):
701 """Detect footprints on an exposure.
706 Exposure to process; DETECTED{,_NEGATIVE} mask plane will be
708 doSmooth : `bool`, optional
709 If
True, smooth the image before detection using a Gaussian
710 of width ``sigma``,
or the measured PSF width of ``exposure``.
711 Set to
False when running on e.g. a pre-convolved image,
or a mask
713 sigma : `float`, optional
714 Gaussian Sigma of PSF (pixels); used
for smoothing
and to grow
715 detections;
if `
None` then measure the sigma of the PSF of the
717 clearMask : `bool`, optional
718 Clear both DETECTED
and DETECTED_NEGATIVE planes before running
720 expId : `dict`, optional
721 Exposure identifier; unused by this implementation, but used
for
722 RNG seed by subclasses.
726 results : `lsst.pipe.base.Struct`
727 A `~lsst.pipe.base.Struct` containing:
730 Positive polarity footprints.
733 Negative polarity footprints.
736 Number of footprints
in positive
or 0
if detection polarity was
739 Number of footprints
in negative
or 0
if detection polarity was
742 Re-estimated background. `
None`
if
743 ``reEstimateBackground==
False``.
744 (`lsst.afw.math.BackgroundList`)
746 Multiplication factor applied to the configured detection
749 maskedImage = exposure.maskedImage
754 psf = self.
getPsf(exposure, sigma=sigma)
756 convolveResults = self.
convolveImage(maskedImage, psf, doSmooth=doSmooth)
757 middle = convolveResults.middle
758 sigma = convolveResults.sigma
761 results.background = afwMath.BackgroundList()
762 if self.config.doTempLocalBackground:
769 results.positive = self.
setPeakSignificance(middle, results.positive, results.positiveThreshold)
770 results.negative = self.
setPeakSignificance(middle, results.negative, results.negativeThreshold,
773 if self.config.reEstimateBackground:
778 self.
display(exposure, results, middle)
783 """Set the significance of each detected peak to the pixel value divided
784 by the appropriate standard-deviation for ``config.thresholdType``.
786 Only sets significance
for "stdev" and "pixel_stdev" thresholdTypes;
787 we leave it undefined
for "value" and "variance" as it does
not have a
788 well-defined meaning
in those cases.
793 Exposure that footprints were detected on, likely the convolved,
794 local background-subtracted image.
796 Footprints detected on the image.
798 Threshold used to find footprints.
799 negative : `bool`, optional
800 Are we calculating
for negative sources?
802 if footprints
is None or footprints.getFootprints() == []:
804 polarity = -1
if negative
else 1
807 mapper = afwTable.SchemaMapper(footprints.getFootprints()[0].peaks.schema)
808 mapper.addMinimalSchema(footprints.getFootprints()[0].peaks.schema)
809 mapper.addOutputField(
"significance", type=float,
810 doc=
"Ratio of peak value to configured standard deviation.")
815 newFootprints = afwDet.FootprintSet(footprints)
816 for old, new
in zip(footprints.getFootprints(), newFootprints.getFootprints()):
817 newPeaks = afwDet.PeakCatalog(mapper.getOutputSchema())
818 newPeaks.extend(old.peaks, mapper=mapper)
819 new.getPeaks().clear()
820 new.setPeakCatalog(newPeaks)
823 if self.config.thresholdType ==
"pixel_stdev":
824 for footprint
in newFootprints.getFootprints():
825 footprint.updatePeakSignificance(exposure.variance, polarity)
826 elif self.config.thresholdType ==
"stdev":
827 sigma = threshold.getValue() / self.config.thresholdValue
828 for footprint
in newFootprints.getFootprints():
829 footprint.updatePeakSignificance(polarity*sigma)
831 for footprint
in newFootprints.getFootprints():
832 for peak
in footprint.peaks:
833 peak[
"significance"] = 0
838 """Make an afw.detection.Threshold object corresponding to the task's
839 configuration and the statistics of the given image.
844 Image to measure noise statistics
from if needed.
845 thresholdParity: `str`
846 One of
"positive" or "negative", to set the kind of fluctuations
847 the Threshold will detect.
849 Factor by which to multiply the configured detection threshold.
850 This
is useful
for tweaking the detection threshold slightly.
857 parity = False if thresholdParity ==
"negative" else True
858 thresholdValue = self.config.thresholdValue
859 thresholdType = self.config.thresholdType
860 if self.config.thresholdType ==
'stdev':
861 bad = image.getMask().getPlaneBitMask(self.config.statsMask)
862 sctrl = afwMath.StatisticsControl()
863 sctrl.setAndMask(bad)
864 stats = afwMath.makeStatistics(image, afwMath.STDEVCLIP, sctrl)
865 thresholdValue *= stats.getValue(afwMath.STDEVCLIP)
866 thresholdType =
'value'
868 threshold = afwDet.createThreshold(thresholdValue*factor, thresholdType, parity)
869 threshold.setIncludeMultiplier(self.config.includeThresholdMultiplier)
870 self.log.debug(
"Detection threshold: %s", threshold)
874 """Update the Peaks in a FootprintSet by detecting new Footprints and
875 Peaks in an image
and using the new Peaks instead of the old ones.
880 Set of Footprints whose Peaks should be updated.
882 Image to detect new Footprints
and Peak
in.
884 Threshold object
for detection.
886 Input Footprints
with fewer Peaks than self.config.nPeaksMaxSimple
887 are
not modified,
and if no new Peaks are detected
in an input
888 Footprint, the brightest original Peak
in that Footprint
is kept.
890 for footprint
in fpSet.getFootprints():
891 oldPeaks = footprint.getPeaks()
892 if len(oldPeaks) <= self.config.nPeaksMaxSimple:
897 sub = image.Factory(image, footprint.getBBox())
898 fpSetForPeaks = afwDet.FootprintSet(
902 self.config.minPixels
904 newPeaks = afwDet.PeakCatalog(oldPeaks.getTable())
905 for fpForPeaks
in fpSetForPeaks.getFootprints():
906 for peak
in fpForPeaks.getPeaks():
907 if footprint.contains(peak.getI()):
908 newPeaks.append(peak)
909 if len(newPeaks) > 0:
911 oldPeaks.extend(newPeaks)
917 """Set the edgeBitmask bits for all of maskedImage outside goodBBox
922 Image on which to set edge bits in the mask.
924 Bounding box of good pixels,
in ``LOCAL`` coordinates.
926 Bit mask to OR
with the existing mask bits
in the region
927 outside ``goodBBox``.
929 msk = maskedImage.getMask()
931 mx0, my0 = maskedImage.getXY0()
932 for x0, y0, w, h
in ([0, 0,
933 msk.getWidth(), goodBBox.getBeginY() - my0],
934 [0, goodBBox.getEndY() - my0, msk.getWidth(),
935 maskedImage.getHeight() - (goodBBox.getEndY() - my0)],
937 goodBBox.getBeginX() - mx0, msk.getHeight()],
938 [goodBBox.getEndX() - mx0, 0,
939 maskedImage.getWidth() - (goodBBox.getEndX() - mx0), msk.getHeight()],
943 edgeMask |= edgeBitmask
947 """Context manager for removing wide (large-scale) background
949 Removing a wide (large-scale) background helps to suppress the
950 detection of large footprints that may overwhelm the deblender.
951 It does, however, set a limit on the maximum scale of objects.
953 The background that we remove will be restored upon exit from
959 Exposure on which to remove large-scale background.
963 context : context manager
964 Context manager that will ensure the temporary wide background
967 doTempWideBackground = self.config.doTempWideBackground
968 if doTempWideBackground:
969 self.log.info(
"Applying temporary wide background subtraction")
970 original = exposure.maskedImage.image.array[:].copy()
971 self.tempWideBackground.
run(exposure).background
974 image = exposure.maskedImage.image
975 mask = exposure.maskedImage.mask
976 noData = mask.array & mask.getPlaneBitMask(
"NO_DATA") > 0
977 isGood = mask.array & mask.getPlaneBitMask(self.config.statsMask) == 0
978 image.array[noData] = np.median(image.array[~noData & isGood])
982 if doTempWideBackground:
983 exposure.maskedImage.image.array[:] = original
987 """Add a set of exposures together.
992 Sequence of exposures to add.
997 An exposure of the same size as each exposure
in ``exposureList``,
998 with the metadata
from ``exposureList[0]``
and a masked image equal
999 to the sum of all the exposure
's masked images.
1001 exposure0 = exposureList[0]
1002 image0 = exposure0.getMaskedImage()
1004 addedImage = image0.Factory(image0, True)
1005 addedImage.setXY0(image0.getXY0())
1007 for exposure
in exposureList[1:]:
1008 image = exposure.getMaskedImage()
1011 addedExposure = exposure0.Factory(addedImage, exposure0.getWcs())
1012 return addedExposure
def tempWideBackgroundContext(self, exposure)
def getPsf(self, exposure, sigma=None)
def __init__(self, schema=None, **kwds)
def makeThreshold(self, image, thresholdParity, factor=1.0)
def setPeakSignificance(self, exposure, footprints, threshold, negative=False)
def run(self, table, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None)
def convolveImage(self, maskedImage, psf, doSmooth=True)
def applyTempLocalBackground(self, exposure, middle, results)
def detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None)
def reEstimateBackground(self, maskedImage, backgrounds)
def updatePeaks(self, fpSet, image, threshold)
def applyThreshold(self, middle, bbox, factor=1.0, factorNeg=None)
def setEdgeBits(maskedImage, goodBBox, edgeBitmask)
def clearUnwantedResults(self, mask, results)
def clearMask(self, mask)
def display(self, exposure, results, convolvedImage=None)
def calculateKernelSize(self, sigma)
def finalizeFootprints(self, mask, results, sigma, factor=1.0, factorNeg=None)
def addExposures(exposureList)