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'],
146 excludeMaskPlanes = lsst.pex.config.ListField(
149 doc=
"Mask planes to exclude when detecting sources."
162 for maskPlane
in (
"DETECTED",
"DETECTED_NEGATIVE"):
168 """Detect peaks and footprints of sources in an image.
170 This task convolves the image with a Gaussian approximation to the PSF,
171 matched to the sigma of the input exposure, because this
is separable
and
172 fast. The PSF would have to be very non-Gaussian
or non-circular
for this
173 approximation to have a significant impact on the signal-to-noise of the
181 Keyword arguments passed to `lsst.pipe.base.Task.__init__`
183 If schema
is not None and configured
for 'both' detections,
184 a
'flags.negative' field will be added to label detections made
with a
189 This task can add fields to the schema, so any code calling this task must
190 ensure that these columns are indeed present
in the input match list.
192 ConfigClass = SourceDetectionConfig
193 _DefaultName = "sourceDetection"
196 pipeBase.Task.__init__(self, **kwds)
197 if schema
is not None and self.config.thresholdPolarity ==
"both":
199 "flags_negative", type=
"Flag",
200 doc=
"set if source was detected as significantly negative"
203 if self.config.thresholdPolarity ==
"both":
204 self.log.warning(
"Detection polarity set to 'both', but no flag will be "
205 "set to distinguish between positive and negative detections")
207 if self.config.reEstimateBackground:
208 self.makeSubtask(
"background")
209 if self.config.doTempLocalBackground:
210 self.makeSubtask(
"tempLocalBackground")
211 if self.config.doTempWideBackground:
212 self.makeSubtask(
"tempWideBackground")
215 def run(self, table, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None):
216 r"""Detect sources and return catalog(s) of detections.
221 Table object that will be used to create the SourceCatalog.
223 Exposure to process; DETECTED mask plane will be set in-place.
225 If
True, smooth the image before detection using a Gaussian of width
226 ``sigma``,
or the measured PSF width. Set to
False when running on
227 e.g. a pre-convolved image,
or a mask plane.
229 Sigma of PSF (pixels); used
for smoothing
and to grow detections;
230 if None then measure the sigma of the PSF of the exposure
232 Clear DETECTED{,_NEGATIVE} planes before running detection.
234 Exposure identifier; unused by this implementation, but used
for
235 RNG seed by subclasses.
239 result : `lsst.pipe.base.Struct`
240 The `~lsst.pipe.base.Struct` contains:
243 Detected sources on the exposure.
246 Positive polarity footprints.
249 Negative polarity footprints.
252 Number of footprints
in positive
or 0
if detection polarity was
255 Number of footprints
in negative
or 0
if detection polarity was
258 Re-estimated background. `
None`
if
259 ``reEstimateBackground==
False``.
260 (`lsst.afw.math.BackgroundList`)
262 Multiplication factor applied to the configured detection
268 Raised
if flags.negative
is needed, but isn
't in table's schema.
269 lsst.pipe.base.TaskError
270 Raised
if sigma=
None, doSmooth=
True and the exposure has no PSF.
274 If you want to avoid dealing
with Sources
and Tables, you can use
279 raise ValueError(
"Table has incorrect Schema")
280 results = self.
detectFootprints(exposure=exposure, doSmooth=doSmooth, sigma=sigma,
281 clearMask=clearMask, expId=expId)
282 sources = afwTable.SourceCatalog(table)
283 sources.reserve(results.numPos + results.numNeg)
285 results.negative.makeSources(sources)
287 for record
in sources:
290 results.positive.makeSources(sources)
291 results.sources = sources
294 def display(self, exposure, results, convolvedImage=None):
295 """Display detections if so configured
297 Displays the ``exposure`` in frame 0, overlays the detection peaks.
299 Requires that ``lsstDebug`` has been set up correctly, so that
300 ``
lsstDebug.Info(
"lsst.meas.algorithms.detection")`` evaluates `
True`.
302 If the ``convolvedImage``
is non-`
None`
and
304 ``convolvedImage`` will be displayed
in frame 1.
309 Exposure to display, on which will be plotted the detections.
310 results : `lsst.pipe.base.Struct`
311 Results of the
'detectFootprints' method, containing positive
and
312 negative footprints (which contain the peak positions that we will
313 plot). This
is a `Struct`
with ``positive``
and ``negative``
316 Convolved image used
for thresholding.
329 afwDisplay.setDefaultMaskTransparency(75)
331 disp0 = afwDisplay.Display(frame=0)
332 disp0.mtv(exposure, title=
"detection")
334 def plotPeaks(fps, ctype):
337 with disp0.Buffering():
338 for fp
in fps.getFootprints():
339 for pp
in fp.getPeaks():
340 disp0.dot(
"+", pp.getFx(), pp.getFy(), ctype=ctype)
341 plotPeaks(results.positive,
"yellow")
342 plotPeaks(results.negative,
"red")
344 if convolvedImage
and display > 1:
345 disp1 = afwDisplay.Display(frame=1)
346 disp1.mtv(convolvedImage, title=
"PSF smoothed")
348 disp2 = afwDisplay.Display(frame=2)
349 disp2.mtv(afwImage.ImageF(np.sqrt(exposure.variance.array)), title=
"stddev")
352 """Apply a temporary local background subtraction
354 This temporary local background serves to suppress noise fluctuations
355 in the wings of bright objects.
357 Peaks
in the footprints will be updated.
362 Exposure
for which to fit local background.
364 Convolved image on which detection will be performed
365 (typically smaller than ``exposure`` because the
366 half-kernel has been removed around the edges).
367 results : `lsst.pipe.base.Struct`
368 Results of the
'detectFootprints' method, containing positive
and
369 negative footprints (which contain the peak positions that we will
370 plot). This
is a `Struct`
with ``positive``
and ``negative``
376 bg = self.tempLocalBackground.fitBackground(exposure.getMaskedImage())
377 bgImage = bg.getImageF(self.tempLocalBackground.config.algorithm,
378 self.tempLocalBackground.config.undersampleStyle)
379 middle -= bgImage.Factory(bgImage, middle.getBBox())
380 if self.config.thresholdPolarity !=
"negative":
381 results.positiveThreshold = self.
makeThreshold(middle,
"positive")
382 self.
updatePeaks(results.positive, middle, results.positiveThreshold)
383 if self.config.thresholdPolarity !=
"positive":
384 results.negativeThreshold = self.
makeThreshold(middle,
"negative")
385 self.
updatePeaks(results.negative, middle, results.negativeThreshold)
388 """Clear the DETECTED and DETECTED_NEGATIVE mask planes.
390 Removes any previous detection mask in preparation
for a new
398 mask &= ~(mask.getPlaneBitMask("DETECTED") | mask.getPlaneBitMask(
"DETECTED_NEGATIVE"))
401 """Calculate the size of the smoothing kernel.
403 Uses the ``nSigmaForKernel`` configuration parameter. Note
404 that that is the full width of the kernel bounding box
405 (so a value of 7 means 3.5 sigma on either side of center).
406 The value will be rounded up to the nearest odd integer.
411 Gaussian sigma of smoothing kernel.
416 Size of the smoothing kernel.
418 return (int(sigma * self.config.nSigmaForKernel + 0.5)//2)*2 + 1
421 """Create a single Gaussian PSF for an exposure.
424 with that, otherwise use the sigma
from the psf of the ``exposure`` to
430 Exposure
from which to retrieve the PSF.
431 sigma : `float`, optional
432 Gaussian sigma to use
if provided.
437 PSF to use
for detection.
442 Raised
if ``sigma``
is not provided
and ``exposure`` does
not
443 contain a ``Psf`` object.
446 psf = exposure.getPsf()
448 raise RuntimeError(
"Unable to determine PSF to use for detection: no sigma provided")
449 sigma = psf.computeShape(psf.getAveragePosition()).getDeterminantRadius()
451 psf = afwDet.GaussianPsf(size, size, sigma)
455 """Convolve the image with the PSF.
457 We convolve the image with a Gaussian approximation to the PSF,
458 because this
is separable
and therefore fast. It
's technically a
459 correlation rather than a convolution, but since we use a symmetric
460 Gaussian there's no difference.
462 The convolution can be disabled with ``doSmooth=
False``. If we do
463 convolve, we mask the edges
as ``EDGE``
and return the convolved image
464 with the edges removed. This
is because we can
't convolve the edges
465 because the kernel would extend off the image.
472 PSF to convolve with (actually
with a Gaussian approximation
475 Actually do the convolution? Set to
False when running on
476 e.g. a pre-convolved image,
or a mask plane.
480 results : `lsst.pipe.base.Struct`
481 The `~lsst.pipe.base.Struct` contains:
486 Gaussian sigma used
for the convolution. (`float`)
488 self.metadata["doSmooth"] = doSmooth
489 sigma = psf.computeShape(psf.getAveragePosition()).getDeterminantRadius()
490 self.metadata[
"sigma"] = sigma
493 middle = maskedImage.Factory(maskedImage, deep=
True)
494 return pipeBase.Struct(middle=middle, sigma=sigma)
499 self.metadata[
"smoothingKernelWidth"] = kWidth
500 gaussFunc = afwMath.GaussianFunction1D(sigma)
501 gaussKernel = afwMath.SeparableKernel(kWidth, kWidth, gaussFunc, gaussFunc)
503 convolvedImage = maskedImage.Factory(maskedImage.getBBox())
505 afwMath.convolve(convolvedImage, maskedImage, gaussKernel, afwMath.ConvolutionControl())
508 goodBBox = gaussKernel.shrinkBBox(convolvedImage.getBBox())
509 middle = convolvedImage.Factory(convolvedImage, goodBBox, afwImage.PARENT,
False)
512 self.
setEdgeBits(maskedImage, goodBBox, maskedImage.getMask().getPlaneBitMask(
"EDGE"))
514 return pipeBase.Struct(middle=middle, sigma=sigma)
517 r"""Apply thresholds to the convolved image
520 The threshold can be modified by the provided multiplication
526 Convolved image to threshold.
528 Bounding box of unconvolved image.
530 Multiplier
for the configured threshold.
531 factorNeg : `float`
or `
None`
532 Multiplier
for the configured threshold
for negative detection polarity.
533 If `
None`, will be set equal to ``factor`` (i.e. equal to the factor used
534 for positive detection polarity).
538 results : `lsst.pipe.base.Struct`
539 The `~lsst.pipe.base.Struct` contains:
542 Positive detection footprints,
if configured.
545 Negative detection footprints,
if configured.
548 Multiplier
for the configured threshold.
551 Multiplier
for the configured threshold
for negative detection polarity.
554 if factorNeg
is None:
556 self.log.info(
"Setting factor for negative detections equal to that for positive "
557 "detections: %f", factor)
558 results = pipeBase.Struct(positive=
None, negative=
None, factor=factor, factorNeg=factorNeg,
559 positiveThreshold=
None, negativeThreshold=
None)
561 if self.config.reEstimateBackground
or self.config.thresholdPolarity !=
"negative":
562 results.positiveThreshold = self.
makeThreshold(middle,
"positive", factor=factor)
563 results.positive = afwDet.FootprintSet(
565 results.positiveThreshold,
567 self.config.minPixels
569 results.positive.setRegion(bbox)
570 if self.config.reEstimateBackground
or self.config.thresholdPolarity !=
"positive":
571 results.negativeThreshold = self.
makeThreshold(middle,
"negative", factor=factorNeg)
572 results.negative = afwDet.FootprintSet(
574 results.negativeThreshold,
576 self.config.minPixels
578 results.negative.setRegion(bbox)
583 """Finalize the detected footprints.
585 Grow the footprints, set the ``DETECTED`` and ``DETECTED_NEGATIVE``
586 mask planes,
and log the results.
588 ``numPos`` (number of positive footprints), ``numPosPeaks`` (number
589 of positive peaks), ``numNeg`` (number of negative footprints),
590 ``numNegPeaks`` (number of negative peaks) entries are added to the
596 Mask image on which to flag detected pixels.
597 results : `lsst.pipe.base.Struct`
598 Struct of detection results, including ``positive``
and
599 ``negative`` entries; modified.
601 Gaussian sigma of PSF.
603 Multiplier
for the configured threshold. Note that this
is only
604 used here
for logging purposes.
605 factorNeg : `float`
or `
None`
606 Multiplier used
for the negative detection polarity threshold.
607 If `
None`, a factor equal to ``factor`` (i.e. equal to the one used
608 for positive detection polarity)
is assumed. Note that this
is only
609 used here
for logging purposes.
611 factorNeg = factor if factorNeg
is None else factorNeg
612 for polarity, maskName
in ((
"positive",
"DETECTED"), (
"negative",
"DETECTED_NEGATIVE")):
613 fpSet = getattr(results, polarity)
616 if self.config.nSigmaToGrow > 0:
617 nGrow = int((self.config.nSigmaToGrow * sigma) + 0.5)
618 self.metadata[
"nGrow"] = nGrow
619 if self.config.combinedGrow:
620 fpSet = afwDet.FootprintSet(fpSet, nGrow, self.config.isotropicGrow)
622 stencil = (afwGeom.Stencil.CIRCLE
if self.config.isotropicGrow
else
623 afwGeom.Stencil.MANHATTAN)
625 fp.dilate(nGrow, stencil)
626 fpSet.setMask(mask, maskName)
627 if not self.config.returnOriginalFootprints:
628 setattr(results, polarity, fpSet)
631 results.numPosPeaks = 0
633 results.numNegPeaks = 0
637 if results.positive
is not None:
638 results.numPos = len(results.positive.getFootprints())
639 results.numPosPeaks = sum(len(fp.getPeaks())
for fp
in results.positive.getFootprints())
640 positive =
" %d positive peaks in %d footprints" % (results.numPosPeaks, results.numPos)
641 if results.negative
is not None:
642 results.numNeg = len(results.negative.getFootprints())
643 results.numNegPeaks = sum(len(fp.getPeaks())
for fp
in results.negative.getFootprints())
644 negative =
" %d negative peaks in %d footprints" % (results.numNegPeaks, results.numNeg)
646 self.log.info(
"Detected%s%s%s to %g +ve and %g -ve %s",
647 positive,
" and" if positive
and negative
else "", negative,
648 self.config.thresholdValue*self.config.includeThresholdMultiplier*factor,
649 self.config.thresholdValue*self.config.includeThresholdMultiplier*factorNeg,
650 "DN" if self.config.thresholdType ==
"value" else "sigma")
653 """Estimate the background after detection
658 Image on which to estimate the background.
659 backgrounds : `lsst.afw.math.BackgroundList`
660 List of backgrounds; modified.
664 bg : `lsst.afw.math.backgroundMI`
665 Empirical background model.
667 bg = self.background.fitBackground(maskedImage)
668 if self.config.adjustBackground:
669 self.log.warning(
"Fiddling the background by %g", self.config.adjustBackground)
670 bg += self.config.adjustBackground
671 self.log.info(
"Resubtracting the background after object detection")
672 maskedImage -= bg.getImageF(self.background.config.algorithm,
673 self.background.config.undersampleStyle)
675 actrl = bg.getBackgroundControl().getApproximateControl()
676 backgrounds.append((bg, getattr(afwMath.Interpolate, self.background.config.algorithm),
677 bg.getAsUsedUndersampleStyle(), actrl.getStyle(), actrl.getOrderX(),
678 actrl.getOrderY(), actrl.getWeighting()))
682 """Clear unwanted results from the Struct of results
684 If we specifically want only positive or only negative detections,
685 drop the ones we don
't want, and its associated mask plane.
691 results : `lsst.pipe.base.Struct`
692 Detection results, with ``positive``
and ``negative`` elements;
695 if self.config.thresholdPolarity ==
"positive":
696 if self.config.reEstimateBackground:
697 mask &= ~mask.getPlaneBitMask(
"DETECTED_NEGATIVE")
698 results.negative =
None
699 elif self.config.thresholdPolarity ==
"negative":
700 if self.config.reEstimateBackground:
701 mask &= ~mask.getPlaneBitMask(
"DETECTED")
702 results.positive =
None
705 def detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None):
706 """Detect footprints on an exposure.
711 Exposure to process; DETECTED{,_NEGATIVE} mask plane will be
713 doSmooth : `bool`, optional
714 If
True, smooth the image before detection using a Gaussian
715 of width ``sigma``,
or the measured PSF width of ``exposure``.
716 Set to
False when running on e.g. a pre-convolved image,
or a mask
718 sigma : `float`, optional
719 Gaussian Sigma of PSF (pixels); used
for smoothing
and to grow
720 detections;
if `
None` then measure the sigma of the PSF of the
722 clearMask : `bool`, optional
723 Clear both DETECTED
and DETECTED_NEGATIVE planes before running
725 expId : `dict`, optional
726 Exposure identifier; unused by this implementation, but used
for
727 RNG seed by subclasses.
731 results : `lsst.pipe.base.Struct`
732 A `~lsst.pipe.base.Struct` containing:
735 Positive polarity footprints.
738 Negative polarity footprints.
741 Number of footprints
in positive
or 0
if detection polarity was
744 Number of footprints
in negative
or 0
if detection polarity was
747 Re-estimated background. `
None`
if
748 ``reEstimateBackground==
False``.
749 (`lsst.afw.math.BackgroundList`)
751 Multiplication factor applied to the configured detection
754 maskedImage = exposure.maskedImage
759 psf = self.
getPsf(exposure, sigma=sigma)
761 convolveResults = self.
convolveImage(maskedImage, psf, doSmooth=doSmooth)
762 middle = convolveResults.middle
763 sigma = convolveResults.sigma
767 results.background = afwMath.BackgroundList()
768 if self.config.doTempLocalBackground:
775 results.positive = self.
setPeakSignificance(middle, results.positive, results.positiveThreshold)
776 results.negative = self.
setPeakSignificance(middle, results.negative, results.negativeThreshold,
779 if self.config.reEstimateBackground:
784 self.
display(exposure, results, middle)
789 """Set the significance of flagged pixels to zero.
793 middle : `lsst.afw.image.ExposureF`
794 Score or maximum likelihood difference image.
795 The image plane will be modified
in place.
798 badPixels = middle.mask.array & badPixelMask > 0
799 middle.image.array[badPixels] = 0
802 """Set the significance of each detected peak to the pixel value divided
803 by the appropriate standard-deviation for ``config.thresholdType``.
805 Only sets significance
for "stdev" and "pixel_stdev" thresholdTypes;
806 we leave it undefined
for "value" and "variance" as it does
not have a
807 well-defined meaning
in those cases.
812 Exposure that footprints were detected on, likely the convolved,
813 local background-subtracted image.
815 Footprints detected on the image.
817 Threshold used to find footprints.
818 negative : `bool`, optional
819 Are we calculating
for negative sources?
821 if footprints
is None or footprints.getFootprints() == []:
823 polarity = -1
if negative
else 1
826 mapper = afwTable.SchemaMapper(footprints.getFootprints()[0].peaks.schema)
827 mapper.addMinimalSchema(footprints.getFootprints()[0].peaks.schema)
828 mapper.addOutputField(
"significance", type=float,
829 doc=
"Ratio of peak value to configured standard deviation.")
834 newFootprints = afwDet.FootprintSet(footprints)
835 for old, new
in zip(footprints.getFootprints(), newFootprints.getFootprints()):
836 newPeaks = afwDet.PeakCatalog(mapper.getOutputSchema())
837 newPeaks.extend(old.peaks, mapper=mapper)
838 new.getPeaks().clear()
839 new.setPeakCatalog(newPeaks)
842 if self.config.thresholdType ==
"pixel_stdev":
843 for footprint
in newFootprints.getFootprints():
844 footprint.updatePeakSignificance(exposure.variance, polarity)
845 elif self.config.thresholdType ==
"stdev":
846 sigma = threshold.getValue() / self.config.thresholdValue
847 for footprint
in newFootprints.getFootprints():
848 footprint.updatePeakSignificance(polarity*sigma)
850 for footprint
in newFootprints.getFootprints():
851 for peak
in footprint.peaks:
852 peak[
"significance"] = 0
857 """Make an afw.detection.Threshold object corresponding to the task's
858 configuration and the statistics of the given image.
863 Image to measure noise statistics
from if needed.
864 thresholdParity: `str`
865 One of
"positive" or "negative", to set the kind of fluctuations
866 the Threshold will detect.
868 Factor by which to multiply the configured detection threshold.
869 This
is useful
for tweaking the detection threshold slightly.
876 parity = False if thresholdParity ==
"negative" else True
877 thresholdValue = self.config.thresholdValue
878 thresholdType = self.config.thresholdType
879 if self.config.thresholdType ==
'stdev':
880 bad = image.getMask().getPlaneBitMask(self.config.statsMask)
881 sctrl = afwMath.StatisticsControl()
882 sctrl.setAndMask(bad)
883 stats = afwMath.makeStatistics(image, afwMath.STDEVCLIP, sctrl)
884 thresholdValue *= stats.getValue(afwMath.STDEVCLIP)
885 thresholdType =
'value'
887 threshold = afwDet.createThreshold(thresholdValue*factor, thresholdType, parity)
888 threshold.setIncludeMultiplier(self.config.includeThresholdMultiplier)
889 self.log.debug(
"Detection threshold: %s", threshold)
893 """Update the Peaks in a FootprintSet by detecting new Footprints and
894 Peaks in an image
and using the new Peaks instead of the old ones.
899 Set of Footprints whose Peaks should be updated.
901 Image to detect new Footprints
and Peak
in.
903 Threshold object
for detection.
905 Input Footprints
with fewer Peaks than self.config.nPeaksMaxSimple
906 are
not modified,
and if no new Peaks are detected
in an input
907 Footprint, the brightest original Peak
in that Footprint
is kept.
909 for footprint
in fpSet.getFootprints():
910 oldPeaks = footprint.getPeaks()
911 if len(oldPeaks) <= self.config.nPeaksMaxSimple:
916 sub = image.Factory(image, footprint.getBBox())
917 fpSetForPeaks = afwDet.FootprintSet(
921 self.config.minPixels
923 newPeaks = afwDet.PeakCatalog(oldPeaks.getTable())
924 for fpForPeaks
in fpSetForPeaks.getFootprints():
925 for peak
in fpForPeaks.getPeaks():
926 if footprint.contains(peak.getI()):
927 newPeaks.append(peak)
928 if len(newPeaks) > 0:
930 oldPeaks.extend(newPeaks)
936 """Set the edgeBitmask bits for all of maskedImage outside goodBBox
941 Image on which to set edge bits in the mask.
943 Bounding box of good pixels,
in ``LOCAL`` coordinates.
945 Bit mask to OR
with the existing mask bits
in the region
946 outside ``goodBBox``.
948 msk = maskedImage.getMask()
950 mx0, my0 = maskedImage.getXY0()
951 for x0, y0, w, h
in ([0, 0,
952 msk.getWidth(), goodBBox.getBeginY() - my0],
953 [0, goodBBox.getEndY() - my0, msk.getWidth(),
954 maskedImage.getHeight() - (goodBBox.getEndY() - my0)],
956 goodBBox.getBeginX() - mx0, msk.getHeight()],
957 [goodBBox.getEndX() - mx0, 0,
958 maskedImage.getWidth() - (goodBBox.getEndX() - mx0), msk.getHeight()],
962 edgeMask |= edgeBitmask
966 """Context manager for removing wide (large-scale) background
968 Removing a wide (large-scale) background helps to suppress the
969 detection of large footprints that may overwhelm the deblender.
970 It does, however, set a limit on the maximum scale of objects.
972 The background that we remove will be restored upon exit from
978 Exposure on which to remove large-scale background.
982 context : context manager
983 Context manager that will ensure the temporary wide background
986 doTempWideBackground = self.config.doTempWideBackground
987 if doTempWideBackground:
988 self.log.info(
"Applying temporary wide background subtraction")
989 original = exposure.maskedImage.image.array[:].copy()
990 self.tempWideBackground.
run(exposure).background
993 image = exposure.maskedImage.image
994 mask = exposure.maskedImage.mask
995 noData = mask.array & mask.getPlaneBitMask(
"NO_DATA") > 0
996 isGood = mask.array & mask.getPlaneBitMask(self.config.statsMask) == 0
997 image.array[noData] = np.median(image.array[~noData & isGood])
1001 if doTempWideBackground:
1002 exposure.maskedImage.image.array[:] = original
1006 """Add a set of exposures together.
1011 Sequence of exposures to add.
1016 An exposure of the same size as each exposure
in ``exposureList``,
1017 with the metadata
from ``exposureList[0]``
and a masked image equal
1018 to the sum of all the exposure
's masked images.
1020 exposure0 = exposureList[0]
1021 image0 = exposure0.getMaskedImage()
1023 addedImage = image0.Factory(image0, True)
1024 addedImage.setXY0(image0.getXY0())
1026 for exposure
in exposureList[1:]:
1027 image = exposure.getMaskedImage()
1030 addedExposure = exposure0.Factory(addedImage, exposure0.getWcs())
1031 return addedExposure
static MaskPixelT getPlaneBitMask(const std::vector< std::string > &names)
makeThreshold(self, image, thresholdParity, factor=1.0)
applyTempLocalBackground(self, exposure, middle, results)
detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None)
calculateKernelSize(self, sigma)
setEdgeBits(maskedImage, goodBBox, edgeBitmask)
tempWideBackgroundContext(self, exposure)
setPeakSignificance(self, exposure, footprints, threshold, negative=False)
clearUnwantedResults(self, mask, results)
updatePeaks(self, fpSet, image, threshold)
getPsf(self, exposure, sigma=None)
applyThreshold(self, middle, bbox, factor=1.0, factorNeg=None)
run(self, table, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None)
convolveImage(self, maskedImage, psf, doSmooth=True)
display(self, exposure, results, convolvedImage=None)
finalizeFootprints(self, mask, results, sigma, factor=1.0, factorNeg=None)
removeBadPixels(self, middle)
__init__(self, schema=None, **kwds)
reEstimateBackground(self, maskedImage, backgrounds)
addExposures(exposureList)