30 __all__ = (
"NoiseReplacerConfig",
"NoiseReplacer",
"DummyNoiseReplacer")
33 noiseSource = lsst.pex.config.ChoiceField(
34 doc=
'How to choose mean and variance of the Gaussian noise we generate?',
37 'measure':
'Measure clipped mean and variance from the whole image',
38 'meta':
'Mean = 0, variance = the "BGMEAN" metadata entry',
39 'variance':
"Mean = 0, variance = the image's variance",
41 default=
'measure', optional=
False
43 noiseOffset = lsst.pex.config.Field(
44 dtype=float, optional=
False, default=0.,
45 doc=
'Add ann offset to the generated noise.'
47 noiseSeedMultiplier = lsst.pex.config.Field(
49 doc=
'The seed multiplier value to use for random number generation. 0 will not set seed.'
54 Class that handles replacing sources with noise during measurement.
56 When measuring a source (or the children associated with a parent source), this class is used
57 to replace its neighbors with noise, using the deblender's definition of the sources as stored
58 in HeavyFootprints attached to the SourceRecords. The algorithm works as follows:
59 - We start by replacing all pixels that are in source Footprints with artificially
60 generated noise (__init__).
61 - When we are about to measure a particular source, we add it back in, by inserting that source's
62 HeavyFootprint (from the deblender) into the image.
63 - When we are done measuring that source, we again replace the HeavyFootprint with (the same)
65 - After measuring all sources, we return the image to its original state.
67 This is a functional copy of the code in the older ReplaceWithNoiseTask, but with a slightly different
68 API needed for the new measurement framework; note that it is not a Task, as the lifetime of a
69 NoiseReplacer now corresponds to a single exposure, not an entire processing run.
72 ConfigClass = NoiseReplacerConfig
74 def __init__(self, config, exposure, footprints, noiseImage=None, exposureId=None, log=None):
76 Initialize the NoiseReplacer.
78 @param[in] config instance of NoiseReplacerConfig
79 @param[in,out] exposure Exposure to be noise replaced. (All sources replaced on return)
80 @param[in] footprints dict of {id: (parent, footprint)};
81 @param[in] noiseImage an afw.image.ImageF used as a predictable noise replacement source
83 @param[in] log pex.logging.Log object to use for status messages; no status messages
84 will be printed if None
86 'footprints' is a dict of {id: (parent, footprint)}; when used in SFM, the ID will be the
87 source ID, but in forced photometry, this will be the reference ID, as that's what we used to
88 determine the deblend families. This routine should create HeavyFootprints for any non-Heavy
89 Footprints, and replace them in the dict. It should then create a dict of HeavyFootprints
90 containing noise, but only for parent objects, then replace all sources with noise.
91 This should ignore any footprints that lay outside the bounding box of the exposure,
92 and clip those that lie on the border.
94 NOTE: as the code currently stands, the heavy footprint for a deblended object must be available
95 from the input catalog. If it is not, it cannot be reproduced here. In that case, the
96 topmost parent in the objects parent chain must be used. The heavy footprint for that source
97 is created in this class from the masked image.
111 mi = exposure.getMaskedImage()
117 for maskname
in [
'THISDET',
'OTHERDET']:
120 plane = mask.getMaskPlane(maskname)
121 if self.
log: self.log.logdebug(
'Mask plane "%s" already existed' % maskname)
124 plane = mask.addMaskPlane(maskname)
125 self.removeplanes.append(maskname)
126 mask.clearMaskPlane(plane)
127 bitmask = mask.getPlaneBitMask(maskname)
128 bitmasks.append(bitmask)
129 if self.
log: self.log.logdebug(
'Mask plane "%s": plane %i, bitmask %i = 0x%x'
130 % (maskname, plane, bitmask, bitmask))
143 for id
in footprints.keys():
146 self.
heavies[id] = afwDet.cast_HeavyFootprintF(fp[1])
159 noisegen = self.
getNoiseGenerator(exposure, noiseImage, noiseMeanVar, exposureId=exposureId)
163 if self.
log: self.log.logdebug(
'Using noise generator: %s' % (str(noisegen)))
164 for id
in self.heavies.keys():
165 fp = footprints[id][1]
166 noiseFp = noisegen.getHeavyFootprint(fp)
177 Insert the heavy footprint of a given source into the exposure
179 @param[in] id id for current source to insert from original footprint dict
181 Also adjusts the mask plane to show the source of this footprint.
184 mi = self.exposure.getMaskedImage()
191 while self.
footprints[usedid][0] != 0
and not usedid
in self.heavies.keys():
200 Remove the heavy footprint of a given source and replace with previous noise
202 @param[in] id id for current source to insert from original footprint dict
204 Also restore the mask plane.
209 mi = self.exposure.getMaskedImage()
216 while self.
footprints[usedid][0] != 0
and not usedid
in self.heavies.keys():
227 End the NoiseReplacer.
229 Restore original data to the exposure from the heavies dictionary
230 Restore the mask planes to their original state
234 mi = self.exposure.getMaskedImage()
237 for id
in self.footprints.keys():
242 mask.removeAndClearMaskPlane(maskname,
True)
252 Generate noise image using parameters given
254 if noiseImage
is not None:
259 if not exposureId
is None and not exposureId == 0:
264 if noiseMeanVar
is not None:
267 noiseMean,noiseVar = noiseMeanVar
268 noiseMean = float(noiseMean)
269 noiseVar = float(noiseVar)
270 noiseStd = math.sqrt(noiseVar)
271 if self.
log: self.log.logdebug(
'Using passed-in noise mean = %g, variance = %g -> stdev %g'
272 % (noiseMean, noiseVar, noiseStd))
275 if self.
log: self.log.logdebug(
'Failed to cast passed-in noiseMeanVar to floats: %s'
276 % (str(noiseMeanVar)))
280 if noiseSource ==
'meta':
282 meta = exposure.getMetadata()
285 bgMean = meta.getAsDouble(
'BGMEAN')
287 noiseStd = math.sqrt(bgMean)
288 if self.
log: self.log.logdebug(
'Using noise variance = (BGMEAN = %g) from exposure metadata'
292 if self.
log: self.log.logdebug(
'Failed to get BGMEAN from exposure metadata')
294 if noiseSource ==
'variance':
295 if self.
log: self.log.logdebug(
'Will draw noise according to the variance plane.')
296 var = exposure.getMaskedImage().getVariance()
300 im = exposure.getMaskedImage().getImage()
302 noiseMean = s.getValue(afwMath.MEANCLIP)
303 noiseStd = s.getValue(afwMath.STDEVCLIP)
304 if self.
log: self.log.logdebug(
"Measured from image: clipped mean = %g, stdev = %g"
305 % (noiseMean,noiseStd))
309 """Syntactic sugar that makes a list of NoiseReplacers (for multiple exposures)
310 behave like a single one.
312 This is only used in the multifit driver, but the logic there is already pretty
313 complex, so it's nice to have this to simplify it.
316 def __init__(self, exposuresById, footprintsByExp):
320 for expId, exposure
in exposuresById.iteritems():
321 self.append(
NoiseReplacer(exposure, footprintsByExp[expId]), expId)
324 """Insert the original pixels for a given source (by id) into the original exposure.
329 """Insert the noise pixels for a given source (by id) into the original exposure.
334 """Cleanup when the use of the Noise replacer is done.
336 for item
in self: self.
end()
340 Base class for noise generators used by the "doReplaceWithNoise" routine:
341 these produce HeavyFootprints filled with noise generated in various ways.
343 This is an abstract base class.
353 return afwImage.MaskedImageF(im)
359 Generates noise by cutting out a subimage from a user-supplied noise Image.
364 img: an afwImage.ImageF
366 self.
mim = afwImage.MaskedImageF(img)
375 Generates noise using the afwMath.Random() and afwMath.randomGaussianImage() routines.
377 This is an abstract base class.
387 rim = afwImage.ImageF(bb.getWidth(), bb.getHeight())
388 rim.setXY0(bb.getMinX(), bb.getMinY())
394 Generates Gaussian noise with a fixed mean and standard deviation.
398 super(FixedGaussianNoiseGenerator, self).
__init__(rand=rand)
403 return 'FixedGaussianNoiseGenerator: mean=%g, std=%g' % (self.
mean, self.
std)
413 Generates Gaussian noise whose variance matches that of the variance plane of the image.
418 var: an afwImage.ImageF; the variance plane.
419 mean: floating-point or afwImage.Image
421 super(VariancePlaneNoiseGenerator, self).
__init__(rand=rand)
423 if mean
is not None and mean == 0.:
428 return 'VariancePlaneNoiseGenerator: mean=' + str(self.
mean)
433 stdev = afwImage.ImageF(self.
var, bb, afwImage.LOCAL,
True)
436 if self.
mean is not None:
443 A do-nothing standin for NoiseReplacer, used when we want to disable NoiseReplacer
445 DummyNoiseReplacer has all the public methods of NoiseReplacer, but none of them do anything.
def __init__
Initialize the NoiseReplacer.
def end
End the NoiseReplacer.
Base class for noise generators used by the "doReplaceWithNoise" routine: these produce HeavyFootprin...
void randomGaussianImage(ImageT *image, Random &rand)
MaskT clearMaskFromFootprint(lsst::afw::image::Mask< MaskT > *mask, Footprint const &footprint, MaskT const bitmask)
(AND ~bitmask) all the Mask's pixels that are in the Footprint; that is, set to zero in the Mask-inte...
A do-nothing standin for NoiseReplacer, used when we want to disable NoiseReplacer.
Generates Gaussian noise whose variance matches that of the variance plane of the image...
Generates Gaussian noise with a fixed mean and standard deviation.
Class that handles replacing sources with noise during measurement.
MaskT setMaskFromFootprint(lsst::afw::image::Mask< MaskT > *mask, Footprint const &footprint, MaskT const bitmask)
OR bitmask into all the Mask's pixels that are in the Footprint.
Statistics makeStatistics(afwImage::Mask< afwImage::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl)
Specialization to handle Masks.
def getNoiseGenerator
Generate noise image using parameters given.
heavyNoise
FIXME: the heavy footprint includes the mask and variance planes, which we shouldn't need (I don't th...
def insertSource
Insert the heavy footprint of a given source into the exposure.
def removeSource
Remove the heavy footprint of a given source and replace with previous noise.
HeavyFootprint< ImagePixelT, MaskPixelT, VariancePixelT > makeHeavyFootprint(Footprint const &foot, lsst::afw::image::MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > const &img, HeavyFootprintCtrl const *ctrl=NULL)
Generates noise using the afwMath.Random() and afwMath.randomGaussianImage() routines.