22 from contextlib
import contextmanager
25 from lsst.pex.config
import Config, Field, ListField, ConfigurableField
29 __all__ = [
"ScaleVarianceConfig",
"ScaleVarianceTask"]
33 background = ConfigurableField(target=SubtractBackgroundTask, doc=
"Background subtraction")
34 maskPlanes = ListField(
36 default=[
"DETECTED",
"DETECTED_NEGATIVE",
"BAD",
"SAT",
"NO_DATA",
"INTRP"],
37 doc=
"Mask planes for pixels to ignore when scaling variance",
39 limit = Field(dtype=float, default=10.0, doc=
"Maximum variance scaling value to permit")
44 self.
background.undersampleStyle =
"REDUCE_INTERP_ORDER" 45 self.
background.ignoredPixelMask = [
"DETECTED",
"DETECTED_NEGATIVE",
"BAD",
"SAT",
"NO_DATA",
"INTRP"]
49 """Scale the variance in a MaskedImage 51 The variance plane in a convolved or warped image (or a coadd derived 52 from warped images) does not accurately reflect the noise properties of 53 the image because variance has been lost to covariance. This Task 54 attempts to correct for this by scaling the variance plane to match 55 the observed variance in the image. This is not perfect (because we're 56 not tracking the covariance) but it's simple and is often good enough. 58 ConfigClass = ScaleVarianceConfig
59 _DefaultName =
"scaleVariance" 62 Task.__init__(self, *args, **kwargs)
63 self.makeSubtask(
"background")
67 """Context manager for subtracting the background 69 We need to subtract the background so that the entire image 70 (apart from objects, which should be clipped) will have the 71 image/sqrt(variance) distributed about zero. 73 This context manager subtracts the background, and ensures it 78 maskedImage : `lsst.afw.image.MaskedImage` 79 Image+mask+variance to have background subtracted and restored. 83 context : context manager 84 Context manager that ensure the background is restored. 86 bg = self.background.fitBackground(maskedImage)
87 bgImage = bg.getImageF(self.background.config.algorithm, self.background.config.undersampleStyle)
88 maskedImage -= bgImage
92 maskedImage += bgImage
94 def run(self, maskedImage):
95 """Rescale the variance in a maskedImage 99 maskedImage : `lsst.afw.image.MaskedImage` 100 Image for which to determine the variance rescaling factor. 105 Variance rescaling factor. 110 If the estimated variance rescaling factor exceeds the 115 if np.isnan(factor)
or factor > self.config.limit:
116 self.log.warn(
"Pixel-based variance rescaling factor (%f) exceeds configured limit (%f); " 117 "trying image-based method", factor, self.config.limit)
119 if np.isnan(factor)
or factor > self.config.limit:
120 raise RuntimeError(
"Variance rescaling factor (%f) exceeds configured limit (%f)" %
121 (factor, self.config.limit))
122 self.log.info(
"Renormalizing variance by %f" % (factor,))
123 maskedImage.variance *= factor
127 """Determine the variance rescaling factor from pixel statistics 129 We calculate SNR = image/sqrt(variance), and the distribution 130 for most of the background-subtracted image should have a standard 131 deviation of unity. The variance rescaling factor is the factor that 132 brings that distribution to have unit standard deviation. 134 This may not work well if the image has a lot of structure in it, as 135 the assumptions are violated. In that case, use an alternate 140 maskedImage : `lsst.afw.image.MaskedImage` 141 Image for which to determine the variance rescaling factor. 146 Variance rescaling factor. 148 variance = maskedImage.variance
149 snr = maskedImage.image.array/np.sqrt(variance.array)
150 maskVal = maskedImage.mask.getPlaneBitMask(self.config.maskPlanes)
151 isGood = ((maskedImage.mask.array & maskVal) == 0) & (maskedImage.variance.array > 0)
154 q1, q3 = np.percentile(snr[isGood], (25, 75))
161 stdev = 0.74*(q3 - q1)
165 """Determine the variance rescaling factor from image statistics 167 We calculate average(SNR) = stdev(image)/median(variance), and 168 the value should be unity. The variance rescaling factor is the 169 factor that brings this value to unity. 171 This may not work well if the pixels from which we measure the 172 standard deviation of the image are not effectively the same pixels 173 from which we measure the median of the variance. In that case, use 178 maskedImage : `lsst.afw.image.MaskedImage` 179 Image for which to determine the variance rescaling factor. 184 Variance rescaling factor. 186 maskVal = maskedImage.mask.getPlaneBitMask(self.config.maskPlanes)
187 isGood = ((maskedImage.mask.array & maskVal) == 0) & (maskedImage.variance.array > 0)
189 q1, q3 = np.percentile(maskedImage.image.array[isGood], (25, 75))
190 ratio = 0.74*(q3 - q1)/np.sqrt(np.median(maskedImage.variance.array[isGood]))
def run(self, maskedImage)
def __init__(self, args, kwargs)
def subtractedBackground(self, maskedImage)
def pixelBased(self, maskedImage)
def imageBased(self, maskedImage)