lsst.ip.isr  16.0-14-g6c7ed55+17
isrFunctions.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010 LSST Corporation.
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 <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 import math
23 
24 import numpy
25 
26 import lsst.afw.geom as afwGeom
27 import lsst.afw.image as afwImage
28 import lsst.afw.detection as afwDetection
29 import lsst.afw.math as afwMath
30 import lsst.meas.algorithms as measAlg
31 import lsst.pex.exceptions as pexExcept
32 import lsst.afw.cameraGeom as camGeom
33 
34 from lsst.pipe.base import Struct
35 
36 
37 def createPsf(fwhm):
38  """Make a double Gaussian PSF
39 
40  @param[in] fwhm FWHM of double Gaussian smoothing kernel
41  @return measAlg.DoubleGaussianPsf
42  """
43  ksize = 4*int(fwhm) + 1
44  return measAlg.DoubleGaussianPsf(ksize, ksize, fwhm/(2*math.sqrt(2*math.log(2))))
45 
46 
47 def transposeMaskedImage(maskedImage):
48  """Make a transposed copy of a masked image
49 
50  @param[in] maskedImage afw.image.MaskedImage to process
51  @return transposed masked image
52  """
53  transposed = maskedImage.Factory(afwGeom.Extent2I(maskedImage.getHeight(), maskedImage.getWidth()))
54  transposed.getImage().getArray()[:] = maskedImage.getImage().getArray().T
55  transposed.getMask().getArray()[:] = maskedImage.getMask().getArray().T
56  transposed.getVariance().getArray()[:] = maskedImage.getVariance().getArray().T
57  return transposed
58 
59 
60 def interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=None):
61  """Interpolate over defects specified in a defect list
62 
63  @param[in,out] maskedImage masked image to process
64  @param[in] defectList defect list
65  @param[in] fwhm FWHM of double Gaussian smoothing kernel
66  @param[in] fallbackValue fallback value if an interpolated value cannot be determined;
67  if None then use clipped mean image value
68  """
69  psf = createPsf(fwhm)
70  if fallbackValue is None:
71  fallbackValue = afwMath.makeStatistics(maskedImage.getImage(), afwMath.MEANCLIP).getValue()
72  if 'INTRP' not in maskedImage.getMask().getMaskPlaneDict():
73  maskedImage.getMask.addMaskPlane('INTRP')
74  measAlg.interpolateOverDefects(maskedImage, psf, defectList, fallbackValue, True)
75 
76 
78  """Compute a defect list from a footprint list, optionally growing the footprints
79 
80  @param[in] fpList footprint list
81  """
82  defectList = []
83  for fp in fpList:
84  for bbox in afwDetection.footprintToBBoxList(fp):
85  defect = measAlg.Defect(bbox)
86  defectList.append(defect)
87  return defectList
88 
89 
90 def transposeDefectList(defectList):
91  """Make a transposed copy of a defect list
92 
93  @param[in] defectList a list of defects (afw.meas.algorithms.Defect)
94  @return a defect list with transposed defects
95  """
96  retDefectList = []
97  for defect in defectList:
98  bbox = defect.getBBox()
99  nbbox = afwGeom.Box2I(afwGeom.Point2I(bbox.getMinY(), bbox.getMinX()),
100  afwGeom.Extent2I(bbox.getDimensions()[1], bbox.getDimensions()[0]))
101  retDefectList.append(measAlg.Defect(nbbox))
102  return retDefectList
103 
104 
105 def maskPixelsFromDefectList(maskedImage, defectList, maskName='BAD'):
106  """Set mask plane based on a defect list
107 
108  @param[in,out] maskedImage afw.image.MaskedImage to process; mask plane is updated
109  @param[in] defectList a list of defects (afw.meas.algorithms.Defect)
110  @param[in] maskName mask plane name
111  """
112  # mask bad pixels
113  mask = maskedImage.getMask()
114  bitmask = mask.getPlaneBitMask(maskName)
115  for defect in defectList:
116  bbox = defect.getBBox()
117  afwGeom.SpanSet(bbox).clippedTo(mask.getBBox()).setMask(mask, bitmask)
118 
119 
120 def getDefectListFromMask(maskedImage, maskName):
121  """Compute a defect list from a specified mask plane
122 
123  @param[in] maskedImage masked image to process
124  @param[in] maskName mask plane name, or list of names
125  """
126  mask = maskedImage.getMask()
127  thresh = afwDetection.Threshold(mask.getPlaneBitMask(maskName), afwDetection.Threshold.BITMASK)
128  fpList = afwDetection.FootprintSet(mask, thresh).getFootprints()
129  return defectListFromFootprintList(fpList)
130 
131 
132 def makeThresholdMask(maskedImage, threshold, growFootprints=1, maskName='SAT'):
133  """Mask pixels based on threshold detection
134 
135  @param[in,out] maskedImage afw.image.MaskedImage to process; the mask is altered
136  @param[in] threshold detection threshold
137  @param[in] growFootprints amount by which to grow footprints of detected regions
138  @param[in] maskName mask plane name
139  @return a list of defects (meas.algrithms.Defect) of regions set in the mask.
140  """
141  # find saturated regions
142  thresh = afwDetection.Threshold(threshold)
143  fs = afwDetection.FootprintSet(maskedImage, thresh)
144 
145  if growFootprints > 0:
146  fs = afwDetection.FootprintSet(fs, growFootprints)
147 
148  fpList = fs.getFootprints()
149  # set mask
150  mask = maskedImage.getMask()
151  bitmask = mask.getPlaneBitMask(maskName)
152  afwDetection.setMaskFromFootprintList(mask, fpList, bitmask)
153 
154  return defectListFromFootprintList(fpList)
155 
156 
157 def interpolateFromMask(maskedImage, fwhm, growFootprints=1, maskName='SAT', fallbackValue=None):
158  """Interpolate over defects identified by a particular mask plane
159 
160  @param[in,out] maskedImage afw.image.MaskedImage to process
161  @param[in] fwhm FWHM of double Gaussian smoothing kernel
162  @param[in] growFootprints amount by which to grow footprints of detected regions
163  @param[in] maskName mask plane name
164  @param[in] fallbackValue value of last resort for interpolation
165  """
166  mask = maskedImage.getMask()
167  thresh = afwDetection.Threshold(mask.getPlaneBitMask(maskName), afwDetection.Threshold.BITMASK)
168  fpSet = afwDetection.FootprintSet(mask, thresh)
169  if growFootprints > 0:
170  fpSet = afwDetection.FootprintSet(fpSet, rGrow=growFootprints, isotropic=False)
171  # If we are interpolating over an area larger than the original masked region, we need
172  # to expand the original mask bit to the full area to explain why we interpolated there.
173  fpSet.setMask(mask, maskName)
174  defectList = defectListFromFootprintList(fpSet.getFootprints())
175  interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=fallbackValue)
176 
177 
178 def saturationCorrection(maskedImage, saturation, fwhm, growFootprints=1, interpolate=True, maskName='SAT',
179  fallbackValue=None):
180  """Mark saturated pixels and optionally interpolate over them
181 
182  @param[in,out] maskedImage afw.image.MaskedImage to process
183  @param[in] saturation saturation level (used as a detection threshold)
184  @param[in] fwhm FWHM of double Gaussian smoothing kernel
185  @param[in] growFootprints amount by which to grow footprints of detected regions
186  @param[in] interpolate interpolate over saturated pixels?
187  @param[in] maskName mask plane name
188  @param[in] fallbackValue value of last resort for interpolation
189  """
190  defectList = makeThresholdMask(
191  maskedImage=maskedImage,
192  threshold=saturation,
193  growFootprints=growFootprints,
194  maskName=maskName,
195  )
196  if interpolate:
197  interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=fallbackValue)
198 
199 
200 def biasCorrection(maskedImage, biasMaskedImage):
201  """Apply bias correction in place
202 
203  @param[in,out] maskedImage masked image to correct
204  @param[in] biasMaskedImage bias, as a masked image
205  """
206  if maskedImage.getBBox(afwImage.LOCAL) != biasMaskedImage.getBBox(afwImage.LOCAL):
207  raise RuntimeError("maskedImage bbox %s != biasMaskedImage bbox %s" %
208  (maskedImage.getBBox(afwImage.LOCAL), biasMaskedImage.getBBox(afwImage.LOCAL)))
209  maskedImage -= biasMaskedImage
210 
211 
212 def darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False):
213  """Apply dark correction in place
214 
215  maskedImage -= dark * expScaling / darkScaling
216 
217  @param[in,out] maskedImage afw.image.MaskedImage to correct
218  @param[in] darkMaskedImage dark afw.image.MaskedImage
219  @param[in] expScale exposure scale
220  @param[in] darkScale dark scale
221  @param[in] invert if True, remove the dark from an already-corrected image
222  """
223  if maskedImage.getBBox(afwImage.LOCAL) != darkMaskedImage.getBBox(afwImage.LOCAL):
224  raise RuntimeError("maskedImage bbox %s != darkMaskedImage bbox %s" %
225  (maskedImage.getBBox(afwImage.LOCAL), darkMaskedImage.getBBox(afwImage.LOCAL)))
226 
227  scale = expScale / darkScale
228  if not invert:
229  maskedImage.scaledMinus(scale, darkMaskedImage)
230  else:
231  maskedImage.scaledPlus(scale, darkMaskedImage)
232 
233 
234 def updateVariance(maskedImage, gain, readNoise):
235  """Set the variance plane based on the image plane
236 
237  @param[in,out] maskedImage afw.image.MaskedImage; image plane is read and variance plane is written
238  @param[in] gain amplifier gain (e-/ADU)
239  @param[in] readNoise amplifier read noise (ADU/pixel)
240  """
241  var = maskedImage.getVariance()
242  var[:] = maskedImage.getImage()
243  var /= gain
244  var += readNoise**2
245 
246 
247 def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False):
248  """Apply flat correction in place
249 
250  @param[in,out] maskedImage afw.image.MaskedImage to correct
251  @param[in] flatMaskedImage flat field afw.image.MaskedImage
252  @param[in] scalingType how to compute flat scale; one of 'MEAN', 'MEDIAN' or 'USER'
253  @param[in] userScale scale to use if scalingType is 'USER', else ignored
254  @param[in] invert if True, unflatten an already-flattened image instead.
255  """
256  if maskedImage.getBBox(afwImage.LOCAL) != flatMaskedImage.getBBox(afwImage.LOCAL):
257  raise RuntimeError("maskedImage bbox %s != flatMaskedImage bbox %s" %
258  (maskedImage.getBBox(afwImage.LOCAL), flatMaskedImage.getBBox(afwImage.LOCAL)))
259 
260  # Figure out scale from the data
261  # Ideally the flats are normalized by the calibration product pipeline, but this allows some flexibility
262  # in the case that the flat is created by some other mechanism.
263  if scalingType in ('MEAN', 'MEDIAN'):
264  scalingType = afwMath.stringToStatisticsProperty(scalingType)
265  flatScale = afwMath.makeStatistics(flatMaskedImage.image, scalingType).getValue()
266  elif scalingType == 'USER':
267  flatScale = userScale
268  else:
269  raise pexExcept.Exception('%s : %s not implemented' % ("flatCorrection", scalingType))
270 
271  if not invert:
272  maskedImage.scaledDivides(1.0/flatScale, flatMaskedImage)
273  else:
274  maskedImage.scaledMultiplies(1.0/flatScale, flatMaskedImage)
275 
276 
277 def illuminationCorrection(maskedImage, illumMaskedImage, illumScale):
278  """Apply illumination correction in place
279 
280  @param[in,out] maskedImage afw.image.MaskedImage to correct
281  @param[in] illumMaskedImage illumination correction masked image
282  @param[in] illumScale scale value for illumination correction
283  """
284  if maskedImage.getBBox(afwImage.LOCAL) != illumMaskedImage.getBBox(afwImage.LOCAL):
285  raise RuntimeError("maskedImage bbox %s != illumMaskedImage bbox %s" %
286  (maskedImage.getBBox(afwImage.LOCAL), illumMaskedImage.getBBox(afwImage.LOCAL)))
287 
288  maskedImage.scaledDivides(1./illumScale, illumMaskedImage)
289 
290 
291 def overscanCorrection(ampMaskedImage, overscanImage, fitType='MEDIAN', order=1, statControl=None,
292  overscanIsInt=True):
293  """Apply overscan correction in-place
294 
295  The ``ampMaskedImage`` and ``overscanImage`` are modified, with the fit
296  subtracted. Note that the ``overscanImage`` should not be a subimage of
297  the ``ampMaskedImage``, to avoid being subtracted twice.
298 
299  Parameters
300  ----------
301  ampMaskedImage : `lsst.afw.image.MaskedImage`
302  Image of amplifier to correct; modified.
303  overscanImage : `lsst.afw.image.Image` or `lsst.afw.image.MaskedImage`
304  Image of overscan; modified.
305  fitType : `str`
306  Type of fit for overscan correction. May be one of:
307 
308  - ``MEAN``: use mean of overscan.
309  - ``MEANCLIP``: use clipped mean of overscan.
310  - ``MEDIAN``: use median of overscan.
311  - ``POLY``: fit with ordinary polynomial.
312  - ``CHEB``: fit with Chebyshev polynomial.
313  - ``LEG``: fit with Legendre polynomial.
314  - ``NATURAL_SPLINE``: fit with natural spline.
315  - ``CUBIC_SPLINE``: fit with cubic spline.
316  - ``AKIMA_SPLINE``: fit with Akima spline.
317 
318  order : `int`
319  Polynomial order or number of spline knots; ignored unless
320  ``fitType`` indicates a polynomial or spline.
321  statControl : `lsst.afw.math.StatisticsControl`
322  Statistics control object. In particular, we pay attention to numSigmaClip
323  overscanIsInt : `bool`
324  Treat the overscan region as consisting of integers, even if it's been
325  converted to float. E.g. handle ties properly
326 
327  Returns
328  -------
329  result : `lsst.pipe.base.Struct`
330  Result struct with components:
331 
332  - ``imageFit``: Value(s) removed from image (scalar or
333  `lsst.afw.image.Image`)
334  - ``overscanFit``: Value(s) removed from overscan (scalar or
335  `lsst.afw.image.Image`)
336  """
337  ampImage = ampMaskedImage.getImage()
338  if statControl is None:
339  statControl = afwMath.StatisticsControl()
340 
341  numSigmaClip = statControl.getNumSigmaClip()
342 
343  if fitType in ('MEAN', 'MEANCLIP'):
344  fitType = afwMath.stringToStatisticsProperty(fitType)
345  offImage = afwMath.makeStatistics(overscanImage, fitType, statControl).getValue()
346  overscanFit = offImage
347  elif fitType in ('MEDIAN',):
348  if overscanIsInt:
349  # we need an image with integer pixels to handle ties properly
350  if hasattr(overscanImage, "image"):
351  imageI = overscanImage.image.convertI()
352  overscanImageI = afwImage.MaskedImageI(imageI, overscanImage.mask, overscanImage.variance)
353  else:
354  overscanImageI = overscanImage.convertI()
355  else:
356  overscanImageI = overscanImage
357 
358  fitType = afwMath.stringToStatisticsProperty(fitType)
359  offImage = afwMath.makeStatistics(overscanImageI, fitType, statControl).getValue()
360  overscanFit = offImage
361 
362  if overscanIsInt:
363  del overscanImageI
364  elif fitType in ('POLY', 'CHEB', 'LEG', 'NATURAL_SPLINE', 'CUBIC_SPLINE', 'AKIMA_SPLINE'):
365  if hasattr(overscanImage, "getImage"):
366  biasArray = overscanImage.getImage().getArray()
367  biasArray = numpy.ma.masked_where(overscanImage.getMask().getArray() & statControl.getAndMask(),
368  biasArray)
369  else:
370  biasArray = overscanImage.getArray()
371  # Fit along the long axis, so collapse along each short row and fit the resulting array
372  shortInd = numpy.argmin(biasArray.shape)
373  if shortInd == 0:
374  # Convert to some 'standard' representation to make things easier
375  biasArray = numpy.transpose(biasArray)
376 
377  # Do a single round of clipping to weed out CR hits and signal leaking into the overscan
378  percentiles = numpy.percentile(biasArray, [25.0, 50.0, 75.0], axis=1)
379  medianBiasArr = percentiles[1]
380  stdevBiasArr = 0.74*(percentiles[2] - percentiles[0]) # robust stdev
381  diff = numpy.abs(biasArray - medianBiasArr[:, numpy.newaxis])
382  biasMaskedArr = numpy.ma.masked_where(diff > numSigmaClip*stdevBiasArr[:, numpy.newaxis], biasArray)
383  collapsed = numpy.mean(biasMaskedArr, axis=1)
384  if collapsed.mask.sum() > 0:
385  collapsed.data[collapsed.mask] = numpy.mean(biasArray.data[collapsed.mask], axis=1)
386  del biasArray, percentiles, stdevBiasArr, diff, biasMaskedArr
387 
388  if shortInd == 0:
389  collapsed = numpy.transpose(collapsed)
390 
391  num = len(collapsed)
392  indices = 2.0*numpy.arange(num)/float(num) - 1.0
393 
394  if fitType in ('POLY', 'CHEB', 'LEG'):
395  # A numpy polynomial
396  poly = numpy.polynomial
397  fitter, evaler = {"POLY": (poly.polynomial.polyfit, poly.polynomial.polyval),
398  "CHEB": (poly.chebyshev.chebfit, poly.chebyshev.chebval),
399  "LEG": (poly.legendre.legfit, poly.legendre.legval),
400  }[fitType]
401 
402  coeffs = fitter(indices, collapsed, order)
403  fitBiasArr = evaler(indices, coeffs)
404  elif 'SPLINE' in fitType:
405  # An afw interpolation
406  numBins = order
407  #
408  # numpy.histogram needs a real array for the mask, but numpy.ma "optimises" the case
409  # no-values-are-masked by replacing the mask array by a scalar, numpy.ma.nomask
410  #
411  # Issue DM-415
412  #
413  collapsedMask = collapsed.mask
414  try:
415  if collapsedMask == numpy.ma.nomask:
416  collapsedMask = numpy.array(len(collapsed)*[numpy.ma.nomask])
417  except ValueError: # If collapsedMask is an array the test fails [needs .all()]
418  pass
419 
420  numPerBin, binEdges = numpy.histogram(indices, bins=numBins,
421  weights=1-collapsedMask.astype(int))
422  # Binning is just a histogram, with weights equal to the values.
423  # Use a similar trick to get the bin centers (this deals with different numbers per bin).
424  with numpy.errstate(invalid="ignore"): # suppress NAN warnings
425  values = numpy.histogram(indices, bins=numBins,
426  weights=collapsed.data*~collapsedMask)[0]/numPerBin
427  binCenters = numpy.histogram(indices, bins=numBins,
428  weights=indices*~collapsedMask)[0]/numPerBin
429  interp = afwMath.makeInterpolate(binCenters.astype(float)[numPerBin > 0],
430  values.astype(float)[numPerBin > 0],
431  afwMath.stringToInterpStyle(fitType))
432  fitBiasArr = numpy.array([interp.interpolate(i) for i in indices])
433 
434  import lsstDebug
435  if lsstDebug.Info(__name__).display:
436  import matplotlib.pyplot as plot
437  figure = plot.figure(1)
438  figure.clear()
439  axes = figure.add_axes((0.1, 0.1, 0.8, 0.8))
440  axes.plot(indices[~collapsedMask], collapsed[~collapsedMask], 'k+')
441  if collapsedMask.sum() > 0:
442  axes.plot(indices[collapsedMask], collapsed.data[collapsedMask], 'b+')
443  axes.plot(indices, fitBiasArr, 'r-')
444  figure.show()
445  prompt = "Press Enter or c to continue [chp]... "
446  while True:
447  ans = input(prompt).lower()
448  if ans in ("", "c",):
449  break
450  if ans in ("p",):
451  import pdb
452  pdb.set_trace()
453  elif ans in ("h", ):
454  print("h[elp] c[ontinue] p[db]")
455  figure.close()
456 
457  offImage = ampImage.Factory(ampImage.getDimensions())
458  offArray = offImage.getArray()
459  overscanFit = afwImage.ImageF(overscanImage.getDimensions())
460  overscanArray = overscanFit.getArray()
461  if shortInd == 1:
462  offArray[:, :] = fitBiasArr[:, numpy.newaxis]
463  overscanArray[:, :] = fitBiasArr[:, numpy.newaxis]
464  else:
465  offArray[:, :] = fitBiasArr[numpy.newaxis, :]
466  overscanArray[:, :] = fitBiasArr[numpy.newaxis, :]
467 
468  # We don't trust any extrapolation: mask those pixels as SUSPECT
469  # This will occur when the top and or bottom edges of the overscan
470  # contain saturated values. The values will be extrapolated from
471  # the surrounding pixels, but we cannot entirely trust the value of
472  # the extrapolation, and will mark the image mask plane to flag the
473  # image as such.
474  mask = ampMaskedImage.getMask()
475  maskArray = mask.getArray() if shortInd == 1 else mask.getArray().transpose()
476  suspect = mask.getPlaneBitMask("SUSPECT")
477  try:
478  if collapsed.mask == numpy.ma.nomask:
479  # There is no mask, so the whole array is fine
480  pass
481  except ValueError: # If collapsed.mask is an array the test fails [needs .all()]
482  for low in range(num):
483  if not collapsed.mask[low]:
484  break
485  if low > 0:
486  maskArray[:low, :] |= suspect
487  for high in range(1, num):
488  if not collapsed.mask[-high]:
489  break
490  if high > 1:
491  maskArray[-high:, :] |= suspect
492 
493  else:
494  raise pexExcept.Exception('%s : %s an invalid overscan type' % ("overscanCorrection", fitType))
495  ampImage -= offImage
496  overscanImage -= overscanFit
497  return Struct(imageFit=offImage, overscanFit=overscanFit)
498 
499 
500 def attachTransmissionCurve(exposure, opticsTransmission=None, filterTransmission=None,
501  sensorTransmission=None, atmosphereTransmission=None):
502  """Attach a TransmissionCurve to an Exposure, given separate curves for
503  different components.
504 
505  Parameters
506  ----------
507  exposure : `lsst.afw.image.Exposure`
508  Exposure object to modify by attaching the product of all given
509  ``TransmissionCurves`` in post-assembly trimmed detector coordinates.
510  Must have a valid ``Detector`` attached that matches the detector
511  associated with sensorTransmission.
512  opticsTransmission : `lsst.afw.image.TransmissionCurve`
513  A ``TransmissionCurve`` that represents the throughput of the optics,
514  to be evaluated in focal-plane coordinates.
515  filterTransmission : `lsst.afw.image.TransmissionCurve`
516  A ``TransmissionCurve`` that represents the throughput of the filter
517  itself, to be evaluated in focal-plane coordinates.
518  sensorTransmission : `lsst.afw.image.TransmissionCurve`
519  A ``TransmissionCurve`` that represents the throughput of the sensor
520  itself, to be evaluated in post-assembly trimmed detector coordinates.
521  atmosphereTransmission : `lsst.afw.image.TransmissionCurve`
522  A ``TransmissionCurve`` that represents the throughput of the
523  atmosphere, assumed to be spatially constant.
524 
525  All ``TransmissionCurve`` arguments are optional; if none are provided, the
526  attached ``TransmissionCurve`` will have unit transmission everywhere.
527 
528  Returns
529  -------
530  combined : ``lsst.afw.image.TransmissionCurve``
531  The TransmissionCurve attached to the exposure.
532  """
533  combined = afwImage.TransmissionCurve.makeIdentity()
534  if atmosphereTransmission is not None:
535  combined *= atmosphereTransmission
536  if opticsTransmission is not None:
537  combined *= opticsTransmission
538  if filterTransmission is not None:
539  combined *= filterTransmission
540  detector = exposure.getDetector()
541  fpToPix = detector.getTransform(fromSys=camGeom.FOCAL_PLANE,
542  toSys=camGeom.PIXELS)
543  combined = combined.transformedBy(fpToPix)
544  if sensorTransmission is not None:
545  combined *= sensorTransmission
546  exposure.getInfo().setTransmissionCurve(combined)
547  return combined
def darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False)
def illuminationCorrection(maskedImage, illumMaskedImage, illumScale)
def saturationCorrection(maskedImage, saturation, fwhm, growFootprints=1, interpolate=True, maskName='SAT', fallbackValue=None)
def transposeDefectList(defectList)
Definition: isrFunctions.py:90
def getDefectListFromMask(maskedImage, maskName)
def interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=None)
Definition: isrFunctions.py:60
def defectListFromFootprintList(fpList)
Definition: isrFunctions.py:77
def transposeMaskedImage(maskedImage)
Definition: isrFunctions.py:47
def biasCorrection(maskedImage, biasMaskedImage)
def interpolateFromMask(maskedImage, fwhm, growFootprints=1, maskName='SAT', fallbackValue=None)
def attachTransmissionCurve(exposure, opticsTransmission=None, filterTransmission=None, sensorTransmission=None, atmosphereTransmission=None)
def overscanCorrection(ampMaskedImage, overscanImage, fitType='MEDIAN', order=1, statControl=None, overscanIsInt=True)
def makeThresholdMask(maskedImage, threshold, growFootprints=1, maskName='SAT')
def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False)
def updateVariance(maskedImage, gain, readNoise)
def maskPixelsFromDefectList(maskedImage, defectList, maskName='BAD')