lsst.pipe.tasks  17.0-5-gf0ac6446
interpImage.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008-2015 AURA/LSST.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <https://www.lsstcorp.org/LegalNotices/>.
21 #
22 from contextlib import contextmanager
23 import lsst.pex.config as pexConfig
24 import lsst.geom
25 import lsst.afw.image as afwImage
26 import lsst.afw.math as afwMath
27 import lsst.meas.algorithms as measAlg
28 import lsst.pipe.base as pipeBase
29 import lsst.ip.isr as ipIsr
30 
31 __all__ = ["InterpImageConfig", "InterpImageTask"]
32 
33 
34 class InterpImageConfig(pexConfig.Config):
35  """Config for InterpImageTask
36  """
37  modelPsf = measAlg.GaussianPsfFactory.makeField(doc="Model Psf factory")
38 
39  useFallbackValueAtEdge = pexConfig.Field(
40  dtype=bool,
41  doc="Smoothly taper to the fallback value at the edge of the image?",
42  default=True,
43  )
44  fallbackValueType = pexConfig.ChoiceField(
45  dtype=str,
46  doc="Type of statistic to calculate edge fallbackValue for interpolation",
47  allowed={
48  "MEAN": "mean",
49  "MEDIAN": "median",
50  "MEANCLIP": "clipped mean",
51  "USER": "user value set in fallbackUserValue config",
52  },
53  default="MEDIAN",
54  )
55  fallbackUserValue = pexConfig.Field(
56  dtype=float,
57  doc="If fallbackValueType is 'USER' then use this as the fallbackValue; ignored otherwise",
58  default=0.0,
59  )
60  negativeFallbackAllowed = pexConfig.Field(
61  dtype=bool,
62  doc=("Allow negative values for egde interpolation fallbackValue? If False, set "
63  "fallbackValue to max(fallbackValue, 0.0)"),
64  default=False,
65  )
66  transpose = pexConfig.Field(dtype=int, default=False,
67  doc="Transpose image before interpolating? "
68  "This allows the interpolation to act over columns instead of rows.")
69 
70  def validate(self):
71  pexConfig.Config.validate(self)
72  if self.useFallbackValueAtEdge:
73  if (not self.negativeFallbackAllowed and self.fallbackValueType == "USER" and
74  self.fallbackUserValue < 0.0):
75  raise ValueError("User supplied fallbackValue is negative (%.2f) but "
76  "negativeFallbackAllowed is False" % self.fallbackUserValue)
77 
78 
79 class InterpImageTask(pipeBase.Task):
80  """Interpolate over bad image pixels
81  """
82  ConfigClass = InterpImageConfig
83  _DefaultName = "interpImage"
84 
85  def _setFallbackValue(self, mi=None):
86  """Set the edge fallbackValue for interpolation
87 
88  @param[in] mi input maksedImage on which to calculate the statistics
89  Must be provided if fallbackValueType != "USER".
90 
91  @return fallbackValue The value set/computed based on the fallbackValueType
92  and negativeFallbackAllowed config parameters
93  """
94  if self.config.fallbackValueType != 'USER':
95  assert mi, "No maskedImage provided"
96  if self.config.fallbackValueType == 'MEAN':
97  fallbackValue = afwMath.makeStatistics(mi, afwMath.MEAN).getValue()
98  elif self.config.fallbackValueType == 'MEDIAN':
99  fallbackValue = afwMath.makeStatistics(mi, afwMath.MEDIAN).getValue()
100  elif self.config.fallbackValueType == 'MEANCLIP':
101  fallbackValue = afwMath.makeStatistics(mi, afwMath.MEANCLIP).getValue()
102  elif self.config.fallbackValueType == 'USER':
103  fallbackValue = self.config.fallbackUserValue
104  else:
105  raise NotImplementedError("%s : %s not implemented" %
106  ("fallbackValueType", self.config.fallbackValueType))
107 
108  if not self.config.negativeFallbackAllowed and fallbackValue < 0.0:
109  self.log.warn("Negative interpolation edge fallback value computed but "
110  "negativeFallbackAllowed is False: setting fallbackValue to 0.0")
111  fallbackValue = max(fallbackValue, 0.0)
112 
113  self.log.info("fallbackValueType %s has been set to %.4f" %
114  (self.config.fallbackValueType, fallbackValue))
115 
116  return fallbackValue
117 
118  @pipeBase.timeMethod
119  def run(self, image, planeName=None, fwhmPixels=None, defects=None):
120  """!Interpolate in place over pixels in a maskedImage marked as bad
121 
122  Pixels to be interpolated are set by either a mask planeName provided
123  by the caller OR a defects list of type measAlg.DefectListT. If both
124  are provided an exception is raised.
125 
126  Note that the interpolation code in meas_algorithms currently doesn't
127  use the input PSF (though it's a required argument), so it's not
128  important to set the input PSF parameters exactly. This PSF is set
129  here as the psf attached to the "image" (i.e if the image passed in
130  is an Exposure). Otherwise, a psf model is created using
131  measAlg.GaussianPsfFactory with the value of fwhmPixels (the value
132  passed in by the caller, or the default defaultFwhm set in
133  measAlg.GaussianPsfFactory if None).
134 
135  @param[in,out] image MaskedImage OR Exposure to be interpolated
136  @param[in] planeName name of mask plane over which to interpolate
137  If None, must provide a defects list.
138  @param[in] fwhmPixels FWHM of core star (pixels)
139  If None the default is used, where the default
140  is set to the exposure psf if available
141  @param[in] defects List of defects of type measAlg.DefectListT
142  over which to interpolate.
143  """
144  try:
145  maskedImage = image.getMaskedImage()
146  except AttributeError:
147  maskedImage = image
148 
149  # set defectList from defects OR mask planeName provided
150  if planeName is None:
151  if defects is None:
152  raise ValueError("No defects or plane name provided")
153  else:
154  defectList = defects
155  planeName = "defects"
156  else:
157  if defects is not None:
158  raise ValueError("Provide EITHER a planeName OR a list of defects, not both")
159  if planeName not in maskedImage.getMask().getMaskPlaneDict():
160  raise ValueError("maskedImage does not contain mask plane %s" % planeName)
161  defectList = ipIsr.getDefectListFromMask(maskedImage, planeName)
162 
163  # set psf from exposure if provided OR using modelPsf with fwhmPixels provided
164  try:
165  psf = image.getPsf()
166  self.log.info("Setting psf for interpolation from image")
167  except AttributeError:
168  self.log.info("Creating psf model for interpolation from fwhm(pixels) = %s" %
169  (str(fwhmPixels) if fwhmPixels is not None else
170  (str(self.config.modelPsf.defaultFwhm)) + " [default]"))
171  psf = self.config.modelPsf.apply(fwhm=fwhmPixels)
172 
173  fallbackValue = 0.0 # interpolateOverDefects needs this to be a float, regardless if it is used
174  if self.config.useFallbackValueAtEdge:
175  fallbackValue = self._setFallbackValue(maskedImage)
176 
177  self.interpolateImage(maskedImage, psf, defectList, fallbackValue)
178 
179  self.log.info("Interpolated over %d %s pixels." % (len(defectList), planeName))
180 
181  @contextmanager
182  def transposeContext(self, maskedImage, defects):
183  """Context manager to potentially transpose an image
184 
185  This applies the ``transpose`` configuration setting.
186 
187  Transposing the image allows us to interpolate along columns instead
188  of rows, which is useful when the saturation trails are typically
189  oriented along rows on the warped/coadded images, instead of along
190  columns as they typically are in raw CCD images.
191 
192  Parameters
193  ----------
194  maskedImage : `lsst.afw.image.MaskedImage`
195  Image on which to perform interpolation.
196  defects : iterable of `lsst.afw.image.Defect`
197  List of defects to interpolate over.
198 
199  Yields
200  ------
201  useImage : `lsst.afw.image.MaskedImage`
202  Image to use for interpolation; it may have been transposed.
203  useDefects : iterable of `lsst.afw.image.Defect`
204  List of defects to use for interpolation; they may have been
205  transposed.
206  """
207  def transposeImage(image):
208  """Transpose an image"""
209  transposed = image.array.T.copy() # Copy to force row-major; required for ndarray+pybind
210  return image.Factory(transposed, False, lsst.geom.Point2I(*reversed(image.getXY0())))
211 
212  useImage = maskedImage
213  useDefects = defects
214  if self.config.transpose:
215  useImage = afwImage.makeMaskedImage(transposeImage(maskedImage.image),
216  transposeImage(maskedImage.mask),
217  transposeImage(maskedImage.variance))
218  useDefects = ipIsr.transposeDefectList(defects)
219  yield useImage, useDefects
220  if self.config.transpose:
221  maskedImage.image.array = useImage.image.array.T
222  maskedImage.mask.array = useImage.mask.array.T
223  maskedImage.variance.array = useImage.variance.array.T
224 
225  def interpolateImage(self, maskedImage, psf, defectList, fallbackValue):
226  """Interpolate over defects in an image
227 
228  Parameters
229  ----------
230  maskedImage : `lsst.afw.image.MaskedImage`
231  Image on which to perform interpolation.
232  psf : `lsst.afw.detection.Psf`
233  Point-spread function; currently unused.
234  defectList : iterable of `lsst.afw.image.Defect`
235  List of defects to interpolate over.
236  fallbackValue : `float`
237  Value to set when interpolation fails.
238  """
239  if not defectList:
240  return
241  with self.transposeContext(maskedImage, defectList) as (image, defects):
242  measAlg.interpolateOverDefects(image, psf, defects, fallbackValue,
243  self.config.useFallbackValueAtEdge)
def transposeContext(self, maskedImage, defects)
Definition: interpImage.py:182
def run(self, image, planeName=None, fwhmPixels=None, defects=None)
Interpolate in place over pixels in a maskedImage marked as bad.
Definition: interpImage.py:119
def interpolateImage(self, maskedImage, psf, defectList, fallbackValue)
Definition: interpImage.py:225