LSST Applications 26.0.0,g0265f82a02+6660c170cc,g07994bdeae+30b05a742e,g0a0026dc87+17526d298f,g0a60f58ba1+17526d298f,g0e4bf8285c+96dd2c2ea9,g0ecae5effc+c266a536c8,g1e7d6db67d+6f7cb1f4bb,g26482f50c6+6346c0633c,g2bbee38e9b+6660c170cc,g2cc88a2952+0a4e78cd49,g3273194fdb+f6908454ef,g337abbeb29+6660c170cc,g337c41fc51+9a8f8f0815,g37c6e7c3d5+7bbafe9d37,g44018dc512+6660c170cc,g4a941329ef+4f7594a38e,g4c90b7bd52+5145c320d2,g58be5f913a+bea990ba40,g635b316a6c+8d6b3a3e56,g67924a670a+bfead8c487,g6ae5381d9b+81bc2a20b4,g93c4d6e787+26b17396bd,g98cecbdb62+ed2cb6d659,g98ffbb4407+81bc2a20b4,g9ddcbc5298+7f7571301f,ga1e77700b3+99e9273977,gae46bcf261+6660c170cc,gb2715bf1a1+17526d298f,gc86a011abf+17526d298f,gcf0d15dbbd+96dd2c2ea9,gdaeeff99f8+0d8dbea60f,gdb4ec4c597+6660c170cc,ge23793e450+96dd2c2ea9,gf041782ebf+171108ac67
LSST Data Management Base Package
Loading...
Searching...
No Matches
dynamicDetection.py
Go to the documentation of this file.
2__all__ = ["DynamicDetectionConfig", "DynamicDetectionTask"]
3
4import numpy as np
5
6from lsst.pex.config import Field, ConfigurableField
7from lsst.pipe.base import Struct, NoWorkFound
8
9from .detection import SourceDetectionConfig, SourceDetectionTask
10from .skyObjects import SkyObjectsTask
11
12from lsst.afw.detection import FootprintSet
13from lsst.afw.geom import makeCdMatrix, makeSkyWcs
14from lsst.afw.table import SourceCatalog, SourceTable
15from lsst.meas.base import ForcedMeasurementTask
16
17import lsst.afw.image
18import lsst.afw.math
19import lsst.geom as geom
20
21
23 """Configuration for DynamicDetectionTask
24 """
25 prelimThresholdFactor = Field(dtype=float, default=0.5,
26 doc="Factor by which to multiply the main detection threshold "
27 "(thresholdValue) to use for first pass (to find sky objects).")
28 prelimNegMultiplier = Field(dtype=float, default=2.5,
29 doc="Multiplier for the negative (relative to positive) polarity "
30 "detections threshold to use for first pass (to find sky objects).")
31 skyObjects = ConfigurableField(target=SkyObjectsTask, doc="Generate sky objects.")
32 doBackgroundTweak = Field(dtype=bool, default=True,
33 doc="Tweak background level so median PSF flux of sky objects is zero?")
34 minFractionSources = Field(dtype=float, default=0.02,
35 doc="Minimum fraction of the requested number of sky sources for dynamic "
36 "detection to be considered a success. If the number of good sky sources "
37 "identified falls below this threshold, a NoWorkFound error is raised so "
38 "that this dataId is no longer considered in downstream processing.")
39 doBrightPrelimDetection = Field(dtype=bool, default=True,
40 doc="Do initial bright detection pass where footprints are grown "
41 "by brightGrowFactor?")
42 brightMultiplier = Field(dtype=float, default=2000.0,
43 doc="Multiplier to apply to the prelimThresholdFactor for the "
44 "\"bright\" detections stage (want this to be large to only "
45 "detect the brightest sources).")
46 brightNegFactor = Field(dtype=float, default=2.2,
47 doc="Factor by which to multiply the threshold for the negative polatiry "
48 "detections for the \"bright\" detections stage (this needs to be fairly "
49 "low given the nature of the negative polarity detections in the very "
50 "large positive polarity threshold).")
51 brightGrowFactor = Field(dtype=int, default=40,
52 doc="Factor by which to grow the footprints of sources detected in the "
53 "\"bright\" detections stage (want this to be large to mask wings of "
54 "bright sources).")
55 brightMaskFractionMax = Field(dtype=float, default=0.95,
56 doc="Maximum allowed fraction of masked pixes from the \"bright\" "
57 "detection stage (to mask regions unsuitable for sky sourcess). "
58 "If this fraction is exeeded, the detection threshold for this stage "
59 "will be increased by bisectFactor until the fraction of masked "
60 "pixels drops below this threshold.")
61 bisectFactor = Field(dtype=float, default=1.2,
62 doc="Factor by which to increase thresholds in brightMaskFractionMax loop.")
63
64 def setDefaults(self):
65 SourceDetectionConfig.setDefaults(self)
66 self.skyObjects.nSources = 1000 # For good statistics
67 for maskStr in ["SAT"]:
68 if maskStr not in self.skyObjects.avoidMask:
69 self.skyObjects.avoidMask.append(maskStr)
70
71
73 """Detection of sources on an image with a dynamic threshold
74
75 We first detect sources using a lower threshold than normal (see config
76 parameter ``prelimThresholdFactor``) in order to identify good sky regions
77 (configurable ``skyObjects``). Then we perform forced PSF photometry on
78 those sky regions. Using those PSF flux measurements and estimated errors,
79 we set the threshold so that the stdev of the measurements matches the
80 median estimated error.
81
82 Besides the usual initialisation of configurables, we also set up
83 the forced measurement which is deliberately not represented in
84 this Task's configuration parameters because we're using it as
85 part of the algorithm and we don't want to allow it to be modified.
86 """
87 ConfigClass = DynamicDetectionConfig
88 _DefaultName = "dynamicDetection"
89
90 def __init__(self, *args, **kwargs):
91
92 SourceDetectionTask.__init__(self, *args, **kwargs)
93 self.makeSubtask("skyObjects")
94
95 # Set up forced measurement.
96 config = ForcedMeasurementTask.ConfigClass()
97 config.plugins.names = ['base_TransformedCentroid', 'base_PsfFlux', 'base_LocalBackground']
98 # We'll need the "centroid" and "psfFlux" slots
99 for slot in ("shape", "psfShape", "apFlux", "modelFlux", "gaussianFlux", "calibFlux"):
100 setattr(config.slots, slot, None)
101 config.copyColumns = {}
102 self.skySchema = SourceTable.makeMinimalSchema()
103 self.skyMeasurement = ForcedMeasurementTask(config=config, name="skyMeasurement", parentTask=self,
104 refSchema=self.skySchema)
105
106 def calculateThreshold(self, exposure, seed, sigma=None, minFractionSourcesFactor=1.0, isBgTweak=False):
107 """Calculate new threshold
108
109 This is the main functional addition to the vanilla
110 `SourceDetectionTask`.
111
112 We identify sky objects and perform forced PSF photometry on
113 them. Using those PSF flux measurements and estimated errors,
114 we set the threshold so that the stdev of the measurements
115 matches the median estimated error.
116
117 Parameters
118 ----------
119 exposure : `lsst.afw.image.Exposure`
120 Exposure on which we're detecting sources.
121 seed : `int`
122 RNG seed to use for finding sky objects.
123 sigma : `float`, optional
124 Gaussian sigma of smoothing kernel; if not provided,
125 will be deduced from the exposure's PSF.
126 minFractionSourcesFactor : `float`
127 Change the fraction of required sky sources from that set in
128 ``self.config.minFractionSources`` by this factor. NOTE: this
129 is intended for use in the background tweak pass (the detection
130 threshold is much lower there, so many more pixels end up marked
131 as DETECTED or DETECTED_NEGATIVE, leaving less room for sky
132 object placement).
133 isBgTweak : `bool`
134 Set to ``True`` for the background tweak pass (for more helpful
135 log messages).
136
137 Returns
138 -------
139 result : `lsst.pipe.base.Struct`
140 Result struct with components:
141
142 ``multiplicative``
143 Multiplicative factor to be applied to the
144 configured detection threshold (`float`).
145 ``additive``
146 Additive factor to be applied to the background
147 level (`float`).
148
149 Raises
150 ------
151 NoWorkFound
152 Raised if the number of good sky sources found is less than the
153 minimum fraction
154 (``self.config.minFractionSources``*``minFractionSourcesFactor``)
155 of the number requested (``self.skyObjects.config.nSources``).
156 """
157 wcsIsNone = exposure.getWcs() is None
158 if wcsIsNone: # create a dummy WCS as needed by ForcedMeasurementTask
159 self.log.info("WCS for exposure is None. Setting a dummy WCS for dynamic detection.")
160 exposure.setWcs(makeSkyWcs(crpix=geom.Point2D(0, 0),
161 crval=geom.SpherePoint(0, 0, geom.degrees),
162 cdMatrix=makeCdMatrix(scale=1e-5*geom.degrees)))
163 fp = self.skyObjects.run(exposure.maskedImage.mask, seed)
164 skyFootprints = FootprintSet(exposure.getBBox())
165 skyFootprints.setFootprints(fp)
166 table = SourceTable.make(self.skyMeasurement.schema)
167 catalog = SourceCatalog(table)
168 catalog.reserve(len(skyFootprints.getFootprints()))
169 skyFootprints.makeSources(catalog)
170 key = catalog.getCentroidSlot().getMeasKey()
171 for source in catalog:
172 peaks = source.getFootprint().getPeaks()
173 assert len(peaks) == 1
174 source.set(key, peaks[0].getF())
175 source.updateCoord(exposure.getWcs())
176
177 # Forced photometry on sky objects
178 self.skyMeasurement.run(catalog, exposure, catalog, exposure.getWcs())
179
180 # Calculate new threshold
181 fluxes = catalog["base_PsfFlux_instFlux"]
182 area = catalog["base_PsfFlux_area"]
183 bg = catalog["base_LocalBackground_instFlux"]
184
185 good = (~catalog["base_PsfFlux_flag"] & ~catalog["base_LocalBackground_flag"]
186 & np.isfinite(fluxes) & np.isfinite(area) & np.isfinite(bg))
187
188 minNumSources = int(self.config.minFractionSources*self.skyObjects.config.nSources)
189 # Reduce the number of sky sources required if requested, but ensure
190 # a minumum of 3.
191 if minFractionSourcesFactor != 1.0:
192 minNumSources = max(3, int(minNumSources*minFractionSourcesFactor))
193 if good.sum() < minNumSources:
194 if not isBgTweak:
195 msg = (f"Insufficient good sky source flux measurements ({good.sum()} < "
196 f"{minNumSources}) for dynamic threshold calculation.")
197 else:
198 msg = (f"Insufficient good sky source flux measurements ({good.sum()} < "
199 f"{minNumSources}) for background tweak calculation.")
200
201 nPix = exposure.mask.array.size
202 badPixelMask = lsst.afw.image.Mask.getPlaneBitMask(["NO_DATA", "BAD"])
203 nGoodPix = np.sum(exposure.mask.array & badPixelMask == 0)
204 if nGoodPix/nPix > 0.2:
205 detectedPixelMask = lsst.afw.image.Mask.getPlaneBitMask(["DETECTED", "DETECTED_NEGATIVE"])
206 nDetectedPix = np.sum(exposure.mask.array & detectedPixelMask != 0)
207 msg += (f" However, {nGoodPix}/{nPix} pixels are not marked NO_DATA or BAD, "
208 "so there should be sufficient area to locate suitable sky sources. "
209 f"Note that {nDetectedPix} of {nGoodPix} \"good\" pixels were marked "
210 "as DETECTED or DETECTED_NEGATIVE.")
211 raise RuntimeError(msg)
212 raise NoWorkFound(msg)
213
214 if not isBgTweak:
215 self.log.info("Number of good sky sources used for dynamic detection: %d (of %d requested).",
216 good.sum(), self.skyObjects.config.nSources)
217 else:
218 self.log.info("Number of good sky sources used for dynamic detection background tweak:"
219 " %d (of %d requested).", good.sum(), self.skyObjects.config.nSources)
220 bgMedian = np.median((fluxes/area)[good])
221
222 lq, uq = np.percentile((fluxes - bg*area)[good], [25.0, 75.0])
223 stdevMeas = 0.741*(uq - lq)
224 medianError = np.median(catalog["base_PsfFlux_instFluxErr"][good])
225 if wcsIsNone:
226 exposure.setWcs(None)
227 return Struct(multiplicative=medianError/stdevMeas, additive=bgMedian)
228
229 def detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None,
230 background=None):
231 """Detect footprints with a dynamic threshold
232
233 This varies from the vanilla ``detectFootprints`` method because we
234 do detection three times: first with a high threshold to detect
235 "bright" (both positive and negative, the latter to identify very
236 over-subtracted regions) sources for which we grow the DETECTED and
237 DETECTED_NEGATIVE masks significantly to account for wings. Second,
238 with a low threshold to mask all non-empty regions of the image. These
239 two masks are combined and used to identify regions of sky
240 uncontaminated by objects. A final round of detection is then done
241 with the new calculated threshold.
242
243 Parameters
244 ----------
245 exposure : `lsst.afw.image.Exposure`
246 Exposure to process; DETECTED{,_NEGATIVE} mask plane will be
247 set in-place.
248 doSmooth : `bool`, optional
249 If True, smooth the image before detection using a Gaussian
250 of width ``sigma``.
251 sigma : `float`, optional
252 Gaussian Sigma of PSF (pixels); used for smoothing and to grow
253 detections; if `None` then measure the sigma of the PSF of the
254 ``exposure``.
255 clearMask : `bool`, optional
256 Clear both DETECTED and DETECTED_NEGATIVE planes before running
257 detection.
258 expId : `int`, optional
259 Exposure identifier, used as a seed for the random number
260 generator. If absent, the seed will be the sum of the image.
261 background : `lsst.afw.math.BackgroundList`, optional
262 Background that was already subtracted from the exposure; will be
263 modified in-place if ``reEstimateBackground=True``.
264
265 Returns
266 -------
267 resutls : `lsst.pipe.base.Struct`
268 The results `~lsst.pipe.base.Struct` contains:
269
270 ``positive``
271 Positive polarity footprints.
273 ``negative``
274 Negative polarity footprints.
276 ``numPos``
277 Number of footprints in positive or 0 if detection polarity was
278 negative. (`int`)
279 ``numNeg``
280 Number of footprints in negative or 0 if detection polarity was
281 positive. (`int`)
282 ``background``
283 Re-estimated background. `None` or the input ``background``
284 if ``reEstimateBackground==False``.
286 ``factor``
287 Multiplication factor applied to the configured detection
288 threshold. (`float`)
289 ``prelim``
290 Results from preliminary detection pass.
291 (`lsst.pipe.base.Struct`)
292 """
293 maskedImage = exposure.maskedImage
294
295 if clearMask:
296 self.clearMask(maskedImage.mask)
297 else:
298 oldDetected = maskedImage.mask.array & maskedImage.mask.getPlaneBitMask(["DETECTED",
299 "DETECTED_NEGATIVE"])
300 nPix = maskedImage.mask.array.size
301 badPixelMask = lsst.afw.image.Mask.getPlaneBitMask(["NO_DATA", "BAD"])
302 nGoodPix = np.sum(maskedImage.mask.array & badPixelMask == 0)
303 self.log.info("Number of good data pixels (i.e. not NO_DATA or BAD): {} ({:.1f}% of total)".
304 format(nGoodPix, 100*nGoodPix/nPix))
305
306 with self.tempWideBackgroundContext(exposure):
307 # Could potentially smooth with a wider kernel than the PSF in
308 # order to better pick up the wings of stars and galaxies, but for
309 # now sticking with the PSF as that's more simple.
310 psf = self.getPsf(exposure, sigma=sigma)
311 convolveResults = self.convolveImage(maskedImage, psf, doSmooth=doSmooth)
312
313 if self.config.doBrightPrelimDetection:
314 brightDetectedMask = self._computeBrightDetectionMask(maskedImage, convolveResults)
315
316 middle = convolveResults.middle
317 sigma = convolveResults.sigma
318 prelim = self.applyThreshold(
319 middle, maskedImage.getBBox(), factor=self.config.prelimThresholdFactor,
320 factorNeg=self.config.prelimNegMultiplier*self.config.prelimThresholdFactor
321 )
323 maskedImage.mask, prelim, sigma, factor=self.config.prelimThresholdFactor,
324 factorNeg=self.config.prelimNegMultiplier*self.config.prelimThresholdFactor
325 )
326 if self.config.doBrightPrelimDetection:
327 # Combine prelim and bright detection masks for multiplier
328 # determination.
329 maskedImage.mask.array |= brightDetectedMask
330
331 # Calculate the proper threshold
332 # seed needs to fit in a C++ 'int' so pybind doesn't choke on it
333 seed = (expId if expId is not None else int(maskedImage.image.array.sum())) % (2**31 - 1)
334 threshResults = self.calculateThreshold(exposure, seed, sigma=sigma)
335 factor = threshResults.multiplicative
336 self.log.info("Modifying configured detection threshold by factor %f to %f",
337 factor, factor*self.config.thresholdValue)
338
339 # Blow away preliminary (low threshold) detection mask
340 self.clearMask(maskedImage.mask)
341 if not clearMask:
342 maskedImage.mask.array |= oldDetected
343
344 # Rinse and repeat thresholding with new calculated threshold
345 results = self.applyThreshold(middle, maskedImage.getBBox(), factor)
346 results.prelim = prelim
347 results.background = background if background is not None else lsst.afw.math.BackgroundList()
348 if self.config.doTempLocalBackground:
349 self.applyTempLocalBackground(exposure, middle, results)
350 self.finalizeFootprints(maskedImage.mask, results, sigma, factor=factor)
351
352 self.clearUnwantedResults(maskedImage.mask, results)
353
354 if self.config.reEstimateBackground:
355 self.reEstimateBackground(maskedImage, results.background)
356
357 self.display(exposure, results, middle)
358
359 if self.config.doBackgroundTweak:
360 # Re-do the background tweak after any temporary backgrounds have
361 # been restored.
362 #
363 # But we want to keep any large-scale background (e.g., scattered
364 # light from bright stars) from being selected for sky objects in
365 # the calculation, so do another detection pass without either the
366 # local or wide temporary background subtraction; the DETECTED
367 # pixels will mark the area to ignore.
368 originalMask = maskedImage.mask.array.copy()
369 try:
370 self.clearMask(exposure.mask)
371 convolveResults = self.convolveImage(maskedImage, psf, doSmooth=doSmooth)
372 tweakDetResults = self.applyThreshold(convolveResults.middle, maskedImage.getBBox(), factor)
373 self.finalizeFootprints(maskedImage.mask, tweakDetResults, sigma, factor=factor)
374 bgLevel = self.calculateThreshold(exposure, seed, sigma=sigma, minFractionSourcesFactor=0.5,
375 isBgTweak=True).additive
376 finally:
377 maskedImage.mask.array[:] = originalMask
378 self.tweakBackground(exposure, bgLevel, results.background)
379
380 return results
381
382 def tweakBackground(self, exposure, bgLevel, bgList=None):
383 """Modify the background by a constant value
384
385 Parameters
386 ----------
387 exposure : `lsst.afw.image.Exposure`
388 Exposure for which to tweak background.
389 bgLevel : `float`
390 Background level to remove
391 bgList : `lsst.afw.math.BackgroundList`, optional
392 List of backgrounds to append to.
393
394 Returns
395 -------
397 Constant background model.
398 """
399 self.log.info("Tweaking background by %f to match sky photometry", bgLevel)
400 exposure.image -= bgLevel
401 bgStats = lsst.afw.image.MaskedImageF(1, 1)
402 bgStats.set(bgLevel, 0, bgLevel)
403 bg = lsst.afw.math.BackgroundMI(exposure.getBBox(), bgStats)
404 bgData = (bg, lsst.afw.math.Interpolate.LINEAR, lsst.afw.math.REDUCE_INTERP_ORDER,
405 lsst.afw.math.ApproximateControl.UNKNOWN, 0, 0, False)
406 if bgList is not None:
407 bgList.append(bgData)
408 return bg
409
410 def _computeBrightDetectionMask(self, maskedImage, convolveResults):
411 """Perform an initial bright source detection pass.
412
413 Perform an initial bright object detection pass using a high detection
414 threshold. The footprints in this pass are grown significantly more
415 than is typical to account for wings around bright sources. The
416 negative polarity detections in this pass help in masking severely
417 over-subtracted regions.
418
419 A maximum fraction of masked pixel from this pass is ensured via
420 the config ``brightMaskFractionMax``. If the masked pixel fraction is
421 above this value, the detection thresholds here are increased by
422 ``bisectFactor`` in a while loop until the detected masked fraction
423 falls below this value.
424
425 Parameters
426 ----------
427 maskedImage : `lsst.afw.image.MaskedImage`
428 Masked image on which to run the detection.
429 convolveResults : `lsst.pipe.base.Struct`
430 The results of the self.convolveImage function with attributes:
431
432 ``middle``
433 Convolved image, without the edges
435 ``sigma``
436 Gaussian sigma used for the convolution (`float`).
437
438 Returns
439 -------
440 brightDetectedMask : `numpy.ndarray`
441 Boolean array representing the union of the bright detection pass
442 DETECTED and DETECTED_NEGATIVE masks.
443 """
444 # Initialize some parameters.
445 brightPosFactor = (
446 self.config.prelimThresholdFactor*self.config.brightMultiplier/self.config.bisectFactor
447 )
448 brightNegFactor = self.config.brightNegFactor/self.config.bisectFactor
449 nPix = 1
450 nPixDet = 1
451 nPixDetNeg = 1
452 brightMaskFractionMax = self.config.brightMaskFractionMax
453
454 # Loop until masked fraction is smaller than
455 # brightMaskFractionMax, increasing the thresholds by
456 # config.bisectFactor on each iteration (rarely necessary
457 # for current defaults).
458 while nPixDetNeg/nPix > brightMaskFractionMax or nPixDet/nPix > brightMaskFractionMax:
459 self.clearMask(maskedImage.mask)
460 brightPosFactor *= self.config.bisectFactor
461 brightNegFactor *= self.config.bisectFactor
462 prelimBright = self.applyThreshold(convolveResults.middle, maskedImage.getBBox(),
463 factor=brightPosFactor, factorNeg=brightNegFactor)
465 maskedImage.mask, prelimBright, convolveResults.sigma*self.config.brightGrowFactor,
466 factor=brightPosFactor, factorNeg=brightNegFactor
467 )
468 # Check that not too many pixels got masked.
469 nPix = maskedImage.mask.array.size
470 nPixDet = countMaskedPixels(maskedImage, "DETECTED")
471 self.log.info("Number (%) of bright DETECTED pix: {} ({:.1f}%)".
472 format(nPixDet, 100*nPixDet/nPix))
473 nPixDetNeg = countMaskedPixels(maskedImage, "DETECTED_NEGATIVE")
474 self.log.info("Number (%) of bright DETECTED_NEGATIVE pix: {} ({:.1f}%)".
475 format(nPixDetNeg, 100*nPixDetNeg/nPix))
476 if nPixDetNeg/nPix > brightMaskFractionMax or nPixDet/nPix > brightMaskFractionMax:
477 self.log.warn("Too high a fraction (%.1f > %.1f) of pixels were masked with current "
478 "\"bright\" detection round thresholds. Increasing by a factor of %f "
479 "and trying again.", max(nPixDetNeg, nPixDet)/nPix,
480 brightMaskFractionMax, self.config.bisectFactor)
481
482 # Save the mask planes from the "bright" detection round, then
483 # clear them before moving on to the "prelim" detection phase.
484 brightDetectedMask = (maskedImage.mask.array
485 & maskedImage.mask.getPlaneBitMask(["DETECTED", "DETECTED_NEGATIVE"]))
486 self.clearMask(maskedImage.mask)
487 return brightDetectedMask
488
489
490def countMaskedPixels(maskedIm, maskPlane):
491 """Count the number of pixels in a given mask plane.
492
493 Parameters
494 ----------
495 maskedIm : `lsst.afw.image.MaskedImage`
496 Masked image to examine.
497 maskPlane : `str`
498 Name of the mask plane to examine.
499
500 Returns
501 -------
502 nPixMasked : `int`
503 Number of pixels with ``maskPlane`` bit set.
504 """
505 maskBit = maskedIm.mask.getPlaneBitMask(maskPlane)
506 nPixMasked = np.sum(np.bitwise_and(maskedIm.mask.array, maskBit))/maskBit
507 return nPixMasked
int max
A set of Footprints, associated with a MaskedImage.
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Definition Exposure.h:72
static MaskPixelT getPlaneBitMask(const std::vector< std::string > &names)
Return the bitmask corresponding to a vector of plane names OR'd together.
Definition Mask.cc:376
A class to manipulate images, masks, and variance as a single object.
Definition MaskedImage.h:74
A class to evaluate image background levels.
Definition Background.h:434
Point in an unspecified spherical coordinate system.
Definition SpherePoint.h:57
applyTempLocalBackground(self, exposure, middle, results)
Definition detection.py:355
applyThreshold(self, middle, bbox, factor=1.0, factorNeg=None)
Definition detection.py:520
convolveImage(self, maskedImage, psf, doSmooth=True)
Definition detection.py:458
display(self, exposure, results, convolvedImage=None)
Definition detection.py:298
finalizeFootprints(self, mask, results, sigma, factor=1.0, factorNeg=None)
Definition detection.py:586
reEstimateBackground(self, maskedImage, backgrounds)
Definition detection.py:656
_computeBrightDetectionMask(self, maskedImage, convolveResults)
detectFootprints(self, exposure, doSmooth=True, sigma=None, clearMask=True, expId=None, background=None)
calculateThreshold(self, exposure, seed, sigma=None, minFractionSourcesFactor=1.0, isBgTweak=False)