29__all__ = (
"NoiseReplacerConfig",
"NoiseReplacer",
"DummyNoiseReplacer")
33 """Noise replacement configuration."""
36 doc=
'How to choose mean and variance of the Gaussian noise we generate?',
39 'measure':
'Measure clipped mean and variance from the whole image',
40 'meta':
'Mean = 0, variance = the "BGMEAN" metadata entry',
41 'variance':
"Mean = 0, variance = the image's variance",
42 'variance_median':
"Mean = 0, variance = median(variance plane)"
44 default=
'measure', optional=
False
47 doc=
'Add ann offset to the generated noise.',
48 dtype=float, optional=
False, default=0.0
52 doc=
"The seed multiplier value to use for random number generation:\n"
53 ">= 1: set the seed deterministically based on exposureId\n"
54 "0: fall back to the afw.math.Random default constructor (which uses a seed value of 1)"
59 r"""Replace sources with noise during measurement.
63 config : `NoiseReplacerConfig`
66 Image in which sources will be replaced by noise. During operation,
67 the image will be modified
in-place to replace all sources. At the end
68 of the measurment procedure, the original sources will be replaced.
70 Mapping of ``id`` to a tuple of ``(parent, Footprint)``. When used
in
71 single-frame measurement, ``id``
is the source ID, but
in forced
72 photometry this
is the reference ID (
as that
is used to determine
74 noiseImage : `lsst.afw.image.ImageF`
75 An image used
as a predictable noise replacement source. Used during
77 log : `lsst.log.log.log.Log`
or `logging.Logger`, optional
78 Logger to use
for status messages; no status messages will be recorded
83 When measuring a source (
or the children associated
with a parent source),
84 this
class is used
to replace its neighbors with noise, using the
85 deblender
's definition of the sources as stored in
86 `~lsst.afw.detection.heavyFootprint.HeavyFootprint`\ s attached to the
90 with artificially generated noise (
in `NoiseReplacer.__init__`).
92 inserting that source
's
93 `~lsst.afw.detection.heavyFootprint.HeavyFootprint` (from the deblender)
100 This
is a functional copy of the code
in the older
101 ``ReplaceWithNoiseTask``, but
with a slightly different API needed
for the
102 new measurement framework; note that it
is not an `~lsst.pipe.base.Task`,
103 as the lifetime of a ``NoiseReplacer`` now corresponds to a single
104 exposure,
not an entire processing run.
106 When processing the ``footprints`` parameter, this routine should create
107 `~lsst.afw.detection.heavyFootprint.HeavyFootprint`\ s
for any non-Heavy
109 should then create a dict of
110 `~lsst.afw.detection.heavyFootprint.HeavyFootprint`\ s containing noise,
111 but only
for parent objects, then replace all sources
with noise. This
112 should ignore any footprints that lay outside the bounding box of the
113 exposure,
and clip those that lie on the border.
115 As the code currently stands, the heavy footprint
for a deblended object
116 must be available
from the input catalog. If it
is not, it cannot be
117 reproduced here. In that case, the topmost parent
in the objects parent
118 chain must be used. The heavy footprint
for that source
is created
in
122 ConfigClass = NoiseReplacerConfig
125 """Image on which the NoiseReplacer is operating (`lsst.afw.image.Exposure`).
129 """Mapping of ``id`` to a tuple of ``(parent, Footprint)`` (`dict`).
133 """Logger used for status messages.
136 def __init__(self, config, exposure, footprints, noiseImage=None, exposureId=None, log=None):
149 mi = exposure.getMaskedImage()
155 for maskname
in [
'THISDET',
'OTHERDET']:
156 if maskname
in mask.getMaskPlaneDict():
158 plane = mask.getMaskPlane(maskname)
160 self.
log.debug(
'Mask plane "%s" already existed', maskname)
163 plane = mask.addMaskPlane(maskname)
165 mask.clearMaskPlane(plane)
166 bitmask = mask.getPlaneBitMask(maskname)
167 bitmasks.append(bitmask)
169 self.
log.debug(
'Mask plane "%s": plane %i, bitmask %i = 0x%x',
170 maskname, plane, bitmask, bitmask)
183 for id, fp
in footprints.items():
198 noisegen = self.
getNoiseGenerator(exposure, noiseImage, noiseMeanVar, exposureId=exposureId)
200 self.
log.debug(
'Using noise generator: %s', str(noisegen))
202 fp = footprints[id][1]
203 noiseFp = noisegen.getHeavyFootprint(fp)
213 """Insert the heavy footprint of a given source into the exposure.
218 ID of the source to insert from original dictionary of footprints.
222 Also adjusts the mask plane to show the source of this footprint.
240 """Replace the heavy footprint of a given source with noise.
242 The same artificial noise is used
as in the original replacement.
247 ID of the source to replace
from original dictionary of footprints.
251 Also restores the mask plane.
273 """End the NoiseReplacer.
275 Restores original data to the exposure from the heavies dictionary
and
276 the mask planes to their original state.
288 mask.removeAndClearMaskPlane(maskname,
True)
297 """Return a generator of artificial noise.
301 noiseGenerator : `lsst.afw.image.noiseReplacer.NoiseGenerator`
303 if noiseImage
is not None:
308 if exposureId
is not None and exposureId != 0:
313 if noiseMeanVar
is not None:
316 noiseMean, noiseVar = noiseMeanVar
317 noiseMean = float(noiseMean)
318 noiseVar = float(noiseVar)
319 noiseStd = math.sqrt(noiseVar)
321 self.
log.debug(
'Using passed-in noise mean = %g, variance = %g -> stdev %g',
322 noiseMean, noiseVar, noiseStd)
326 self.
log.debug(
'Failed to cast passed-in noiseMeanVar to floats: %s',
331 if noiseSource ==
'meta':
333 meta = exposure.getMetadata()
336 bgMean = meta.getAsDouble(
'BGMEAN')
338 noiseStd = math.sqrt(bgMean)
340 self.
log.debug(
'Using noise variance = (BGMEAN = %g) from exposure metadata',
345 self.
log.debug(
'Failed to get BGMEAN from exposure metadata')
347 if noiseSource ==
'variance':
349 self.
log.debug(
'Will draw noise according to the variance plane.')
350 var = exposure.getMaskedImage().getVariance()
353 if noiseSource ==
'variance_median':
355 self.
log.debug(
'Will draw noise using the median of the variance plane.')
356 var = exposure.getMaskedImage().getVariance()
358 varMedian = s.getValue(afwMath.MEDIAN)
360 self.
log.debug(
"Measured from variance: median variance = %g",
365 im = exposure.getMaskedImage().getImage()
367 noiseMean = s.getValue(afwMath.MEANCLIP)
368 noiseStd = s.getValue(afwMath.STDEVCLIP)
370 self.
log.debug(
"Measured from image: clipped mean = %g, stdev = %g",
376 """Make a list of NoiseReplacers behave like a single one.
378 This class provides conenient syntactic sugar
for noise replacement across
383 This
is only used
in the MultiFit driver, but the logic there
is already
384 pretty complex, so it
's nice to have this to simplify it.
387 def __init__(self, exposuresById, footprintsByExp):
391 for expId, exposure
in exposuresById.items():
392 self.append(
NoiseReplacer(exposure, footprintsByExp[expId]), expId)
395 """Insert original pixels of the given source (by id) into the exposure.
401 """Insert noise pixels of the given source (by id) into the exposure.
407 """Clean-up when the use of the noise replacer is done.
414 r"""Base class for noise generators.
416 Derived classes produce
417 `~lsst.afw.detection.heavyFootprint.HeavyFootprint`\ s filled with noise
418 generated
in various ways.
422 This
is an abstract base
class.
432 return afwImage.MaskedImageF(im)
439 """Generate noise by extracting a sub-image from a user-supplied image.
443 img : `lsst.afw.image.ImageF`
444 An image to use as the basis of noise generation.
448 self.
mim = afwImage.MaskedImageF(img)
457 """Abstract base for Gaussian noise generators.
467 rim = afwImage.ImageF(bb.getWidth(), bb.getHeight())
468 rim.setXY0(bb.getMinX(), bb.getMinY())
474 """Generates Gaussian noise with a fixed mean and standard deviation.
478 super(FixedGaussianNoiseGenerator, self).
__init__(rand=rand)
483 return 'FixedGaussianNoiseGenerator: mean=%g, std=%g' % (self.
mean, self.
std)
493 """Generates Gaussian noise with variance matching an image variance plane.
497 var : `lsst.afw.image.ImageF`
498 The input variance image.
500 Mean value
for the generated noise.
504 super(VariancePlaneNoiseGenerator, self).
__init__(rand=rand)
506 if mean
is not None and mean == 0.:
511 return 'VariancePlaneNoiseGenerator: mean=' + str(self.
mean)
516 stdev = afwImage.ImageF(self.
var, bb, afwImage.LOCAL,
True)
519 if self.
mean is not None:
525 """A noise replacer which does nothing.
527 This is used when we need to disable noise replacement.
531 This has all the public methods of `NoiseReplacer`, but none of them do
A class to contain the data, WCS, and other information needed to describe an image of the sky.
A class to represent a 2-dimensional array of pixels.
A class that can be used to generate sequences of random numbers according to a number of different a...
Record class that contains measurements made on a single exposure.
__init__(self, mean, std, rand=None)
__init__(self, rand=None)
getHeavyFootprint(self, fp)
__init__(self, config, exposure, footprints, noiseImage=None, exposureId=None, log=None)
getNoiseGenerator(self, exposure, noiseImage, noiseMeanVar, exposureId=None)
__init__(self, exposuresById, footprintsByExp)
__init__(self, var, mean=None, rand=None)
daf::base::PropertyList * list
HeavyFootprint< ImagePixelT, MaskPixelT, VariancePixelT > makeHeavyFootprint(Footprint const &foot, lsst::afw::image::MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > const &img, HeavyFootprintCtrl const *ctrl=nullptr)
Create a HeavyFootprint with footprint defined by the given Footprint and pixel values from the given...
void randomGaussianImage(ImageT *image, Random &rand)
Set image to random numbers with a gaussian N(0, 1) distribution.
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Handle a watered-down front-end to the constructor (no variance)