LSSTApplications  17.0+124,17.0+14,17.0+73,18.0.0+37,18.0.0+80,18.0.0-4-g68ffd23+4,18.1.0-1-g0001055+12,18.1.0-1-g03d53ef+5,18.1.0-1-g1349e88+55,18.1.0-1-g2505f39+44,18.1.0-1-g5315e5e+4,18.1.0-1-g5e4b7ea+14,18.1.0-1-g7e8fceb+4,18.1.0-1-g85f8cd4+48,18.1.0-1-g8ff0b9f+4,18.1.0-1-ga2c679d+1,18.1.0-1-gd55f500+35,18.1.0-10-gb58edde+2,18.1.0-11-g0997b02+4,18.1.0-13-gfe4edf0b+12,18.1.0-14-g259bd21+21,18.1.0-19-gdb69f3f+2,18.1.0-2-g5f9922c+24,18.1.0-2-gd3b74e5+11,18.1.0-2-gfbf3545+32,18.1.0-26-g728bddb4+5,18.1.0-27-g6ff7ca9+2,18.1.0-3-g52aa583+25,18.1.0-3-g8ea57af+9,18.1.0-3-gb69f684+42,18.1.0-3-gfcaddf3+6,18.1.0-32-gd8786685a,18.1.0-4-gf3f9b77+6,18.1.0-5-g1dd662b+2,18.1.0-5-g6dbcb01+41,18.1.0-6-gae77429+3,18.1.0-7-g9d75d83+9,18.1.0-7-gae09a6d+30,18.1.0-9-gc381ef5+4,w.2019.45
LSSTDataManagementBasePackage
Functions
lsst.ip.isr.isrFunctions Namespace Reference

Functions

def createPsf (fwhm)
 
def transposeMaskedImage (maskedImage)
 
def interpolateDefectList (maskedImage, defectList, fwhm, fallbackValue=None)
 
def makeThresholdMask (maskedImage, threshold, growFootprints=1, maskName='SAT')
 
def interpolateFromMask (maskedImage, fwhm, growSaturatedFootprints=1, maskNameList=['SAT'], fallbackValue=None)
 
def saturationCorrection (maskedImage, saturation, fwhm, growFootprints=1, interpolate=True, maskName='SAT', fallbackValue=None)
 
def trimToMatchCalibBBox (rawMaskedImage, calibMaskedImage)
 
def biasCorrection (maskedImage, biasMaskedImage, trimToFit=False)
 
def darkCorrection (maskedImage, darkMaskedImage, expScale, darkScale, invert=False, trimToFit=False)
 
def updateVariance (maskedImage, gain, readNoise)
 
def flatCorrection (maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False, trimToFit=False)
 
def illuminationCorrection (maskedImage, illumMaskedImage, illumScale, trimToFit=True)
 
def overscanCorrection (ampMaskedImage, overscanImage, fitType='MEDIAN', order=1, collapseRej=3.0, statControl=None, overscanIsInt=True)
 
def brighterFatterCorrection (exposure, kernel, maxIter, threshold, applyGain)
 
def gainContext (exp, image, apply)
 
def attachTransmissionCurve (exposure, opticsTransmission=None, filterTransmission=None, sensorTransmission=None, atmosphereTransmission=None)
 
def addDistortionModel (exposure, camera)
 Update the WCS in exposure with a distortion model based on camera geometry. More...
 
def applyGains (exposure, normalizeGains=False)
 
def widenSaturationTrails (mask)
 
def setBadRegions (exposure, badStatistic="MEDIAN")
 

Function Documentation

◆ addDistortionModel()

def lsst.ip.isr.isrFunctions.addDistortionModel (   exposure,
  camera 
)

Update the WCS in exposure with a distortion model based on camera geometry.

Parameters

exposure : lsst.afw.image.Exposure Exposure to process. Must contain a Detector and WCS. The exposure is modified. camera : lsst.afw.cameraGeom.Camera Camera geometry.

Raises

RuntimeError Raised if exposure is lacking a Detector or WCS, or if camera is None.

Notes

Add a model for optical distortion based on geometry found in camera and the exposure's detector. The raw input exposure is assumed have a TAN WCS that has no compensation for optical distortion. Two other possibilities are:

  • The raw input exposure already has a model for optical distortion, as is the case for raw DECam data. In that case you should set config.doAddDistortionModel False.
  • The raw input exposure has a model for distortion, but it has known deficiencies severe enough to be worth fixing (e.g. because they cause problems for fitting a better WCS). In that case you should override this method with a version suitable for your raw data.

Definition at line 857 of file isrFunctions.py.

857 def addDistortionModel(exposure, camera):
858  """!Update the WCS in exposure with a distortion model based on camera
859  geometry.
860 
861  Parameters
862  ----------
863  exposure : `lsst.afw.image.Exposure`
864  Exposure to process. Must contain a Detector and WCS. The
865  exposure is modified.
866  camera : `lsst.afw.cameraGeom.Camera`
867  Camera geometry.
868 
869  Raises
870  ------
871  RuntimeError
872  Raised if ``exposure`` is lacking a Detector or WCS, or if
873  ``camera`` is None.
874  Notes
875  -----
876  Add a model for optical distortion based on geometry found in ``camera``
877  and the ``exposure``'s detector. The raw input exposure is assumed
878  have a TAN WCS that has no compensation for optical distortion.
879  Two other possibilities are:
880  - The raw input exposure already has a model for optical distortion,
881  as is the case for raw DECam data.
882  In that case you should set config.doAddDistortionModel False.
883  - The raw input exposure has a model for distortion, but it has known
884  deficiencies severe enough to be worth fixing (e.g. because they
885  cause problems for fitting a better WCS). In that case you should
886  override this method with a version suitable for your raw data.
887 
888  """
889  wcs = exposure.getWcs()
890  if wcs is None:
891  raise RuntimeError("exposure has no WCS")
892  if camera is None:
893  raise RuntimeError("camera is None")
894  detector = exposure.getDetector()
895  if detector is None:
896  raise RuntimeError("exposure has no Detector")
897  pixelToFocalPlane = detector.getTransform(camGeom.PIXELS, camGeom.FOCAL_PLANE)
898  focalPlaneToFieldAngle = camera.getTransformMap().getTransform(camGeom.FOCAL_PLANE,
899  camGeom.FIELD_ANGLE)
900  distortedWcs = makeDistortedTanWcs(wcs, pixelToFocalPlane, focalPlaneToFieldAngle)
901  exposure.setWcs(distortedWcs)
902 
903 
def addDistortionModel(exposure, camera)
Update the WCS in exposure with a distortion model based on camera geometry.
def makeDistortedTanWcs(tanWcs, pixelToFocalPlane, focalPlaneToFieldAngle)

◆ applyGains()

def lsst.ip.isr.isrFunctions.applyGains (   exposure,
  normalizeGains = False 
)
Scale an exposure by the amplifier gains.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to process.  The image is modified.
normalizeGains : `Bool`, optional
    If True, then amplifiers are scaled to force the median of
    each amplifier to equal the median of those medians.

Definition at line 904 of file isrFunctions.py.

904 def applyGains(exposure, normalizeGains=False):
905  """Scale an exposure by the amplifier gains.
906 
907  Parameters
908  ----------
909  exposure : `lsst.afw.image.Exposure`
910  Exposure to process. The image is modified.
911  normalizeGains : `Bool`, optional
912  If True, then amplifiers are scaled to force the median of
913  each amplifier to equal the median of those medians.
914  """
915  ccd = exposure.getDetector()
916  ccdImage = exposure.getMaskedImage()
917 
918  medians = []
919  for amp in ccd:
920  sim = ccdImage.Factory(ccdImage, amp.getBBox())
921  sim *= amp.getGain()
922 
923  if normalizeGains:
924  medians.append(numpy.median(sim.getImage().getArray()))
925 
926  if normalizeGains:
927  median = numpy.median(numpy.array(medians))
928  for index, amp in enumerate(ccd):
929  sim = ccdImage.Factory(ccdImage, amp.getBBox())
930  if medians[index] != 0.0:
931  sim *= median/medians[index]
932 
933 
def applyGains(exposure, normalizeGains=False)

◆ attachTransmissionCurve()

def lsst.ip.isr.isrFunctions.attachTransmissionCurve (   exposure,
  opticsTransmission = None,
  filterTransmission = None,
  sensorTransmission = None,
  atmosphereTransmission = None 
)
Attach a TransmissionCurve to an Exposure, given separate curves for
different components.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure object to modify by attaching the product of all given
    ``TransmissionCurves`` in post-assembly trimmed detector coordinates.
    Must have a valid ``Detector`` attached that matches the detector
    associated with sensorTransmission.
opticsTransmission : `lsst.afw.image.TransmissionCurve`
    A ``TransmissionCurve`` that represents the throughput of the optics,
    to be evaluated in focal-plane coordinates.
filterTransmission : `lsst.afw.image.TransmissionCurve`
    A ``TransmissionCurve`` that represents the throughput of the filter
    itself, to be evaluated in focal-plane coordinates.
sensorTransmission : `lsst.afw.image.TransmissionCurve`
    A ``TransmissionCurve`` that represents the throughput of the sensor
    itself, to be evaluated in post-assembly trimmed detector coordinates.
atmosphereTransmission : `lsst.afw.image.TransmissionCurve`
    A ``TransmissionCurve`` that represents the throughput of the
    atmosphere, assumed to be spatially constant.

Returns
-------
combined : `lsst.afw.image.TransmissionCurve`
    The TransmissionCurve attached to the exposure.

Notes
-----
All ``TransmissionCurve`` arguments are optional; if none are provided, the
attached ``TransmissionCurve`` will have unit transmission everywhere.

Definition at line 804 of file isrFunctions.py.

804  sensorTransmission=None, atmosphereTransmission=None):
805  """Attach a TransmissionCurve to an Exposure, given separate curves for
806  different components.
807 
808  Parameters
809  ----------
810  exposure : `lsst.afw.image.Exposure`
811  Exposure object to modify by attaching the product of all given
812  ``TransmissionCurves`` in post-assembly trimmed detector coordinates.
813  Must have a valid ``Detector`` attached that matches the detector
814  associated with sensorTransmission.
815  opticsTransmission : `lsst.afw.image.TransmissionCurve`
816  A ``TransmissionCurve`` that represents the throughput of the optics,
817  to be evaluated in focal-plane coordinates.
818  filterTransmission : `lsst.afw.image.TransmissionCurve`
819  A ``TransmissionCurve`` that represents the throughput of the filter
820  itself, to be evaluated in focal-plane coordinates.
821  sensorTransmission : `lsst.afw.image.TransmissionCurve`
822  A ``TransmissionCurve`` that represents the throughput of the sensor
823  itself, to be evaluated in post-assembly trimmed detector coordinates.
824  atmosphereTransmission : `lsst.afw.image.TransmissionCurve`
825  A ``TransmissionCurve`` that represents the throughput of the
826  atmosphere, assumed to be spatially constant.
827 
828  Returns
829  -------
830  combined : `lsst.afw.image.TransmissionCurve`
831  The TransmissionCurve attached to the exposure.
832 
833  Notes
834  -----
835  All ``TransmissionCurve`` arguments are optional; if none are provided, the
836  attached ``TransmissionCurve`` will have unit transmission everywhere.
837  """
838  combined = afwImage.TransmissionCurve.makeIdentity()
839  if atmosphereTransmission is not None:
840  combined *= atmosphereTransmission
841  if opticsTransmission is not None:
842  combined *= opticsTransmission
843  if filterTransmission is not None:
844  combined *= filterTransmission
845  detector = exposure.getDetector()
846  fpToPix = detector.getTransform(fromSys=camGeom.FOCAL_PLANE,
847  toSys=camGeom.PIXELS)
848  combined = combined.transformedBy(fpToPix)
849  if sensorTransmission is not None:
850  combined *= sensorTransmission
851  exposure.getInfo().setTransmissionCurve(combined)
852  return combined
853 
854 
855 @deprecated(reason="Camera geometry-based SkyWcs are now set when reading raws. To be removed after v19.",
856  category=FutureWarning)

◆ biasCorrection()

def lsst.ip.isr.isrFunctions.biasCorrection (   maskedImage,
  biasMaskedImage,
  trimToFit = False 
)
Apply bias correction in place.

Parameters
----------
maskedImage : `lsst.afw.image.MaskedImage`
   Image to process.  The image is modified by this method.
biasMaskedImage : `lsst.afw.image.MaskedImage`
    Bias image of the same size as ``maskedImage``
trimToFit : `Bool`, optional
    If True, raw data is symmetrically trimmed to match
    calibration size.

Raises
------
RuntimeError
    Raised if ``maskedImage`` and ``biasMaskedImage`` do not have
    the same size.

Definition at line 253 of file isrFunctions.py.

253 def biasCorrection(maskedImage, biasMaskedImage, trimToFit=False):
254  """Apply bias correction in place.
255 
256  Parameters
257  ----------
258  maskedImage : `lsst.afw.image.MaskedImage`
259  Image to process. The image is modified by this method.
260  biasMaskedImage : `lsst.afw.image.MaskedImage`
261  Bias image of the same size as ``maskedImage``
262  trimToFit : `Bool`, optional
263  If True, raw data is symmetrically trimmed to match
264  calibration size.
265 
266  Raises
267  ------
268  RuntimeError
269  Raised if ``maskedImage`` and ``biasMaskedImage`` do not have
270  the same size.
271 
272  """
273  if trimToFit:
274  maskedImage = trimToMatchCalibBBox(maskedImage, biasMaskedImage)
275 
276  if maskedImage.getBBox(afwImage.LOCAL) != biasMaskedImage.getBBox(afwImage.LOCAL):
277  raise RuntimeError("maskedImage bbox %s != biasMaskedImage bbox %s" %
278  (maskedImage.getBBox(afwImage.LOCAL), biasMaskedImage.getBBox(afwImage.LOCAL)))
279  maskedImage -= biasMaskedImage
280 
281 
def trimToMatchCalibBBox(rawMaskedImage, calibMaskedImage)
def biasCorrection(maskedImage, biasMaskedImage, trimToFit=False)

◆ brighterFatterCorrection()

def lsst.ip.isr.isrFunctions.brighterFatterCorrection (   exposure,
  kernel,
  maxIter,
  threshold,
  applyGain 
)
Apply brighter fatter correction in place for the image.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to have brighter-fatter correction applied.  Modified
    by this method.
kernel : `numpy.ndarray`
    Brighter-fatter kernel to apply.
maxIter : scalar
    Number of correction iterations to run.
threshold : scalar
    Convergence threshold in terms of the sum of absolute
    deviations between an iteration and the previous one.
applyGain : `Bool`
    If True, then the exposure values are scaled by the gain prior
    to correction.

Returns
-------
diff : `float`
    Final difference between iterations achieved in correction.
iteration : `int`
    Number of iterations used to calculate correction.

Notes
-----
This correction takes a kernel that has been derived from flat
field images to redistribute the charge.  The gradient of the
kernel is the deflection field due to the accumulated charge.

Given the original image I(x) and the kernel K(x) we can compute
the corrected image Ic(x) using the following equation:

Ic(x) = I(x) + 0.5*d/dx(I(x)*d/dx(int( dy*K(x-y)*I(y))))

To evaluate the derivative term we expand it as follows:

0.5 * ( d/dx(I(x))*d/dx(int(dy*K(x-y)*I(y))) + I(x)*d^2/dx^2(int(dy* K(x-y)*I(y))) )

Because we use the measured counts instead of the incident counts
we apply the correction iteratively to reconstruct the original
counts and the correction.  We stop iterating when the summed
difference between the current corrected image and the one from
the previous iteration is below the threshold.  We do not require
convergence because the number of iterations is too large a
computational cost.  How we define the threshold still needs to be
evaluated, the current default was shown to work reasonably well
on a small set of images.  For more information on the method see
DocuShare Document-19407.

The edges as defined by the kernel are not corrected because they
have spurious values due to the convolution.

Definition at line 650 of file isrFunctions.py.

650 def brighterFatterCorrection(exposure, kernel, maxIter, threshold, applyGain):
651  """Apply brighter fatter correction in place for the image.
652 
653  Parameters
654  ----------
655  exposure : `lsst.afw.image.Exposure`
656  Exposure to have brighter-fatter correction applied. Modified
657  by this method.
658  kernel : `numpy.ndarray`
659  Brighter-fatter kernel to apply.
660  maxIter : scalar
661  Number of correction iterations to run.
662  threshold : scalar
663  Convergence threshold in terms of the sum of absolute
664  deviations between an iteration and the previous one.
665  applyGain : `Bool`
666  If True, then the exposure values are scaled by the gain prior
667  to correction.
668 
669  Returns
670  -------
671  diff : `float`
672  Final difference between iterations achieved in correction.
673  iteration : `int`
674  Number of iterations used to calculate correction.
675 
676  Notes
677  -----
678  This correction takes a kernel that has been derived from flat
679  field images to redistribute the charge. The gradient of the
680  kernel is the deflection field due to the accumulated charge.
681 
682  Given the original image I(x) and the kernel K(x) we can compute
683  the corrected image Ic(x) using the following equation:
684 
685  Ic(x) = I(x) + 0.5*d/dx(I(x)*d/dx(int( dy*K(x-y)*I(y))))
686 
687  To evaluate the derivative term we expand it as follows:
688 
689  0.5 * ( d/dx(I(x))*d/dx(int(dy*K(x-y)*I(y))) + I(x)*d^2/dx^2(int(dy* K(x-y)*I(y))) )
690 
691  Because we use the measured counts instead of the incident counts
692  we apply the correction iteratively to reconstruct the original
693  counts and the correction. We stop iterating when the summed
694  difference between the current corrected image and the one from
695  the previous iteration is below the threshold. We do not require
696  convergence because the number of iterations is too large a
697  computational cost. How we define the threshold still needs to be
698  evaluated, the current default was shown to work reasonably well
699  on a small set of images. For more information on the method see
700  DocuShare Document-19407.
701 
702  The edges as defined by the kernel are not corrected because they
703  have spurious values due to the convolution.
704  """
705  image = exposure.getMaskedImage().getImage()
706 
707  # The image needs to be units of electrons/holes
708  with gainContext(exposure, image, applyGain):
709 
710  kLx = numpy.shape(kernel)[0]
711  kLy = numpy.shape(kernel)[1]
712  kernelImage = afwImage.ImageD(kLx, kLy)
713  kernelImage.getArray()[:, :] = kernel
714  tempImage = image.clone()
715 
716  nanIndex = numpy.isnan(tempImage.getArray())
717  tempImage.getArray()[nanIndex] = 0.
718 
719  outImage = afwImage.ImageF(image.getDimensions())
720  corr = numpy.zeros_like(image.getArray())
721  prev_image = numpy.zeros_like(image.getArray())
722  convCntrl = afwMath.ConvolutionControl(False, True, 1)
723  fixedKernel = afwMath.FixedKernel(kernelImage)
724 
725  # Define boundary by convolution region. The region that the correction will be
726  # calculated for is one fewer in each dimension because of the second derivative terms.
727  # NOTE: these need to use integer math, as we're using start:end as numpy index ranges.
728  startX = kLx//2
729  endX = -kLx//2
730  startY = kLy//2
731  endY = -kLy//2
732 
733  for iteration in range(maxIter):
734 
735  afwMath.convolve(outImage, tempImage, fixedKernel, convCntrl)
736  tmpArray = tempImage.getArray()
737  outArray = outImage.getArray()
738 
739  with numpy.errstate(invalid="ignore", over="ignore"):
740  # First derivative term
741  gradTmp = numpy.gradient(tmpArray[startY:endY, startX:endX])
742  gradOut = numpy.gradient(outArray[startY:endY, startX:endX])
743  first = (gradTmp[0]*gradOut[0] + gradTmp[1]*gradOut[1])[1:-1, 1:-1]
744 
745  # Second derivative term
746  diffOut20 = numpy.diff(outArray, 2, 0)[startY:endY, startX + 1:endX - 1]
747  diffOut21 = numpy.diff(outArray, 2, 1)[startY + 1:endY - 1, startX:endX]
748  second = tmpArray[startY + 1:endY - 1, startX + 1:endX - 1]*(diffOut20 + diffOut21)
749 
750  corr[startY + 1:endY - 1, startX + 1:endX - 1] = 0.5*(first + second)
751 
752  tmpArray[:, :] = image.getArray()[:, :]
753  tmpArray[nanIndex] = 0.
754  tmpArray[startY:endY, startX:endX] += corr[startY:endY, startX:endX]
755 
756  if iteration > 0:
757  diff = numpy.sum(numpy.abs(prev_image - tmpArray))
758 
759  if diff < threshold:
760  break
761  prev_image[:, :] = tmpArray[:, :]
762 
763  image.getArray()[startY + 1:endY - 1, startX + 1:endX - 1] += \
764  corr[startY + 1:endY - 1, startX + 1:endX - 1]
765 
766  return diff, iteration
767 
768 
769 @contextmanager
def brighterFatterCorrection(exposure, kernel, maxIter, threshold, applyGain)
Parameters to control convolution.
Definition: ConvolveImage.h:50
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, bool doNormalize, bool doCopyEdge=false)
Old, deprecated version of convolve.
def gainContext(exp, image, apply)
A kernel created from an Image.
Definition: Kernel.h:506

◆ createPsf()

def lsst.ip.isr.isrFunctions.createPsf (   fwhm)
Make a double Gaussian PSF.

Parameters
----------
fwhm : scalar
    FWHM of double Gaussian smoothing kernel.

Returns
-------
psf : `lsst.meas.algorithms.DoubleGaussianPsf`
    The created smoothing kernel.

Definition at line 41 of file isrFunctions.py.

41 def createPsf(fwhm):
42  """Make a double Gaussian PSF.
43 
44  Parameters
45  ----------
46  fwhm : scalar
47  FWHM of double Gaussian smoothing kernel.
48 
49  Returns
50  -------
51  psf : `lsst.meas.algorithms.DoubleGaussianPsf`
52  The created smoothing kernel.
53  """
54  ksize = 4*int(fwhm) + 1
55  return measAlg.DoubleGaussianPsf(ksize, ksize, fwhm/(2*math.sqrt(2*math.log(2))))
56 
57 

◆ darkCorrection()

def lsst.ip.isr.isrFunctions.darkCorrection (   maskedImage,
  darkMaskedImage,
  expScale,
  darkScale,
  invert = False,
  trimToFit = False 
)
Apply dark correction in place.

Parameters
----------
maskedImage : `lsst.afw.image.MaskedImage`
   Image to process.  The image is modified by this method.
darkMaskedImage : `lsst.afw.image.MaskedImage`
    Dark image of the same size as ``maskedImage``.
expScale : scalar
    Dark exposure time for ``maskedImage``.
darkScale : scalar
    Dark exposure time for ``darkMaskedImage``.
invert : `Bool`, optional
    If True, re-add the dark to an already corrected image.
trimToFit : `Bool`, optional
    If True, raw data is symmetrically trimmed to match
    calibration size.

Raises
------
RuntimeError
    Raised if ``maskedImage`` and ``darkMaskedImage`` do not have
    the same size.

Notes
-----
The dark correction is applied by calculating:
    maskedImage -= dark * expScaling / darkScaling

Definition at line 282 of file isrFunctions.py.

282 def darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False, trimToFit=False):
283  """Apply dark correction in place.
284 
285  Parameters
286  ----------
287  maskedImage : `lsst.afw.image.MaskedImage`
288  Image to process. The image is modified by this method.
289  darkMaskedImage : `lsst.afw.image.MaskedImage`
290  Dark image of the same size as ``maskedImage``.
291  expScale : scalar
292  Dark exposure time for ``maskedImage``.
293  darkScale : scalar
294  Dark exposure time for ``darkMaskedImage``.
295  invert : `Bool`, optional
296  If True, re-add the dark to an already corrected image.
297  trimToFit : `Bool`, optional
298  If True, raw data is symmetrically trimmed to match
299  calibration size.
300 
301  Raises
302  ------
303  RuntimeError
304  Raised if ``maskedImage`` and ``darkMaskedImage`` do not have
305  the same size.
306 
307  Notes
308  -----
309  The dark correction is applied by calculating:
310  maskedImage -= dark * expScaling / darkScaling
311  """
312  if trimToFit:
313  maskedImage = trimToMatchCalibBBox(maskedImage, darkMaskedImage)
314 
315  if maskedImage.getBBox(afwImage.LOCAL) != darkMaskedImage.getBBox(afwImage.LOCAL):
316  raise RuntimeError("maskedImage bbox %s != darkMaskedImage bbox %s" %
317  (maskedImage.getBBox(afwImage.LOCAL), darkMaskedImage.getBBox(afwImage.LOCAL)))
318 
319  scale = expScale / darkScale
320  if not invert:
321  maskedImage.scaledMinus(scale, darkMaskedImage)
322  else:
323  maskedImage.scaledPlus(scale, darkMaskedImage)
324 
325 
def trimToMatchCalibBBox(rawMaskedImage, calibMaskedImage)
def darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False, trimToFit=False)

◆ flatCorrection()

def lsst.ip.isr.isrFunctions.flatCorrection (   maskedImage,
  flatMaskedImage,
  scalingType,
  userScale = 1.0,
  invert = False,
  trimToFit = False 
)
Apply flat correction in place.

Parameters
----------
maskedImage : `lsst.afw.image.MaskedImage`
    Image to process.  The image is modified.
flatMaskedImage : `lsst.afw.image.MaskedImage`
    Flat image of the same size as ``maskedImage``
scalingType : str
    Flat scale computation method.  Allowed values are 'MEAN',
    'MEDIAN', or 'USER'.
userScale : scalar, optional
    Scale to use if ``scalingType``='USER'.
invert : `Bool`, optional
    If True, unflatten an already flattened image.
trimToFit : `Bool`, optional
    If True, raw data is symmetrically trimmed to match
    calibration size.

Raises
------
RuntimeError
    Raised if ``maskedImage`` and ``flatMaskedImage`` do not have
    the same size or if ``scalingType`` is not an allowed value.

Definition at line 344 of file isrFunctions.py.

344 def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False, trimToFit=False):
345  """Apply flat correction in place.
346 
347  Parameters
348  ----------
349  maskedImage : `lsst.afw.image.MaskedImage`
350  Image to process. The image is modified.
351  flatMaskedImage : `lsst.afw.image.MaskedImage`
352  Flat image of the same size as ``maskedImage``
353  scalingType : str
354  Flat scale computation method. Allowed values are 'MEAN',
355  'MEDIAN', or 'USER'.
356  userScale : scalar, optional
357  Scale to use if ``scalingType``='USER'.
358  invert : `Bool`, optional
359  If True, unflatten an already flattened image.
360  trimToFit : `Bool`, optional
361  If True, raw data is symmetrically trimmed to match
362  calibration size.
363 
364  Raises
365  ------
366  RuntimeError
367  Raised if ``maskedImage`` and ``flatMaskedImage`` do not have
368  the same size or if ``scalingType`` is not an allowed value.
369  """
370  if trimToFit:
371  maskedImage = trimToMatchCalibBBox(maskedImage, flatMaskedImage)
372 
373  if maskedImage.getBBox(afwImage.LOCAL) != flatMaskedImage.getBBox(afwImage.LOCAL):
374  raise RuntimeError("maskedImage bbox %s != flatMaskedImage bbox %s" %
375  (maskedImage.getBBox(afwImage.LOCAL), flatMaskedImage.getBBox(afwImage.LOCAL)))
376 
377  # Figure out scale from the data
378  # Ideally the flats are normalized by the calibration product pipeline, but this allows some flexibility
379  # in the case that the flat is created by some other mechanism.
380  if scalingType in ('MEAN', 'MEDIAN'):
381  scalingType = afwMath.stringToStatisticsProperty(scalingType)
382  flatScale = afwMath.makeStatistics(flatMaskedImage.image, scalingType).getValue()
383  elif scalingType == 'USER':
384  flatScale = userScale
385  else:
386  raise RuntimeError('%s : %s not implemented' % ("flatCorrection", scalingType))
387 
388  if not invert:
389  maskedImage.scaledDivides(1.0/flatScale, flatMaskedImage)
390  else:
391  maskedImage.scaledMultiplies(1.0/flatScale, flatMaskedImage)
392 
393 
def trimToMatchCalibBBox(rawMaskedImage, calibMaskedImage)
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
The makeStatistics() overload to handle lsst::afw::math::MaskedVector<>
Definition: Statistics.h:520
Property stringToStatisticsProperty(std::string const property)
Conversion function to switch a string to a Property (see Statistics.h)
Definition: Statistics.cc:747
def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False, trimToFit=False)

◆ gainContext()

def lsst.ip.isr.isrFunctions.gainContext (   exp,
  image,
  apply 
)
Context manager that applies and removes gain.

Parameters
----------
exp : `lsst.afw.image.Exposure`
    Exposure to apply/remove gain.
image : `lsst.afw.image.Image`
    Image to apply/remove gain.
apply : `Bool`
    If True, apply and remove the amplifier gain.

Yields
------
exp : `lsst.afw.image.Exposure`
    Exposure with the gain applied.

Definition at line 770 of file isrFunctions.py.

770 def gainContext(exp, image, apply):
771  """Context manager that applies and removes gain.
772 
773  Parameters
774  ----------
775  exp : `lsst.afw.image.Exposure`
776  Exposure to apply/remove gain.
777  image : `lsst.afw.image.Image`
778  Image to apply/remove gain.
779  apply : `Bool`
780  If True, apply and remove the amplifier gain.
781 
782  Yields
783  ------
784  exp : `lsst.afw.image.Exposure`
785  Exposure with the gain applied.
786  """
787  if apply:
788  ccd = exp.getDetector()
789  for amp in ccd:
790  sim = image.Factory(image, amp.getBBox())
791  sim *= amp.getGain()
792 
793  try:
794  yield exp
795  finally:
796  if apply:
797  ccd = exp.getDetector()
798  for amp in ccd:
799  sim = image.Factory(image, amp.getBBox())
800  sim /= amp.getGain()
801 
802 
def gainContext(exp, image, apply)

◆ illuminationCorrection()

def lsst.ip.isr.isrFunctions.illuminationCorrection (   maskedImage,
  illumMaskedImage,
  illumScale,
  trimToFit = True 
)
Apply illumination correction in place.

Parameters
----------
maskedImage : `lsst.afw.image.MaskedImage`
    Image to process.  The image is modified.
illumMaskedImage : `lsst.afw.image.MaskedImage`
    Illumination correction image of the same size as ``maskedImage``.
illumScale : scalar
    Scale factor for the illumination correction.
trimToFit : `Bool`, optional
    If True, raw data is symmetrically trimmed to match
    calibration size.

Raises
------
RuntimeError
    Raised if ``maskedImage`` and ``illumMaskedImage`` do not have
    the same size.

Definition at line 394 of file isrFunctions.py.

394 def illuminationCorrection(maskedImage, illumMaskedImage, illumScale, trimToFit=True):
395  """Apply illumination correction in place.
396 
397  Parameters
398  ----------
399  maskedImage : `lsst.afw.image.MaskedImage`
400  Image to process. The image is modified.
401  illumMaskedImage : `lsst.afw.image.MaskedImage`
402  Illumination correction image of the same size as ``maskedImage``.
403  illumScale : scalar
404  Scale factor for the illumination correction.
405  trimToFit : `Bool`, optional
406  If True, raw data is symmetrically trimmed to match
407  calibration size.
408 
409  Raises
410  ------
411  RuntimeError
412  Raised if ``maskedImage`` and ``illumMaskedImage`` do not have
413  the same size.
414  """
415  if trimToFit:
416  maskedImage = trimToMatchCalibBBox(maskedImage, illumMaskedImage)
417 
418  if maskedImage.getBBox(afwImage.LOCAL) != illumMaskedImage.getBBox(afwImage.LOCAL):
419  raise RuntimeError("maskedImage bbox %s != illumMaskedImage bbox %s" %
420  (maskedImage.getBBox(afwImage.LOCAL), illumMaskedImage.getBBox(afwImage.LOCAL)))
421 
422  maskedImage.scaledDivides(1.0/illumScale, illumMaskedImage)
423 
424 
def trimToMatchCalibBBox(rawMaskedImage, calibMaskedImage)
def illuminationCorrection(maskedImage, illumMaskedImage, illumScale, trimToFit=True)

◆ interpolateDefectList()

def lsst.ip.isr.isrFunctions.interpolateDefectList (   maskedImage,
  defectList,
  fwhm,
  fallbackValue = None 
)
Interpolate over defects specified in a defect list.

Parameters
----------
maskedImage : `lsst.afw.image.MaskedImage`
    Image to process.
defectList : `lsst.meas.algorithms.Defects`
    List of defects to interpolate over.
fwhm : scalar
    FWHM of double Gaussian smoothing kernel.
fallbackValue : scalar, optional
    Fallback value if an interpolated value cannot be determined.
    If None, then the clipped mean of the image is used.

Definition at line 78 of file isrFunctions.py.

78 def interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=None):
79  """Interpolate over defects specified in a defect list.
80 
81  Parameters
82  ----------
83  maskedImage : `lsst.afw.image.MaskedImage`
84  Image to process.
85  defectList : `lsst.meas.algorithms.Defects`
86  List of defects to interpolate over.
87  fwhm : scalar
88  FWHM of double Gaussian smoothing kernel.
89  fallbackValue : scalar, optional
90  Fallback value if an interpolated value cannot be determined.
91  If None, then the clipped mean of the image is used.
92  """
93  psf = createPsf(fwhm)
94  if fallbackValue is None:
95  fallbackValue = afwMath.makeStatistics(maskedImage.getImage(), afwMath.MEANCLIP).getValue()
96  if 'INTRP' not in maskedImage.getMask().getMaskPlaneDict():
97  maskedImage.getMask().addMaskPlane('INTRP')
98  measAlg.interpolateOverDefects(maskedImage, psf, defectList, fallbackValue, True)
99  return maskedImage
100 
101 
def interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=None)
Definition: isrFunctions.py:78
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
The makeStatistics() overload to handle lsst::afw::math::MaskedVector<>
Definition: Statistics.h:520

◆ interpolateFromMask()

def lsst.ip.isr.isrFunctions.interpolateFromMask (   maskedImage,
  fwhm,
  growSaturatedFootprints = 1,
  maskNameList = ['SAT'],
  fallbackValue = None 
)
Interpolate over defects identified by a particular set of mask planes.

Parameters
----------
maskedImage : `lsst.afw.image.MaskedImage`
    Image to process.
fwhm : scalar
    FWHM of double Gaussian smoothing kernel.
growSaturatedFootprints : scalar, optional
    Number of pixels to grow footprints for saturated pixels.
maskNameList : `List` of `str`, optional
    Mask plane name.
fallbackValue : scalar, optional
    Value of last resort for interpolation.

Definition at line 138 of file isrFunctions.py.

138  maskNameList=['SAT'], fallbackValue=None):
139  """Interpolate over defects identified by a particular set of mask planes.
140 
141  Parameters
142  ----------
143  maskedImage : `lsst.afw.image.MaskedImage`
144  Image to process.
145  fwhm : scalar
146  FWHM of double Gaussian smoothing kernel.
147  growSaturatedFootprints : scalar, optional
148  Number of pixels to grow footprints for saturated pixels.
149  maskNameList : `List` of `str`, optional
150  Mask plane name.
151  fallbackValue : scalar, optional
152  Value of last resort for interpolation.
153  """
154  mask = maskedImage.getMask()
155 
156  if growSaturatedFootprints > 0 and "SAT" in maskNameList:
157  thresh = afwDetection.Threshold(mask.getPlaneBitMask("SAT"), afwDetection.Threshold.BITMASK)
158  fpSet = afwDetection.FootprintSet(mask, thresh)
159  # If we are interpolating over an area larger than the original masked region, we need
160  # to expand the original mask bit to the full area to explain why we interpolated there.
161  fpSet = afwDetection.FootprintSet(fpSet, rGrow=growSaturatedFootprints, isotropic=False)
162  fpSet.setMask(mask, "SAT")
163 
164  thresh = afwDetection.Threshold(mask.getPlaneBitMask(maskNameList), afwDetection.Threshold.BITMASK)
165  fpSet = afwDetection.FootprintSet(mask, thresh)
166  defectList = measAlg.Defects.fromFootprintList(fpSet.getFootprints())
167 
168  interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=fallbackValue)
169 
170  return maskedImage
171 
172 
def interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=None)
Definition: isrFunctions.py:78

◆ makeThresholdMask()

def lsst.ip.isr.isrFunctions.makeThresholdMask (   maskedImage,
  threshold,
  growFootprints = 1,
  maskName = 'SAT' 
)
Mask pixels based on threshold detection.

Parameters
----------
maskedImage : `lsst.afw.image.MaskedImage`
    Image to process.  Only the mask plane is updated.
threshold : scalar
    Detection threshold.
growFootprints : scalar, optional
    Number of pixels to grow footprints of detected regions.
maskName : str, optional
    Mask plane name, or list of names to convert

Returns
-------
defectList : `lsst.meas.algorithms.Defects`
    Defect list constructed from pixels above the threshold.

Definition at line 102 of file isrFunctions.py.

102 def makeThresholdMask(maskedImage, threshold, growFootprints=1, maskName='SAT'):
103  """Mask pixels based on threshold detection.
104 
105  Parameters
106  ----------
107  maskedImage : `lsst.afw.image.MaskedImage`
108  Image to process. Only the mask plane is updated.
109  threshold : scalar
110  Detection threshold.
111  growFootprints : scalar, optional
112  Number of pixels to grow footprints of detected regions.
113  maskName : str, optional
114  Mask plane name, or list of names to convert
115 
116  Returns
117  -------
118  defectList : `lsst.meas.algorithms.Defects`
119  Defect list constructed from pixels above the threshold.
120  """
121  # find saturated regions
122  thresh = afwDetection.Threshold(threshold)
123  fs = afwDetection.FootprintSet(maskedImage, thresh)
124 
125  if growFootprints > 0:
126  fs = afwDetection.FootprintSet(fs, rGrow=growFootprints, isotropic=False)
127  fpList = fs.getFootprints()
128 
129  # set mask
130  mask = maskedImage.getMask()
131  bitmask = mask.getPlaneBitMask(maskName)
132  afwDetection.setMaskFromFootprintList(mask, fpList, bitmask)
133 
134  return measAlg.Defects.fromFootprintList(fpList)
135 
136 
def makeThresholdMask(maskedImage, threshold, growFootprints=1, maskName='SAT')

◆ overscanCorrection()

def lsst.ip.isr.isrFunctions.overscanCorrection (   ampMaskedImage,
  overscanImage,
  fitType = 'MEDIAN',
  order = 1,
  collapseRej = 3.0,
  statControl = None,
  overscanIsInt = True 
)
Apply overscan correction in place.

Parameters
----------
ampMaskedImage : `lsst.afw.image.MaskedImage`
    Image of amplifier to correct; modified.
overscanImage : `lsst.afw.image.Image` or `lsst.afw.image.MaskedImage`
    Image of overscan; modified.
fitType : `str`
    Type of fit for overscan correction. May be one of:

    - ``MEAN``: use mean of overscan.
    - ``MEANCLIP``: use clipped mean of overscan.
    - ``MEDIAN``: use median of overscan.
    - ``POLY``: fit with ordinary polynomial.
    - ``CHEB``: fit with Chebyshev polynomial.
    - ``LEG``: fit with Legendre polynomial.
    - ``NATURAL_SPLINE``: fit with natural spline.
    - ``CUBIC_SPLINE``: fit with cubic spline.
    - ``AKIMA_SPLINE``: fit with Akima spline.

order : `int`
    Polynomial order or number of spline knots; ignored unless
    ``fitType`` indicates a polynomial or spline.
statControl : `lsst.afw.math.StatisticsControl`
    Statistics control object.  In particular, we pay attention to numSigmaClip
overscanIsInt : `bool`
    Treat the overscan region as consisting of integers, even if it's been
    converted to float.  E.g. handle ties properly.

Returns
-------
result : `lsst.pipe.base.Struct`
    Result struct with components:

    - ``imageFit``: Value(s) removed from image (scalar or
        `lsst.afw.image.Image`)
    - ``overscanFit``: Value(s) removed from overscan (scalar or
        `lsst.afw.image.Image`)
    - ``overscanImage``: Overscan corrected overscan region
        (`lsst.afw.image.Image`)
Raises
------
pexExcept.Exception
    Raised if ``fitType`` is not an allowed value.

Notes
-----
The ``ampMaskedImage`` and ``overscanImage`` are modified, with the fit
subtracted. Note that the ``overscanImage`` should not be a subimage of
the ``ampMaskedImage``, to avoid being subtracted twice.

Debug plots are available for the SPLINE fitTypes by setting the
`debug.display` for `name` == "lsst.ip.isr.isrFunctions".  These
plots show the scatter plot of the overscan data (collapsed along
the perpendicular dimension) as a function of position on the CCD
(normalized between +/-1).

Definition at line 426 of file isrFunctions.py.

426  statControl=None, overscanIsInt=True):
427  """Apply overscan correction in place.
428 
429  Parameters
430  ----------
431  ampMaskedImage : `lsst.afw.image.MaskedImage`
432  Image of amplifier to correct; modified.
433  overscanImage : `lsst.afw.image.Image` or `lsst.afw.image.MaskedImage`
434  Image of overscan; modified.
435  fitType : `str`
436  Type of fit for overscan correction. May be one of:
437 
438  - ``MEAN``: use mean of overscan.
439  - ``MEANCLIP``: use clipped mean of overscan.
440  - ``MEDIAN``: use median of overscan.
441  - ``POLY``: fit with ordinary polynomial.
442  - ``CHEB``: fit with Chebyshev polynomial.
443  - ``LEG``: fit with Legendre polynomial.
444  - ``NATURAL_SPLINE``: fit with natural spline.
445  - ``CUBIC_SPLINE``: fit with cubic spline.
446  - ``AKIMA_SPLINE``: fit with Akima spline.
447 
448  order : `int`
449  Polynomial order or number of spline knots; ignored unless
450  ``fitType`` indicates a polynomial or spline.
451  statControl : `lsst.afw.math.StatisticsControl`
452  Statistics control object. In particular, we pay attention to numSigmaClip
453  overscanIsInt : `bool`
454  Treat the overscan region as consisting of integers, even if it's been
455  converted to float. E.g. handle ties properly.
456 
457  Returns
458  -------
459  result : `lsst.pipe.base.Struct`
460  Result struct with components:
461 
462  - ``imageFit``: Value(s) removed from image (scalar or
463  `lsst.afw.image.Image`)
464  - ``overscanFit``: Value(s) removed from overscan (scalar or
465  `lsst.afw.image.Image`)
466  - ``overscanImage``: Overscan corrected overscan region
467  (`lsst.afw.image.Image`)
468  Raises
469  ------
470  pexExcept.Exception
471  Raised if ``fitType`` is not an allowed value.
472 
473  Notes
474  -----
475  The ``ampMaskedImage`` and ``overscanImage`` are modified, with the fit
476  subtracted. Note that the ``overscanImage`` should not be a subimage of
477  the ``ampMaskedImage``, to avoid being subtracted twice.
478 
479  Debug plots are available for the SPLINE fitTypes by setting the
480  `debug.display` for `name` == "lsst.ip.isr.isrFunctions". These
481  plots show the scatter plot of the overscan data (collapsed along
482  the perpendicular dimension) as a function of position on the CCD
483  (normalized between +/-1).
484  """
485  ampImage = ampMaskedImage.getImage()
486  if statControl is None:
487  statControl = afwMath.StatisticsControl()
488 
489  numSigmaClip = statControl.getNumSigmaClip()
490 
491  if fitType in ('MEAN', 'MEANCLIP'):
492  fitType = afwMath.stringToStatisticsProperty(fitType)
493  offImage = afwMath.makeStatistics(overscanImage, fitType, statControl).getValue()
494  overscanFit = offImage
495  elif fitType in ('MEDIAN',):
496  if overscanIsInt:
497  # we need an image with integer pixels to handle ties properly
498  if hasattr(overscanImage, "image"):
499  imageI = overscanImage.image.convertI()
500  overscanImageI = afwImage.MaskedImageI(imageI, overscanImage.mask, overscanImage.variance)
501  else:
502  overscanImageI = overscanImage.convertI()
503  else:
504  overscanImageI = overscanImage
505 
506  fitType = afwMath.stringToStatisticsProperty(fitType)
507  offImage = afwMath.makeStatistics(overscanImageI, fitType, statControl).getValue()
508  overscanFit = offImage
509 
510  if overscanIsInt:
511  del overscanImageI
512  elif fitType in ('POLY', 'CHEB', 'LEG', 'NATURAL_SPLINE', 'CUBIC_SPLINE', 'AKIMA_SPLINE'):
513  if hasattr(overscanImage, "getImage"):
514  biasArray = overscanImage.getImage().getArray()
515  biasArray = numpy.ma.masked_where(overscanImage.getMask().getArray() & statControl.getAndMask(),
516  biasArray)
517  else:
518  biasArray = overscanImage.getArray()
519  # Fit along the long axis, so collapse along each short row and fit the resulting array
520  shortInd = numpy.argmin(biasArray.shape)
521  if shortInd == 0:
522  # Convert to some 'standard' representation to make things easier
523  biasArray = numpy.transpose(biasArray)
524 
525  # Do a single round of clipping to weed out CR hits and signal leaking into the overscan
526  percentiles = numpy.percentile(biasArray, [25.0, 50.0, 75.0], axis=1)
527  medianBiasArr = percentiles[1]
528  stdevBiasArr = 0.74*(percentiles[2] - percentiles[0]) # robust stdev
529  diff = numpy.abs(biasArray - medianBiasArr[:, numpy.newaxis])
530  biasMaskedArr = numpy.ma.masked_where(diff > numSigmaClip*stdevBiasArr[:, numpy.newaxis], biasArray)
531  collapsed = numpy.mean(biasMaskedArr, axis=1)
532  if collapsed.mask.sum() > 0:
533  collapsed.data[collapsed.mask] = numpy.mean(biasArray.data[collapsed.mask], axis=1)
534  del biasArray, percentiles, stdevBiasArr, diff, biasMaskedArr
535 
536  if shortInd == 0:
537  collapsed = numpy.transpose(collapsed)
538 
539  num = len(collapsed)
540  indices = 2.0*numpy.arange(num)/float(num) - 1.0
541 
542  if fitType in ('POLY', 'CHEB', 'LEG'):
543  # A numpy polynomial
544  poly = numpy.polynomial
545  fitter, evaler = {"POLY": (poly.polynomial.polyfit, poly.polynomial.polyval),
546  "CHEB": (poly.chebyshev.chebfit, poly.chebyshev.chebval),
547  "LEG": (poly.legendre.legfit, poly.legendre.legval),
548  }[fitType]
549 
550  coeffs = fitter(indices, collapsed, order)
551  fitBiasArr = evaler(indices, coeffs)
552  elif 'SPLINE' in fitType:
553  # An afw interpolation
554  numBins = order
555  #
556  # numpy.histogram needs a real array for the mask, but numpy.ma "optimises" the case
557  # no-values-are-masked by replacing the mask array by a scalar, numpy.ma.nomask
558  #
559  # Issue DM-415
560  #
561  collapsedMask = collapsed.mask
562  try:
563  if collapsedMask == numpy.ma.nomask:
564  collapsedMask = numpy.array(len(collapsed)*[numpy.ma.nomask])
565  except ValueError: # If collapsedMask is an array the test fails [needs .all()]
566  pass
567 
568  numPerBin, binEdges = numpy.histogram(indices, bins=numBins,
569  weights=1-collapsedMask.astype(int))
570  # Binning is just a histogram, with weights equal to the values.
571  # Use a similar trick to get the bin centers (this deals with different numbers per bin).
572  with numpy.errstate(invalid="ignore"): # suppress NAN warnings
573  values = numpy.histogram(indices, bins=numBins,
574  weights=collapsed.data*~collapsedMask)[0]/numPerBin
575  binCenters = numpy.histogram(indices, bins=numBins,
576  weights=indices*~collapsedMask)[0]/numPerBin
577  interp = afwMath.makeInterpolate(binCenters.astype(float)[numPerBin > 0],
578  values.astype(float)[numPerBin > 0],
580  fitBiasArr = numpy.array([interp.interpolate(i) for i in indices])
581 
582  import lsstDebug
583  if lsstDebug.Info(__name__).display:
584  import matplotlib.pyplot as plot
585  figure = plot.figure(1)
586  figure.clear()
587  axes = figure.add_axes((0.1, 0.1, 0.8, 0.8))
588  axes.plot(indices[~collapsedMask], collapsed[~collapsedMask], 'k+')
589  if collapsedMask.sum() > 0:
590  axes.plot(indices[collapsedMask], collapsed.data[collapsedMask], 'b+')
591  axes.plot(indices, fitBiasArr, 'r-')
592  plot.xlabel("centered/scaled position along overscan region")
593  plot.ylabel("pixel value/fit value")
594  figure.show()
595  prompt = "Press Enter or c to continue [chp]... "
596  while True:
597  ans = input(prompt).lower()
598  if ans in ("", "c",):
599  break
600  if ans in ("p",):
601  import pdb
602  pdb.set_trace()
603  elif ans in ("h", ):
604  print("h[elp] c[ontinue] p[db]")
605  plot.close()
606 
607  offImage = ampImage.Factory(ampImage.getDimensions())
608  offArray = offImage.getArray()
609  overscanFit = afwImage.ImageF(overscanImage.getDimensions())
610  overscanArray = overscanFit.getArray()
611  if shortInd == 1:
612  offArray[:, :] = fitBiasArr[:, numpy.newaxis]
613  overscanArray[:, :] = fitBiasArr[:, numpy.newaxis]
614  else:
615  offArray[:, :] = fitBiasArr[numpy.newaxis, :]
616  overscanArray[:, :] = fitBiasArr[numpy.newaxis, :]
617 
618  # We don't trust any extrapolation: mask those pixels as SUSPECT
619  # This will occur when the top and or bottom edges of the overscan
620  # contain saturated values. The values will be extrapolated from
621  # the surrounding pixels, but we cannot entirely trust the value of
622  # the extrapolation, and will mark the image mask plane to flag the
623  # image as such.
624  mask = ampMaskedImage.getMask()
625  maskArray = mask.getArray() if shortInd == 1 else mask.getArray().transpose()
626  suspect = mask.getPlaneBitMask("SUSPECT")
627  try:
628  if collapsed.mask == numpy.ma.nomask:
629  # There is no mask, so the whole array is fine
630  pass
631  except ValueError: # If collapsed.mask is an array the test fails [needs .all()]
632  for low in range(num):
633  if not collapsed.mask[low]:
634  break
635  if low > 0:
636  maskArray[:low, :] |= suspect
637  for high in range(1, num):
638  if not collapsed.mask[-high]:
639  break
640  if high > 1:
641  maskArray[-high:, :] |= suspect
642 
643  else:
644  raise pexExcept.Exception('%s : %s an invalid overscan type' % ("overscanCorrection", fitType))
645  ampImage -= offImage
646  overscanImage -= overscanFit
647  return Struct(imageFit=offImage, overscanFit=overscanFit, overscanImage=overscanImage)
648 
649 
Interpolate::Style stringToInterpStyle(std::string const &style)
Conversion function to switch a string to an Interpolate::Style.
Definition: Interpolate.cc:257
Provides consistent interface for LSST exceptions.
Definition: Exception.h:107
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
The makeStatistics() overload to handle lsst::afw::math::MaskedVector<>
Definition: Statistics.h:520
Property stringToStatisticsProperty(std::string const property)
Conversion function to switch a string to a Property (see Statistics.h)
Definition: Statistics.cc:747
Pass parameters to a Statistics object.
Definition: Statistics.h:93
std::shared_ptr< Interpolate > makeInterpolate(ndarray::Array< double const, 1 > const &x, ndarray::Array< double const, 1 > const &y, Interpolate::Style const style=Interpolate::AKIMA_SPLINE)
Definition: Interpolate.cc:353

◆ saturationCorrection()

def lsst.ip.isr.isrFunctions.saturationCorrection (   maskedImage,
  saturation,
  fwhm,
  growFootprints = 1,
  interpolate = True,
  maskName = 'SAT',
  fallbackValue = None 
)
Mark saturated pixels and optionally interpolate over them

Parameters
----------
maskedImage : `lsst.afw.image.MaskedImage`
    Image to process.
saturation  : scalar
    Saturation level used as the detection threshold.
fwhm : scalar
    FWHM of double Gaussian smoothing kernel.
growFootprints : scalar, optional
    Number of pixels to grow footprints of detected regions.
interpolate : Bool, optional
    If True, saturated pixels are interpolated over.
maskName : str, optional
    Mask plane name.
fallbackValue : scalar, optional
    Value of last resort for interpolation.

Definition at line 174 of file isrFunctions.py.

174  fallbackValue=None):
175  """Mark saturated pixels and optionally interpolate over them
176 
177  Parameters
178  ----------
179  maskedImage : `lsst.afw.image.MaskedImage`
180  Image to process.
181  saturation : scalar
182  Saturation level used as the detection threshold.
183  fwhm : scalar
184  FWHM of double Gaussian smoothing kernel.
185  growFootprints : scalar, optional
186  Number of pixels to grow footprints of detected regions.
187  interpolate : Bool, optional
188  If True, saturated pixels are interpolated over.
189  maskName : str, optional
190  Mask plane name.
191  fallbackValue : scalar, optional
192  Value of last resort for interpolation.
193  """
194  defectList = makeThresholdMask(
195  maskedImage=maskedImage,
196  threshold=saturation,
197  growFootprints=growFootprints,
198  maskName=maskName,
199  )
200  if interpolate:
201  interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=fallbackValue)
202 
203  return maskedImage
204 
205 
def interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=None)
Definition: isrFunctions.py:78
def makeThresholdMask(maskedImage, threshold, growFootprints=1, maskName='SAT')

◆ setBadRegions()

def lsst.ip.isr.isrFunctions.setBadRegions (   exposure,
  badStatistic = "MEDIAN" 
)
Set all BAD areas of the chip to the average of the rest of the exposure

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to mask.  The exposure mask is modified.
badStatistic : `str`, optional
    Statistic to use to generate the replacement value from the
    image data.  Allowed values are 'MEDIAN' or 'MEANCLIP'.

Returns
-------
badPixelCount : scalar
    Number of bad pixels masked.
badPixelValue : scalar
    Value substituted for bad pixels.

Raises
------
RuntimeError
    Raised if `badStatistic` is not an allowed value.

Definition at line 981 of file isrFunctions.py.

981 def setBadRegions(exposure, badStatistic="MEDIAN"):
982  """Set all BAD areas of the chip to the average of the rest of the exposure
983 
984  Parameters
985  ----------
986  exposure : `lsst.afw.image.Exposure`
987  Exposure to mask. The exposure mask is modified.
988  badStatistic : `str`, optional
989  Statistic to use to generate the replacement value from the
990  image data. Allowed values are 'MEDIAN' or 'MEANCLIP'.
991 
992  Returns
993  -------
994  badPixelCount : scalar
995  Number of bad pixels masked.
996  badPixelValue : scalar
997  Value substituted for bad pixels.
998 
999  Raises
1000  ------
1001  RuntimeError
1002  Raised if `badStatistic` is not an allowed value.
1003  """
1004  if badStatistic == "MEDIAN":
1005  statistic = afwMath.MEDIAN
1006  elif badStatistic == "MEANCLIP":
1007  statistic = afwMath.MEANCLIP
1008  else:
1009  raise RuntimeError("Impossible method %s of bad region correction" % badStatistic)
1010 
1011  mi = exposure.getMaskedImage()
1012  mask = mi.getMask()
1013  BAD = mask.getPlaneBitMask("BAD")
1014  INTRP = mask.getPlaneBitMask("INTRP")
1015 
1016  sctrl = afwMath.StatisticsControl()
1017  sctrl.setAndMask(BAD)
1018  value = afwMath.makeStatistics(mi, statistic, sctrl).getValue()
1019 
1020  maskArray = mask.getArray()
1021  imageArray = mi.getImage().getArray()
1022  badPixels = numpy.logical_and((maskArray & BAD) > 0, (maskArray & INTRP) == 0)
1023  imageArray[:] = numpy.where(badPixels, value, imageArray)
1024 
1025  return badPixels.sum(), value
1026 
def setBadRegions(exposure, badStatistic="MEDIAN")
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
The makeStatistics() overload to handle lsst::afw::math::MaskedVector<>
Definition: Statistics.h:520
Pass parameters to a Statistics object.
Definition: Statistics.h:93

◆ transposeMaskedImage()

def lsst.ip.isr.isrFunctions.transposeMaskedImage (   maskedImage)
Make a transposed copy of a masked image.

Parameters
----------
maskedImage : `lsst.afw.image.MaskedImage`
    Image to process.

Returns
-------
transposed : `lsst.afw.image.MaskedImage`
    The transposed copy of the input image.

Definition at line 58 of file isrFunctions.py.

58 def transposeMaskedImage(maskedImage):
59  """Make a transposed copy of a masked image.
60 
61  Parameters
62  ----------
63  maskedImage : `lsst.afw.image.MaskedImage`
64  Image to process.
65 
66  Returns
67  -------
68  transposed : `lsst.afw.image.MaskedImage`
69  The transposed copy of the input image.
70  """
71  transposed = maskedImage.Factory(lsst.geom.Extent2I(maskedImage.getHeight(), maskedImage.getWidth()))
72  transposed.getImage().getArray()[:] = maskedImage.getImage().getArray().T
73  transposed.getMask().getArray()[:] = maskedImage.getMask().getArray().T
74  transposed.getVariance().getArray()[:] = maskedImage.getVariance().getArray().T
75  return transposed
76 
77 
def transposeMaskedImage(maskedImage)
Definition: isrFunctions.py:58

◆ trimToMatchCalibBBox()

def lsst.ip.isr.isrFunctions.trimToMatchCalibBBox (   rawMaskedImage,
  calibMaskedImage 
)
Compute number of edge trim pixels to match the calibration data.

Use the dimension difference between the raw exposure and the
calibration exposure to compute the edge trim pixels.  This trim
is applied symmetrically, with the same number of pixels masked on
each side.

Parameters
----------
rawMaskedImage : `lsst.afw.image.MaskedImage`
    Image to trim.
calibMaskedImage : `lsst.afw.image.MaskedImage`
    Calibration image to draw new bounding box from.

Returns
-------
replacementMaskedImage : `lsst.afw.image.MaskedImage`
    ``rawMaskedImage`` trimmed to the appropriate size
Raises
------
RuntimeError
   Rasied if ``rawMaskedImage`` cannot be symmetrically trimmed to
   match ``calibMaskedImage``.

Definition at line 206 of file isrFunctions.py.

206 def trimToMatchCalibBBox(rawMaskedImage, calibMaskedImage):
207  """Compute number of edge trim pixels to match the calibration data.
208 
209  Use the dimension difference between the raw exposure and the
210  calibration exposure to compute the edge trim pixels. This trim
211  is applied symmetrically, with the same number of pixels masked on
212  each side.
213 
214  Parameters
215  ----------
216  rawMaskedImage : `lsst.afw.image.MaskedImage`
217  Image to trim.
218  calibMaskedImage : `lsst.afw.image.MaskedImage`
219  Calibration image to draw new bounding box from.
220 
221  Returns
222  -------
223  replacementMaskedImage : `lsst.afw.image.MaskedImage`
224  ``rawMaskedImage`` trimmed to the appropriate size
225  Raises
226  ------
227  RuntimeError
228  Rasied if ``rawMaskedImage`` cannot be symmetrically trimmed to
229  match ``calibMaskedImage``.
230  """
231  nx, ny = rawMaskedImage.getBBox().getDimensions() - calibMaskedImage.getBBox().getDimensions()
232  if nx != ny:
233  raise RuntimeError("Raw and calib maskedImages are trimmed differently in X and Y.")
234  if nx % 2 != 0:
235  raise RuntimeError("Calibration maskedImage is trimmed unevenly in X.")
236  if nx < 0:
237  raise RuntimeError("Calibration maskedImage is larger than raw data.")
238 
239  nEdge = nx//2
240  if nEdge > 0:
241  replacementMaskedImage = rawMaskedImage[nEdge:-nEdge, nEdge:-nEdge, afwImage.LOCAL]
242  SourceDetectionTask.setEdgeBits(
243  rawMaskedImage,
244  replacementMaskedImage.getBBox(),
245  rawMaskedImage.getMask().getPlaneBitMask("EDGE")
246  )
247  else:
248  replacementMaskedImage = rawMaskedImage
249 
250  return replacementMaskedImage
251 
252 
def trimToMatchCalibBBox(rawMaskedImage, calibMaskedImage)

◆ updateVariance()

def lsst.ip.isr.isrFunctions.updateVariance (   maskedImage,
  gain,
  readNoise 
)
Set the variance plane based on the image plane.

Parameters
----------
maskedImage : `lsst.afw.image.MaskedImage`
    Image to process.  The variance plane is modified.
gain : scalar
    The amplifier gain in electrons/ADU.
readNoise : scalar
    The amplifier read nmoise in ADU/pixel.

Definition at line 326 of file isrFunctions.py.

326 def updateVariance(maskedImage, gain, readNoise):
327  """Set the variance plane based on the image plane.
328 
329  Parameters
330  ----------
331  maskedImage : `lsst.afw.image.MaskedImage`
332  Image to process. The variance plane is modified.
333  gain : scalar
334  The amplifier gain in electrons/ADU.
335  readNoise : scalar
336  The amplifier read nmoise in ADU/pixel.
337  """
338  var = maskedImage.getVariance()
339  var[:] = maskedImage.getImage()
340  var /= gain
341  var += readNoise**2
342 
343 
def updateVariance(maskedImage, gain, readNoise)

◆ widenSaturationTrails()

def lsst.ip.isr.isrFunctions.widenSaturationTrails (   mask)
Grow the saturation trails by an amount dependent on the width of the trail.

Parameters
----------
mask : `lsst.afw.image.Mask`
    Mask which will have the saturated areas grown.

Definition at line 934 of file isrFunctions.py.

934 def widenSaturationTrails(mask):
935  """Grow the saturation trails by an amount dependent on the width of the trail.
936 
937  Parameters
938  ----------
939  mask : `lsst.afw.image.Mask`
940  Mask which will have the saturated areas grown.
941  """
942 
943  extraGrowDict = {}
944  for i in range(1, 6):
945  extraGrowDict[i] = 0
946  for i in range(6, 8):
947  extraGrowDict[i] = 1
948  for i in range(8, 10):
949  extraGrowDict[i] = 3
950  extraGrowMax = 4
951 
952  if extraGrowMax <= 0:
953  return
954 
955  saturatedBit = mask.getPlaneBitMask("SAT")
956 
957  xmin, ymin = mask.getBBox().getMin()
958  width = mask.getWidth()
959 
960  thresh = afwDetection.Threshold(saturatedBit, afwDetection.Threshold.BITMASK)
961  fpList = afwDetection.FootprintSet(mask, thresh).getFootprints()
962 
963  for fp in fpList:
964  for s in fp.getSpans():
965  x0, x1 = s.getX0(), s.getX1()
966 
967  extraGrow = extraGrowDict.get(x1 - x0 + 1, extraGrowMax)
968  if extraGrow > 0:
969  y = s.getY() - ymin
970  x0 -= xmin + extraGrow
971  x1 -= xmin - extraGrow
972 
973  if x0 < 0:
974  x0 = 0
975  if x1 >= width - 1:
976  x1 = width - 1
977 
978  mask.array[y, x0:x1+1] |= saturatedBit
979 
980