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.fpSets = results.copy()
287 results.sources = sources
290 def display(self, exposure, results, convolvedImage=None):
291 """Display detections if so configured
293 Displays the ``exposure`` in frame 0, overlays the detection peaks.
295 Requires that ``lsstDebug`` has been set up correctly, so that
296 ``
lsstDebug.Info(
"lsst.meas.algorithms.detection")`` evaluates `
True`.
298 If the ``convolvedImage``
is non-`
None`
and
300 ``convolvedImage`` will be displayed
in frame 1.
305 Exposure to display, on which will be plotted the detections.
306 results : `lsst.pipe.base.Struct`
307 Results of the
'detectFootprints' method, containing positive
and
308 negative footprints (which contain the peak positions that we will
309 plot). This
is a `Struct`
with ``positive``
and ``negative``
312 Convolved image used
for thresholding.
325 afwDisplay.setDefaultMaskTransparency(75)
327 disp0 = afwDisplay.Display(frame=0)
328 disp0.mtv(exposure, title=
"detection")
330 def plotPeaks(fps, ctype):
333 with disp0.Buffering():
334 for fp
in fps.getFootprints():
335 for pp
in fp.getPeaks():
336 disp0.dot(
"+", pp.getFx(), pp.getFy(), ctype=ctype)
337 plotPeaks(results.positive,
"yellow")
338 plotPeaks(results.negative,
"red")
340 if convolvedImage
and display > 1:
341 disp1 = afwDisplay.Display(frame=1)
342 disp1.mtv(convolvedImage, title=
"PSF smoothed")
344 disp2 = afwDisplay.Display(frame=2)
345 disp2.mtv(afwImage.ImageF(np.sqrt(exposure.variance.array)), title=
"stddev")
348 """Apply a temporary local background subtraction
350 This temporary local background serves to suppress noise fluctuations
351 in the wings of bright objects.
353 Peaks
in the footprints will be updated.
358 Exposure
for which to fit local background.
360 Convolved image on which detection will be performed
361 (typically smaller than ``exposure`` because the
362 half-kernel has been removed around the edges).
363 results : `lsst.pipe.base.Struct`
364 Results of the
'detectFootprints' method, containing positive
and
365 negative footprints (which contain the peak positions that we will
366 plot). This
is a `Struct`
with ``positive``
and ``negative``
372 bg = self.tempLocalBackground.fitBackground(exposure.getMaskedImage())
373 bgImage = bg.getImageF(self.tempLocalBackground.config.algorithm,
374 self.tempLocalBackground.config.undersampleStyle)
375 middle -= bgImage.Factory(bgImage, middle.getBBox())
376 if self.config.thresholdPolarity !=
"negative":
377 results.positiveThreshold = self.
makeThreshold(middle,
"positive")
378 self.
updatePeaks(results.positive, middle, results.positiveThreshold)
379 if self.config.thresholdPolarity !=
"positive":
380 results.negativeThreshold = self.
makeThreshold(middle,
"negative")
381 self.
updatePeaks(results.negative, middle, results.negativeThreshold)
384 """Clear the DETECTED and DETECTED_NEGATIVE mask planes.
386 Removes any previous detection mask in preparation
for a new
394 mask &= ~(mask.getPlaneBitMask("DETECTED") | mask.getPlaneBitMask(
"DETECTED_NEGATIVE"))
397 """Calculate the size of the smoothing kernel.
399 Uses the ``nSigmaForKernel`` configuration parameter. Note
400 that that is the full width of the kernel bounding box
401 (so a value of 7 means 3.5 sigma on either side of center).
402 The value will be rounded up to the nearest odd integer.
407 Gaussian sigma of smoothing kernel.
412 Size of the smoothing kernel.
414 return (int(sigma * self.config.nSigmaForKernel + 0.5)//2)*2 + 1
417 """Create a single Gaussian PSF for an exposure.
420 with that, otherwise use the sigma
from the psf of the ``exposure`` to
426 Exposure
from which to retrieve the PSF.
427 sigma : `float`, optional
428 Gaussian sigma to use
if provided.
433 PSF to use
for detection.
438 Raised
if ``sigma``
is not provided
and ``exposure`` does
not
439 contain a ``Psf`` object.
442 psf = exposure.getPsf()
444 raise RuntimeError(
"Unable to determine PSF to use for detection: no sigma provided")
445 sigma = psf.computeShape(psf.getAveragePosition()).getDeterminantRadius()
447 psf = afwDet.GaussianPsf(size, size, sigma)
451 """Convolve the image with the PSF.
453 We convolve the image with a Gaussian approximation to the PSF,
454 because this
is separable
and therefore fast. It
's technically a
455 correlation rather than a convolution, but since we use a symmetric
456 Gaussian there's no difference.
458 The convolution can be disabled with ``doSmooth=
False``. If we do
459 convolve, we mask the edges
as ``EDGE``
and return the convolved image
460 with the edges removed. This
is because we can
't convolve the edges
461 because the kernel would extend off the image.
468 PSF to convolve with (actually
with a Gaussian approximation
471 Actually do the convolution? Set to
False when running on
472 e.g. a pre-convolved image,
or a mask plane.
476 results : `lsst.pipe.base.Struct`
477 The `~lsst.pipe.base.Struct` contains:
482 Gaussian sigma used
for the convolution. (`float`)
484 self.metadata["doSmooth"] = doSmooth
485 sigma = psf.computeShape(psf.getAveragePosition()).getDeterminantRadius()
486 self.metadata[
"sigma"] = sigma
489 middle = maskedImage.Factory(maskedImage, deep=
True)
490 return pipeBase.Struct(middle=middle, sigma=sigma)
495 self.metadata[
"smoothingKernelWidth"] = kWidth
496 gaussFunc = afwMath.GaussianFunction1D(sigma)
497 gaussKernel = afwMath.SeparableKernel(kWidth, kWidth, gaussFunc, gaussFunc)
499 convolvedImage = maskedImage.Factory(maskedImage.getBBox())
501 afwMath.convolve(convolvedImage, maskedImage, gaussKernel, afwMath.ConvolutionControl())
504 goodBBox = gaussKernel.shrinkBBox(convolvedImage.getBBox())
505 middle = convolvedImage.Factory(convolvedImage, goodBBox, afwImage.PARENT,
False)
508 self.
setEdgeBits(maskedImage, goodBBox, maskedImage.getMask().getPlaneBitMask(
"EDGE"))
510 return pipeBase.Struct(middle=middle, sigma=sigma)
513 r"""Apply thresholds to the convolved image
516 The threshold can be modified by the provided multiplication
522 Convolved image to threshold.
524 Bounding box of unconvolved image.
526 Multiplier
for the configured threshold.
530 results : `lsst.pipe.base.Struct`
531 The `~lsst.pipe.base.Struct` contains:
534 Positive detection footprints,
if configured.
537 Negative detection footprints,
if configured.
540 Multiplier
for the configured threshold.
543 results = pipeBase.Struct(positive=None, negative=
None, factor=factor,
544 positiveThreshold=
None, negativeThreshold=
None)
546 if self.config.reEstimateBackground
or self.config.thresholdPolarity !=
"negative":
547 results.positiveThreshold = self.
makeThreshold(middle,
"positive", factor=factor)
548 results.positive = afwDet.FootprintSet(
550 results.positiveThreshold,
552 self.config.minPixels
554 results.positive.setRegion(bbox)
555 if self.config.reEstimateBackground
or self.config.thresholdPolarity !=
"positive":
556 results.negativeThreshold = self.
makeThreshold(middle,
"negative", factor=factor)
557 results.negative = afwDet.FootprintSet(
559 results.negativeThreshold,
561 self.config.minPixels
563 results.negative.setRegion(bbox)
568 """Finalize the detected footprints.
570 Grow the footprints, set the ``DETECTED`` and ``DETECTED_NEGATIVE``
571 mask planes,
and log the results.
573 ``numPos`` (number of positive footprints), ``numPosPeaks`` (number
574 of positive peaks), ``numNeg`` (number of negative footprints),
575 ``numNegPeaks`` (number of negative peaks) entries are added to the
581 Mask image on which to flag detected pixels.
582 results : `lsst.pipe.base.Struct`
583 Struct of detection results, including ``positive``
and
584 ``negative`` entries; modified.
586 Gaussian sigma of PSF.
588 Multiplier
for the configured threshold.
590 for polarity, maskName
in ((
"positive",
"DETECTED"), (
"negative",
"DETECTED_NEGATIVE")):
591 fpSet = getattr(results, polarity)
594 if self.config.nSigmaToGrow > 0:
595 nGrow = int((self.config.nSigmaToGrow * sigma) + 0.5)
596 self.metadata[
"nGrow"] = nGrow
597 if self.config.combinedGrow:
598 fpSet = afwDet.FootprintSet(fpSet, nGrow, self.config.isotropicGrow)
600 stencil = (afwGeom.Stencil.CIRCLE
if self.config.isotropicGrow
else
601 afwGeom.Stencil.MANHATTAN)
603 fp.dilate(nGrow, stencil)
604 fpSet.setMask(mask, maskName)
605 if not self.config.returnOriginalFootprints:
606 setattr(results, polarity, fpSet)
609 results.numPosPeaks = 0
611 results.numNegPeaks = 0
615 if results.positive
is not None:
616 results.numPos = len(results.positive.getFootprints())
617 results.numPosPeaks = sum(len(fp.getPeaks())
for fp
in results.positive.getFootprints())
618 positive =
" %d positive peaks in %d footprints" % (results.numPosPeaks, results.numPos)
619 if results.negative
is not None:
620 results.numNeg = len(results.negative.getFootprints())
621 results.numNegPeaks = sum(len(fp.getPeaks())
for fp
in results.negative.getFootprints())
622 negative =
" %d negative peaks in %d footprints" % (results.numNegPeaks, results.numNeg)
624 self.log.info(
"Detected%s%s%s to %g %s",
625 positive,
" and" if positive
and negative
else "", negative,
626 self.config.thresholdValue*self.config.includeThresholdMultiplier*factor,
627 "DN" if self.config.thresholdType ==
"value" else "sigma")
630 """Estimate the background after detection
635 Image on which to estimate the background.
636 backgrounds : `lsst.afw.math.BackgroundList`
637 List of backgrounds; modified.
641 bg : `lsst.afw.math.backgroundMI`
642 Empirical background model.
644 bg = self.background.fitBackground(maskedImage)
645 if self.config.adjustBackground:
646 self.log.warning(
"Fiddling the background by %g", self.config.adjustBackground)
647 bg += self.config.adjustBackground
648 self.log.info(
"Resubtracting the background after object detection")
649 maskedImage -= bg.getImageF(self.background.config.algorithm,
650 self.background.config.undersampleStyle)
652 actrl = bg.getBackgroundControl().getApproximateControl()
653 backgrounds.append((bg, getattr(afwMath.Interpolate, self.background.config.algorithm),
654 bg.getAsUsedUndersampleStyle(), actrl.getStyle(), actrl.getOrderX(),
655 actrl.getOrderY(), actrl.getWeighting()))
659 """Clear unwanted results from the Struct of results
661 If we specifically want only positive or only negative detections,
662 drop the ones we don
't want, and its associated mask plane.
668 results : `lsst.pipe.base.Struct`
669 Detection results, with ``positive``
and ``negative`` elements;
672 if self.config.thresholdPolarity ==
"positive":
673 if self.config.reEstimateBackground:
674 mask &= ~mask.getPlaneBitMask(
"DETECTED_NEGATIVE")
675 results.negative =
None
676 elif self.config.thresholdPolarity ==
"negative":
677 if self.config.reEstimateBackground:
678 mask &= ~mask.getPlaneBitMask(
"DETECTED")
679 results.positive =
None
682 def detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None):
683 """Detect footprints on an exposure.
688 Exposure to process; DETECTED{,_NEGATIVE} mask plane will be
690 doSmooth : `bool`, optional
691 If
True, smooth the image before detection using a Gaussian
692 of width ``sigma``,
or the measured PSF width of ``exposure``.
693 Set to
False when running on e.g. a pre-convolved image,
or a mask
695 sigma : `float`, optional
696 Gaussian Sigma of PSF (pixels); used
for smoothing
and to grow
697 detections;
if `
None` then measure the sigma of the PSF of the
699 clearMask : `bool`, optional
700 Clear both DETECTED
and DETECTED_NEGATIVE planes before running
702 expId : `dict`, optional
703 Exposure identifier; unused by this implementation, but used
for
704 RNG seed by subclasses.
708 results : `lsst.pipe.base.Struct`
709 A `~lsst.pipe.base.Struct` containing:
712 Positive polarity footprints.
715 Negative polarity footprints.
718 Number of footprints
in positive
or 0
if detection polarity was
721 Number of footprints
in negative
or 0
if detection polarity was
724 Re-estimated background. `
None`
if
725 ``reEstimateBackground==
False``.
726 (`lsst.afw.math.BackgroundList`)
728 Multiplication factor applied to the configured detection
731 maskedImage = exposure.maskedImage
736 psf = self.
getPsf(exposure, sigma=sigma)
738 convolveResults = self.
convolveImage(maskedImage, psf, doSmooth=doSmooth)
739 middle = convolveResults.middle
740 sigma = convolveResults.sigma
743 results.background = afwMath.BackgroundList()
744 if self.config.doTempLocalBackground:
751 results.positive = self.
setPeakSignificance(middle, results.positive, results.positiveThreshold)
752 results.negative = self.
setPeakSignificance(middle, results.negative, results.negativeThreshold,
755 if self.config.reEstimateBackground:
760 self.
display(exposure, results, middle)
765 """Set the significance of each detected peak to the pixel value divided
766 by the appropriate standard-deviation for ``config.thresholdType``.
768 Only sets significance
for "stdev" and "pixel_stdev" thresholdTypes;
769 we leave it undefined
for "value" and "variance" as it does
not have a
770 well-defined meaning
in those cases.
775 Exposure that footprints were detected on, likely the convolved,
776 local background-subtracted image.
778 Footprints detected on the image.
780 Threshold used to find footprints.
781 negative : `bool`, optional
782 Are we calculating
for negative sources?
784 if footprints
is None or footprints.getFootprints() == []:
786 polarity = -1
if negative
else 1
789 mapper = afwTable.SchemaMapper(footprints.getFootprints()[0].peaks.schema)
790 mapper.addMinimalSchema(footprints.getFootprints()[0].peaks.schema)
791 mapper.addOutputField(
"significance", type=float,
792 doc=
"Ratio of peak value to configured standard deviation.")
797 newFootprints = afwDet.FootprintSet(footprints)
798 for old, new
in zip(footprints.getFootprints(), newFootprints.getFootprints()):
799 newPeaks = afwDet.PeakCatalog(mapper.getOutputSchema())
800 newPeaks.extend(old.peaks, mapper=mapper)
801 new.getPeaks().clear()
802 new.setPeakCatalog(newPeaks)
805 if self.config.thresholdType ==
"pixel_stdev":
806 for footprint
in newFootprints.getFootprints():
807 footprint.updatePeakSignificance(exposure.variance, polarity)
808 elif self.config.thresholdType ==
"stdev":
809 sigma = threshold.getValue() / self.config.thresholdValue
810 for footprint
in newFootprints.getFootprints():
811 footprint.updatePeakSignificance(polarity*sigma)
813 for footprint
in newFootprints.getFootprints():
814 for peak
in footprint.peaks:
815 peak[
"significance"] = 0
820 """Make an afw.detection.Threshold object corresponding to the task's
821 configuration and the statistics of the given image.
826 Image to measure noise statistics
from if needed.
827 thresholdParity: `str`
828 One of
"positive" or "negative", to set the kind of fluctuations
829 the Threshold will detect.
831 Factor by which to multiply the configured detection threshold.
832 This
is useful
for tweaking the detection threshold slightly.
839 parity = False if thresholdParity ==
"negative" else True
840 thresholdValue = self.config.thresholdValue
841 thresholdType = self.config.thresholdType
842 if self.config.thresholdType ==
'stdev':
843 bad = image.getMask().getPlaneBitMask(self.config.statsMask)
844 sctrl = afwMath.StatisticsControl()
845 sctrl.setAndMask(bad)
846 stats = afwMath.makeStatistics(image, afwMath.STDEVCLIP, sctrl)
847 thresholdValue *= stats.getValue(afwMath.STDEVCLIP)
848 thresholdType =
'value'
850 threshold = afwDet.createThreshold(thresholdValue*factor, thresholdType, parity)
851 threshold.setIncludeMultiplier(self.config.includeThresholdMultiplier)
852 self.log.debug(
"Detection threshold: %s", threshold)
856 """Update the Peaks in a FootprintSet by detecting new Footprints and
857 Peaks in an image
and using the new Peaks instead of the old ones.
862 Set of Footprints whose Peaks should be updated.
864 Image to detect new Footprints
and Peak
in.
866 Threshold object
for detection.
868 Input Footprints
with fewer Peaks than self.config.nPeaksMaxSimple
869 are
not modified,
and if no new Peaks are detected
in an input
870 Footprint, the brightest original Peak
in that Footprint
is kept.
872 for footprint
in fpSet.getFootprints():
873 oldPeaks = footprint.getPeaks()
874 if len(oldPeaks) <= self.config.nPeaksMaxSimple:
879 sub = image.Factory(image, footprint.getBBox())
880 fpSetForPeaks = afwDet.FootprintSet(
884 self.config.minPixels
886 newPeaks = afwDet.PeakCatalog(oldPeaks.getTable())
887 for fpForPeaks
in fpSetForPeaks.getFootprints():
888 for peak
in fpForPeaks.getPeaks():
889 if footprint.contains(peak.getI()):
890 newPeaks.append(peak)
891 if len(newPeaks) > 0:
893 oldPeaks.extend(newPeaks)
899 """Set the edgeBitmask bits for all of maskedImage outside goodBBox
904 Image on which to set edge bits in the mask.
906 Bounding box of good pixels,
in ``LOCAL`` coordinates.
908 Bit mask to OR
with the existing mask bits
in the region
909 outside ``goodBBox``.
911 msk = maskedImage.getMask()
913 mx0, my0 = maskedImage.getXY0()
914 for x0, y0, w, h
in ([0, 0,
915 msk.getWidth(), goodBBox.getBeginY() - my0],
916 [0, goodBBox.getEndY() - my0, msk.getWidth(),
917 maskedImage.getHeight() - (goodBBox.getEndY() - my0)],
919 goodBBox.getBeginX() - mx0, msk.getHeight()],
920 [goodBBox.getEndX() - mx0, 0,
921 maskedImage.getWidth() - (goodBBox.getEndX() - mx0), msk.getHeight()],
925 edgeMask |= edgeBitmask
929 """Context manager for removing wide (large-scale) background
931 Removing a wide (large-scale) background helps to suppress the
932 detection of large footprints that may overwhelm the deblender.
933 It does, however, set a limit on the maximum scale of objects.
935 The background that we remove will be restored upon exit from
941 Exposure on which to remove large-scale background.
945 context : context manager
946 Context manager that will ensure the temporary wide background
949 doTempWideBackground = self.config.doTempWideBackground
950 if doTempWideBackground:
951 self.log.info(
"Applying temporary wide background subtraction")
952 original = exposure.maskedImage.image.array[:].copy()
953 self.tempWideBackground.
run(exposure).background
956 image = exposure.maskedImage.image
957 mask = exposure.maskedImage.mask
958 noData = mask.array & mask.getPlaneBitMask(
"NO_DATA") > 0
959 isGood = mask.array & mask.getPlaneBitMask(self.config.statsMask) == 0
960 image.array[noData] = np.median(image.array[~noData & isGood])
964 if doTempWideBackground:
965 exposure.maskedImage.image.array[:] = original
969 """Add a set of exposures together.
974 Sequence of exposures to add.
979 An exposure of the same size as each exposure
in ``exposureList``,
980 with the metadata
from ``exposureList[0]``
and a masked image equal
981 to the sum of all the exposure
's masked images.
983 exposure0 = exposureList[0]
984 image0 = exposure0.getMaskedImage()
986 addedImage = image0.Factory(image0, True)
987 addedImage.setXY0(image0.getXY0())
989 for exposure
in exposureList[1:]:
990 image = exposure.getMaskedImage()
993 addedExposure = exposure0.Factory(addedImage, exposure0.getWcs())
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 finalizeFootprints(self, mask, results, sigma, factor=1.0)
def setEdgeBits(maskedImage, goodBBox, edgeBitmask)
def clearUnwantedResults(self, mask, results)
def clearMask(self, mask)
def display(self, exposure, results, convolvedImage=None)
def applyThreshold(self, middle, bbox, factor=1.0)
def calculateKernelSize(self, sigma)
def addExposures(exposureList)