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)
 
   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]))