LSST Applications  21.0.0-147-g0e635eb1+1acddb5be5,22.0.0+052faf71bd,22.0.0+1ea9a8b2b2,22.0.0+6312710a6c,22.0.0+729191ecac,22.0.0+7589c3a021,22.0.0+9f079a9461,22.0.1-1-g7d6de66+b8044ec9de,22.0.1-1-g87000a6+536b1ee016,22.0.1-1-g8e32f31+6312710a6c,22.0.1-10-gd060f87+016f7cdc03,22.0.1-12-g9c3108e+df145f6f68,22.0.1-16-g314fa6d+c825727ab8,22.0.1-19-g93a5c75+d23f2fb6d8,22.0.1-19-gb93eaa13+aab3ef7709,22.0.1-2-g8ef0a89+b8044ec9de,22.0.1-2-g92698f7+9f079a9461,22.0.1-2-ga9b0f51+052faf71bd,22.0.1-2-gac51dbf+052faf71bd,22.0.1-2-gb66926d+6312710a6c,22.0.1-2-gcb770ba+09e3807989,22.0.1-20-g32debb5+b8044ec9de,22.0.1-23-gc2439a9a+fb0756638e,22.0.1-3-g496fd5d+09117f784f,22.0.1-3-g59f966b+1e6ba2c031,22.0.1-3-g849a1b8+f8b568069f,22.0.1-3-gaaec9c0+c5c846a8b1,22.0.1-32-g5ddfab5d3+60ce4897b0,22.0.1-4-g037fbe1+64e601228d,22.0.1-4-g8623105+b8044ec9de,22.0.1-5-g096abc9+d18c45d440,22.0.1-5-g15c806e+57f5c03693,22.0.1-7-gba73697+57f5c03693,master-g6e05de7fdc+c1283a92b8,master-g72cdda8301+729191ecac,w.2021.39
LSST Data Management Base Package
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.ip.isr as ipIsr
28 import lsst.meas.algorithms as measAlg
29 import lsst.pipe.base as pipeBase
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.useFallbackValueAtEdgeuseFallbackValueAtEdge:
73  if (not self.negativeFallbackAllowednegativeFallbackAllowed and self.fallbackValueTypefallbackValueType == "USER"
74  and self.fallbackUserValuefallbackUserValue < 0.0):
75  raise ValueError("User supplied fallbackValue is negative (%.2f) but "
76  "negativeFallbackAllowed is False" % self.fallbackUserValuefallbackUserValue)
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.warning("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 `~lsst.meas.algorithms.Defects`
124  If both 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 ipIsr.Defects
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  if not isinstance(defects, ipIsr.Defects):
155  defectList = ipIsr.Defects(defects)
156  else:
157  defectList = defects
158  planeName = "defects"
159  else:
160  if defects is not None:
161  raise ValueError("Provide EITHER a planeName OR a list of defects, not both")
162  if planeName not in maskedImage.getMask().getMaskPlaneDict():
163  raise ValueError("maskedImage does not contain mask plane %s" % planeName)
164  defectList = ipIsr.Defects.fromMask(maskedImage, planeName)
165 
166  # set psf from exposure if provided OR using modelPsf with fwhmPixels provided
167  try:
168  psf = image.getPsf()
169  self.log.info("Setting psf for interpolation from image")
170  except AttributeError:
171  self.log.info("Creating psf model for interpolation from fwhm(pixels) = %s",
172  str(fwhmPixels) if fwhmPixels is not None else
173  (str(self.config.modelPsf.defaultFwhm)) + " [default]")
174  psf = self.config.modelPsf.apply(fwhm=fwhmPixels)
175 
176  fallbackValue = 0.0 # interpolateOverDefects needs this to be a float, regardless if it is used
177  if self.config.useFallbackValueAtEdge:
178  fallbackValue = self._setFallbackValue_setFallbackValue(maskedImage)
179 
180  self.interpolateImageinterpolateImage(maskedImage, psf, defectList, fallbackValue)
181 
182  self.log.info("Interpolated over %d %s pixels.", len(defectList), planeName)
183 
184  @contextmanager
185  def transposeContext(self, maskedImage, defects):
186  """Context manager to potentially transpose an image
187 
188  This applies the ``transpose`` configuration setting.
189 
190  Transposing the image allows us to interpolate along columns instead
191  of rows, which is useful when the saturation trails are typically
192  oriented along rows on the warped/coadded images, instead of along
193  columns as they typically are in raw CCD images.
194 
195  Parameters
196  ----------
197  maskedImage : `lsst.afw.image.MaskedImage`
198  Image on which to perform interpolation.
199  defects : `lsst.meas.algorithms.Defects`
200  List of defects to interpolate over.
201 
202  Yields
203  ------
204  useImage : `lsst.afw.image.MaskedImage`
205  Image to use for interpolation; it may have been transposed.
206  useDefects : `lsst.meas.algorithms.Defects`
207  List of defects to use for interpolation; they may have been
208  transposed.
209  """
210  def transposeImage(image):
211  """Transpose an image"""
212  transposed = image.array.T.copy() # Copy to force row-major; required for ndarray+pybind
213  return image.Factory(transposed, False, lsst.geom.Point2I(*reversed(image.getXY0())))
214 
215  useImage = maskedImage
216  useDefects = defects
217  if self.config.transpose:
218  useImage = afwImage.makeMaskedImage(transposeImage(maskedImage.image),
219  transposeImage(maskedImage.mask),
220  transposeImage(maskedImage.variance))
221  useDefects = defects.transpose()
222  yield useImage, useDefects
223  if self.config.transpose:
224  maskedImage.image.array = useImage.image.array.T
225  maskedImage.mask.array = useImage.mask.array.T
226  maskedImage.variance.array = useImage.variance.array.T
227 
228  def interpolateImage(self, maskedImage, psf, defectList, fallbackValue):
229  """Interpolate over defects in an image
230 
231  Parameters
232  ----------
233  maskedImage : `lsst.afw.image.MaskedImage`
234  Image on which to perform interpolation.
235  psf : `lsst.afw.detection.Psf`
236  Point-spread function; currently unused.
237  defectList : `lsst.meas.algorithms.Defects`
238  List of defects to interpolate over.
239  fallbackValue : `float`
240  Value to set when interpolation fails.
241  """
242  if not defectList:
243  return
244  with self.transposeContexttransposeContext(maskedImage, defectList) as (image, defects):
245  measAlg.interpolateOverDefects(image, psf, defects, fallbackValue,
246  self.config.useFallbackValueAtEdge)
int max
def interpolateImage(self, maskedImage, psf, defectList, fallbackValue)
Definition: interpImage.py:228
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 transposeContext(self, maskedImage, defects)
Definition: interpImage.py:185
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > * makeMaskedImage(typename std::shared_ptr< Image< ImagePixelT >> image, typename std::shared_ptr< Mask< MaskPixelT >> mask=Mask< MaskPixelT >(), typename std::shared_ptr< Image< VariancePixelT >> variance=Image< VariancePixelT >())
A function to return a MaskedImage of the correct type (cf.
Definition: MaskedImage.h:1240
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Handle a watered-down front-end to the constructor (no variance)
Definition: Statistics.h:359
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations.