LSSTApplications  16.0-10-g9d3e444,16.0-11-g09ed895+3,16.0-11-g12e47bd+4,16.0-11-g9bb73b2+10,16.0-12-g5c924a4+10,16.0-15-g7af1f30,16.0-15-gdd5ca33+2,16.0-16-gf0259e2+1,16.0-17-g31abd91+11,16.0-17-g5cf0468+3,16.0-18-g51a54b3+3,16.0-18-ga4d4bcb+5,16.0-18-gcf94535+2,16.0-19-g9d290d5+2,16.0-2-g0febb12+22,16.0-2-g9d5294e+73,16.0-2-ga8830df+7,16.0-21-g3d035912+2,16.0-26-g8e79609,16.0-28-gfc9ea6c+9,16.0-29-ge8801f9+4,16.0-3-ge00e371+38,16.0-4-g18f3627+17,16.0-4-g5f3a788+21,16.0-4-ga3eb747+11,16.0-4-gabf74b7+33,16.0-4-gb13d127+7,16.0-5-g27fb78a+11,16.0-5-g6a53317+38,16.0-5-gb3f8a4b+91,16.0-51-gbbe9c988+3,16.0-6-g9321be7+5,16.0-6-gcbc7b31+47,16.0-6-gf49912c+33,16.0-7-gd2eeba5+56,16.0-75-gbf7a9a820,16.0-8-g21fd5fe+34,16.0-8-g3a9f023+24,16.0-9-gf3bc169+2,16.0-9-gf5c1f43+12,master-gd73dc1d098+5,w.2019.02
LSSTDataManagementBasePackage
Public Member Functions | Public Attributes | Static Public Attributes | List of all members
lsst.ip.isr.isrTask.IsrTask Class Reference
Inheritance diagram for lsst.ip.isr.isrTask.IsrTask:

Public Member Functions

def __init__ (self, args, kwargs)
 
def readIsrData (self, dataRef, rawExposure)
 Retrieve necessary frames for instrument signature removal. More...
 
def run (self, ccdExposure, camera=None, bias=None, linearizer=None, crosstalkSources=None, dark=None, flat=None, bfKernel=None, defects=None, fringes=None, opticsTransmission=None, filterTransmission=None, sensorTransmission=None, atmosphereTransmission=None)
 Perform instrument signature removal on an exposure. More...
 
def runDataRef (self, sensorRef)
 
def getIsrExposure (self, dataRef, datasetType, immediate=True)
 Retrieve a calibration dataset for removing instrument signature. More...
 
def convertIntToFloat (self, exposure)
 
def maskAmplifier (self, ccdExposure, amp, defects)
 
def overscanCorrection (self, ccdExposure, amp)
 
def updateVariance (self, ampExposure, amp, overscanImage=None)
 
def darkCorrection (self, exposure, darkExposure, invert=False)
 Apply dark correction in place. More...
 
def doLinearize (self, detector)
 Check if linearization is needed for the detector cameraGeom. More...
 
def flatCorrection (self, exposure, flatExposure, invert=False)
 Apply flat correction in place. More...
 
def saturationDetection (self, exposure, amp)
 Detect saturated pixels and mask them using mask plane config.saturatedMaskName, in place. More...
 
def saturationInterpolation (self, ccdExposure)
 Interpolate over saturated pixels, in place. More...
 
def suspectDetection (self, exposure, amp)
 Detect suspect pixels and mask them using mask plane config.suspectMaskName, in place. More...
 
def maskAndInterpDefect (self, ccdExposure, defectBaseList)
 Mask defects using mask plane "BAD" and interpolate over them, in place. More...
 
def maskAndInterpNan (self, exposure)
 Mask NaNs using mask plane "UNMASKEDNAN" and interpolate over them, in place. More...
 
def measureBackground (self, exposure, IsrQaConfig=None)
 
def roughZeroPoint (self, exposure)
 
def setValidPolygonIntersect (self, ccdExposure, fpPolygon)
 Set the valid polygon as the intersection of fpPolygon and the ccd corners. More...
 
def flatContext (self, exp, flat, dark=None)
 
def debugView (self, exposure, stepname)
 

Public Attributes

 vignettePolygon
 

Static Public Attributes

 ConfigClass = IsrTaskConfig
 

Detailed Description

Apply common instrument signature correction algorithms to a raw frame.

The process for correcting imaging data is very similar from
camera to camera.  This task provides a vanilla implementation of
doing these corrections, including the ability to turn certain
corrections off if they are not needed.  The inputs to the primary
method, `run()`, are a raw exposure to be corrected and the
calibration data products. The raw input is a single chip sized
mosaic of all amps including overscans and other non-science
pixels.  The method `runDataRef()` identifies and defines the
calibration data products, and is intended for use by a
`lsst.pipe.base.cmdLineTask.CmdLineTask` and takes as input only a
`daf.persistence.butlerSubset.ButlerDataRef`.  This task may be
subclassed for different camera, although the most camera specific
methods have been split into subtasks that can be redirected
appropriately.

The __init__ method sets up the subtasks for ISR processing, using
the defaults from `lsst.ip.isr`.

Parameters
----------
args : `list`
    Positional arguments passed to the Task constructor. None used at this time.
kwargs : `dict`, optional
    Keyword arguments passed on to the Task constructor. None used at this time.

Definition at line 546 of file isrTask.py.

Constructor & Destructor Documentation

◆ __init__()

def lsst.ip.isr.isrTask.IsrTask.__init__ (   self,
  args,
  kwargs 
)

Definition at line 577 of file isrTask.py.

577  def __init__(self, *args, **kwargs):
578  pipeBase.Task.__init__(self, *args, **kwargs)
579 
580  self.makeSubtask("assembleCcd")
581  self.makeSubtask("crosstalk")
582  self.makeSubtask("strayLight")
583  self.makeSubtask("fringe")
584  self.makeSubtask("masking")
585  self.makeSubtask("vignette")
586 
def __init__(self, minimum, dataRange, Q)

Member Function Documentation

◆ convertIntToFloat()

def lsst.ip.isr.isrTask.IsrTask.convertIntToFloat (   self,
  exposure 
)
Convert exposure image from uint16 to float.

If the exposure does not need to be converted, the input is
immediately returned.  For exposures that are converted to use
floating point pixels, the variance is set to unity and the
mask to zero.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
   The raw exposure to be converted.

Returns
-------
newexposure : `lsst.afw.image.Exposure`
   The input ``exposure``, converted to floating point pixels.

Raises
------
RuntimeError
    Raised if the exposure type cannot be converted to float.

Definition at line 1139 of file isrTask.py.

1139  def convertIntToFloat(self, exposure):
1140  """Convert exposure image from uint16 to float.
1141 
1142  If the exposure does not need to be converted, the input is
1143  immediately returned. For exposures that are converted to use
1144  floating point pixels, the variance is set to unity and the
1145  mask to zero.
1146 
1147  Parameters
1148  ----------
1149  exposure : `lsst.afw.image.Exposure`
1150  The raw exposure to be converted.
1151 
1152  Returns
1153  -------
1154  newexposure : `lsst.afw.image.Exposure`
1155  The input ``exposure``, converted to floating point pixels.
1156 
1157  Raises
1158  ------
1159  RuntimeError
1160  Raised if the exposure type cannot be converted to float.
1161 
1162  """
1163  if isinstance(exposure, afwImage.ExposureF):
1164  # Nothing to be done
1165  return exposure
1166  if not hasattr(exposure, "convertF"):
1167  raise RuntimeError("Unable to convert exposure (%s) to float" % type(exposure))
1168 
1169  newexposure = exposure.convertF()
1170  newexposure.variance[:] = 1
1171  newexposure.mask[:] = 0x0
1172 
1173  return newexposure
1174 
table::Key< int > type
Definition: Detector.cc:164

◆ darkCorrection()

def lsst.ip.isr.isrTask.IsrTask.darkCorrection (   self,
  exposure,
  darkExposure,
  invert = False 
)

Apply dark correction in place.

Parameters

exposure : lsst.afw.image.Exposure Exposure to process. darkExposure : lsst.afw.image.Exposure Dark exposure of the same size as exposure. invert : Bool, optional If True, re-add the dark to an already corrected image.

Raises

RuntimeError Raised if either exposure or darkExposure do not have their dark time defined.

See Also

lsst.ip.isr.isrFunctions.darkCorrection

Definition at line 1441 of file isrTask.py.

1441  def darkCorrection(self, exposure, darkExposure, invert=False):
1442  """!Apply dark correction in place.
1443 
1444  Parameters
1445  ----------
1446  exposure : `lsst.afw.image.Exposure`
1447  Exposure to process.
1448  darkExposure : `lsst.afw.image.Exposure`
1449  Dark exposure of the same size as ``exposure``.
1450  invert : `Bool`, optional
1451  If True, re-add the dark to an already corrected image.
1452 
1453  Raises
1454  ------
1455  RuntimeError
1456  Raised if either ``exposure`` or ``darkExposure`` do not
1457  have their dark time defined.
1458 
1459  See Also
1460  --------
1461  lsst.ip.isr.isrFunctions.darkCorrection
1462  """
1463  expScale = exposure.getInfo().getVisitInfo().getDarkTime()
1464  if math.isnan(expScale):
1465  raise RuntimeError("Exposure darktime is NAN")
1466  darkScale = darkExposure.getInfo().getVisitInfo().getDarkTime()
1467  if math.isnan(darkScale):
1468  raise RuntimeError("Dark calib darktime is NAN")
1469  isrFunctions.darkCorrection(
1470  maskedImage=exposure.getMaskedImage(),
1471  darkMaskedImage=darkExposure.getMaskedImage(),
1472  expScale=expScale,
1473  darkScale=darkScale,
1474  invert=invert,
1475  trimToFit=self.config.doTrimToMatchCalib
1476  )
1477 
def darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False, trimToFit=False)

◆ debugView()

def lsst.ip.isr.isrTask.IsrTask.debugView (   self,
  exposure,
  stepname 
)
Utility function to examine ISR exposure at different stages.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to view.
stepname : `str`
    State of processing to view.

Definition at line 1822 of file isrTask.py.

1822  def debugView(self, exposure, stepname):
1823  """Utility function to examine ISR exposure at different stages.
1824 
1825  Parameters
1826  ----------
1827  exposure : `lsst.afw.image.Exposure`
1828  Exposure to view.
1829  stepname : `str`
1830  State of processing to view.
1831  """
1832  frame = getDebugFrame(self._display, stepname)
1833  if frame:
1834  display = getDisplay(frame)
1835  display.scale('asinh', 'zscale')
1836  display.mtv(exposure)
1837 
1838 
def getDebugFrame(debugDisplay, name)
Definition: lsstDebug.py:90

◆ doLinearize()

def lsst.ip.isr.isrTask.IsrTask.doLinearize (   self,
  detector 
)

Check if linearization is needed for the detector cameraGeom.

Checks config.doLinearize and the linearity type of the first amplifier.

Parameters

detector : lsst.afw.cameraGeom.Detector Detector to get linearity type from.

Returns

doLinearize : Bool If True, linearization should be performed.

Definition at line 1478 of file isrTask.py.

1478  def doLinearize(self, detector):
1479  """!Check if linearization is needed for the detector cameraGeom.
1480 
1481  Checks config.doLinearize and the linearity type of the first
1482  amplifier.
1483 
1484  Parameters
1485  ----------
1486  detector : `lsst.afw.cameraGeom.Detector`
1487  Detector to get linearity type from.
1488 
1489  Returns
1490  -------
1491  doLinearize : `Bool`
1492  If True, linearization should be performed.
1493  """
1494  return self.config.doLinearize and \
1495  detector.getAmpInfoCatalog()[0].getLinearityType() != NullLinearityType
1496 

◆ flatContext()

def lsst.ip.isr.isrTask.IsrTask.flatContext (   self,
  exp,
  flat,
  dark = None 
)
Context manager that applies and removes flats and darks,
if the task is configured to apply them.

Parameters
----------
exp : `lsst.afw.image.Exposure`
    Exposure to process.
flat : `lsst.afw.image.Exposure`
    Flat exposure the same size as ``exp``.
dark : `lsst.afw.image.Exposure`, optional
    Dark exposure the same size as ``exp``.

Yields
------
exp : `lsst.afw.image.Exposure`
    The flat and dark corrected exposure.

Definition at line 1792 of file isrTask.py.

1792  def flatContext(self, exp, flat, dark=None):
1793  """Context manager that applies and removes flats and darks,
1794  if the task is configured to apply them.
1795 
1796  Parameters
1797  ----------
1798  exp : `lsst.afw.image.Exposure`
1799  Exposure to process.
1800  flat : `lsst.afw.image.Exposure`
1801  Flat exposure the same size as ``exp``.
1802  dark : `lsst.afw.image.Exposure`, optional
1803  Dark exposure the same size as ``exp``.
1804 
1805  Yields
1806  ------
1807  exp : `lsst.afw.image.Exposure`
1808  The flat and dark corrected exposure.
1809  """
1810  if self.config.doDark and dark is not None:
1811  self.darkCorrection(exp, dark)
1812  if self.config.doFlat:
1813  self.flatCorrection(exp, flat)
1814  try:
1815  yield exp
1816  finally:
1817  if self.config.doFlat:
1818  self.flatCorrection(exp, flat, invert=True)
1819  if self.config.doDark and dark is not None:
1820  self.darkCorrection(exp, dark, invert=True)
1821 

◆ flatCorrection()

def lsst.ip.isr.isrTask.IsrTask.flatCorrection (   self,
  exposure,
  flatExposure,
  invert = False 
)

Apply flat correction in place.

Parameters

exposure : lsst.afw.image.Exposure Exposure to process. flatExposure : lsst.afw.image.Exposure Flat exposure of the same size as exposure. invert : Bool, optional If True, unflatten an already flattened image.

See Also

lsst.ip.isr.isrFunctions.flatCorrection

Definition at line 1497 of file isrTask.py.

1497  def flatCorrection(self, exposure, flatExposure, invert=False):
1498  """!Apply flat correction in place.
1499 
1500  Parameters
1501  ----------
1502  exposure : `lsst.afw.image.Exposure`
1503  Exposure to process.
1504  flatExposure : `lsst.afw.image.Exposure`
1505  Flat exposure of the same size as ``exposure``.
1506  invert : `Bool`, optional
1507  If True, unflatten an already flattened image.
1508 
1509  See Also
1510  --------
1511  lsst.ip.isr.isrFunctions.flatCorrection
1512  """
1513  isrFunctions.flatCorrection(
1514  maskedImage=exposure.getMaskedImage(),
1515  flatMaskedImage=flatExposure.getMaskedImage(),
1516  scalingType=self.config.flatScalingType,
1517  userScale=self.config.flatUserScale,
1518  invert=invert,
1519  trimToFit=self.config.doTrimToMatchCalib
1520  )
1521 
def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False, trimToFit=False)

◆ getIsrExposure()

def lsst.ip.isr.isrTask.IsrTask.getIsrExposure (   self,
  dataRef,
  datasetType,
  immediate = True 
)

Retrieve a calibration dataset for removing instrument signature.

Parameters

dataRef : daf.persistence.butlerSubset.ButlerDataRef DataRef of the detector data to find calibration datasets for. datasetType : str Type of dataset to retrieve (e.g. 'bias', 'flat', etc). immediate : Bool If True, disable butler proxies to enable error handling within this routine.

Returns

exposure : lsst.afw.image.Exposure Requested calibration frame.

Raises

RuntimeError Raised if no matching calibration frame can be found.

Definition at line 1098 of file isrTask.py.

1098  def getIsrExposure(self, dataRef, datasetType, immediate=True):
1099  """!Retrieve a calibration dataset for removing instrument signature.
1100 
1101  Parameters
1102  ----------
1103 
1104  dataRef : `daf.persistence.butlerSubset.ButlerDataRef`
1105  DataRef of the detector data to find calibration datasets
1106  for.
1107  datasetType : `str`
1108  Type of dataset to retrieve (e.g. 'bias', 'flat', etc).
1109  immediate : `Bool`
1110  If True, disable butler proxies to enable error handling
1111  within this routine.
1112 
1113  Returns
1114  -------
1115  exposure : `lsst.afw.image.Exposure`
1116  Requested calibration frame.
1117 
1118  Raises
1119  ------
1120  RuntimeError
1121  Raised if no matching calibration frame can be found.
1122  """
1123  try:
1124  exp = dataRef.get(datasetType, immediate=immediate)
1125  except Exception as exc1:
1126  if not self.config.fallbackFilterName:
1127  raise RuntimeError("Unable to retrieve %s for %s: %s" % (datasetType, dataRef.dataId, exc1))
1128  try:
1129  exp = dataRef.get(datasetType, filter=self.config.fallbackFilterName, immediate=immediate)
1130  except Exception as exc2:
1131  raise RuntimeError("Unable to retrieve %s for %s, even with fallback filter %s: %s AND %s" %
1132  (datasetType, dataRef.dataId, self.config.fallbackFilterName, exc1, exc2))
1133  self.log.warn("Using fallback calibration from filter %s" % self.config.fallbackFilterName)
1134 
1135  if self.config.doAssembleIsrExposures:
1136  exp = self.assembleCcd.assembleCcd(exp)
1137  return exp
1138 

◆ maskAmplifier()

def lsst.ip.isr.isrTask.IsrTask.maskAmplifier (   self,
  ccdExposure,
  amp,
  defects 
)
Identify bad amplifiers, saturated and suspect pixels.

Parameters
----------
ccdExposure : `lsst.afw.image.Exposure`
    Input exposure to be masked.
amp : `lsst.afw.table.AmpInfoCatalog`
    Catalog of parameters defining the amplifier on this
    exposure to mask.
defects : `list`
    List of defects.  Used to determine if the entire
    amplifier is bad.

Returns
-------
badAmp : `Bool`
    If this is true, the entire amplifier area is covered by
    defects and unusable.

Definition at line 1175 of file isrTask.py.

1175  def maskAmplifier(self, ccdExposure, amp, defects):
1176  """Identify bad amplifiers, saturated and suspect pixels.
1177 
1178  Parameters
1179  ----------
1180  ccdExposure : `lsst.afw.image.Exposure`
1181  Input exposure to be masked.
1182  amp : `lsst.afw.table.AmpInfoCatalog`
1183  Catalog of parameters defining the amplifier on this
1184  exposure to mask.
1185  defects : `list`
1186  List of defects. Used to determine if the entire
1187  amplifier is bad.
1188 
1189  Returns
1190  -------
1191  badAmp : `Bool`
1192  If this is true, the entire amplifier area is covered by
1193  defects and unusable.
1194 
1195  """
1196  maskedImage = ccdExposure.getMaskedImage()
1197 
1198  badAmp = False
1199 
1200  # Check if entire amp region is defined as a defect (need to use amp.getBBox() for correct
1201  # comparison with current defects definition.
1202  if defects is not None:
1203  badAmp = bool(sum([v.getBBox().contains(amp.getBBox()) for v in defects]))
1204 
1205  # In the case of a bad amp, we will set mask to "BAD" (here use amp.getRawBBox() for correct
1206  # association with pixels in current ccdExposure).
1207  if badAmp:
1208  dataView = afwImage.MaskedImageF(maskedImage, amp.getRawBBox(),
1209  afwImage.PARENT)
1210  maskView = dataView.getMask()
1211  maskView |= maskView.getPlaneBitMask("BAD")
1212  del maskView
1213  return badAmp
1214 
1215  # Mask remaining defects after assembleCcd() to allow for defects that cross amplifier boundaries.
1216  # Saturation and suspect pixels can be masked now, though.
1217  limits = dict()
1218  if self.config.doSaturation and not badAmp:
1219  limits.update({self.config.saturatedMaskName: amp.getSaturation()})
1220  if self.config.doSuspect and not badAmp:
1221  limits.update({self.config.suspectMaskName: amp.getSuspectLevel()})
1222 
1223  for maskName, maskThreshold in limits.items():
1224  if not math.isnan(maskThreshold):
1225  dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
1226  isrFunctions.makeThresholdMask(
1227  maskedImage=dataView,
1228  threshold=maskThreshold,
1229  growFootprints=0,
1230  maskName=maskName
1231  )
1232 
1233  # Determine if we've fully masked this amplifier with SUSPECT and SAT pixels.
1234  maskView = afwImage.Mask(maskedImage.getMask(), amp.getRawDataBBox(),
1235  afwImage.PARENT)
1236  maskVal = maskView.getPlaneBitMask([self.config.saturatedMaskName,
1237  self.config.suspectMaskName])
1238  if numpy.all(maskView.getArray() & maskVal > 0):
1239  badAmp = True
1240 
1241  return badAmp
1242 
bool contains(VertexIterator const begin, VertexIterator const end, UnitVector3d const &v)
Represent a 2-dimensional array of bitmask pixels.
Definition: Mask.h:78

◆ maskAndInterpDefect()

def lsst.ip.isr.isrTask.IsrTask.maskAndInterpDefect (   self,
  ccdExposure,
  defectBaseList 
)

Mask defects using mask plane "BAD" and interpolate over them, in place.

Parameters

ccdExposure : lsst.afw.image.Exposure Exposure to process. defectBaseList : List List of defects to mask and interpolate.

Notes

Call this after CCD assembly, since defects may cross amplifier boundaries.

Definition at line 1606 of file isrTask.py.

1606  def maskAndInterpDefect(self, ccdExposure, defectBaseList):
1607  """!Mask defects using mask plane "BAD" and interpolate over them, in place.
1608 
1609  Parameters
1610  ----------
1611  ccdExposure : `lsst.afw.image.Exposure`
1612  Exposure to process.
1613  defectBaseList : `List`
1614  List of defects to mask and interpolate.
1615 
1616  Notes
1617  -----
1618  Call this after CCD assembly, since defects may cross amplifier boundaries.
1619  """
1620  maskedImage = ccdExposure.getMaskedImage()
1621  defectList = []
1622  for d in defectBaseList:
1623  bbox = d.getBBox()
1624  nd = measAlg.Defect(bbox)
1625  defectList.append(nd)
1626  isrFunctions.maskPixelsFromDefectList(maskedImage, defectList, maskName='BAD')
1627  isrFunctions.interpolateDefectList(
1628  maskedImage=maskedImage,
1629  defectList=defectList,
1630  fwhm=self.config.fwhm,
1631  )
1632 
1633  if self.config.numEdgeSuspect > 0:
1634  goodBBox = maskedImage.getBBox()
1635  # This makes a bbox numEdgeSuspect pixels smaller than the image on each side
1636  goodBBox.grow(-self.config.numEdgeSuspect)
1637  # Mask pixels outside goodBBox as SUSPECT
1638  SourceDetectionTask.setEdgeBits(
1639  maskedImage,
1640  goodBBox,
1641  maskedImage.getMask().getPlaneBitMask("SUSPECT")
1642  )
1643 

◆ maskAndInterpNan()

def lsst.ip.isr.isrTask.IsrTask.maskAndInterpNan (   self,
  exposure 
)

Mask NaNs using mask plane "UNMASKEDNAN" and interpolate over them, in place.

Parameters

exposure : lsst.afw.image.Exposure Exposure to process.

Notes

We mask and interpolate over all NaNs, including those that are masked with other bits (because those may or may not be interpolated over later, and we want to remove all NaNs). Despite this behaviour, the "UNMASKEDNAN" mask plane is used to preserve the historical name.

Definition at line 1644 of file isrTask.py.

1644  def maskAndInterpNan(self, exposure):
1645  """!Mask NaNs using mask plane "UNMASKEDNAN" and interpolate over them, in place.
1646 
1647  Parameters
1648  ----------
1649  exposure : `lsst.afw.image.Exposure`
1650  Exposure to process.
1651 
1652  Notes
1653  -----
1654  We mask and interpolate over all NaNs, including those
1655  that are masked with other bits (because those may or may
1656  not be interpolated over later, and we want to remove all
1657  NaNs). Despite this behaviour, the "UNMASKEDNAN" mask plane
1658  is used to preserve the historical name.
1659  """
1660  maskedImage = exposure.getMaskedImage()
1661 
1662  # Find and mask NaNs
1663  maskedImage.getMask().addMaskPlane("UNMASKEDNAN")
1664  maskVal = maskedImage.getMask().getPlaneBitMask("UNMASKEDNAN")
1665  numNans = maskNans(maskedImage, maskVal)
1666  self.metadata.set("NUMNANS", numNans)
1667 
1668  # Interpolate over these previously-unmasked NaNs
1669  if numNans > 0:
1670  self.log.warn("There were %i unmasked NaNs", numNans)
1671  nanDefectList = isrFunctions.getDefectListFromMask(
1672  maskedImage=maskedImage,
1673  maskName='UNMASKEDNAN',
1674  )
1675  isrFunctions.interpolateDefectList(
1676  maskedImage=exposure.getMaskedImage(),
1677  defectList=nanDefectList,
1678  fwhm=self.config.fwhm,
1679  )
1680 
daf::base::PropertySet * set
Definition: fits.cc:832
size_t maskNans(afw::image::MaskedImage< PixelT > const &mi, afw::image::MaskPixel maskVal, afw::image::MaskPixel allow=0)
Mask NANs in an image.
Definition: Isr.cc:34

◆ measureBackground()

def lsst.ip.isr.isrTask.IsrTask.measureBackground (   self,
  exposure,
  IsrQaConfig = None 
)
Measure the image background in subgrids, for quality control purposes.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to process.
IsrQaConfig : `lsst.ip.isr.isrQa.IsrQaConfig`
    Configuration object containing parameters on which background
    statistics and subgrids to use.

Definition at line 1681 of file isrTask.py.

1681  def measureBackground(self, exposure, IsrQaConfig=None):
1682  """Measure the image background in subgrids, for quality control purposes.
1683 
1684  Parameters
1685  ----------
1686  exposure : `lsst.afw.image.Exposure`
1687  Exposure to process.
1688  IsrQaConfig : `lsst.ip.isr.isrQa.IsrQaConfig`
1689  Configuration object containing parameters on which background
1690  statistics and subgrids to use.
1691  """
1692  if IsrQaConfig is not None:
1693  statsControl = afwMath.StatisticsControl(IsrQaConfig.flatness.clipSigma,
1694  IsrQaConfig.flatness.nIter)
1695  maskVal = exposure.getMaskedImage().getMask().getPlaneBitMask(["BAD", "SAT", "DETECTED"])
1696  statsControl.setAndMask(maskVal)
1697  maskedImage = exposure.getMaskedImage()
1698  stats = afwMath.makeStatistics(maskedImage, afwMath.MEDIAN | afwMath.STDEVCLIP, statsControl)
1699  skyLevel = stats.getValue(afwMath.MEDIAN)
1700  skySigma = stats.getValue(afwMath.STDEVCLIP)
1701  self.log.info("Flattened sky level: %f +/- %f" % (skyLevel, skySigma))
1702  metadata = exposure.getMetadata()
1703  metadata.set('SKYLEVEL', skyLevel)
1704  metadata.set('SKYSIGMA', skySigma)
1705 
1706  # calcluating flatlevel over the subgrids
1707  stat = afwMath.MEANCLIP if IsrQaConfig.flatness.doClip else afwMath.MEAN
1708  meshXHalf = int(IsrQaConfig.flatness.meshX/2.)
1709  meshYHalf = int(IsrQaConfig.flatness.meshY/2.)
1710  nX = int((exposure.getWidth() + meshXHalf) / IsrQaConfig.flatness.meshX)
1711  nY = int((exposure.getHeight() + meshYHalf) / IsrQaConfig.flatness.meshY)
1712  skyLevels = numpy.zeros((nX, nY))
1713 
1714  for j in range(nY):
1715  yc = meshYHalf + j * IsrQaConfig.flatness.meshY
1716  for i in range(nX):
1717  xc = meshXHalf + i * IsrQaConfig.flatness.meshX
1718 
1719  xLLC = xc - meshXHalf
1720  yLLC = yc - meshYHalf
1721  xURC = xc + meshXHalf - 1
1722  yURC = yc + meshYHalf - 1
1723 
1724  bbox = afwGeom.Box2I(afwGeom.Point2I(xLLC, yLLC), afwGeom.Point2I(xURC, yURC))
1725  miMesh = maskedImage.Factory(exposure.getMaskedImage(), bbox, afwImage.LOCAL)
1726 
1727  skyLevels[i, j] = afwMath.makeStatistics(miMesh, stat, statsControl).getValue()
1728 
1729  good = numpy.where(numpy.isfinite(skyLevels))
1730  skyMedian = numpy.median(skyLevels[good])
1731  flatness = (skyLevels[good] - skyMedian) / skyMedian
1732  flatness_rms = numpy.std(flatness)
1733  flatness_pp = flatness.max() - flatness.min() if len(flatness) > 0 else numpy.nan
1734 
1735  self.log.info("Measuring sky levels in %dx%d grids: %f" % (nX, nY, skyMedian))
1736  self.log.info("Sky flatness in %dx%d grids - pp: %f rms: %f" %
1737  (nX, nY, flatness_pp, flatness_rms))
1738 
1739  metadata.set('FLATNESS_PP', float(flatness_pp))
1740  metadata.set('FLATNESS_RMS', float(flatness_rms))
1741  metadata.set('FLATNESS_NGRIDS', '%dx%d' % (nX, nY))
1742  metadata.set('FLATNESS_MESHX', IsrQaConfig.flatness.meshX)
1743  metadata.set('FLATNESS_MESHY', IsrQaConfig.flatness.meshY)
1744 
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
An integer coordinate rectangle.
Definition: Box.h:54

◆ overscanCorrection()

def lsst.ip.isr.isrTask.IsrTask.overscanCorrection (   self,
  ccdExposure,
  amp 
)
Apply overscan correction in place.

This method does initial pixel rejection of the overscan
region.  The overscan can also be optionally segmented to
allow for discontinuous overscan responses to be fit
separately.  The actual overscan subtraction is performed by
the `lsst.ip.isr.isrFunctions.overscanCorrection` function,
which is called here after the amplifier is preprocessed.

Parameters
----------
ccdExposure : `lsst.afw.image.Exposure`
    Exposure to have overscan correction performed.
amp : `lsst.afw.table.AmpInfoCatalog`
    The amplifier to consider while correcting the overscan.

Returns
-------
overscanResults : `lsst.pipe.base.Struct`
    Result struct with components:
    - ``imageFit`` : scalar or `lsst.afw.image.Image`
Value or fit subtracted from the amplifier image data.
    - ``overscanFit`` : scalar or `lsst.afw.image.Image`
Value or fit subtracted from the overscan image data.
    - ``overscanImage`` : `lsst.afw.image.Image`
Image of the overscan region with the overscan
correction applied. This quantity is used to estimate
the amplifier read noise empirically.

Raises
------
RuntimeError
    Raised if the ``amp`` does not contain raw pixel information.

See Also
--------
lsst.ip.isr.isrFunctions.overscanCorrection

Definition at line 1243 of file isrTask.py.

1243  def overscanCorrection(self, ccdExposure, amp):
1244  """Apply overscan correction in place.
1245 
1246  This method does initial pixel rejection of the overscan
1247  region. The overscan can also be optionally segmented to
1248  allow for discontinuous overscan responses to be fit
1249  separately. The actual overscan subtraction is performed by
1250  the `lsst.ip.isr.isrFunctions.overscanCorrection` function,
1251  which is called here after the amplifier is preprocessed.
1252 
1253  Parameters
1254  ----------
1255  ccdExposure : `lsst.afw.image.Exposure`
1256  Exposure to have overscan correction performed.
1257  amp : `lsst.afw.table.AmpInfoCatalog`
1258  The amplifier to consider while correcting the overscan.
1259 
1260  Returns
1261  -------
1262  overscanResults : `lsst.pipe.base.Struct`
1263  Result struct with components:
1264  - ``imageFit`` : scalar or `lsst.afw.image.Image`
1265  Value or fit subtracted from the amplifier image data.
1266  - ``overscanFit`` : scalar or `lsst.afw.image.Image`
1267  Value or fit subtracted from the overscan image data.
1268  - ``overscanImage`` : `lsst.afw.image.Image`
1269  Image of the overscan region with the overscan
1270  correction applied. This quantity is used to estimate
1271  the amplifier read noise empirically.
1272 
1273  Raises
1274  ------
1275  RuntimeError
1276  Raised if the ``amp`` does not contain raw pixel information.
1277 
1278  See Also
1279  --------
1280  lsst.ip.isr.isrFunctions.overscanCorrection
1281  """
1282  if not amp.getHasRawInfo():
1283  raise RuntimeError("This method must be executed on an amp with raw information.")
1284 
1285  if amp.getRawHorizontalOverscanBBox().isEmpty():
1286  self.log.info("ISR_OSCAN: No overscan region. Not performing overscan correction.")
1287  return None
1288 
1289  # Construct views
1290  ampImage = afwImage.MaskedImageF(ccdExposure.getMaskedImage(), amp.getRawDataBBox(),
1291  afwImage.PARENT)
1292  overscanImage = afwImage.MaskedImageF(ccdExposure.getMaskedImage(),
1293  amp.getRawHorizontalOverscanBBox(),
1294  afwImage.PARENT)
1295  overscanArray = overscanImage.getImage().getArray()
1296 
1297  statControl = afwMath.StatisticsControl()
1298  statControl.setAndMask(ccdExposure.getMaskedImage().getMask().getPlaneBitMask("SAT"))
1299 
1300  # Determine the bounding boxes
1301  dataBBox = amp.getRawDataBBox()
1302  oscanBBox = amp.getRawHorizontalOverscanBBox()
1303  x0 = 0
1304  x1 = 0
1305 
1306  prescanBBox = amp.getRawPrescanBBox()
1307  if (oscanBBox.getBeginX() > prescanBBox.getBeginX()): # amp is at the right
1308  x0 += self.config.overscanNumLeadingColumnsToSkip
1309  x1 -= self.config.overscanNumTrailingColumnsToSkip
1310  else:
1311  x0 += self.config.overscanNumTrailingColumnsToSkip
1312  x1 -= self.config.overscanNumLeadingColumnsToSkip
1313 
1314  # Determine if we need to work on subregions of the amplifier and overscan.
1315  imageBBoxes = []
1316  overscanBBoxes = []
1317 
1318  if ((self.config.overscanBiasJump and
1319  self.config.overscanBiasJumpLocation) and
1320  (ccdExposure.getMetadata().exists(self.config.overscanBiasJumpKeyword) and
1321  ccdExposure.getMetadata().getScalar(self.config.overscanBiasJumpKeyword) in
1322  self.config.overscanBiasJumpDevices)):
1323  if amp.getReadoutCorner() in (afwTable.LL, afwTable.LR):
1324  yLower = self.config.overscanBiasJumpLocation
1325  yUpper = dataBBox.getHeight() - yLower
1326  else:
1327  yUpper = self.config.overscanBiasJumpLocation
1328  yLower = dataBBox.getHeight() - yUpper
1329 
1330  imageBBoxes.append(afwGeom.Box2I(dataBBox.getBegin(),
1331  afwGeom.Extent2I(dataBBox.getWidth(), yLower)))
1332  overscanBBoxes.append(afwGeom.Box2I(oscanBBox.getBegin() +
1333  afwGeom.Extent2I(x0, 0),
1334  afwGeom.Extent2I(oscanBBox.getWidth() + x1, yLower)))
1335 
1336  imageBBoxes.append(afwGeom.Box2I(dataBBox.getBegin() + afwGeom.Extent2I(0, yLower),
1337  afwGeom.Extent2I(dataBBox.getWidth(), yUpper)))
1338 
1339  overscanBBoxes.append(afwGeom.Box2I(oscanBBox.getBegin() + afwGeom.Extent2I(x0, yLower),
1340  afwGeom.Extent2I(oscanBBox.getWidth() + x1, yUpper)))
1341  else:
1342  imageBBoxes.append(afwGeom.Box2I(dataBBox.getBegin(),
1343  afwGeom.Extent2I(dataBBox.getWidth(), dataBBox.getHeight())))
1344 
1345  overscanBBoxes.append(afwGeom.Box2I(oscanBBox.getBegin() + afwGeom.Extent2I(x0, 0),
1346  afwGeom.Extent2I(oscanBBox.getWidth() + x1,
1347  oscanBBox.getHeight())))
1348 
1349  # Perform overscan correction on subregions, ensuring saturated pixels are masked.
1350  for imageBBox, overscanBBox in zip(imageBBoxes, overscanBBoxes):
1351  ampImage = afwImage.MaskedImageF(ccdExposure.getMaskedImage(), imageBBox,
1352  afwImage.PARENT)
1353  overscanImage = afwImage.MaskedImageF(ccdExposure.getMaskedImage(), overscanBBox,
1354  afwImage.PARENT)
1355 
1356  overscanArray = overscanImage.getImage().getArray()
1357  median = numpy.ma.median(numpy.ma.masked_where(overscanImage.getMask().getArray(),
1358  overscanArray))
1359  bad = numpy.where(numpy.abs(overscanArray - median) > self.config.overscanMaxDev)
1360  overscanImage.getMask().getArray()[bad] = overscanImage.getMask().getPlaneBitMask("SAT")
1361 
1362  statControl = afwMath.StatisticsControl()
1363  statControl.setAndMask(ccdExposure.getMaskedImage().getMask().getPlaneBitMask("SAT"))
1364 
1365  overscanResults = isrFunctions.overscanCorrection(ampMaskedImage=ampImage,
1366  overscanImage=overscanImage,
1367  fitType=self.config.overscanFitType,
1368  order=self.config.overscanOrder,
1369  collapseRej=self.config.overscanNumSigmaClip,
1370  statControl=statControl,
1371  overscanIsInt=self.config.overscanIsInt
1372  )
1373 
1374  # Measure average overscan levels and record them in the metadata
1375  levelStat = afwMath.MEDIAN
1376  sigmaStat = afwMath.STDEVCLIP
1377 
1378  sctrl = afwMath.StatisticsControl(self.config.qa.flatness.clipSigma,
1379  self.config.qa.flatness.nIter)
1380  metadata = ccdExposure.getMetadata()
1381  ampNum = amp.getName()
1382  if self.config.overscanFitType in ("MEDIAN", "MEAN", "MEANCLIP"):
1383  metadata.set("ISR_OSCAN_LEVEL%s" % ampNum, overscanResults.overscanFit)
1384  metadata.set("ISR_OSCAN_SIGMA%s" % ampNum, 0.0)
1385  else:
1386  stats = afwMath.makeStatistics(overscanResults.overscanFit, levelStat | sigmaStat, sctrl)
1387  metadata.set("ISR_OSCAN_LEVEL%s" % ampNum, stats.getValue(levelStat))
1388  metadata.set("ISR_OSCAN_SIGMA%s" % ampNum, stats.getValue(sigmaStat))
1389 
1390  return overscanResults
1391 
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
def exists(env)
Definition: cuda.py:60
An integer coordinate rectangle.
Definition: Box.h:54
def overscanCorrection(ampMaskedImage, overscanImage, fitType='MEDIAN', order=1, collapseRej=3.0, statControl=None, overscanIsInt=True)

◆ readIsrData()

def lsst.ip.isr.isrTask.IsrTask.readIsrData (   self,
  dataRef,
  rawExposure 
)

Retrieve necessary frames for instrument signature removal.

Pre-fetching all required ISR data products limits the IO required by the ISR. Any conflict between the calibration data available and that needed for ISR is also detected prior to doing processing, allowing it to fail quickly.

Parameters

dataRef : daf.persistence.butlerSubset.ButlerDataRef Butler reference of the detector data to be processed rawExposure : afw.image.Exposure The raw exposure that will later be corrected with the retrieved calibration data; should not be modified in this method.

Returns

result : lsst.pipe.base.Struct Result struct with components (which may be None):

fringes: fringe calibration frame (afw.image.Exposure)

  • seed: random seed derived from the ccdExposureId for random number generator (uint32)
    • 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.

Definition at line 587 of file isrTask.py.

587  def readIsrData(self, dataRef, rawExposure):
588  """!Retrieve necessary frames for instrument signature removal.
589 
590  Pre-fetching all required ISR data products limits the IO
591  required by the ISR. Any conflict between the calibration data
592  available and that needed for ISR is also detected prior to
593  doing processing, allowing it to fail quickly.
594 
595  Parameters
596  ----------
597  dataRef : `daf.persistence.butlerSubset.ButlerDataRef`
598  Butler reference of the detector data to be processed
599  rawExposure : `afw.image.Exposure`
600  The raw exposure that will later be corrected with the
601  retrieved calibration data; should not be modified in this
602  method.
603 
604  Returns
605  -------
606  result : `lsst.pipe.base.Struct`
607  Result struct with components (which may be `None`):
608  - ``bias``: bias calibration frame (`afw.image.Exposure`)
609  - ``linearizer``: functor for linearization (`ip.isr.linearize.LinearizeBase`)
610  - ``crosstalkSources``: list of possible crosstalk sources (`list`)
611  - ``dark``: dark calibration frame (`afw.image.Exposure`)
612  - ``flat``: flat calibration frame (`afw.image.Exposure`)
613  - ``bfKernel``: Brighter-Fatter kernel (`numpy.ndarray`)
614  - ``defects``: list of defects (`list`)
615  - ``fringes``: `lsst.pipe.base.Struct` with components:
616  - ``fringes``: fringe calibration frame (`afw.image.Exposure`)
617  - ``seed``: random seed derived from the ccdExposureId for random
618  number generator (`uint32`)
619  - ``opticsTransmission``: `lsst.afw.image.TransmissionCurve`
620  A ``TransmissionCurve`` that represents the throughput of the optics,
621  to be evaluated in focal-plane coordinates.
622  - ``filterTransmission`` : `lsst.afw.image.TransmissionCurve`
623  A ``TransmissionCurve`` that represents the throughput of the filter
624  itself, to be evaluated in focal-plane coordinates.
625  - ``sensorTransmission`` : `lsst.afw.image.TransmissionCurve`
626  A ``TransmissionCurve`` that represents the throughput of the sensor
627  itself, to be evaluated in post-assembly trimmed detector coordinates.
628  - ``atmosphereTransmission`` : `lsst.afw.image.TransmissionCurve`
629  A ``TransmissionCurve`` that represents the throughput of the
630  atmosphere, assumed to be spatially constant.
631 
632  """
633  ccd = rawExposure.getDetector()
634  rawExposure.mask.addMaskPlane("UNMASKEDNAN") # needed to match pre DM-15862 processing.
635  biasExposure = (self.getIsrExposure(dataRef, self.config.biasDataProductName)
636  if self.config.doBias else None)
637  # immediate=True required for functors and linearizers are functors; see ticket DM-6515
638  linearizer = (dataRef.get("linearizer", immediate=True)
639  if self.doLinearize(ccd) else None)
640  crosstalkSources = (self.crosstalk.prepCrosstalk(dataRef)
641  if self.config.doCrosstalk else None)
642  darkExposure = (self.getIsrExposure(dataRef, self.config.darkDataProductName)
643  if self.config.doDark else None)
644  flatExposure = (self.getIsrExposure(dataRef, self.config.flatDataProductName)
645  if self.config.doFlat else None)
646  brighterFatterKernel = (dataRef.get("bfKernel")
647  if self.config.doBrighterFatter else None)
648  defectList = (dataRef.get("defects")
649  if self.config.doDefect else None)
650  fringeStruct = (self.fringe.readFringes(dataRef, assembler=self.assembleCcd
651  if self.config.doAssembleIsrExposures else None)
652  if self.config.doFringe and self.fringe.checkFilter(rawExposure)
653  else pipeBase.Struct(fringes=None))
654 
655  if self.config.doAttachTransmissionCurve:
656  opticsTransmission = (dataRef.get("transmission_optics")
657  if self.config.doUseOpticsTransmission else None)
658  filterTransmission = (dataRef.get("transmission_filter")
659  if self.config.doUseFilterTransmission else None)
660  sensorTransmission = (dataRef.get("transmission_sensor")
661  if self.config.doUseSensorTransmission else None)
662  atmosphereTransmission = (dataRef.get("transmission_atmosphere")
663  if self.config.doUseAtmosphereTransmission else None)
664  else:
665  opticsTransmission = None
666  filterTransmission = None
667  sensorTransmission = None
668  atmosphereTransmission = None
669 
670  # Struct should include only kwargs to run()
671  return pipeBase.Struct(bias=biasExposure,
672  linearizer=linearizer,
673  crosstalkSources=crosstalkSources,
674  dark=darkExposure,
675  flat=flatExposure,
676  bfKernel=brighterFatterKernel,
677  defects=defectList,
678  fringes=fringeStruct,
679  opticsTransmission=opticsTransmission,
680  filterTransmission=filterTransmission,
681  sensorTransmission=sensorTransmission,
682  atmosphereTransmission=atmosphereTransmission,
683  )
684 

◆ roughZeroPoint()

def lsst.ip.isr.isrTask.IsrTask.roughZeroPoint (   self,
  exposure 
)
Set an approximate magnitude zero point for the exposure.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to process.

Definition at line 1745 of file isrTask.py.

1745  def roughZeroPoint(self, exposure):
1746  """Set an approximate magnitude zero point for the exposure.
1747 
1748  Parameters
1749  ----------
1750  exposure : `lsst.afw.image.Exposure`
1751  Exposure to process.
1752  """
1753  filterName = afwImage.Filter(exposure.getFilter().getId()).getName() # Canonical name for filter
1754  if filterName in self.config.fluxMag0T1:
1755  fluxMag0 = self.config.fluxMag0T1[filterName]
1756  else:
1757  self.log.warn("No rough magnitude zero point set for filter %s" % filterName)
1758  fluxMag0 = self.config.defaultFluxMag0T1
1759 
1760  expTime = exposure.getInfo().getVisitInfo().getExposureTime()
1761  if not expTime > 0: # handle NaN as well as <= 0
1762  self.log.warn("Non-positive exposure time; skipping rough zero point")
1763  return
1764 
1765  self.log.info("Setting rough magnitude zero point: %f" % (2.5*math.log10(fluxMag0*expTime),))
1766  exposure.getCalib().setFluxMag0(fluxMag0*expTime)
1767 
Holds an integer identifier for an LSST filter.
Definition: Filter.h:141

◆ run()

def lsst.ip.isr.isrTask.IsrTask.run (   self,
  ccdExposure,
  camera = None,
  bias = None,
  linearizer = None,
  crosstalkSources = None,
  dark = None,
  flat = None,
  bfKernel = None,
  defects = None,
  fringes = None,
  opticsTransmission = None,
  filterTransmission = None,
  sensorTransmission = None,
  atmosphereTransmission = None 
)

Perform instrument signature removal on an exposure.

Steps included in the ISR processing, in order performed, are:

  • saturation and suspect pixel masking
  • overscan subtraction
  • CCD assembly of individual amplifiers
  • bias subtraction
  • variance image construction
  • linearization of non-linear response
  • crosstalk masking
  • brighter-fatter correction
  • dark subtraction
  • fringe correction
  • stray light subtraction
  • flat correction
  • masking of known defects and camera specific features
  • vignette calculation
  • appending transmission curve and distortion model

Parameters

ccdExposure : lsst.afw.image.Exposure The raw exposure that is to be run through ISR. The exposure is modified by this method. camera : lsst.afw.cameraGeom.Camera, optional The camera geometry for this exposure. Used to select the distortion model appropriate for this data. bias : lsst.afw.image.Exposure, optional Bias calibration frame. linearizer : lsst.ip.isr.linearize.LinearizeBase, optional Functor for linearization. crosstalkSources : list, optional List of possible crosstalk sources. dark : lsst.afw.image.Exposure, optional Dark calibration frame. flat : lsst.afw.image.Exposure, optional Flat calibration frame. bfKernel : numpy.ndarray, optional Brighter-fatter kernel. defects : list, optional List of defects. fringes : lsst.pipe.base.Struct, optional Struct containing the fringe correction data, with elements:

  • fringes: fringe calibration frame (afw.image.Exposure)
  • seed: random seed derived from the ccdExposureId for random number generator (uint32) opticsTransmission: lsst.afw.image.TransmissionCurve, optional 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

result : lsst.pipe.base.Struct Result struct with component:

  • exposure : afw.image.Exposure The fully ISR corrected exposure.
  • ossThumb : numpy.ndarray Thumbnail image of the exposure after overscan subtraction.
  • flattenedThumb : numpy.ndarray Thumbnail image of the exposure after flat-field correction.

Raises

RuntimeError Raised if a configuration option is set to True, but the required calibration data has not been specified.

Notes

The current processed exposure can be viewed by setting the appropriate lsstDebug entries in the debug.display dictionary. The names of these entries correspond to some of the IsrTaskConfig Boolean options, with the value denoting the frame to use. The exposure is shown inside the matching option check and after the processing of that step has finished. The steps with debug points are:

doAssembleCcd doBias doCrosstalk doBrighterFatter doDark doFringe doStrayLight doFlat

In addition, setting the "postISRCCD" entry displays the exposure after all ISR processing has finished.

Definition at line 690 of file isrTask.py.

690  ):
691  """!Perform instrument signature removal on an exposure.
692 
693  Steps included in the ISR processing, in order performed, are:
694  - saturation and suspect pixel masking
695  - overscan subtraction
696  - CCD assembly of individual amplifiers
697  - bias subtraction
698  - variance image construction
699  - linearization of non-linear response
700  - crosstalk masking
701  - brighter-fatter correction
702  - dark subtraction
703  - fringe correction
704  - stray light subtraction
705  - flat correction
706  - masking of known defects and camera specific features
707  - vignette calculation
708  - appending transmission curve and distortion model
709 
710  Parameters
711  ----------
712  ccdExposure : `lsst.afw.image.Exposure`
713  The raw exposure that is to be run through ISR. The
714  exposure is modified by this method.
715  camera : `lsst.afw.cameraGeom.Camera`, optional
716  The camera geometry for this exposure. Used to select the
717  distortion model appropriate for this data.
718  bias : `lsst.afw.image.Exposure`, optional
719  Bias calibration frame.
720  linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional
721  Functor for linearization.
722  crosstalkSources : `list`, optional
723  List of possible crosstalk sources.
724  dark : `lsst.afw.image.Exposure`, optional
725  Dark calibration frame.
726  flat : `lsst.afw.image.Exposure`, optional
727  Flat calibration frame.
728  bfKernel : `numpy.ndarray`, optional
729  Brighter-fatter kernel.
730  defects : `list`, optional
731  List of defects.
732  fringes : `lsst.pipe.base.Struct`, optional
733  Struct containing the fringe correction data, with
734  elements:
735  - ``fringes``: fringe calibration frame (`afw.image.Exposure`)
736  - ``seed``: random seed derived from the ccdExposureId for random
737  number generator (`uint32`)
738  opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional
739  A ``TransmissionCurve`` that represents the throughput of the optics,
740  to be evaluated in focal-plane coordinates.
741  filterTransmission : `lsst.afw.image.TransmissionCurve`
742  A ``TransmissionCurve`` that represents the throughput of the filter
743  itself, to be evaluated in focal-plane coordinates.
744  sensorTransmission : `lsst.afw.image.TransmissionCurve`
745  A ``TransmissionCurve`` that represents the throughput of the sensor
746  itself, to be evaluated in post-assembly trimmed detector coordinates.
747  atmosphereTransmission : `lsst.afw.image.TransmissionCurve`
748  A ``TransmissionCurve`` that represents the throughput of the
749  atmosphere, assumed to be spatially constant.
750 
751  Returns
752  -------
753  result : `lsst.pipe.base.Struct`
754  Result struct with component:
755  - ``exposure`` : `afw.image.Exposure`
756  The fully ISR corrected exposure.
757  - ``ossThumb`` : `numpy.ndarray`
758  Thumbnail image of the exposure after overscan subtraction.
759  - ``flattenedThumb`` : `numpy.ndarray`
760  Thumbnail image of the exposure after flat-field correction.
761 
762  Raises
763  ------
764  RuntimeError
765  Raised if a configuration option is set to True, but the
766  required calibration data has not been specified.
767 
768  Notes
769  -----
770  The current processed exposure can be viewed by setting the
771  appropriate lsstDebug entries in the `debug.display`
772  dictionary. The names of these entries correspond to some of
773  the IsrTaskConfig Boolean options, with the value denoting the
774  frame to use. The exposure is shown inside the matching
775  option check and after the processing of that step has
776  finished. The steps with debug points are:
777 
778  doAssembleCcd
779  doBias
780  doCrosstalk
781  doBrighterFatter
782  doDark
783  doFringe
784  doStrayLight
785  doFlat
786 
787  In addition, setting the "postISRCCD" entry displays the
788  exposure after all ISR processing has finished.
789 
790  """
791  if isinstance(ccdExposure, ButlerDataRef):
792  return self.runDataRef(ccdExposure)
793 
794  self.log.info("Performing ISR on exposure %s" %
795  (ccdExposure.getInfo().getVisitInfo().getExposureId()))
796  ccd = ccdExposure.getDetector()
797 
798  if not ccd:
799  assert not self.config.doAssembleCcd, "You need a Detector to run assembleCcd"
800  ccd = [FakeAmp(ccdExposure, self.config)]
801 
802  # Validate Input
803  if self.config.doBias and bias is None:
804  raise RuntimeError("Must supply a bias exposure if config.doBias=True.")
805  if self.doLinearize(ccd) and linearizer is None:
806  raise RuntimeError("Must supply a linearizer if config.doLinearize=True for this detector.")
807  if self.config.doBrighterFatter and bfKernel is None:
808  raise RuntimeError("Must supply a kernel if config.doBrighterFatter=True.")
809  if self.config.doDark and dark is None:
810  raise RuntimeError("Must supply a dark exposure if config.doDark=True.")
811  if fringes is None:
812  fringes = pipeBase.Struct(fringes=None)
813  if self.config.doFringe and not isinstance(fringes, pipeBase.Struct):
814  raise RuntimeError("Must supply fringe exposure as a pipeBase.Struct.")
815  if self.config.doFlat and flat is None:
816  raise RuntimeError("Must supply a flat exposure if config.doFlat=True.")
817  if self.config.doDefect and defects is None:
818  raise RuntimeError("Must supply defects if config.doDefect=True.")
819  if self.config.doAddDistortionModel and camera is None:
820  raise RuntimeError("Must supply camera if config.doAddDistortionModel=True.")
821 
822  # Begin ISR processing.
823  if self.config.doConvertIntToFloat:
824  self.log.info("Converting exposure to floating point values")
825  ccdExposure = self.convertIntToFloat(ccdExposure)
826 
827  # Amplifier level processing.
828  overscans = []
829  for amp in ccd:
830  # if ccdExposure is one amp, check for coverage to prevent performing ops multiple times
831  if ccdExposure.getBBox().contains(amp.getBBox()):
832  # Check for fully masked bad amplifiers, and generate masks for SUSPECT and SATURATED values.
833  badAmp = self.maskAmplifier(ccdExposure, amp, defects)
834 
835  if self.config.doOverscan and not badAmp:
836  # Overscan correction on amp-by-amp basis.
837  overscanResults = self.overscanCorrection(ccdExposure, amp)
838  self.log.debug("Corrected overscan for amplifier %s" % (amp.getName()))
839  if self.config.qa is not None and self.config.qa.saveStats is True:
840  if isinstance(overscanResults.overscanFit, float):
841  qaMedian = overscanResults.overscanFit
842  qaStdev = float("NaN")
843  else:
844  qaStats = afwMath.makeStatistics(overscanResults.overscanFit,
845  afwMath.MEDIAN | afwMath.STDEVCLIP)
846  qaMedian = qaStats.getValue(afwMath.MEDIAN)
847  qaStdev = qaStats.getValue(afwMath.STDEVCLIP)
848 
849  self.metadata.set("ISR OSCAN {} MEDIAN".format(amp.getName()), qaMedian)
850  self.metadata.set("ISR OSCAN {} STDEV".format(amp.getName()), qaStdev)
851  self.log.debug(" Overscan stats for amplifer %s: %f +/- %f" %
852  (amp.getName(), qaMedian, qaStdev))
853  ccdExposure.getMetadata().set('OVERSCAN', "Overscan corrected")
854  else:
855  self.log.warn("Amplifier %s is bad." % (amp.getName()))
856  overscanResults = None
857 
858  overscans.append(overscanResults if overscanResults is not None else None)
859  else:
860  self.log.info("Skipped OSCAN")
861 
862  if self.config.doCrosstalk and self.config.doCrosstalkBeforeAssemble:
863  self.log.info("Applying crosstalk correction.")
864  self.crosstalk.run(ccdExposure, crosstalkSources=crosstalkSources)
865  self.debugView(ccdExposure, "doCrosstalk")
866 
867  if self.config.doAssembleCcd:
868  self.log.info("Assembling CCD from amplifiers")
869  ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
870 
871  if self.config.expectWcs and not ccdExposure.getWcs():
872  self.log.warn("No WCS found in input exposure")
873  self.debugView(ccdExposure, "doAssembleCcd")
874 
875  ossThumb = None
876  if self.config.qa.doThumbnailOss:
877  ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
878 
879  if self.config.doBias:
880  self.log.info("Applying bias correction.")
881  isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
882  trimToFit=self.config.doTrimToMatchCalib)
883  self.debugView(ccdExposure, "doBias")
884 
885  if self.config.doVariance:
886  for amp, overscanResults in zip(ccd, overscans):
887  if ccdExposure.getBBox().contains(amp.getBBox()):
888  self.log.debug("Constructing variance map for amplifer %s" % (amp.getName()))
889  ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
890  if overscanResults is not None:
891  self.updateVariance(ampExposure, amp,
892  overscanImage=overscanResults.overscanImage)
893  else:
894  self.updateVariance(ampExposure, amp,
895  overscanImage=None)
896  if self.config.qa is not None and self.config.qa.saveStats is True:
897  qaStats = afwMath.makeStatistics(ampExposure.getVariance(),
898  afwMath.MEDIAN | afwMath.STDEVCLIP)
899  self.metadata.set("ISR VARIANCE {} MEDIAN".format(amp.getName()),
900  qaStats.getValue(afwMath.MEDIAN))
901  self.metadata.set("ISR VARIANCE {} STDEV".format(amp.getName()),
902  qaStats.getValue(afwMath.STDEVCLIP))
903  self.log.debug(" Variance stats for amplifer %s: %f +/- %f" %
904  (amp.getName(), qaStats.getValue(afwMath.MEDIAN),
905  qaStats.getValue(afwMath.STDEVCLIP)))
906 
907  if self.doLinearize(ccd):
908  self.log.info("Applying linearizer.")
909  linearizer(image=ccdExposure.getMaskedImage().getImage(), detector=ccd, log=self.log)
910 
911  if self.config.doCrosstalk and not self.config.doCrosstalkBeforeAssemble:
912  self.log.info("Applying crosstalk correction.")
913  self.crosstalk.run(ccdExposure, crosstalkSources=crosstalkSources)
914  self.debugView(ccdExposure, "doCrosstalk")
915 
916  if self.config.doWidenSaturationTrails:
917  self.log.info("Widening saturation trails.")
918  isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
919 
920  interpolationDone = False
921  if self.config.doBrighterFatter:
922  # We need to apply flats and darks before we can interpolate, and we
923  # need to interpolate before we do B-F, but we do B-F without the
924  # flats and darks applied so we can work in units of electrons or holes.
925  # This context manager applies and then removes the darks and flats.
926  with self.flatContext(ccdExposure, flat, dark):
927  if self.config.doDefect:
928  self.maskAndInterpDefect(ccdExposure, defects)
929 
930  if self.config.doSaturationInterpolation:
931  self.saturationInterpolation(ccdExposure)
932 
933  self.maskAndInterpNan(ccdExposure)
934  interpolationDone = True
935 
936  if self.config.brighterFatterLevel == 'DETECTOR':
937  kernelElement = bfKernel
938  else:
939  # TODO: DM-15631 for implementing this
940  raise NotImplementedError("per-amplifier brighter-fatter correction not yet implemented")
941  self.log.info("Applying brighter fatter correction.")
942  isrFunctions.brighterFatterCorrection(ccdExposure, kernelElement,
943  self.config.brighterFatterMaxIter,
944  self.config.brighterFatterThreshold,
945  self.config.brighterFatterApplyGain,
946  )
947  self.debugView(ccdExposure, "doBrighterFatter")
948 
949  if self.config.doDark:
950  self.log.info("Applying dark correction.")
951  self.darkCorrection(ccdExposure, dark)
952  self.debugView(ccdExposure, "doDark")
953 
954  if self.config.doFringe and not self.config.fringeAfterFlat:
955  self.log.info("Applying fringe correction before flat.")
956  self.fringe.run(ccdExposure, **fringes.getDict())
957  self.debugView(ccdExposure, "doFringe")
958 
959  if self.config.doStrayLight:
960  self.log.info("Applying stray light correction.")
961  self.strayLight.run(ccdExposure)
962  self.debugView(ccdExposure, "doStrayLight")
963 
964  if self.config.doFlat:
965  self.log.info("Applying flat correction.")
966  self.flatCorrection(ccdExposure, flat)
967  self.debugView(ccdExposure, "doFlat")
968 
969  if self.config.doApplyGains:
970  self.log.info("Applying gain correction instead of flat.")
971  isrFunctions.applyGains(ccdExposure, self.config.normalizeGains)
972 
973  if self.config.doDefect and not interpolationDone:
974  self.log.info("Masking and interpolating defects.")
975  self.maskAndInterpDefect(ccdExposure, defects)
976 
977  if self.config.doSaturation and not interpolationDone:
978  self.log.info("Interpolating saturated pixels.")
979  self.saturationInterpolation(ccdExposure)
980 
981  if self.config.doNanInterpAfterFlat or not interpolationDone:
982  self.log.info("Masking and interpolating NAN value pixels.")
983  self.maskAndInterpNan(ccdExposure)
984 
985  if self.config.doFringe and self.config.fringeAfterFlat:
986  self.log.info("Applying fringe correction after flat.")
987  self.fringe.run(ccdExposure, **fringes.getDict())
988 
989  if self.config.doSetBadRegions:
990  badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
991  self.log.info("Set %d BAD pixels to %f." % (badPixelCount, badPixelValue))
992 
993  flattenedThumb = None
994  if self.config.qa.doThumbnailFlattened:
995  flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
996 
997  if self.config.doCameraSpecificMasking:
998  self.log.info("Masking regions for camera specific reasons.")
999  self.masking.run(ccdExposure)
1000 
1001  self.roughZeroPoint(ccdExposure)
1002 
1003  if self.config.doVignette:
1004  self.log.info("Constructing Vignette polygon.")
1005  self.vignettePolygon = self.vignette.run(ccdExposure)
1006 
1007  if self.config.vignette.doWriteVignettePolygon:
1008  self.setValidPolygonIntersect(ccdExposure, self.vignettePolygon)
1009 
1010  if self.config.doAttachTransmissionCurve:
1011  self.log.info("Adding transmission curves.")
1012  isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1013  filterTransmission=filterTransmission,
1014  sensorTransmission=sensorTransmission,
1015  atmosphereTransmission=atmosphereTransmission)
1016 
1017  if self.config.doAddDistortionModel:
1018  self.log.info("Adding a distortion model to the WCS.")
1019  isrFunctions.addDistortionModel(exposure=ccdExposure, camera=camera)
1020 
1021  if self.config.doMeasureBackground:
1022  self.log.info("Measuring background level:")
1023  self.measureBackground(ccdExposure, self.config.qa)
1024 
1025  if self.config.qa is not None and self.config.qa.saveStats is True:
1026  for amp in ccd:
1027  ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1028  qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1029  afwMath.MEDIAN | afwMath.STDEVCLIP)
1030  self.metadata.set("ISR BACKGROUND {} MEDIAN".format(amp.getName()),
1031  qaStats.getValue(afwMath.MEDIAN))
1032  self.metadata.set("ISR BACKGROUND {} STDEV".format(amp.getName()),
1033  qaStats.getValue(afwMath.STDEVCLIP))
1034  self.log.debug(" Background stats for amplifer %s: %f +/- %f" %
1035  (amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1036  qaStats.getValue(afwMath.STDEVCLIP)))
1037 
1038  self.debugView(ccdExposure, "postISRCCD")
1039 
1040  return pipeBase.Struct(
1041  exposure=ccdExposure,
1042  ossThumb=ossThumb,
1043  flattenedThumb=flattenedThumb
1044  )
1045 
bool contains(VertexIterator const begin, VertexIterator const end, UnitVector3d const &v)
daf::base::PropertySet * set
Definition: fits.cc:832
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
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:168

◆ runDataRef()

def lsst.ip.isr.isrTask.IsrTask.runDataRef (   self,
  sensorRef 
)
Perform instrument signature removal on a ButlerDataRef of a Sensor.

This method contains the `CmdLineTask` interface to the ISR
processing.  All IO is handled here, freeing the `run()` method
to manage only pixel-level calculations.  The steps performed
are:
- Read in necessary detrending/isr/calibration data.
- Process raw exposure in `run()`.
- Persist the ISR-corrected exposure as "postISRCCD" if
  config.doWrite=True.

Parameters
----------
sensorRef : `daf.persistence.butlerSubset.ButlerDataRef`
    DataRef of the detector data to be processed

Returns
-------
result : `lsst.pipe.base.Struct`
    Result struct with component:
    - ``exposure`` : `afw.image.Exposure`
The fully ISR corrected exposure.

Raises
------
RuntimeError
    Raised if a configuration option is set to True, but the
    required calibration data does not exist.

Definition at line 1047 of file isrTask.py.

1047  def runDataRef(self, sensorRef):
1048  """Perform instrument signature removal on a ButlerDataRef of a Sensor.
1049 
1050  This method contains the `CmdLineTask` interface to the ISR
1051  processing. All IO is handled here, freeing the `run()` method
1052  to manage only pixel-level calculations. The steps performed
1053  are:
1054  - Read in necessary detrending/isr/calibration data.
1055  - Process raw exposure in `run()`.
1056  - Persist the ISR-corrected exposure as "postISRCCD" if
1057  config.doWrite=True.
1058 
1059  Parameters
1060  ----------
1061  sensorRef : `daf.persistence.butlerSubset.ButlerDataRef`
1062  DataRef of the detector data to be processed
1063 
1064  Returns
1065  -------
1066  result : `lsst.pipe.base.Struct`
1067  Result struct with component:
1068  - ``exposure`` : `afw.image.Exposure`
1069  The fully ISR corrected exposure.
1070 
1071  Raises
1072  ------
1073  RuntimeError
1074  Raised if a configuration option is set to True, but the
1075  required calibration data does not exist.
1076 
1077  """
1078  self.log.info("Performing ISR on sensor %s" % (sensorRef.dataId))
1079  ccdExposure = sensorRef.get(self.config.datasetType)
1080 
1081  camera = sensorRef.get("camera")
1082  if camera is None and self.config.doAddDistortionModel:
1083  raise RuntimeError("config.doAddDistortionModel is True "
1084  "but could not get a camera from the butler")
1085  isrData = self.readIsrData(sensorRef, ccdExposure)
1086 
1087  result = self.run(ccdExposure, camera=camera, **isrData.getDict())
1088 
1089  if self.config.doWrite:
1090  sensorRef.put(result.exposure, "postISRCCD")
1091  if result.ossThumb is not None:
1092  isrQa.writeThumbnail(sensorRef, result.ossThumb, "ossThumb")
1093  if result.flattenedThumb is not None:
1094  isrQa.writeThumbnail(sensorRef, result.flattenedThumb, "flattenedThumb")
1095 
1096  return result
1097 

◆ saturationDetection()

def lsst.ip.isr.isrTask.IsrTask.saturationDetection (   self,
  exposure,
  amp 
)

Detect saturated pixels and mask them using mask plane config.saturatedMaskName, in place.

Parameters

exposure : lsst.afw.image.Exposure Exposure to process. Only the amplifier DataSec is processed. amp : lsst.afw.table.AmpInfoCatalog Amplifier detector data.

See Also

lsst.ip.isr.isrFunctions.makeThresholdMask

Definition at line 1522 of file isrTask.py.

1522  def saturationDetection(self, exposure, amp):
1523  """!Detect saturated pixels and mask them using mask plane config.saturatedMaskName, in place.
1524 
1525  Parameters
1526  ----------
1527  exposure : `lsst.afw.image.Exposure`
1528  Exposure to process. Only the amplifier DataSec is processed.
1529  amp : `lsst.afw.table.AmpInfoCatalog`
1530  Amplifier detector data.
1531 
1532  See Also
1533  --------
1534  lsst.ip.isr.isrFunctions.makeThresholdMask
1535  """
1536  if not math.isnan(amp.getSaturation()):
1537  maskedImage = exposure.getMaskedImage()
1538  dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
1539  isrFunctions.makeThresholdMask(
1540  maskedImage=dataView,
1541  threshold=amp.getSaturation(),
1542  growFootprints=0,
1543  maskName=self.config.saturatedMaskName,
1544  )
1545 

◆ saturationInterpolation()

def lsst.ip.isr.isrTask.IsrTask.saturationInterpolation (   self,
  ccdExposure 
)

Interpolate over saturated pixels, in place.

This method should be called after saturationDetection, to ensure that the saturated pixels have been identified in the SAT mask. It should also be called after assembleCcd, since saturated regions may cross amplifier boundaries.

Parameters

exposure : lsst.afw.image.Exposure Exposure to process.

See Also

lsst.ip.isr.isrTask.saturationDetection lsst.ip.isr.isrFunctions.interpolateFromMask

Definition at line 1546 of file isrTask.py.

1546  def saturationInterpolation(self, ccdExposure):
1547  """!Interpolate over saturated pixels, in place.
1548 
1549  This method should be called after `saturationDetection`, to
1550  ensure that the saturated pixels have been identified in the
1551  SAT mask. It should also be called after `assembleCcd`, since
1552  saturated regions may cross amplifier boundaries.
1553 
1554  Parameters
1555  ----------
1556  exposure : `lsst.afw.image.Exposure`
1557  Exposure to process.
1558 
1559  See Also
1560  --------
1561  lsst.ip.isr.isrTask.saturationDetection
1562  lsst.ip.isr.isrFunctions.interpolateFromMask
1563  """
1564  isrFunctions.interpolateFromMask(
1565  maskedImage=ccdExposure.getMaskedImage(),
1566  fwhm=self.config.fwhm,
1567  growFootprints=self.config.growSaturationFootprintSize,
1568  maskName=self.config.saturatedMaskName,
1569  )
1570 

◆ setValidPolygonIntersect()

def lsst.ip.isr.isrTask.IsrTask.setValidPolygonIntersect (   self,
  ccdExposure,
  fpPolygon 
)

Set the valid polygon as the intersection of fpPolygon and the ccd corners.

Parameters

ccdExposure : lsst.afw.image.Exposure Exposure to process. fpPolygon : lsst.afw.geom.Polygon Polygon in focal plane coordinates.

Definition at line 1768 of file isrTask.py.

1768  def setValidPolygonIntersect(self, ccdExposure, fpPolygon):
1769  """!Set the valid polygon as the intersection of fpPolygon and the ccd corners.
1770 
1771  Parameters
1772  ----------
1773  ccdExposure : `lsst.afw.image.Exposure`
1774  Exposure to process.
1775  fpPolygon : `lsst.afw.geom.Polygon`
1776  Polygon in focal plane coordinates.
1777  """
1778  # Get ccd corners in focal plane coordinates
1779  ccd = ccdExposure.getDetector()
1780  fpCorners = ccd.getCorners(FOCAL_PLANE)
1781  ccdPolygon = Polygon(fpCorners)
1782 
1783  # Get intersection of ccd corners with fpPolygon
1784  intersect = ccdPolygon.intersectionSingle(fpPolygon)
1785 
1786  # Transform back to pixel positions and build new polygon
1787  ccdPoints = ccd.transform(intersect, FOCAL_PLANE, PIXELS)
1788  validPolygon = Polygon(ccdPoints)
1789  ccdExposure.getInfo().setValidPolygon(validPolygon)
1790 

◆ suspectDetection()

def lsst.ip.isr.isrTask.IsrTask.suspectDetection (   self,
  exposure,
  amp 
)

Detect suspect pixels and mask them using mask plane config.suspectMaskName, in place.

Parameters

exposure : lsst.afw.image.Exposure Exposure to process. Only the amplifier DataSec is processed. amp : lsst.afw.table.AmpInfoCatalog Amplifier detector data.

See Also

lsst.ip.isr.isrFunctions.makeThresholdMask

Notes

Suspect pixels are pixels whose value is greater than amp.getSuspectLevel(). This is intended to indicate pixels that may be affected by unknown systematics; for example if non-linearity corrections above a certain level are unstable then that would be a useful value for suspectLevel. A value of nan indicates that no such level exists and no pixels are to be masked as suspicious.

Definition at line 1571 of file isrTask.py.

1571  def suspectDetection(self, exposure, amp):
1572  """!Detect suspect pixels and mask them using mask plane config.suspectMaskName, in place.
1573 
1574  Parameters
1575  ----------
1576  exposure : `lsst.afw.image.Exposure`
1577  Exposure to process. Only the amplifier DataSec is processed.
1578  amp : `lsst.afw.table.AmpInfoCatalog`
1579  Amplifier detector data.
1580 
1581  See Also
1582  --------
1583  lsst.ip.isr.isrFunctions.makeThresholdMask
1584 
1585  Notes
1586  -----
1587  Suspect pixels are pixels whose value is greater than amp.getSuspectLevel().
1588  This is intended to indicate pixels that may be affected by unknown systematics;
1589  for example if non-linearity corrections above a certain level are unstable
1590  then that would be a useful value for suspectLevel. A value of `nan` indicates
1591  that no such level exists and no pixels are to be masked as suspicious.
1592  """
1593  suspectLevel = amp.getSuspectLevel()
1594  if math.isnan(suspectLevel):
1595  return
1596 
1597  maskedImage = exposure.getMaskedImage()
1598  dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
1599  isrFunctions.makeThresholdMask(
1600  maskedImage=dataView,
1601  threshold=suspectLevel,
1602  growFootprints=0,
1603  maskName=self.config.suspectMaskName,
1604  )
1605 

◆ updateVariance()

def lsst.ip.isr.isrTask.IsrTask.updateVariance (   self,
  ampExposure,
  amp,
  overscanImage = None 
)
Set the variance plane using the amplifier gain and read noise

The read noise is calculated from the ``overscanImage`` if the
``doEmpiricalReadNoise`` option is set in the configuration; otherwise
the value from the amplifier data is used.

Parameters
----------
ampExposure : `lsst.afw.image.Exposure`
    Exposure to process.
amp : `lsst.afw.table.AmpInfoRecord` or `FakeAmp`
    Amplifier detector data.
overscanImage : `lsst.afw.image.MaskedImage`, optional.
    Image of overscan, required only for empirical read noise.

See also
--------
lsst.ip.isr.isrFunctions.updateVariance

Definition at line 1392 of file isrTask.py.

1392  def updateVariance(self, ampExposure, amp, overscanImage=None):
1393  """Set the variance plane using the amplifier gain and read noise
1394 
1395  The read noise is calculated from the ``overscanImage`` if the
1396  ``doEmpiricalReadNoise`` option is set in the configuration; otherwise
1397  the value from the amplifier data is used.
1398 
1399  Parameters
1400  ----------
1401  ampExposure : `lsst.afw.image.Exposure`
1402  Exposure to process.
1403  amp : `lsst.afw.table.AmpInfoRecord` or `FakeAmp`
1404  Amplifier detector data.
1405  overscanImage : `lsst.afw.image.MaskedImage`, optional.
1406  Image of overscan, required only for empirical read noise.
1407 
1408  See also
1409  --------
1410  lsst.ip.isr.isrFunctions.updateVariance
1411  """
1412  maskPlanes = [self.config.saturatedMaskName, self.config.suspectMaskName]
1413  gain = amp.getGain()
1414 
1415  if math.isnan(gain):
1416  gain = 1.0
1417  self.log.warn("Gain set to NAN! Updating to 1.0 to generate Poisson variance.")
1418  elif gain <= 0:
1419  patchedGain = 1.0
1420  self.log.warn("Gain for amp %s == %g <= 0; setting to %f" %
1421  (amp.getName(), gain, patchedGain))
1422  gain = patchedGain
1423 
1424  if self.config.doEmpiricalReadNoise and overscanImage is None:
1425  self.log.info("Overscan is none for EmpiricalReadNoise")
1426 
1427  if self.config.doEmpiricalReadNoise and overscanImage is not None:
1428  stats = afwMath.StatisticsControl()
1429  stats.setAndMask(overscanImage.mask.getPlaneBitMask(maskPlanes))
1430  readNoise = afwMath.makeStatistics(overscanImage, afwMath.STDEVCLIP, stats).getValue()
1431  self.log.info("Calculated empirical read noise for amp %s: %f", amp.getName(), readNoise)
1432  else:
1433  readNoise = amp.getReadNoise()
1434 
1435  isrFunctions.updateVariance(
1436  maskedImage=ampExposure.getMaskedImage(),
1437  gain=gain,
1438  readNoise=readNoise,
1439  )
1440 
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
def updateVariance(maskedImage, gain, readNoise)

Member Data Documentation

◆ ConfigClass

lsst.ip.isr.isrTask.IsrTask.ConfigClass = IsrTaskConfig
static

Definition at line 574 of file isrTask.py.

◆ vignettePolygon

lsst.ip.isr.isrTask.IsrTask.vignettePolygon

Definition at line 1005 of file isrTask.py.


The documentation for this class was generated from the following file: