24 __all__ = (
"SourceDetectionConfig",
"SourceDetectionTask",
"addExposures")
26 from contextlib
import contextmanager
39 from .subtractBackground
import SubtractBackgroundTask
43 """!Configuration parameters for the SourceDetectionTask 45 minPixels = pexConfig.RangeField(
46 doc=
"detected sources with fewer than the specified number of pixels will be ignored",
47 dtype=int, optional=
False, default=1, min=0,
49 isotropicGrow = pexConfig.Field(
50 doc=
"Pixels should be grown as isotropically as possible (slower)",
51 dtype=bool, optional=
False, default=
False,
53 combinedGrow = pexConfig.Field(
54 doc=
"Grow all footprints at the same time? This allows disconnected footprints to merge.",
55 dtype=bool, default=
True,
57 nSigmaToGrow = pexConfig.Field(
58 doc=
"Grow detections by nSigmaToGrow * [PSF RMS width]; if 0 then do not grow",
59 dtype=float, default=2.4,
61 returnOriginalFootprints = pexConfig.Field(
62 doc=
"Grow detections to set the image mask bits, but return the original (not-grown) footprints",
63 dtype=bool, optional=
False, default=
False,
65 thresholdValue = pexConfig.RangeField(
66 doc=
"Threshold for footprints",
67 dtype=float, optional=
False, default=5.0, min=0.0,
69 includeThresholdMultiplier = pexConfig.RangeField(
70 doc=
"Include threshold relative to thresholdValue",
71 dtype=float, default=1.0, min=0.0,
73 thresholdType = pexConfig.ChoiceField(
74 doc=
"specifies the desired flavor of Threshold",
75 dtype=str, optional=
False, default=
"stdev",
77 "variance":
"threshold applied to image variance",
78 "stdev":
"threshold applied to image std deviation",
79 "value":
"threshold applied to image value",
80 "pixel_stdev":
"threshold applied to per-pixel std deviation",
83 thresholdPolarity = pexConfig.ChoiceField(
84 doc=
"specifies whether to detect positive, or negative sources, or both",
85 dtype=str, optional=
False, default=
"positive",
87 "positive":
"detect only positive sources",
88 "negative":
"detect only negative sources",
89 "both":
"detect both positive and negative sources",
92 adjustBackground = pexConfig.Field(
94 doc=
"Fiddle factor to add to the background; debugging only",
97 reEstimateBackground = pexConfig.Field(
99 doc=
"Estimate the background again after final source detection?",
100 default=
True, optional=
False,
102 background = pexConfig.ConfigurableField(
103 doc=
"Background re-estimation; ignored if reEstimateBackground false",
104 target=SubtractBackgroundTask,
106 tempLocalBackground = pexConfig.ConfigurableField(
107 doc=(
"A local (small-scale), temporary background estimation step run between " 108 "detecting above-threshold regions and detecting the peaks within " 109 "them; used to avoid detecting spuerious peaks in the wings."),
110 target=SubtractBackgroundTask,
112 doTempLocalBackground = pexConfig.Field(
114 doc=
"Enable temporary local background subtraction? (see tempLocalBackground)",
117 tempWideBackground = pexConfig.ConfigurableField(
118 doc=(
"A wide (large-scale) background estimation and removal before footprint and peak detection. " 119 "It is added back into the image after detection. The purpose is to suppress very large " 120 "footprints (e.g., from large artifacts) that the deblender may choke on."),
121 target=SubtractBackgroundTask,
123 doTempWideBackground = pexConfig.Field(
125 doc=
"Do temporary wide (large-scale) background subtraction before footprint detection?",
128 nPeaksMaxSimple = pexConfig.Field(
130 doc=(
"The maximum number of peaks in a Footprint before trying to " 131 "replace its peaks using the temporary local background"),
134 nSigmaForKernel = pexConfig.Field(
136 doc=(
"Multiple of PSF RMS size to use for convolution kernel bounding box size; " 137 "note that this is not a half-size. The size will be rounded up to the nearest odd integer"),
140 statsMask = pexConfig.ListField(
142 doc=
"Mask planes to ignore when calculating statistics of image (for thresholdType=stdev)",
143 default=[
'BAD',
'SAT',
'EDGE',
'NO_DATA'],
156 for maskPlane
in (
"DETECTED",
"DETECTED_NEGATIVE"):
170 @anchor SourceDetectionTask_ 172 @brief Detect positive and negative sources on an exposure and return a new @link table.SourceCatalog@endlink. 174 @section meas_algorithms_detection_Contents Contents 176 - @ref meas_algorithms_detection_Purpose 177 - @ref meas_algorithms_detection_Initialize 178 - @ref meas_algorithms_detection_Invoke 179 - @ref meas_algorithms_detection_Config 180 - @ref meas_algorithms_detection_Debug 181 - @ref meas_algorithms_detection_Example 183 @section meas_algorithms_detection_Purpose Description 185 @copybrief SourceDetectionTask 187 @section meas_algorithms_detection_Initialize Task initialisation 189 @copydoc \_\_init\_\_ 191 @section meas_algorithms_detection_Invoke Invoking the Task 195 @section meas_algorithms_detection_Config Configuration parameters 197 See @ref SourceDetectionConfig 199 @section meas_algorithms_detection_Debug Debug variables 201 The @link lsst.pipe.base.cmdLineTask.CmdLineTask command line task@endlink interface supports a 202 flag @c -d to import @b debug.py from your @c PYTHONPATH; see @ref baseDebug for more about @b debug.py files. 204 The available variables in SourceDetectionTask are: 208 - If True, display the exposure on ds9's frame 0. +ve detections in blue, -ve detections in cyan 209 - If display > 1, display the convolved exposure on frame 1 212 @section meas_algorithms_detection_Example A complete example of using SourceDetectionTask 214 This code is in @link measAlgTasks.py@endlink in the examples directory, and can be run as @em e.g. 216 examples/measAlgTasks.py --ds9 218 @dontinclude measAlgTasks.py 219 The example also runs the SourceMeasurementTask; see @ref meas_algorithms_measurement_Example for more 222 Import the task (there are some other standard imports; read the file if you're confused) 223 @skipline SourceDetectionTask 225 We need to create our task before processing any data as the task constructor 226 can add an extra column to the schema, but first we need an almost-empty Schema 227 @skipline makeMinimalSchema 228 after which we can call the constructor: 229 @skip SourceDetectionTask.ConfigClass 232 We're now ready to process the data (we could loop over multiple exposures/catalogues using the same 233 task objects). First create the output table: 236 And process the image 238 (You may not be happy that the threshold was set in the config before creating the Task rather than being set 239 separately for each exposure. You @em can reset it just before calling the run method if you must, but we 240 should really implement a better solution). 242 We can then unpack and use the results: 247 To investigate the @ref meas_algorithms_detection_Debug, put something like 251 di = lsstDebug.getInfo(name) # N.b. lsstDebug.Info(name) would call us recursively 252 if name == "lsst.meas.algorithms.detection": 257 lsstDebug.Info = DebugInfo 259 into your debug.py file and run measAlgTasks.py with the @c --debug flag. 261 ConfigClass = SourceDetectionConfig
262 _DefaultName =
"sourceDetection" 265 """!Create the detection task. Most arguments are simply passed onto pipe.base.Task. 267 @param schema An lsst::afw::table::Schema used to create the output lsst.afw.table.SourceCatalog 268 @param **kwds Keyword arguments passed to lsst.pipe.base.task.Task.__init__. 270 If schema is not None and configured for 'both' detections, 271 a 'flags.negative' field will be added to label detections made with a 274 @note This task can add fields to the schema, so any code calling this task must ensure that 275 these columns are indeed present in the input match list; see @ref Example 277 pipeBase.Task.__init__(self, **kwds)
278 if schema
is not None and self.config.thresholdPolarity ==
"both":
280 "flags_negative", type=
"Flag",
281 doc=
"set if source was detected as significantly negative" 284 if self.config.thresholdPolarity ==
"both":
285 self.log.
warn(
"Detection polarity set to 'both', but no flag will be " 286 "set to distinguish between positive and negative detections")
288 if self.config.reEstimateBackground:
289 self.makeSubtask(
"background")
290 if self.config.doTempLocalBackground:
291 self.makeSubtask(
"tempLocalBackground")
292 if self.config.doTempWideBackground:
293 self.makeSubtask(
"tempWideBackground")
296 def run(self, table, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None):
297 """!Run source detection and create a SourceCatalog. 299 @param table lsst.afw.table.SourceTable object that will be used to create the SourceCatalog. 300 @param exposure Exposure to process; DETECTED mask plane will be set in-place. 301 @param doSmooth if True, smooth the image before detection using a Gaussian of width sigma 303 @param sigma sigma of PSF (pixels); used for smoothing and to grow detections; 304 if None then measure the sigma of the PSF of the exposure (default: None) 305 @param clearMask Clear DETECTED{,_NEGATIVE} planes before running detection (default: True) 306 @param expId Exposure identifier (integer); unused by this implementation, but used for 307 RNG seed by subclasses. 309 @return a lsst.pipe.base.Struct with: 310 - sources -- an lsst.afw.table.SourceCatalog object 311 - fpSets --- lsst.pipe.base.Struct returned by @link detectFootprints @endlink 313 @throws ValueError if flags.negative is needed, but isn't in table's schema 314 @throws lsst.pipe.base.TaskError if sigma=None, doSmooth=True and the exposure has no PSF 317 If you want to avoid dealing with Sources and Tables, you can use detectFootprints() 318 to just get the afw::detection::FootprintSet%s. 321 raise ValueError(
"Table has incorrect Schema")
322 results = self.
detectFootprints(exposure=exposure, doSmooth=doSmooth, sigma=sigma,
323 clearMask=clearMask, expId=expId)
325 sources.reserve(results.numPos + results.numNeg)
327 results.negative.makeSources(sources)
329 for record
in sources:
332 results.positive.makeSources(sources)
333 results.fpSets = results.copy()
334 results.sources = sources
338 makeSourceCatalog = run
340 def display(self, exposure, results, convolvedImage=None):
341 """Display detections if so configured 343 Displays the ``exposure`` in frame 0, overlays the detection peaks. 345 Requires that ``lsstDebug`` has been set up correctly, so that 346 ``lsstDebug.Info("lsst.meas.algorithms.detection")`` evaluates `True`. 348 If the ``convolvedImage`` is non-`None` and 349 ``lsstDebug.Info("lsst.meas.algorithms.detection") > 1``, the 350 ``convolvedImage`` will be displayed in frame 1. 354 exposure : `lsst.afw.image.Exposure` 355 Exposure to display, on which will be plotted the detections. 356 results : `lsst.pipe.base.Struct` 357 Results of the 'detectFootprints' method, containing positive and 358 negative footprints (which contain the peak positions that we will 359 plot). This is a `Struct` with ``positive`` and ``negative`` 360 elements that are of type `lsst.afw.detection.FootprintSet`. 361 convolvedImage : `lsst.afw.image.Image`, optional 362 Convolved image used for thresholding. 376 disp0.mtv(exposure, title=
"detection")
378 def plotPeaks(fps, ctype):
381 with disp0.Buffering():
382 for fp
in fps.getFootprints():
383 for pp
in fp.getPeaks():
384 disp0.dot(
"+", pp.getFx(), pp.getFy(), ctype=ctype)
385 plotPeaks(results.positive,
"yellow")
386 plotPeaks(results.negative,
"red")
388 if convolvedImage
and display > 1:
390 disp1.mtv(convolvedImage, title=
"PSF smoothed")
393 """Apply a temporary local background subtraction 395 This temporary local background serves to suppress noise fluctuations 396 in the wings of bright objects. 398 Peaks in the footprints will be updated. 402 exposure : `lsst.afw.image.Exposure` 403 Exposure for which to fit local background. 404 middle : `lsst.afw.image.MaskedImage` 405 Convolved image on which detection will be performed 406 (typically smaller than ``exposure`` because the 407 half-kernel has been removed around the edges). 408 results : `lsst.pipe.base.Struct` 409 Results of the 'detectFootprints' method, containing positive and 410 negative footprints (which contain the peak positions that we will 411 plot). This is a `Struct` with ``positive`` and ``negative`` 412 elements that are of type `lsst.afw.detection.FootprintSet`. 417 bg = self.tempLocalBackground.fitBackground(exposure.getMaskedImage())
418 bgImage = bg.getImageF()
419 middle -= bgImage.Factory(bgImage, middle.getBBox())
422 if self.config.thresholdPolarity !=
"negative":
423 self.
updatePeaks(results.positive, middle, thresholdPos)
424 if self.config.thresholdPolarity !=
"positive":
425 self.
updatePeaks(results.negative, middle, thresholdNeg)
428 """Clear the DETECTED and DETECTED_NEGATIVE mask planes 430 Removes any previous detection mask in preparation for a new 435 mask : `lsst.afw.image.Mask` 438 mask &= ~(mask.getPlaneBitMask(
"DETECTED") | mask.getPlaneBitMask(
"DETECTED_NEGATIVE"))
441 """Calculate size of smoothing kernel 443 Uses the ``nSigmaForKernel`` configuration parameter. Note 444 that that is the full width of the kernel bounding box 445 (so a value of 7 means 3.5 sigma on either side of center). 446 The value will be rounded up to the nearest odd integer. 451 Gaussian sigma of smoothing kernel. 456 Size of the smoothing kernel. 458 return (
int(sigma * self.config.nSigmaForKernel + 0.5)//2)*2 + 1
461 """Retrieve the PSF for an exposure 463 If ``sigma`` is provided, we make a ``GaussianPsf`` with that, 464 otherwise use the one from the ``exposure``. 468 exposure : `lsst.afw.image.Exposure` 469 Exposure from which to retrieve the PSF. 470 sigma : `float`, optional 471 Gaussian sigma to use if provided. 475 psf : `lsst.afw.detection.Psf` 476 PSF to use for detection. 479 psf = exposure.getPsf()
481 raise RuntimeError(
"Unable to determine PSF to use for detection: no sigma provided")
482 sigma = psf.computeShape().getDeterminantRadius()
488 """Convolve the image with the PSF 490 We convolve the image with a Gaussian approximation to the PSF, 491 because this is separable and therefore fast. It's technically a 492 correlation rather than a convolution, but since we use a symmetric 493 Gaussian there's no difference. 495 The convolution can be disabled with ``doSmooth=False``. If we do 496 convolve, we mask the edges as ``EDGE`` and return the convolved image 497 with the edges removed. This is because we can't convolve the edges 498 because the kernel would extend off the image. 502 maskedImage : `lsst.afw.image.MaskedImage` 504 psf : `lsst.afw.detection.Psf` 505 PSF to convolve with (actually with a Gaussian approximation 508 Actually do the convolution? 510 Return Struct contents 511 ---------------------- 512 middle : `lsst.afw.image.MaskedImage` 513 Convolved image, without the edges. 515 Gaussian sigma used for the convolution. 517 self.metadata.
set(
"doSmooth", doSmooth)
518 sigma = psf.computeShape().getDeterminantRadius()
519 self.metadata.
set(
"sigma", sigma)
522 middle = maskedImage.Factory(maskedImage)
523 return pipeBase.Struct(middle=middle, sigma=sigma)
528 self.metadata.
set(
"smoothingKernelWidth", kWidth)
529 gaussFunc = afwMath.GaussianFunction1D(sigma)
532 convolvedImage = maskedImage.Factory(maskedImage.getBBox())
538 goodBBox = gaussKernel.shrinkBBox(convolvedImage.getBBox())
539 middle = convolvedImage.Factory(convolvedImage, goodBBox, afwImage.PARENT,
False)
543 self.
setEdgeBits(maskedImage, goodBBox, maskedImage.getMask().getPlaneBitMask(
"EDGE"))
545 return pipeBase.Struct(middle=middle, sigma=sigma)
548 """Apply thresholds to the convolved image 550 Identifies ``Footprint``s, both positive and negative. 552 The threshold can be modified by the provided multiplication 557 middle : `lsst.afw.image.MaskedImage` 558 Convolved image to threshold. 559 bbox : `lsst.geom.Box2I` 560 Bounding box of unconvolved image. 562 Multiplier for the configured threshold. 564 Return Struct contents 565 ---------------------- 566 positive : `lsst.afw.detection.FootprintSet` or `None` 567 Positive detection footprints, if configured. 568 negative : `lsst.afw.detection.FootprintSet` or `None` 569 Negative detection footprints, if configured. 571 Multiplier for the configured threshold. 573 results = pipeBase.Struct(positive=
None, negative=
None, factor=factor)
575 if self.config.reEstimateBackground
or self.config.thresholdPolarity !=
"negative":
576 threshold = self.
makeThreshold(middle,
"positive", factor=factor)
581 self.config.minPixels
583 results.positive.setRegion(bbox)
584 if self.config.reEstimateBackground
or self.config.thresholdPolarity !=
"positive":
585 threshold = self.
makeThreshold(middle,
"negative", factor=factor)
590 self.config.minPixels
592 results.negative.setRegion(bbox)
597 """Finalize the detected footprints 599 Grows the footprints, sets the ``DETECTED`` and ``DETECTED_NEGATIVE`` 600 mask planes, and logs the results. 602 ``numPos`` (number of positive footprints), ``numPosPeaks`` (number 603 of positive peaks), ``numNeg`` (number of negative footprints), 604 ``numNegPeaks`` (number of negative peaks) entries are added to the 609 mask : `lsst.afw.image.Mask` 610 Mask image on which to flag detected pixels. 611 results : `lsst.pipe.base.Struct` 612 Struct of detection results, including ``positive`` and 613 ``negative`` entries; modified. 615 Gaussian sigma of PSF. 617 Multiplier for the configured threshold. 619 for polarity, maskName
in ((
"positive",
"DETECTED"), (
"negative",
"DETECTED_NEGATIVE")):
620 fpSet = getattr(results, polarity)
623 if self.config.nSigmaToGrow > 0:
624 nGrow =
int((self.config.nSigmaToGrow * sigma) + 0.5)
625 self.metadata.
set(
"nGrow", nGrow)
626 if self.config.combinedGrow:
629 stencil = (afwGeom.Stencil.CIRCLE
if self.config.isotropicGrow
else 630 afwGeom.Stencil.MANHATTAN)
632 fp.dilate(nGrow, stencil)
633 fpSet.setMask(mask, maskName)
634 if not self.config.returnOriginalFootprints:
635 setattr(results, polarity, fpSet)
638 results.numPosPeaks = 0
640 results.numNegPeaks = 0
644 if results.positive
is not None:
645 results.numPos = len(results.positive.getFootprints())
646 results.numPosPeaks = sum(len(fp.getPeaks())
for fp
in results.positive.getFootprints())
647 positive =
" %d positive peaks in %d footprints" % (results.numPosPeaks, results.numPos)
648 if results.negative
is not None:
649 results.numNeg = len(results.negative.getFootprints())
650 results.numNegPeaks = sum(len(fp.getPeaks())
for fp
in results.negative.getFootprints())
651 negative =
" %d negative peaks in %d footprints" % (results.numNegPeaks, results.numNeg)
653 self.log.
info(
"Detected%s%s%s to %g %s" %
654 (positive,
" and" if positive
and negative
else "", negative,
655 self.config.thresholdValue*self.config.includeThresholdMultiplier*factor,
656 "DN" if self.config.thresholdType ==
"value" else "sigma"))
659 """Estimate the background after detection 663 maskedImage : `lsst.afw.image.MaskedImage` 664 Image on which to estimate the background. 665 backgrounds : `lsst.afw.math.BackgroundList` 666 List of backgrounds; modified. 670 bg : `lsst.afw.math.backgroundMI` 671 Empirical background model. 673 bg = self.background.fitBackground(maskedImage)
674 if self.config.adjustBackground:
675 self.log.
warn(
"Fiddling the background by %g", self.config.adjustBackground)
676 bg += self.config.adjustBackground
677 self.log.
info(
"Resubtracting the background after object detection")
678 maskedImage -= bg.getImageF()
679 backgrounds.append(bg)
683 """Clear unwanted results from the Struct of results 685 If we specifically want only positive or only negative detections, 686 drop the ones we don't want, and its associated mask plane. 690 mask : `lsst.afw.image.Mask` 692 results : `lsst.pipe.base.Struct` 693 Detection results, with ``positive`` and ``negative`` elements; 696 if self.config.thresholdPolarity ==
"positive":
697 if self.config.reEstimateBackground:
698 mask &= ~mask.getPlaneBitMask(
"DETECTED_NEGATIVE")
699 results.negative =
None 700 elif self.config.thresholdPolarity ==
"negative":
701 if self.config.reEstimateBackground:
702 mask &= ~mask.getPlaneBitMask(
"DETECTED")
703 results.positive =
None 706 def detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None):
707 """Detect footprints. 711 exposure : `lsst.afw.image.Exposure` 712 Exposure to process; DETECTED{,_NEGATIVE} mask plane will be 714 doSmooth : `bool`, optional 715 If True, smooth the image before detection using a Gaussian 717 sigma : `float`, optional 718 Gaussian Sigma of PSF (pixels); used for smoothing and to grow 719 detections; if `None` then measure the sigma of the PSF of the 721 clearMask : `bool`, optional 722 Clear both DETECTED and DETECTED_NEGATIVE planes before running 724 expId : `dict`, optional 725 Exposure identifier; unused by this implementation, but used for 726 RNG seed by subclasses. 728 Return Struct contents 729 ---------------------- 730 positive : `lsst.afw.detection.FootprintSet` 731 Positive polarity footprints (may be `None`) 732 negative : `lsst.afw.detection.FootprintSet` 733 Negative polarity footprints (may be `None`) 735 Number of footprints in positive or 0 if detection polarity was 738 Number of footprints in negative or 0 if detection polarity was 740 background : `lsst.afw.math.BackgroundList` 741 Re-estimated background. `None` if 742 ``reEstimateBackground==False``. 744 Multiplication factor applied to the configured detection 747 maskedImage = exposure.maskedImage
752 psf = self.
getPsf(exposure, sigma=sigma)
754 convolveResults = self.
convolveImage(maskedImage, psf, doSmooth=doSmooth)
755 middle = convolveResults.middle
756 sigma = convolveResults.sigma
760 if self.config.doTempLocalBackground:
764 if self.config.reEstimateBackground:
768 self.
display(exposure, results, middle)
773 """Make an afw.detection.Threshold object corresponding to the task's 774 configuration and the statistics of the given image. 778 image : `afw.image.MaskedImage` 779 Image to measure noise statistics from if needed. 780 thresholdParity: `str` 781 One of "positive" or "negative", to set the kind of fluctuations 782 the Threshold will detect. 784 Factor by which to multiply the configured detection threshold. 785 This is useful for tweaking the detection threshold slightly. 789 threshold : `lsst.afw.detection.Threshold` 792 parity =
False if thresholdParity ==
"negative" else True 793 thresholdValue = self.config.thresholdValue
794 thresholdType = self.config.thresholdType
795 if self.config.thresholdType ==
'stdev':
796 bad = image.getMask().getPlaneBitMask(self.config.statsMask)
798 sctrl.setAndMask(bad)
800 thresholdValue *= stats.getValue(afwMath.STDEVCLIP)
801 thresholdType =
'value' 804 threshold.setIncludeMultiplier(self.config.includeThresholdMultiplier)
808 """Update the Peaks in a FootprintSet by detecting new Footprints and 809 Peaks in an image and using the new Peaks instead of the old ones. 813 fpSet : `afw.detection.FootprintSet` 814 Set of Footprints whose Peaks should be updated. 815 image : `afw.image.MaskedImage` 816 Image to detect new Footprints and Peak in. 817 threshold : `afw.detection.Threshold` 818 Threshold object for detection. 820 Input Footprints with fewer Peaks than self.config.nPeaksMaxSimple 821 are not modified, and if no new Peaks are detected in an input 822 Footprint, the brightest original Peak in that Footprint is kept. 824 for footprint
in fpSet.getFootprints():
825 oldPeaks = footprint.getPeaks()
826 if len(oldPeaks) <= self.config.nPeaksMaxSimple:
831 sub = image.Factory(image, footprint.getBBox())
836 self.config.minPixels
839 for fpForPeaks
in fpSetForPeaks.getFootprints():
840 for peak
in fpForPeaks.getPeaks():
841 if footprint.contains(peak.getI()):
842 newPeaks.append(peak)
843 if len(newPeaks) > 0:
845 oldPeaks.extend(newPeaks)
851 """Set the edgeBitmask bits for all of maskedImage outside goodBBox 855 maskedImage : `lsst.afw.image.MaskedImage` 856 Image on which to set edge bits in the mask. 857 goodBBox : `lsst.geom.Box2I` 858 Bounding box of good pixels, in ``LOCAL`` coordinates. 859 edgeBitmask : `lsst.afw.image.MaskPixel` 860 Bit mask to OR with the existing mask bits in the region 861 outside ``goodBBox``. 863 msk = maskedImage.getMask()
865 mx0, my0 = maskedImage.getXY0()
866 for x0, y0, w, h
in ([0, 0,
867 msk.getWidth(), goodBBox.getBeginY() - my0],
868 [0, goodBBox.getEndY() - my0, msk.getWidth(),
869 maskedImage.getHeight() - (goodBBox.getEndY() - my0)],
871 goodBBox.getBeginX() - mx0, msk.getHeight()],
872 [goodBBox.getEndX() - mx0, 0,
873 maskedImage.getWidth() - (goodBBox.getEndX() - mx0), msk.getHeight()],
877 edgeMask |= edgeBitmask
881 """Context manager for removing wide (large-scale) background 883 Removing a wide (large-scale) background helps to suppress the 884 detection of large footprints that may overwhelm the deblender. 885 It does, however, set a limit on the maximum scale of objects. 887 The background that we remove will be restored upon exit from 892 exposure : `lsst.afw.image.Exposure` 893 Exposure on which to remove large-scale background. 897 context : context manager 898 Context manager that will ensure the temporary wide background 901 doTempWideBackground = self.config.doTempWideBackground
902 if doTempWideBackground:
903 self.log.
info(
"Applying temporary wide background subtraction")
904 original = exposure.maskedImage.image.array[:].copy()
905 self.tempWideBackground.
run(exposure).background
908 image = exposure.maskedImage.image
909 mask = exposure.maskedImage.mask
910 noData = mask.array & mask.getPlaneBitMask(
"NO_DATA") > 0
911 isGood = mask.array & mask.getPlaneBitMask(self.config.statsMask) == 0
912 image.array[noData] = np.median(image.array[~noData & isGood])
916 if doTempWideBackground:
917 exposure.maskedImage.image.array[:] = original
921 """Add a set of exposures together. 925 exposureList : `list` of `lsst.afw.image.Exposure` 926 Sequence of exposures to add. 930 addedExposure : `lsst.afw.image.Exposure` 931 An exposure of the same size as each exposure in ``exposureList``, 932 with the metadata from ``exposureList[0]`` and a masked image equal 933 to the sum of all the exposure's masked images. 935 exposure0 = exposureList[0]
936 image0 = exposure0.getMaskedImage()
938 addedImage = image0.Factory(image0,
True)
939 addedImage.setXY0(image0.getXY0())
941 for exposure
in exposureList[1:]:
942 image = exposure.getMaskedImage()
945 addedExposure = exposure0.Factory(addedImage, exposure0.getWcs())
def updatePeaks(self, fpSet, image, threshold)
def applyThreshold(self, middle, bbox, factor=1.0)
def addExposures(exposureList)
Parameters to control convolution.
def tempWideBackgroundContext(self, exposure)
def detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None)
def convolveImage(self, maskedImage, psf, doSmooth=True)
A kernel described by a pair of functions: func(x, y) = colFunc(x) * rowFunc(y)
def applyTempLocalBackground(self, exposure, middle, results)
def calculateKernelSize(self, sigma)
daf::base::PropertySet * set
def display(self, exposure, results, convolvedImage=None)
Detect positive and negative sources on an exposure and return a new table.SourceCatalog.
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<>
def makeThreshold(self, image, thresholdParity, factor=1.0)
Pass parameters to a Statistics object.
Configuration parameters for the SourceDetectionTask.
def clearUnwantedResults(self, mask, results)
def getPsf(self, exposure, sigma=None)
Threshold createThreshold(const double value, const std::string type="value", const bool polarity=true)
Factory method for creating Threshold objects.
def finalizeFootprints(self, mask, results, sigma, factor=1.0)
def __init__(self, schema=None, kwds)
Create the detection task.
def reEstimateBackground(self, maskedImage, backgrounds)
def run(self, table, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None)
Run source detection and create a SourceCatalog.
def clearMask(self, mask)
def setEdgeBits(maskedImage, goodBBox, edgeBitmask)
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, bool doNormalize, bool doCopyEdge=false)
Old, deprecated version of convolve.
An integer coordinate rectangle.
A circularly symmetric Gaussian Psf class with no spatial variation, intended mostly for testing purp...