30 noiseSource = lsst.pex.config.ChoiceField(
31 doc=
'How to choose mean and variance of the Gaussian noise we generate?',
34 'measure':
'Measure clipped mean and variance from the whole image',
35 'meta':
'Mean = 0, variance = the "BGMEAN" metadata entry',
36 'variance':
"Mean = 0, variance = the image's variance",
38 default=
'measure', optional=
False
40 noiseOffset = lsst.pex.config.Field(
41 dtype=float, optional=
False, default=0.,
42 doc=
'Add ann offset to the generated noise.'
44 noiseSeedMultiplier = lsst.pex.config.Field(
46 doc=
'The seed multiplier value to use for random number generation. 0 will not set seed.'
51 Class that handles replacing sources with noise during measurement.
53 When measuring a source (or the children associated with a parent source), this class is used
54 to replace its neighbors with noise, using the deblender's definition of the sources as stored
55 in HeavyFootprints attached to the SourceRecords. The algorithm works as follows:
56 - We start by replacing all pixels that are in source Footprints with artificially
57 generated noise (__init__).
58 - When we are about to measure a particular source, we add it back in, by inserting that source's
59 HeavyFootprint (from the deblender) into the image.
60 - When we are done measuring that source, we again replace the HeavyFootprint with (the same)
62 - After measuring all sources, we return the image to its original state.
64 This is a functional copy of the code in the older ReplaceWithNoiseTask, but with a slightly different
65 API needed for the new measurement framework; note that it is not a Task, as the lifetime of a
66 NoiseReplacer now corresponds to a single exposure, not an entire processing run.
69 ConfigClass = NoiseReplacerConfig
71 def __init__(self, config, exposure, footprints, noiseImage=None, exposureId=None, log=None):
73 Initialize the NoiseReplacer.
75 @param[in] config instance of NoiseReplacerConfig
76 @param[in,out] exposure Exposure to be noise replaced. (All sources replaced on return)
77 @param[in] footprints dict of {id: (parent, footprint)};
78 @param[in] noiseImage an afw.image.ImageF used as a predictable noise replacement source
80 @param[in] log pex.logging.Log object to use for status messages; no status messages
81 will be printed if None
83 'footprints' is a dict of {id: (parent, footprint)}; when used in SFM, the ID will be the
84 source ID, but in forced photometry, this will be the reference ID, as that's what we used to
85 determine the deblend families. This routine should create HeavyFootprints for any non-Heavy
86 Footprints, and replace them in the dict. It should then create a dict of HeavyFootprints
87 containing noise, but only for parent objects, then replace all sources with noise.
88 This should ignore any footprints that lay outside the bounding box of the exposure,
89 and clip those that lie on the border.
91 NOTE: as the code currently stands, the heavy footprint for a deblended object must be available
92 from the input catalog. If it is not, it cannot be reproduced here. In that case, the
93 topmost parent in the objects parent chain must be used. The heavy footprint for that source
94 is created in this class from the masked image.
108 mi = exposure.getMaskedImage()
114 for maskname
in [
'THISDET',
'OTHERDET']:
117 plane = mask.getMaskPlane(maskname)
118 if self.
log: self.log.logdebug(
'Mask plane "%s" already existed' % maskname)
121 plane = mask.addMaskPlane(maskname)
122 self.removeplanes.append(maskname)
123 mask.clearMaskPlane(plane)
124 bitmask = mask.getPlaneBitMask(maskname)
125 bitmasks.append(bitmask)
126 if self.
log: self.log.logdebug(
'Mask plane "%s": plane %i, bitmask %i = 0x%x'
127 % (maskname, plane, bitmask, bitmask))
140 for id
in footprints.keys():
143 self.
heavies[id] = afwDet.cast_HeavyFootprintF(fp[1])
156 noisegen = self.
getNoiseGenerator(exposure, noiseImage, noiseMeanVar, exposureId=exposureId)
160 if self.
log: self.log.logdebug(
'Using noise generator: %s' % (str(noisegen)))
161 for id
in self.heavies.keys():
162 fp = footprints[id][1]
163 noiseFp = noisegen.getHeavyFootprint(fp)
174 Insert the heavy footprint of a given source into the exposure
176 @param[in] id id for current source to insert from original footprint dict
178 Also adjusts the mask plane to show the source of this footprint.
181 mi = self.exposure.getMaskedImage()
188 while self.
footprints[usedid][0] != 0
and not usedid
in self.heavies.keys():
197 Remove the heavy footprint of a given source and replace with previous noise
199 @param[in] id id for current source to insert from original footprint dict
201 Also restore the mask plane.
206 mi = self.exposure.getMaskedImage()
213 while self.
footprints[usedid][0] != 0
and not usedid
in self.heavies.keys():
224 End the NoiseReplacer.
226 Restore original data to the exposure from the heavies dictionary
227 Restore the mask planes to their original state
231 mi = self.exposure.getMaskedImage()
234 for id
in self.footprints.keys():
240 mask.removeAndClearMaskPlane(maskname,
True)
250 Generate noise image using parameters given
252 if noiseImage
is not None:
257 if not exposureId
is None and not exposureId == 0:
262 if noiseMeanVar
is not None:
265 noiseMean,noiseVar = noiseMeanVar
266 noiseMean = float(noiseMean)
267 noiseVar = float(noiseVar)
268 noiseStd = math.sqrt(noiseVar)
269 if self.
log: self.log.logdebug(
'Using passed-in noise mean = %g, variance = %g -> stdev %g'
270 % (noiseMean, noiseVar, noiseStd))
273 if self.
log: self.log.logdebug(
'Failed to cast passed-in noiseMeanVar to floats: %s'
274 % (str(noiseMeanVar)))
278 if noiseSource ==
'meta':
280 meta = exposure.getMetadata()
283 bgMean = meta.getAsDouble(
'BGMEAN')
285 noiseStd = math.sqrt(bgMean)
286 if self.
log: self.log.logdebug(
'Using noise variance = (BGMEAN = %g) from exposure metadata'
290 if self.
log: self.log.logdebug(
'Failed to get BGMEAN from exposure metadata')
292 if noiseSource ==
'variance':
293 if self.
log: self.log.logdebug(
'Will draw noise according to the variance plane.')
294 var = exposure.getMaskedImage().getVariance()
298 im = exposure.getMaskedImage().getImage()
300 noiseMean = s.getValue(afwMath.MEANCLIP)
301 noiseStd = s.getValue(afwMath.STDEVCLIP)
302 if self.
log: self.log.logdebug(
"Measured from image: clipped mean = %g, stdev = %g"
303 % (noiseMean,noiseStd))
307 """Syntactic sugar that makes a list of NoiseReplacers (for multiple exposures)
308 behave like a single one.
310 This is only used in the multifit driver, but the logic there is already pretty
311 complex, so it's nice to have this to simplify it.
314 def __init__(self, exposuresById, footprintsByExp):
318 for expId, exposure
in exposuresById.iteritems():
319 self.append(
NoiseReplacer(exposure, footprintsByExp[expId]), expId)
322 """Insert the original pixels for a given source (by id) into the original exposure.
327 """Insert the noise pixels for a given source (by id) into the original exposure.
332 """Cleanup when the use of the Noise replacer is done.
334 for item
in self: self.
end()
338 Base class for noise generators used by the "doReplaceWithNoise" routine:
339 these produce HeavyFootprints filled with noise generated in various ways.
341 This is an abstract base class.
351 return afwImage.MaskedImageF(im)
357 Generates noise by cutting out a subimage from a user-supplied noise Image.
362 img: an afwImage.ImageF
364 self.
mim = afwImage.MaskedImageF(img)
373 Generates noise using the afwMath.Random() and afwMath.randomGaussianImage() routines.
375 This is an abstract base class.
385 rim = afwImage.ImageF(bb.getWidth(), bb.getHeight())
386 rim.setXY0(bb.getMinX(), bb.getMinY())
392 Generates Gaussian noise with a fixed mean and standard deviation.
396 super(FixedGaussianNoiseGenerator, self).
__init__(rand=rand)
401 return 'FixedGaussianNoiseGenerator: mean=%g, std=%g' % (self.
mean, self.
std)
411 Generates Gaussian noise whose variance matches that of the variance plane of the image.
416 var: an afwImage.ImageF; the variance plane.
417 mean: floating-point or afwImage.Image
419 super(VariancePlaneNoiseGenerator, self).
__init__(rand=rand)
421 if mean
is not None and mean == 0.:
426 return 'VariancePlaneNoiseGenerator: mean=' + str(self.
mean)
431 stdev = afwImage.ImageF(self.
var, bb, afwImage.LOCAL,
True)
434 if self.
mean is not None:
441 A do-nothing standin for NoiseReplacer, used when we want to disable NoiseReplacer
443 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.