22 from __future__
import absolute_import, division, print_function
23 import lsst.pex.config
as pexConfig
24 import lsst.afw.math
as afwMath
25 import lsst.meas.algorithms
as measAlg
26 import lsst.pipe.base
as pipeBase
27 import lsst.ip.isr
as ipIsr
29 __all__ = [
"InterpImageConfig",
"InterpImageTask"]
33 """Config for InterpImageTask
35 modelPsf = measAlg.GaussianPsfFactory.makeField(doc=
"Model Psf factory")
37 useFallbackValueAtEdge = pexConfig.Field(
39 doc=
"Smoothly taper to the fallback value at the edge of the image?",
42 fallbackValueType = pexConfig.ChoiceField(
44 doc=
"Type of statistic to calculate edge fallbackValue for interpolation",
48 "MEANCLIP":
"clipped mean",
49 "USER":
"user value set in fallbackUserValue config",
53 fallbackUserValue = pexConfig.Field(
55 doc=
"If fallbackValueType is 'USER' then use this as the fallbackValue; ignored otherwise",
58 negativeFallbackAllowed = pexConfig.Field(
60 doc=(
"Allow negative values for egde interpolation fallbackValue? If False, set "
61 "fallbackValue to max(fallbackValue, 0.0)"),
66 pexConfig.Config.validate(self)
70 raise ValueError(
"User supplied fallbackValue is negative (%.2f) but "
75 """Interpolate over bad image pixels
77 ConfigClass = InterpImageConfig
78 _DefaultName =
"interpImage"
80 def _setFallbackValue(self, mi=None):
81 """Set the edge fallbackValue for interpolation
83 \param[in] mi input maksedImage on which to calculate the statistics
84 Must be provided if fallbackValueType != "USER".
86 \return fallbackValue The value set/computed based on the fallbackValueType
87 and negativeFallbackAllowed config parameters
89 if self.config.fallbackValueType !=
'USER':
90 assert mi,
"No maskedImage provided"
91 if self.config.fallbackValueType ==
'MEAN':
92 fallbackValue = afwMath.makeStatistics(mi, afwMath.MEAN).getValue()
93 elif self.config.fallbackValueType ==
'MEDIAN':
94 fallbackValue = afwMath.makeStatistics(mi, afwMath.MEDIAN).getValue()
95 elif self.config.fallbackValueType ==
'MEANCLIP':
96 fallbackValue = afwMath.makeStatistics(mi, afwMath.MEANCLIP).getValue()
97 elif self.config.fallbackValueType ==
'USER':
98 fallbackValue = self.config.fallbackUserValue
100 raise NotImplementedError(
"%s : %s not implemented" %
101 (
"fallbackValueType", self.config.fallbackValueType))
103 if not self.config.negativeFallbackAllowed
and fallbackValue < 0.0:
104 self.log.warn(
"Negative interpolation edge fallback value computed but "
105 "negativeFallbackAllowed is False: setting fallbackValue to 0.0")
106 fallbackValue = max(fallbackValue, 0.0)
108 self.log.info(
"fallbackValueType %s has been set to %.4f" %
109 (self.config.fallbackValueType, fallbackValue))
114 def run(self, image, planeName=None, fwhmPixels=None, defects=None):
115 """!Interpolate in place over pixels in a maskedImage marked as bad
117 Pixels to be interpolated are set by either a mask planeName provided
118 by the caller OR a defects list of type measAlg.DefectListT. If both
119 are provided an exception is raised.
121 Note that the interpolation code in meas_algorithms currently doesn't
122 use the input PSF (though it's a required argument), so it's not
123 important to set the input PSF parameters exactly. This PSF is set
124 here as the psf attached to the "image" (i.e if the image passed in
125 is an Exposure). Otherwise, a psf model is created using
126 measAlg.GaussianPsfFactory with the value of fwhmPixels (the value
127 passed in by the caller, or the default defaultFwhm set in
128 measAlg.GaussianPsfFactory if None).
130 \param[in,out] image MaskedImage OR Exposure to be interpolated
131 \param[in] planeName name of mask plane over which to interpolate
132 If None, must provide a defects list.
133 \param[in] fwhmPixels FWHM of core star (pixels)
134 If None the default is used, where the default
135 is set to the exposure psf if available
136 \param[in] defects List of defects of type measAlg.DefectListT
137 over which to interpolate.
140 maskedImage = image.getMaskedImage()
141 except AttributeError:
145 if planeName
is None:
147 raise ValueError(
"No defects or plane name provided")
150 planeName =
"defects"
152 if defects
is not None:
153 raise ValueError(
"Provide EITHER a planeName OR a list of defects, not both")
154 if planeName
not in maskedImage.getMask().getMaskPlaneDict():
155 raise ValueError(
"maskedImage does not contain mask plane %s" % planeName)
156 defectList = ipIsr.getDefectListFromMask(maskedImage, planeName, growFootprints=0)
161 self.log.info(
"Setting psf for interpolation from image")
162 except AttributeError:
163 self.log.info(
"Creating psf model for interpolation from fwhm(pixels) = %s" %
164 (str(fwhmPixels)
if fwhmPixels
is not None else
165 (str(self.config.modelPsf.defaultFwhm)) +
" [default]"))
166 psf = self.config.modelPsf.apply(fwhm=fwhmPixels)
169 if self.config.useFallbackValueAtEdge:
172 measAlg.interpolateOverDefects(maskedImage, psf, defectList, fallbackValue,
173 self.config.useFallbackValueAtEdge)
175 self.log.info(
"Interpolated over %d %s pixels." % (len(defectList), planeName))
tuple useFallbackValueAtEdge
def run
Interpolate in place over pixels in a maskedImage marked as bad.
tuple negativeFallbackAllowed