LSSTApplications  16.0-10-g0ee56ad+5,16.0-11-ga33d1f2+5,16.0-12-g3ef5c14+3,16.0-12-g71e5ef5+18,16.0-12-gbdf3636+3,16.0-13-g118c103+3,16.0-13-g8f68b0a+3,16.0-15-gbf5c1cb+4,16.0-16-gfd17674+3,16.0-17-g7c01f5c+3,16.0-18-g0a50484+1,16.0-20-ga20f992+8,16.0-21-g0e05fd4+6,16.0-21-g15e2d33+4,16.0-22-g62d8060+4,16.0-22-g847a80f+4,16.0-25-gf00d9b8+1,16.0-28-g3990c221+4,16.0-3-gf928089+3,16.0-32-g88a4f23+5,16.0-34-gd7987ad+3,16.0-37-gc7333cb+2,16.0-4-g10fc685+2,16.0-4-g18f3627+26,16.0-4-g5f3a788+26,16.0-5-gaf5c3d7+4,16.0-5-gcc1f4bb+1,16.0-6-g3b92700+4,16.0-6-g4412fcd+3,16.0-6-g7235603+4,16.0-69-g2562ce1b+2,16.0-8-g14ebd58+4,16.0-8-g2df868b+1,16.0-8-g4cec79c+6,16.0-8-gadf6c7a+1,16.0-8-gfc7ad86,16.0-82-g59ec2a54a+1,16.0-9-g5400cdc+2,16.0-9-ge6233d7+5,master-g2880f2d8cf+3,v17.0.rc1
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, kwargs)
 
def getInputDatasetTypes (cls, config)
 
def getOutputDatasetTypes (cls, config)
 
def adaptArgsAndRun (self, inputData, inputDataIds, outputDataIds, butler)
 
def makeDatasetType (self, dsConfig)
 
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, detectorNum=None, isGen3=False)
 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 ensureExposure (self, inputExp, camera, detectorNum)
 
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 665 of file isrTask.py.

Constructor & Destructor Documentation

◆ __init__()

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

Definition at line 696 of file isrTask.py.

696  def __init__(self, **kwargs):
697  super().__init__(**kwargs)
698  self.makeSubtask("assembleCcd")
699  self.makeSubtask("crosstalk")
700  self.makeSubtask("strayLight")
701  self.makeSubtask("fringe")
702  self.makeSubtask("masking")
703  self.makeSubtask("vignette")
704 
def __init__(self, minimum, dataRange, Q)

Member Function Documentation

◆ adaptArgsAndRun()

def lsst.ip.isr.isrTask.IsrTask.adaptArgsAndRun (   self,
  inputData,
  inputDataIds,
  outputDataIds,
  butler 
)

Definition at line 755 of file isrTask.py.

755  def adaptArgsAndRun(self, inputData, inputDataIds, outputDataIds, butler):
756  try:
757  inputData['detectorNum'] = int(inputDataIds['ccdExposure']['detector'])
758  except Exception as e:
759  raise ValueError(f"Failure to find valid detectorNum value for Dataset {inputDataIds}: {e}")
760 
761  inputData['isGen3'] = True
762 
763  if self.config.doLinearize is True:
764  if 'linearizer' not in inputData.keys():
765  detector = inputData['camera'][inputData['detectorNum']]
766  linearityName = detector.getAmpInfoCatalog()[0].getLinearityType()
767  inputData['linearizer'] = linearize.getLinearityTypeByName(linearityName)()
768 
769  if inputData['defects'] is not None:
770  # defects is loaded as a BaseCatalog with columns x0, y0, width, height.
771  # masking expects a list of defects defined by their bounding box
772  defectList = []
773 
774  for r in inputData['defects']:
775  bbox = afwGeom.BoxI(afwGeom.PointI(r.get("x0"), r.get("y0")),
776  afwGeom.ExtentI(r.get("width"), r.get("height")))
777  defectList.append(Defect(bbox))
778 
779  inputData['defects'] = defectList
780 
781  # Broken: DM-17169
782  # ci_hsc does not use crosstalkSources, as it's intra-CCD CT only. This needs to be
783  # fixed for non-HSC cameras in the future.
784  # inputData['crosstalkSources'] = (self.crosstalk.prepCrosstalk(inputDataIds['ccdExposure'])
785  # if self.config.doCrosstalk else None)
786 
787  # Broken: DM-17152
788  # Fringes are not tested to be handled correctly by Gen3 butler.
789  # inputData['fringes'] = (self.fringe.readFringes(inputDataIds['ccdExposure'],
790  # assembler=self.assembleCcd
791  # if self.config.doAssembleIsrExposures else None)
792  # if self.config.doFringe and
793  # self.fringe.checkFilter(inputData['ccdExposure'])
794  # else pipeBase.Struct(fringes=None))
795 
796  return super().adaptArgsAndRun(inputData, inputDataIds, outputDataIds, butler)
797 
An integer coordinate rectangle.
Definition: Box.h:54

◆ 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 1423 of file isrTask.py.

1423  def convertIntToFloat(self, exposure):
1424  """Convert exposure image from uint16 to float.
1425 
1426  If the exposure does not need to be converted, the input is
1427  immediately returned. For exposures that are converted to use
1428  floating point pixels, the variance is set to unity and the
1429  mask to zero.
1430 
1431  Parameters
1432  ----------
1433  exposure : `lsst.afw.image.Exposure`
1434  The raw exposure to be converted.
1435 
1436  Returns
1437  -------
1438  newexposure : `lsst.afw.image.Exposure`
1439  The input ``exposure``, converted to floating point pixels.
1440 
1441  Raises
1442  ------
1443  RuntimeError
1444  Raised if the exposure type cannot be converted to float.
1445 
1446  """
1447  if isinstance(exposure, afwImage.ExposureF):
1448  # Nothing to be done
1449  return exposure
1450  if not hasattr(exposure, "convertF"):
1451  raise RuntimeError("Unable to convert exposure (%s) to float" % type(exposure))
1452 
1453  newexposure = exposure.convertF()
1454  newexposure.variance[:] = 1
1455  newexposure.mask[:] = 0x0
1456 
1457  return newexposure
1458 
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 1714 of file isrTask.py.

1714  def darkCorrection(self, exposure, darkExposure, invert=False):
1715  """!Apply dark correction in place.
1716 
1717  Parameters
1718  ----------
1719  exposure : `lsst.afw.image.Exposure`
1720  Exposure to process.
1721  darkExposure : `lsst.afw.image.Exposure`
1722  Dark exposure of the same size as ``exposure``.
1723  invert : `Bool`, optional
1724  If True, re-add the dark to an already corrected image.
1725 
1726  Raises
1727  ------
1728  RuntimeError
1729  Raised if either ``exposure`` or ``darkExposure`` do not
1730  have their dark time defined.
1731 
1732  See Also
1733  --------
1734  lsst.ip.isr.isrFunctions.darkCorrection
1735  """
1736  expScale = exposure.getInfo().getVisitInfo().getDarkTime()
1737  if math.isnan(expScale):
1738  raise RuntimeError("Exposure darktime is NAN")
1739  if darkExposure.getInfo().getVisitInfo() is not None:
1740  darkScale = darkExposure.getInfo().getVisitInfo().getDarkTime()
1741  else:
1742  # DM-17444: darkExposure.getInfo.getVisitInfo() is None
1743  # so getDarkTime() does not exist.
1744  darkScale = 1.0
1745 
1746  if math.isnan(darkScale):
1747  raise RuntimeError("Dark calib darktime is NAN")
1748  isrFunctions.darkCorrection(
1749  maskedImage=exposure.getMaskedImage(),
1750  darkMaskedImage=darkExposure.getMaskedImage(),
1751  expScale=expScale,
1752  darkScale=darkScale,
1753  invert=invert,
1754  trimToFit=self.config.doTrimToMatchCalib
1755  )
1756 
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 2101 of file isrTask.py.

2101  def debugView(self, exposure, stepname):
2102  """Utility function to examine ISR exposure at different stages.
2103 
2104  Parameters
2105  ----------
2106  exposure : `lsst.afw.image.Exposure`
2107  Exposure to view.
2108  stepname : `str`
2109  State of processing to view.
2110  """
2111  frame = getDebugFrame(self._display, stepname)
2112  if frame:
2113  display = getDisplay(frame)
2114  display.scale('asinh', 'zscale')
2115  display.mtv(exposure)
2116 
2117 
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 1757 of file isrTask.py.

1757  def doLinearize(self, detector):
1758  """!Check if linearization is needed for the detector cameraGeom.
1759 
1760  Checks config.doLinearize and the linearity type of the first
1761  amplifier.
1762 
1763  Parameters
1764  ----------
1765  detector : `lsst.afw.cameraGeom.Detector`
1766  Detector to get linearity type from.
1767 
1768  Returns
1769  -------
1770  doLinearize : `Bool`
1771  If True, linearization should be performed.
1772  """
1773  return self.config.doLinearize and \
1774  detector.getAmpInfoCatalog()[0].getLinearityType() != NullLinearityType
1775 

◆ ensureExposure()

def lsst.ip.isr.isrTask.IsrTask.ensureExposure (   self,
  inputExp,
  camera,
  detectorNum 
)
Ensure that the data returned by Butler is a fully constructed exposure.

ISR requires exposure-level image data for historical reasons, so if we did
not recieve that from Butler, construct it from what we have, modifying the
input in place.

Parameters
----------
inputExp : `lsst.afw.image.Exposure`, `lsst.afw.image.DecoratedImageU`, or
   `lsst.afw.image.ImageF`
    The input data structure obtained from Butler.
camera : `lsst.afw.cameraGeom.camera`
    The camera associated with the image.  Used to find the appropriate
    detector.
detectorNum : `int`
    The detector this exposure should match.

Returns
-------
inputExp : `lsst.afw.image.Exposure`
    The re-constructed exposure, with appropriate detector parameters.

Raises
------
TypeError
    Raised if the input data cannot be used to construct an exposure.

Definition at line 1379 of file isrTask.py.

1379  def ensureExposure(self, inputExp, camera, detectorNum):
1380  """Ensure that the data returned by Butler is a fully constructed exposure.
1381 
1382  ISR requires exposure-level image data for historical reasons, so if we did
1383  not recieve that from Butler, construct it from what we have, modifying the
1384  input in place.
1385 
1386  Parameters
1387  ----------
1388  inputExp : `lsst.afw.image.Exposure`, `lsst.afw.image.DecoratedImageU`, or
1389  `lsst.afw.image.ImageF`
1390  The input data structure obtained from Butler.
1391  camera : `lsst.afw.cameraGeom.camera`
1392  The camera associated with the image. Used to find the appropriate
1393  detector.
1394  detectorNum : `int`
1395  The detector this exposure should match.
1396 
1397  Returns
1398  -------
1399  inputExp : `lsst.afw.image.Exposure`
1400  The re-constructed exposure, with appropriate detector parameters.
1401 
1402  Raises
1403  ------
1404  TypeError
1405  Raised if the input data cannot be used to construct an exposure.
1406  """
1407  if isinstance(inputExp, afwImage.DecoratedImageU):
1408  inputExp = afwImage.makeExposure(afwImage.makeMaskedImage(inputExp))
1409  elif isinstance(inputExp, afwImage.ImageF):
1410  inputExp = afwImage.makeExposure(afwImage.makeMaskedImage(inputExp))
1411  elif isinstance(inputExp, afwImage.MaskedImageF):
1412  inputExp = afwImage.makeExposure(inputExp)
1413  elif isinstance(inputExp, afwImage.Exposure):
1414  pass
1415  else:
1416  raise TypeError(f"Input Exposure is not known type in isrTask.ensureExposure: {type(inputExp)}")
1417 
1418  if inputExp.getDetector() is None:
1419  inputExp.setDetector(camera[detectorNum])
1420 
1421  return inputExp
1422 
A class to contain the data, WCS, and other information needed to describe an image of the sky...
Definition: Exposure.h:72
MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > * makeMaskedImage(typename std::shared_ptr< Image< ImagePixelT >> image, typename std::shared_ptr< Mask< MaskPixelT >> mask=Mask< MaskPixelT >(), typename std::shared_ptr< Image< VariancePixelT >> variance=Image< VariancePixelT >())
A function to return a MaskedImage of the correct type (cf.
Definition: MaskedImage.h:1280
std::shared_ptr< Exposure< ImagePixelT, MaskPixelT, VariancePixelT > > makeExposure(MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > &mimage, std::shared_ptr< geom::SkyWcs const > wcs=std::shared_ptr< geom::SkyWcs const >())
A function to return an Exposure of the correct type (cf.
Definition: Exposure.h:446

◆ 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 2071 of file isrTask.py.

2071  def flatContext(self, exp, flat, dark=None):
2072  """Context manager that applies and removes flats and darks,
2073  if the task is configured to apply them.
2074 
2075  Parameters
2076  ----------
2077  exp : `lsst.afw.image.Exposure`
2078  Exposure to process.
2079  flat : `lsst.afw.image.Exposure`
2080  Flat exposure the same size as ``exp``.
2081  dark : `lsst.afw.image.Exposure`, optional
2082  Dark exposure the same size as ``exp``.
2083 
2084  Yields
2085  ------
2086  exp : `lsst.afw.image.Exposure`
2087  The flat and dark corrected exposure.
2088  """
2089  if self.config.doDark and dark is not None:
2090  self.darkCorrection(exp, dark)
2091  if self.config.doFlat:
2092  self.flatCorrection(exp, flat)
2093  try:
2094  yield exp
2095  finally:
2096  if self.config.doFlat:
2097  self.flatCorrection(exp, flat, invert=True)
2098  if self.config.doDark and dark is not None:
2099  self.darkCorrection(exp, dark, invert=True)
2100 

◆ 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 1776 of file isrTask.py.

1776  def flatCorrection(self, exposure, flatExposure, invert=False):
1777  """!Apply flat correction in place.
1778 
1779  Parameters
1780  ----------
1781  exposure : `lsst.afw.image.Exposure`
1782  Exposure to process.
1783  flatExposure : `lsst.afw.image.Exposure`
1784  Flat exposure of the same size as ``exposure``.
1785  invert : `Bool`, optional
1786  If True, unflatten an already flattened image.
1787 
1788  See Also
1789  --------
1790  lsst.ip.isr.isrFunctions.flatCorrection
1791  """
1792  isrFunctions.flatCorrection(
1793  maskedImage=exposure.getMaskedImage(),
1794  flatMaskedImage=flatExposure.getMaskedImage(),
1795  scalingType=self.config.flatScalingType,
1796  userScale=self.config.flatUserScale,
1797  invert=invert,
1798  trimToFit=self.config.doTrimToMatchCalib
1799  )
1800 
def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False, trimToFit=False)

◆ getInputDatasetTypes()

def lsst.ip.isr.isrTask.IsrTask.getInputDatasetTypes (   cls,
  config 
)

Definition at line 706 of file isrTask.py.

706  def getInputDatasetTypes(cls, config):
707  inputTypeDict = super().getInputDatasetTypes(config)
708 
709  # Delete entries from the dictionary of InputDatasetTypes that we know we don't
710  # need because the configuration tells us we will not be bothering with the
711  # correction that uses that IDT.
712  if config.doBias is not True:
713  del inputTypeDict["bias"]
714  if config.doLinearize is not True:
715  del inputTypeDict["linearizer"]
716  if config.doCrosstalk is not True:
717  del inputTypeDict["crosstalkSources"]
718  if config.doBrighterFatter is not True:
719  del inputTypeDict["bfKernel"]
720  if config.doDefect is not True:
721  del inputTypeDict["defects"]
722  if config.doDark is not True:
723  del inputTypeDict["dark"]
724  if config.doFlat is not True:
725  del inputTypeDict["flat"]
726  if config.doAttachTransmissionCurve is not True:
727  del inputTypeDict["opticsTransmission"]
728  del inputTypeDict["filterTransmission"]
729  del inputTypeDict["sensorTransmission"]
730  del inputTypeDict["atmosphereTransmission"]
731  if config.doUseOpticsTransmission is not True:
732  del inputTypeDict["opticsTransmission"]
733  if config.doUseFilterTransmission is not True:
734  del inputTypeDict["filterTransmission"]
735  if config.doUseSensorTransmission is not True:
736  del inputTypeDict["sensorTransmission"]
737  if config.doUseAtmosphereTransmission is not True:
738  del inputTypeDict["atmosphereTransmission"]
739 
740  return inputTypeDict
741 

◆ 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 1338 of file isrTask.py.

1338  def getIsrExposure(self, dataRef, datasetType, immediate=True):
1339  """!Retrieve a calibration dataset for removing instrument signature.
1340 
1341  Parameters
1342  ----------
1343 
1344  dataRef : `daf.persistence.butlerSubset.ButlerDataRef`
1345  DataRef of the detector data to find calibration datasets
1346  for.
1347  datasetType : `str`
1348  Type of dataset to retrieve (e.g. 'bias', 'flat', etc).
1349  immediate : `Bool`
1350  If True, disable butler proxies to enable error handling
1351  within this routine.
1352 
1353  Returns
1354  -------
1355  exposure : `lsst.afw.image.Exposure`
1356  Requested calibration frame.
1357 
1358  Raises
1359  ------
1360  RuntimeError
1361  Raised if no matching calibration frame can be found.
1362  """
1363  try:
1364  exp = dataRef.get(datasetType, immediate=immediate)
1365  except Exception as exc1:
1366  if not self.config.fallbackFilterName:
1367  raise RuntimeError("Unable to retrieve %s for %s: %s" % (datasetType, dataRef.dataId, exc1))
1368  try:
1369  exp = dataRef.get(datasetType, filter=self.config.fallbackFilterName, immediate=immediate)
1370  except Exception as exc2:
1371  raise RuntimeError("Unable to retrieve %s for %s, even with fallback filter %s: %s AND %s" %
1372  (datasetType, dataRef.dataId, self.config.fallbackFilterName, exc1, exc2))
1373  self.log.warn("Using fallback calibration from filter %s" % self.config.fallbackFilterName)
1374 
1375  if self.config.doAssembleIsrExposures:
1376  exp = self.assembleCcd.assembleCcd(exp)
1377  return exp
1378 

◆ getOutputDatasetTypes()

def lsst.ip.isr.isrTask.IsrTask.getOutputDatasetTypes (   cls,
  config 
)

Definition at line 743 of file isrTask.py.

743  def getOutputDatasetTypes(cls, config):
744  outputTypeDict = super().getOutputDatasetTypes(config)
745 
746  if config.qa.doThumbnailOss is not True:
747  del outputTypeDict["outputOssThumbnail"]
748  if config.qa.doThumbnailFlattened is not True:
749  del outputTypeDict["outputFlattenedThumbnail"]
750  if config.doWrite is not True:
751  del outputTypeDict["outputExposure"]
752 
753  return outputTypeDict
754 

◆ makeDatasetType()

def lsst.ip.isr.isrTask.IsrTask.makeDatasetType (   self,
  dsConfig 
)

Definition at line 798 of file isrTask.py.

798  def makeDatasetType(self, dsConfig):
799  return super().makeDatasetType(dsConfig)
800 

◆ 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 1459 of file isrTask.py.

1459  def maskAmplifier(self, ccdExposure, amp, defects):
1460  """Identify bad amplifiers, saturated and suspect pixels.
1461 
1462  Parameters
1463  ----------
1464  ccdExposure : `lsst.afw.image.Exposure`
1465  Input exposure to be masked.
1466  amp : `lsst.afw.table.AmpInfoCatalog`
1467  Catalog of parameters defining the amplifier on this
1468  exposure to mask.
1469  defects : `list`
1470  List of defects. Used to determine if the entire
1471  amplifier is bad.
1472 
1473  Returns
1474  -------
1475  badAmp : `Bool`
1476  If this is true, the entire amplifier area is covered by
1477  defects and unusable.
1478 
1479  """
1480  maskedImage = ccdExposure.getMaskedImage()
1481 
1482  badAmp = False
1483 
1484  # Check if entire amp region is defined as a defect (need to use amp.getBBox() for correct
1485  # comparison with current defects definition.
1486  if defects is not None:
1487  badAmp = bool(sum([v.getBBox().contains(amp.getBBox()) for v in defects]))
1488 
1489  # In the case of a bad amp, we will set mask to "BAD" (here use amp.getRawBBox() for correct
1490  # association with pixels in current ccdExposure).
1491  if badAmp:
1492  dataView = afwImage.MaskedImageF(maskedImage, amp.getRawBBox(),
1493  afwImage.PARENT)
1494  maskView = dataView.getMask()
1495  maskView |= maskView.getPlaneBitMask("BAD")
1496  del maskView
1497  return badAmp
1498 
1499  # Mask remaining defects after assembleCcd() to allow for defects that cross amplifier boundaries.
1500  # Saturation and suspect pixels can be masked now, though.
1501  limits = dict()
1502  if self.config.doSaturation and not badAmp:
1503  limits.update({self.config.saturatedMaskName: amp.getSaturation()})
1504  if self.config.doSuspect and not badAmp:
1505  limits.update({self.config.suspectMaskName: amp.getSuspectLevel()})
1506 
1507  for maskName, maskThreshold in limits.items():
1508  if not math.isnan(maskThreshold):
1509  dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
1510  isrFunctions.makeThresholdMask(
1511  maskedImage=dataView,
1512  threshold=maskThreshold,
1513  growFootprints=0,
1514  maskName=maskName
1515  )
1516 
1517  # Determine if we've fully masked this amplifier with SUSPECT and SAT pixels.
1518  maskView = afwImage.Mask(maskedImage.getMask(), amp.getRawDataBBox(),
1519  afwImage.PARENT)
1520  maskVal = maskView.getPlaneBitMask([self.config.saturatedMaskName,
1521  self.config.suspectMaskName])
1522  if numpy.all(maskView.getArray() & maskVal > 0):
1523  badAmp = True
1524 
1525  return badAmp
1526 
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 1885 of file isrTask.py.

1885  def maskAndInterpDefect(self, ccdExposure, defectBaseList):
1886  """!Mask defects using mask plane "BAD" and interpolate over them, in place.
1887 
1888  Parameters
1889  ----------
1890  ccdExposure : `lsst.afw.image.Exposure`
1891  Exposure to process.
1892  defectBaseList : `List`
1893  List of defects to mask and interpolate.
1894 
1895  Notes
1896  -----
1897  Call this after CCD assembly, since defects may cross amplifier boundaries.
1898  """
1899  maskedImage = ccdExposure.getMaskedImage()
1900  defectList = []
1901  for d in defectBaseList:
1902  bbox = d.getBBox()
1903  nd = measAlg.Defect(bbox)
1904  defectList.append(nd)
1905  isrFunctions.maskPixelsFromDefectList(maskedImage, defectList, maskName='BAD')
1906  isrFunctions.interpolateDefectList(
1907  maskedImage=maskedImage,
1908  defectList=defectList,
1909  fwhm=self.config.fwhm,
1910  )
1911 
1912  if self.config.numEdgeSuspect > 0:
1913  goodBBox = maskedImage.getBBox()
1914  # This makes a bbox numEdgeSuspect pixels smaller than the image on each side
1915  goodBBox.grow(-self.config.numEdgeSuspect)
1916  # Mask pixels outside goodBBox as SUSPECT
1917  SourceDetectionTask.setEdgeBits(
1918  maskedImage,
1919  goodBBox,
1920  maskedImage.getMask().getPlaneBitMask("SUSPECT")
1921  )
1922 

◆ 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 1923 of file isrTask.py.

1923  def maskAndInterpNan(self, exposure):
1924  """!Mask NaNs using mask plane "UNMASKEDNAN" and interpolate over them, in place.
1925 
1926  Parameters
1927  ----------
1928  exposure : `lsst.afw.image.Exposure`
1929  Exposure to process.
1930 
1931  Notes
1932  -----
1933  We mask and interpolate over all NaNs, including those
1934  that are masked with other bits (because those may or may
1935  not be interpolated over later, and we want to remove all
1936  NaNs). Despite this behaviour, the "UNMASKEDNAN" mask plane
1937  is used to preserve the historical name.
1938  """
1939  maskedImage = exposure.getMaskedImage()
1940 
1941  # Find and mask NaNs
1942  maskedImage.getMask().addMaskPlane("UNMASKEDNAN")
1943  maskVal = maskedImage.getMask().getPlaneBitMask("UNMASKEDNAN")
1944  numNans = maskNans(maskedImage, maskVal)
1945  self.metadata.set("NUMNANS", numNans)
1946 
1947  # Interpolate over these previously-unmasked NaNs
1948  if numNans > 0:
1949  self.log.warn("There were %i unmasked NaNs", numNans)
1950  nanDefectList = isrFunctions.getDefectListFromMask(
1951  maskedImage=maskedImage,
1952  maskName='UNMASKEDNAN',
1953  )
1954  isrFunctions.interpolateDefectList(
1955  maskedImage=exposure.getMaskedImage(),
1956  defectList=nanDefectList,
1957  fwhm=self.config.fwhm,
1958  )
1959 
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 1960 of file isrTask.py.

1960  def measureBackground(self, exposure, IsrQaConfig=None):
1961  """Measure the image background in subgrids, for quality control purposes.
1962 
1963  Parameters
1964  ----------
1965  exposure : `lsst.afw.image.Exposure`
1966  Exposure to process.
1967  IsrQaConfig : `lsst.ip.isr.isrQa.IsrQaConfig`
1968  Configuration object containing parameters on which background
1969  statistics and subgrids to use.
1970  """
1971  if IsrQaConfig is not None:
1972  statsControl = afwMath.StatisticsControl(IsrQaConfig.flatness.clipSigma,
1973  IsrQaConfig.flatness.nIter)
1974  maskVal = exposure.getMaskedImage().getMask().getPlaneBitMask(["BAD", "SAT", "DETECTED"])
1975  statsControl.setAndMask(maskVal)
1976  maskedImage = exposure.getMaskedImage()
1977  stats = afwMath.makeStatistics(maskedImage, afwMath.MEDIAN | afwMath.STDEVCLIP, statsControl)
1978  skyLevel = stats.getValue(afwMath.MEDIAN)
1979  skySigma = stats.getValue(afwMath.STDEVCLIP)
1980  self.log.info("Flattened sky level: %f +/- %f" % (skyLevel, skySigma))
1981  metadata = exposure.getMetadata()
1982  metadata.set('SKYLEVEL', skyLevel)
1983  metadata.set('SKYSIGMA', skySigma)
1984 
1985  # calcluating flatlevel over the subgrids
1986  stat = afwMath.MEANCLIP if IsrQaConfig.flatness.doClip else afwMath.MEAN
1987  meshXHalf = int(IsrQaConfig.flatness.meshX/2.)
1988  meshYHalf = int(IsrQaConfig.flatness.meshY/2.)
1989  nX = int((exposure.getWidth() + meshXHalf) / IsrQaConfig.flatness.meshX)
1990  nY = int((exposure.getHeight() + meshYHalf) / IsrQaConfig.flatness.meshY)
1991  skyLevels = numpy.zeros((nX, nY))
1992 
1993  for j in range(nY):
1994  yc = meshYHalf + j * IsrQaConfig.flatness.meshY
1995  for i in range(nX):
1996  xc = meshXHalf + i * IsrQaConfig.flatness.meshX
1997 
1998  xLLC = xc - meshXHalf
1999  yLLC = yc - meshYHalf
2000  xURC = xc + meshXHalf - 1
2001  yURC = yc + meshYHalf - 1
2002 
2003  bbox = afwGeom.Box2I(afwGeom.Point2I(xLLC, yLLC), afwGeom.Point2I(xURC, yURC))
2004  miMesh = maskedImage.Factory(exposure.getMaskedImage(), bbox, afwImage.LOCAL)
2005 
2006  skyLevels[i, j] = afwMath.makeStatistics(miMesh, stat, statsControl).getValue()
2007 
2008  good = numpy.where(numpy.isfinite(skyLevels))
2009  skyMedian = numpy.median(skyLevels[good])
2010  flatness = (skyLevels[good] - skyMedian) / skyMedian
2011  flatness_rms = numpy.std(flatness)
2012  flatness_pp = flatness.max() - flatness.min() if len(flatness) > 0 else numpy.nan
2013 
2014  self.log.info("Measuring sky levels in %dx%d grids: %f" % (nX, nY, skyMedian))
2015  self.log.info("Sky flatness in %dx%d grids - pp: %f rms: %f" %
2016  (nX, nY, flatness_pp, flatness_rms))
2017 
2018  metadata.set('FLATNESS_PP', float(flatness_pp))
2019  metadata.set('FLATNESS_RMS', float(flatness_rms))
2020  metadata.set('FLATNESS_NGRIDS', '%dx%d' % (nX, nY))
2021  metadata.set('FLATNESS_MESHX', IsrQaConfig.flatness.meshX)
2022  metadata.set('FLATNESS_MESHY', IsrQaConfig.flatness.meshY)
2023 
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 1527 of file isrTask.py.

1527  def overscanCorrection(self, ccdExposure, amp):
1528  """Apply overscan correction in place.
1529 
1530  This method does initial pixel rejection of the overscan
1531  region. The overscan can also be optionally segmented to
1532  allow for discontinuous overscan responses to be fit
1533  separately. The actual overscan subtraction is performed by
1534  the `lsst.ip.isr.isrFunctions.overscanCorrection` function,
1535  which is called here after the amplifier is preprocessed.
1536 
1537  Parameters
1538  ----------
1539  ccdExposure : `lsst.afw.image.Exposure`
1540  Exposure to have overscan correction performed.
1541  amp : `lsst.afw.table.AmpInfoCatalog`
1542  The amplifier to consider while correcting the overscan.
1543 
1544  Returns
1545  -------
1546  overscanResults : `lsst.pipe.base.Struct`
1547  Result struct with components:
1548  - ``imageFit`` : scalar or `lsst.afw.image.Image`
1549  Value or fit subtracted from the amplifier image data.
1550  - ``overscanFit`` : scalar or `lsst.afw.image.Image`
1551  Value or fit subtracted from the overscan image data.
1552  - ``overscanImage`` : `lsst.afw.image.Image`
1553  Image of the overscan region with the overscan
1554  correction applied. This quantity is used to estimate
1555  the amplifier read noise empirically.
1556 
1557  Raises
1558  ------
1559  RuntimeError
1560  Raised if the ``amp`` does not contain raw pixel information.
1561 
1562  See Also
1563  --------
1564  lsst.ip.isr.isrFunctions.overscanCorrection
1565  """
1566  if not amp.getHasRawInfo():
1567  raise RuntimeError("This method must be executed on an amp with raw information.")
1568 
1569  if amp.getRawHorizontalOverscanBBox().isEmpty():
1570  self.log.info("ISR_OSCAN: No overscan region. Not performing overscan correction.")
1571  return None
1572 
1573  statControl = afwMath.StatisticsControl()
1574  statControl.setAndMask(ccdExposure.mask.getPlaneBitMask("SAT"))
1575 
1576  # Determine the bounding boxes
1577  dataBBox = amp.getRawDataBBox()
1578  oscanBBox = amp.getRawHorizontalOverscanBBox()
1579  dx0 = 0
1580  dx1 = 0
1581 
1582  prescanBBox = amp.getRawPrescanBBox()
1583  if (oscanBBox.getBeginX() > prescanBBox.getBeginX()): # amp is at the right
1584  dx0 += self.config.overscanNumLeadingColumnsToSkip
1585  dx1 -= self.config.overscanNumTrailingColumnsToSkip
1586  else:
1587  dx0 += self.config.overscanNumTrailingColumnsToSkip
1588  dx1 -= self.config.overscanNumLeadingColumnsToSkip
1589 
1590  # Determine if we need to work on subregions of the amplifier and overscan.
1591  imageBBoxes = []
1592  overscanBBoxes = []
1593 
1594  if ((self.config.overscanBiasJump and
1595  self.config.overscanBiasJumpLocation) and
1596  (ccdExposure.getMetadata().exists(self.config.overscanBiasJumpKeyword) and
1597  ccdExposure.getMetadata().getScalar(self.config.overscanBiasJumpKeyword) in
1598  self.config.overscanBiasJumpDevices)):
1599  if amp.getReadoutCorner() in (afwTable.LL, afwTable.LR):
1600  yLower = self.config.overscanBiasJumpLocation
1601  yUpper = dataBBox.getHeight() - yLower
1602  else:
1603  yUpper = self.config.overscanBiasJumpLocation
1604  yLower = dataBBox.getHeight() - yUpper
1605 
1606  imageBBoxes.append(afwGeom.Box2I(dataBBox.getBegin(),
1607  afwGeom.Extent2I(dataBBox.getWidth(), yLower)))
1608  overscanBBoxes.append(afwGeom.Box2I(oscanBBox.getBegin() +
1609  afwGeom.Extent2I(dx0, 0),
1610  afwGeom.Extent2I(oscanBBox.getWidth() - dx0 + dx1,
1611  yLower)))
1612 
1613  imageBBoxes.append(afwGeom.Box2I(dataBBox.getBegin() + afwGeom.Extent2I(0, yLower),
1614  afwGeom.Extent2I(dataBBox.getWidth(), yUpper)))
1615  overscanBBoxes.append(afwGeom.Box2I(oscanBBox.getBegin() + afwGeom.Extent2I(dx0, yLower),
1616  afwGeom.Extent2I(oscanBBox.getWidth() - dx0 + dx1,
1617  yUpper)))
1618  else:
1619  imageBBoxes.append(afwGeom.Box2I(dataBBox.getBegin(),
1620  afwGeom.Extent2I(dataBBox.getWidth(), dataBBox.getHeight())))
1621  overscanBBoxes.append(afwGeom.Box2I(oscanBBox.getBegin() + afwGeom.Extent2I(dx0, 0),
1622  afwGeom.Extent2I(oscanBBox.getWidth() - dx0 + dx1,
1623  oscanBBox.getHeight())))
1624 
1625  # Perform overscan correction on subregions, ensuring saturated pixels are masked.
1626  for imageBBox, overscanBBox in zip(imageBBoxes, overscanBBoxes):
1627  ampImage = ccdExposure.maskedImage[imageBBox]
1628  overscanImage = ccdExposure.maskedImage[overscanBBox]
1629 
1630  overscanArray = overscanImage.image.array
1631  median = numpy.ma.median(numpy.ma.masked_where(overscanImage.mask.array, overscanArray))
1632  bad = numpy.where(numpy.abs(overscanArray - median) > self.config.overscanMaxDev)
1633  overscanImage.mask.array[bad] = overscanImage.mask.getPlaneBitMask("SAT")
1634 
1635  statControl = afwMath.StatisticsControl()
1636  statControl.setAndMask(ccdExposure.mask.getPlaneBitMask("SAT"))
1637 
1638  overscanResults = isrFunctions.overscanCorrection(ampMaskedImage=ampImage,
1639  overscanImage=overscanImage,
1640  fitType=self.config.overscanFitType,
1641  order=self.config.overscanOrder,
1642  collapseRej=self.config.overscanNumSigmaClip,
1643  statControl=statControl,
1644  overscanIsInt=self.config.overscanIsInt
1645  )
1646 
1647  # Measure average overscan levels and record them in the metadata
1648  levelStat = afwMath.MEDIAN
1649  sigmaStat = afwMath.STDEVCLIP
1650 
1651  sctrl = afwMath.StatisticsControl(self.config.qa.flatness.clipSigma,
1652  self.config.qa.flatness.nIter)
1653  metadata = ccdExposure.getMetadata()
1654  ampNum = amp.getName()
1655  if self.config.overscanFitType in ("MEDIAN", "MEAN", "MEANCLIP"):
1656  metadata.set("ISR_OSCAN_LEVEL%s" % ampNum, overscanResults.overscanFit)
1657  metadata.set("ISR_OSCAN_SIGMA%s" % ampNum, 0.0)
1658  else:
1659  stats = afwMath.makeStatistics(overscanResults.overscanFit, levelStat | sigmaStat, sctrl)
1660  metadata.set("ISR_OSCAN_LEVEL%s" % ampNum, stats.getValue(levelStat))
1661  metadata.set("ISR_OSCAN_SIGMA%s" % ampNum, stats.getValue(sigmaStat))
1662 
1663  return overscanResults
1664 
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
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 801 of file isrTask.py.

801  def readIsrData(self, dataRef, rawExposure):
802  """!Retrieve necessary frames for instrument signature removal.
803 
804  Pre-fetching all required ISR data products limits the IO
805  required by the ISR. Any conflict between the calibration data
806  available and that needed for ISR is also detected prior to
807  doing processing, allowing it to fail quickly.
808 
809  Parameters
810  ----------
811  dataRef : `daf.persistence.butlerSubset.ButlerDataRef`
812  Butler reference of the detector data to be processed
813  rawExposure : `afw.image.Exposure`
814  The raw exposure that will later be corrected with the
815  retrieved calibration data; should not be modified in this
816  method.
817 
818  Returns
819  -------
820  result : `lsst.pipe.base.Struct`
821  Result struct with components (which may be `None`):
822  - ``bias``: bias calibration frame (`afw.image.Exposure`)
823  - ``linearizer``: functor for linearization (`ip.isr.linearize.LinearizeBase`)
824  - ``crosstalkSources``: list of possible crosstalk sources (`list`)
825  - ``dark``: dark calibration frame (`afw.image.Exposure`)
826  - ``flat``: flat calibration frame (`afw.image.Exposure`)
827  - ``bfKernel``: Brighter-Fatter kernel (`numpy.ndarray`)
828  - ``defects``: list of defects (`list`)
829  - ``fringes``: `lsst.pipe.base.Struct` with components:
830  - ``fringes``: fringe calibration frame (`afw.image.Exposure`)
831  - ``seed``: random seed derived from the ccdExposureId for random
832  number generator (`uint32`)
833  - ``opticsTransmission``: `lsst.afw.image.TransmissionCurve`
834  A ``TransmissionCurve`` that represents the throughput of the optics,
835  to be evaluated in focal-plane coordinates.
836  - ``filterTransmission`` : `lsst.afw.image.TransmissionCurve`
837  A ``TransmissionCurve`` that represents the throughput of the filter
838  itself, to be evaluated in focal-plane coordinates.
839  - ``sensorTransmission`` : `lsst.afw.image.TransmissionCurve`
840  A ``TransmissionCurve`` that represents the throughput of the sensor
841  itself, to be evaluated in post-assembly trimmed detector coordinates.
842  - ``atmosphereTransmission`` : `lsst.afw.image.TransmissionCurve`
843  A ``TransmissionCurve`` that represents the throughput of the
844  atmosphere, assumed to be spatially constant.
845 
846  """
847  ccd = rawExposure.getDetector()
848  rawExposure.mask.addMaskPlane("UNMASKEDNAN") # needed to match pre DM-15862 processing.
849  biasExposure = (self.getIsrExposure(dataRef, self.config.biasDataProductName)
850  if self.config.doBias else None)
851  # immediate=True required for functors and linearizers are functors; see ticket DM-6515
852  linearizer = (dataRef.get("linearizer", immediate=True)
853  if self.doLinearize(ccd) else None)
854  crosstalkSources = (self.crosstalk.prepCrosstalk(dataRef)
855  if self.config.doCrosstalk else None)
856  darkExposure = (self.getIsrExposure(dataRef, self.config.darkDataProductName)
857  if self.config.doDark else None)
858  flatExposure = (self.getIsrExposure(dataRef, self.config.flatDataProductName)
859  if self.config.doFlat else None)
860  brighterFatterKernel = (dataRef.get("bfKernel")
861  if self.config.doBrighterFatter else None)
862  defectList = (dataRef.get("defects")
863  if self.config.doDefect else None)
864  fringeStruct = (self.fringe.readFringes(dataRef, assembler=self.assembleCcd
865  if self.config.doAssembleIsrExposures else None)
866  if self.config.doFringe and self.fringe.checkFilter(rawExposure)
867  else pipeBase.Struct(fringes=None))
868 
869  if self.config.doAttachTransmissionCurve:
870  opticsTransmission = (dataRef.get("transmission_optics")
871  if self.config.doUseOpticsTransmission else None)
872  filterTransmission = (dataRef.get("transmission_filter")
873  if self.config.doUseFilterTransmission else None)
874  sensorTransmission = (dataRef.get("transmission_sensor")
875  if self.config.doUseSensorTransmission else None)
876  atmosphereTransmission = (dataRef.get("transmission_atmosphere")
877  if self.config.doUseAtmosphereTransmission else None)
878  else:
879  opticsTransmission = None
880  filterTransmission = None
881  sensorTransmission = None
882  atmosphereTransmission = None
883 
884  # Struct should include only kwargs to run()
885  return pipeBase.Struct(bias=biasExposure,
886  linearizer=linearizer,
887  crosstalkSources=crosstalkSources,
888  dark=darkExposure,
889  flat=flatExposure,
890  bfKernel=brighterFatterKernel,
891  defects=defectList,
892  fringes=fringeStruct,
893  opticsTransmission=opticsTransmission,
894  filterTransmission=filterTransmission,
895  sensorTransmission=sensorTransmission,
896  atmosphereTransmission=atmosphereTransmission,
897  )
898 

◆ 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 2024 of file isrTask.py.

2024  def roughZeroPoint(self, exposure):
2025  """Set an approximate magnitude zero point for the exposure.
2026 
2027  Parameters
2028  ----------
2029  exposure : `lsst.afw.image.Exposure`
2030  Exposure to process.
2031  """
2032  filterName = afwImage.Filter(exposure.getFilter().getId()).getName() # Canonical name for filter
2033  if filterName in self.config.fluxMag0T1:
2034  fluxMag0 = self.config.fluxMag0T1[filterName]
2035  else:
2036  self.log.warn("No rough magnitude zero point set for filter %s" % filterName)
2037  fluxMag0 = self.config.defaultFluxMag0T1
2038 
2039  expTime = exposure.getInfo().getVisitInfo().getExposureTime()
2040  if not expTime > 0: # handle NaN as well as <= 0
2041  self.log.warn("Non-positive exposure time; skipping rough zero point")
2042  return
2043 
2044  self.log.info("Setting rough magnitude zero point: %f" % (2.5*math.log10(fluxMag0*expTime),))
2045  exposure.getCalib().setFluxMag0(fluxMag0*expTime)
2046 
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,
  detectorNum = None,
  isGen3 = False 
)

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. detectorNum : int, optional The integer number for the detector to process. isGen3 : bool, optional Flag this call to run() as using the Gen3 butler environment.

Returns

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

  • exposure : afw.image.Exposure The fully ISR corrected exposure.
  • outputExposure : afw.image.Exposure An alias for 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 905 of file isrTask.py.

905  ):
906  """!Perform instrument signature removal on an exposure.
907 
908  Steps included in the ISR processing, in order performed, are:
909  - saturation and suspect pixel masking
910  - overscan subtraction
911  - CCD assembly of individual amplifiers
912  - bias subtraction
913  - variance image construction
914  - linearization of non-linear response
915  - crosstalk masking
916  - brighter-fatter correction
917  - dark subtraction
918  - fringe correction
919  - stray light subtraction
920  - flat correction
921  - masking of known defects and camera specific features
922  - vignette calculation
923  - appending transmission curve and distortion model
924 
925  Parameters
926  ----------
927  ccdExposure : `lsst.afw.image.Exposure`
928  The raw exposure that is to be run through ISR. The
929  exposure is modified by this method.
930  camera : `lsst.afw.cameraGeom.Camera`, optional
931  The camera geometry for this exposure. Used to select the
932  distortion model appropriate for this data.
933  bias : `lsst.afw.image.Exposure`, optional
934  Bias calibration frame.
935  linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional
936  Functor for linearization.
937  crosstalkSources : `list`, optional
938  List of possible crosstalk sources.
939  dark : `lsst.afw.image.Exposure`, optional
940  Dark calibration frame.
941  flat : `lsst.afw.image.Exposure`, optional
942  Flat calibration frame.
943  bfKernel : `numpy.ndarray`, optional
944  Brighter-fatter kernel.
945  defects : `list`, optional
946  List of defects.
947  fringes : `lsst.pipe.base.Struct`, optional
948  Struct containing the fringe correction data, with
949  elements:
950  - ``fringes``: fringe calibration frame (`afw.image.Exposure`)
951  - ``seed``: random seed derived from the ccdExposureId for random
952  number generator (`uint32`)
953  opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional
954  A ``TransmissionCurve`` that represents the throughput of the optics,
955  to be evaluated in focal-plane coordinates.
956  filterTransmission : `lsst.afw.image.TransmissionCurve`
957  A ``TransmissionCurve`` that represents the throughput of the filter
958  itself, to be evaluated in focal-plane coordinates.
959  sensorTransmission : `lsst.afw.image.TransmissionCurve`
960  A ``TransmissionCurve`` that represents the throughput of the sensor
961  itself, to be evaluated in post-assembly trimmed detector coordinates.
962  atmosphereTransmission : `lsst.afw.image.TransmissionCurve`
963  A ``TransmissionCurve`` that represents the throughput of the
964  atmosphere, assumed to be spatially constant.
965  detectorNum : `int`, optional
966  The integer number for the detector to process.
967  isGen3 : bool, optional
968  Flag this call to run() as using the Gen3 butler environment.
969 
970  Returns
971  -------
972  result : `lsst.pipe.base.Struct`
973  Result struct with component:
974  - ``exposure`` : `afw.image.Exposure`
975  The fully ISR corrected exposure.
976  - ``outputExposure`` : `afw.image.Exposure`
977  An alias for `exposure`
978  - ``ossThumb`` : `numpy.ndarray`
979  Thumbnail image of the exposure after overscan subtraction.
980  - ``flattenedThumb`` : `numpy.ndarray`
981  Thumbnail image of the exposure after flat-field correction.
982 
983  Raises
984  ------
985  RuntimeError
986  Raised if a configuration option is set to True, but the
987  required calibration data has not been specified.
988 
989  Notes
990  -----
991  The current processed exposure can be viewed by setting the
992  appropriate lsstDebug entries in the `debug.display`
993  dictionary. The names of these entries correspond to some of
994  the IsrTaskConfig Boolean options, with the value denoting the
995  frame to use. The exposure is shown inside the matching
996  option check and after the processing of that step has
997  finished. The steps with debug points are:
998 
999  doAssembleCcd
1000  doBias
1001  doCrosstalk
1002  doBrighterFatter
1003  doDark
1004  doFringe
1005  doStrayLight
1006  doFlat
1007 
1008  In addition, setting the "postISRCCD" entry displays the
1009  exposure after all ISR processing has finished.
1010 
1011  """
1012 
1013  if isGen3 is True:
1014  # Gen3 currently cannot automatically do configuration overrides.
1015  # DM-15257 looks to discuss this issue.
1016 
1017  self.config.doFringe = False
1018 
1019  # Configure input exposures;
1020  if detectorNum is None:
1021  raise RuntimeError("Must supply the detectorNum if running as Gen3")
1022 
1023  ccdExposure = self.ensureExposure(ccdExposure, camera, detectorNum)
1024  bias = self.ensureExposure(bias, camera, detectorNum)
1025  dark = self.ensureExposure(dark, camera, detectorNum)
1026  flat = self.ensureExposure(flat, camera, detectorNum)
1027  else:
1028  if isinstance(ccdExposure, ButlerDataRef):
1029  return self.runDataRef(ccdExposure)
1030 
1031  ccd = ccdExposure.getDetector()
1032 
1033  if not ccd:
1034  assert not self.config.doAssembleCcd, "You need a Detector to run assembleCcd"
1035  ccd = [FakeAmp(ccdExposure, self.config)]
1036 
1037  # Validate Input
1038  if self.config.doBias and bias is None:
1039  raise RuntimeError("Must supply a bias exposure if config.doBias=True.")
1040  if self.doLinearize(ccd) and linearizer is None:
1041  raise RuntimeError("Must supply a linearizer if config.doLinearize=True for this detector.")
1042  if self.config.doBrighterFatter and bfKernel is None:
1043  raise RuntimeError("Must supply a kernel if config.doBrighterFatter=True.")
1044  if self.config.doDark and dark is None:
1045  raise RuntimeError("Must supply a dark exposure if config.doDark=True.")
1046  if fringes is None:
1047  fringes = pipeBase.Struct(fringes=None)
1048  if self.config.doFringe and not isinstance(fringes, pipeBase.Struct):
1049  raise RuntimeError("Must supply fringe exposure as a pipeBase.Struct.")
1050  if self.config.doFlat and flat is None:
1051  raise RuntimeError("Must supply a flat exposure if config.doFlat=True.")
1052  if self.config.doDefect and defects is None:
1053  raise RuntimeError("Must supply defects if config.doDefect=True.")
1054  if self.config.doAddDistortionModel and camera is None:
1055  raise RuntimeError("Must supply camera if config.doAddDistortionModel=True.")
1056 
1057  # Begin ISR processing.
1058  if self.config.doConvertIntToFloat:
1059  self.log.info("Converting exposure to floating point values")
1060  ccdExposure = self.convertIntToFloat(ccdExposure)
1061 
1062  # Amplifier level processing.
1063  overscans = []
1064  for amp in ccd:
1065  # if ccdExposure is one amp, check for coverage to prevent performing ops multiple times
1066  if ccdExposure.getBBox().contains(amp.getBBox()):
1067  # Check for fully masked bad amplifiers, and generate masks for SUSPECT and SATURATED values.
1068  badAmp = self.maskAmplifier(ccdExposure, amp, defects)
1069 
1070  if self.config.doOverscan and not badAmp:
1071  # Overscan correction on amp-by-amp basis.
1072  overscanResults = self.overscanCorrection(ccdExposure, amp)
1073  self.log.debug("Corrected overscan for amplifier %s" % (amp.getName()))
1074  if self.config.qa is not None and self.config.qa.saveStats is True:
1075  if isinstance(overscanResults.overscanFit, float):
1076  qaMedian = overscanResults.overscanFit
1077  qaStdev = float("NaN")
1078  else:
1079  qaStats = afwMath.makeStatistics(overscanResults.overscanFit,
1080  afwMath.MEDIAN | afwMath.STDEVCLIP)
1081  qaMedian = qaStats.getValue(afwMath.MEDIAN)
1082  qaStdev = qaStats.getValue(afwMath.STDEVCLIP)
1083 
1084  self.metadata.set("ISR OSCAN {} MEDIAN".format(amp.getName()), qaMedian)
1085  self.metadata.set("ISR OSCAN {} STDEV".format(amp.getName()), qaStdev)
1086  self.log.debug(" Overscan stats for amplifer %s: %f +/- %f" %
1087  (amp.getName(), qaMedian, qaStdev))
1088  ccdExposure.getMetadata().set('OVERSCAN', "Overscan corrected")
1089  else:
1090  self.log.warn("Amplifier %s is bad." % (amp.getName()))
1091  overscanResults = None
1092 
1093  overscans.append(overscanResults if overscanResults is not None else None)
1094  else:
1095  self.log.info("Skipped OSCAN")
1096 
1097  if self.config.doCrosstalk and self.config.doCrosstalkBeforeAssemble:
1098  self.log.info("Applying crosstalk correction.")
1099  self.crosstalk.run(ccdExposure, crosstalkSources=crosstalkSources)
1100  self.debugView(ccdExposure, "doCrosstalk")
1101 
1102  if self.config.doAssembleCcd:
1103  self.log.info("Assembling CCD from amplifiers")
1104  ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1105 
1106  if self.config.expectWcs and not ccdExposure.getWcs():
1107  self.log.warn("No WCS found in input exposure")
1108  self.debugView(ccdExposure, "doAssembleCcd")
1109 
1110  ossThumb = None
1111  if self.config.qa.doThumbnailOss:
1112  ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1113 
1114  if self.config.doBias:
1115  self.log.info("Applying bias correction.")
1116  isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1117  trimToFit=self.config.doTrimToMatchCalib)
1118  self.debugView(ccdExposure, "doBias")
1119 
1120  if self.config.doVariance:
1121  for amp, overscanResults in zip(ccd, overscans):
1122  if ccdExposure.getBBox().contains(amp.getBBox()):
1123  self.log.debug("Constructing variance map for amplifer %s" % (amp.getName()))
1124  ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1125  if overscanResults is not None:
1126  self.updateVariance(ampExposure, amp,
1127  overscanImage=overscanResults.overscanImage)
1128  else:
1129  self.updateVariance(ampExposure, amp,
1130  overscanImage=None)
1131  if self.config.qa is not None and self.config.qa.saveStats is True:
1132  qaStats = afwMath.makeStatistics(ampExposure.getVariance(),
1133  afwMath.MEDIAN | afwMath.STDEVCLIP)
1134  self.metadata.set("ISR VARIANCE {} MEDIAN".format(amp.getName()),
1135  qaStats.getValue(afwMath.MEDIAN))
1136  self.metadata.set("ISR VARIANCE {} STDEV".format(amp.getName()),
1137  qaStats.getValue(afwMath.STDEVCLIP))
1138  self.log.debug(" Variance stats for amplifer %s: %f +/- %f" %
1139  (amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1140  qaStats.getValue(afwMath.STDEVCLIP)))
1141 
1142  if self.doLinearize(ccd):
1143  self.log.info("Applying linearizer.")
1144  linearizer(image=ccdExposure.getMaskedImage().getImage(), detector=ccd, log=self.log)
1145 
1146  if self.config.doCrosstalk and not self.config.doCrosstalkBeforeAssemble:
1147  self.log.info("Applying crosstalk correction.")
1148  self.crosstalk.run(ccdExposure, crosstalkSources=crosstalkSources)
1149  self.debugView(ccdExposure, "doCrosstalk")
1150 
1151  if self.config.doWidenSaturationTrails:
1152  self.log.info("Widening saturation trails.")
1153  isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1154 
1155  interpolationDone = False
1156  if self.config.doBrighterFatter:
1157  # We need to apply flats and darks before we can interpolate, and we
1158  # need to interpolate before we do B-F, but we do B-F without the
1159  # flats and darks applied so we can work in units of electrons or holes.
1160  # This context manager applies and then removes the darks and flats.
1161  with self.flatContext(ccdExposure, flat, dark):
1162  if self.config.doDefect:
1163  self.maskAndInterpDefect(ccdExposure, defects)
1164 
1165  if self.config.doSaturationInterpolation:
1166  self.saturationInterpolation(ccdExposure)
1167 
1168  self.maskAndInterpNan(ccdExposure)
1169  interpolationDone = True
1170 
1171  if self.config.brighterFatterLevel == 'DETECTOR':
1172  kernelElement = bfKernel
1173  else:
1174  # TODO: DM-15631 for implementing this
1175  raise NotImplementedError("per-amplifier brighter-fatter correction not yet implemented")
1176  self.log.info("Applying brighter fatter correction.")
1177  isrFunctions.brighterFatterCorrection(ccdExposure, kernelElement,
1178  self.config.brighterFatterMaxIter,
1179  self.config.brighterFatterThreshold,
1180  self.config.brighterFatterApplyGain,
1181  )
1182  self.debugView(ccdExposure, "doBrighterFatter")
1183 
1184  if self.config.doDark:
1185  self.log.info("Applying dark correction.")
1186  self.darkCorrection(ccdExposure, dark)
1187  self.debugView(ccdExposure, "doDark")
1188 
1189  if self.config.doFringe and not self.config.fringeAfterFlat:
1190  self.log.info("Applying fringe correction before flat.")
1191  self.fringe.run(ccdExposure, **fringes.getDict())
1192  self.debugView(ccdExposure, "doFringe")
1193 
1194  if self.config.doStrayLight:
1195  self.log.info("Applying stray light correction.")
1196  self.strayLight.run(ccdExposure)
1197  self.debugView(ccdExposure, "doStrayLight")
1198 
1199  if self.config.doFlat:
1200  self.log.info("Applying flat correction.")
1201  self.flatCorrection(ccdExposure, flat)
1202  self.debugView(ccdExposure, "doFlat")
1203 
1204  if self.config.doApplyGains:
1205  self.log.info("Applying gain correction instead of flat.")
1206  isrFunctions.applyGains(ccdExposure, self.config.normalizeGains)
1207 
1208  if self.config.doDefect and not interpolationDone:
1209  self.log.info("Masking and interpolating defects.")
1210  self.maskAndInterpDefect(ccdExposure, defects)
1211 
1212  if self.config.doSaturation and not interpolationDone:
1213  self.log.info("Interpolating saturated pixels.")
1214  self.saturationInterpolation(ccdExposure)
1215 
1216  if self.config.doNanInterpAfterFlat or not interpolationDone:
1217  self.log.info("Masking and interpolating NAN value pixels.")
1218  self.maskAndInterpNan(ccdExposure)
1219 
1220  if self.config.doFringe and self.config.fringeAfterFlat:
1221  self.log.info("Applying fringe correction after flat.")
1222  self.fringe.run(ccdExposure, **fringes.getDict())
1223 
1224  if self.config.doSetBadRegions:
1225  badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
1226  self.log.info("Set %d BAD pixels to %f." % (badPixelCount, badPixelValue))
1227 
1228  flattenedThumb = None
1229  if self.config.qa.doThumbnailFlattened:
1230  flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1231 
1232  if self.config.doCameraSpecificMasking:
1233  self.log.info("Masking regions for camera specific reasons.")
1234  self.masking.run(ccdExposure)
1235 
1236  self.roughZeroPoint(ccdExposure)
1237 
1238  if self.config.doVignette:
1239  self.log.info("Constructing Vignette polygon.")
1240  self.vignettePolygon = self.vignette.run(ccdExposure)
1241 
1242  if self.config.vignette.doWriteVignettePolygon:
1243  self.setValidPolygonIntersect(ccdExposure, self.vignettePolygon)
1244 
1245  if self.config.doAttachTransmissionCurve:
1246  self.log.info("Adding transmission curves.")
1247  isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1248  filterTransmission=filterTransmission,
1249  sensorTransmission=sensorTransmission,
1250  atmosphereTransmission=atmosphereTransmission)
1251 
1252  if self.config.doAddDistortionModel:
1253  self.log.info("Adding a distortion model to the WCS.")
1254  isrFunctions.addDistortionModel(exposure=ccdExposure, camera=camera)
1255 
1256  if self.config.doMeasureBackground:
1257  self.log.info("Measuring background level:")
1258  self.measureBackground(ccdExposure, self.config.qa)
1259 
1260  if self.config.qa is not None and self.config.qa.saveStats is True:
1261  for amp in ccd:
1262  ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1263  qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1264  afwMath.MEDIAN | afwMath.STDEVCLIP)
1265  self.metadata.set("ISR BACKGROUND {} MEDIAN".format(amp.getName()),
1266  qaStats.getValue(afwMath.MEDIAN))
1267  self.metadata.set("ISR BACKGROUND {} STDEV".format(amp.getName()),
1268  qaStats.getValue(afwMath.STDEVCLIP))
1269  self.log.debug(" Background stats for amplifer %s: %f +/- %f" %
1270  (amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1271  qaStats.getValue(afwMath.STDEVCLIP)))
1272 
1273  self.debugView(ccdExposure, "postISRCCD")
1274 
1275  return pipeBase.Struct(
1276  exposure=ccdExposure,
1277  ossThumb=ossThumb,
1278  flattenedThumb=flattenedThumb,
1279 
1280  outputExposure=ccdExposure,
1281  outputOssThumbnail=ossThumb,
1282  outputFlattenedThumbnail=flattenedThumb,
1283  )
1284 
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 1286 of file isrTask.py.

1286  def runDataRef(self, sensorRef):
1287  """Perform instrument signature removal on a ButlerDataRef of a Sensor.
1288 
1289  This method contains the `CmdLineTask` interface to the ISR
1290  processing. All IO is handled here, freeing the `run()` method
1291  to manage only pixel-level calculations. The steps performed
1292  are:
1293  - Read in necessary detrending/isr/calibration data.
1294  - Process raw exposure in `run()`.
1295  - Persist the ISR-corrected exposure as "postISRCCD" if
1296  config.doWrite=True.
1297 
1298  Parameters
1299  ----------
1300  sensorRef : `daf.persistence.butlerSubset.ButlerDataRef`
1301  DataRef of the detector data to be processed
1302 
1303  Returns
1304  -------
1305  result : `lsst.pipe.base.Struct`
1306  Result struct with component:
1307  - ``exposure`` : `afw.image.Exposure`
1308  The fully ISR corrected exposure.
1309 
1310  Raises
1311  ------
1312  RuntimeError
1313  Raised if a configuration option is set to True, but the
1314  required calibration data does not exist.
1315 
1316  """
1317  self.log.info("Performing ISR on sensor %s" % (sensorRef.dataId))
1318 
1319  ccdExposure = sensorRef.get(self.config.datasetType)
1320 
1321  camera = sensorRef.get("camera")
1322  if camera is None and self.config.doAddDistortionModel:
1323  raise RuntimeError("config.doAddDistortionModel is True "
1324  "but could not get a camera from the butler")
1325  isrData = self.readIsrData(sensorRef, ccdExposure)
1326 
1327  result = self.run(ccdExposure, camera=camera, **isrData.getDict())
1328 
1329  if self.config.doWrite:
1330  sensorRef.put(result.exposure, "postISRCCD")
1331  if result.ossThumb is not None:
1332  isrQa.writeThumbnail(sensorRef, result.ossThumb, "ossThumb")
1333  if result.flattenedThumb is not None:
1334  isrQa.writeThumbnail(sensorRef, result.flattenedThumb, "flattenedThumb")
1335 
1336  return result
1337 

◆ 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 1801 of file isrTask.py.

1801  def saturationDetection(self, exposure, amp):
1802  """!Detect saturated pixels and mask them using mask plane config.saturatedMaskName, in place.
1803 
1804  Parameters
1805  ----------
1806  exposure : `lsst.afw.image.Exposure`
1807  Exposure to process. Only the amplifier DataSec is processed.
1808  amp : `lsst.afw.table.AmpInfoCatalog`
1809  Amplifier detector data.
1810 
1811  See Also
1812  --------
1813  lsst.ip.isr.isrFunctions.makeThresholdMask
1814  """
1815  if not math.isnan(amp.getSaturation()):
1816  maskedImage = exposure.getMaskedImage()
1817  dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
1818  isrFunctions.makeThresholdMask(
1819  maskedImage=dataView,
1820  threshold=amp.getSaturation(),
1821  growFootprints=0,
1822  maskName=self.config.saturatedMaskName,
1823  )
1824 

◆ 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 1825 of file isrTask.py.

1825  def saturationInterpolation(self, ccdExposure):
1826  """!Interpolate over saturated pixels, in place.
1827 
1828  This method should be called after `saturationDetection`, to
1829  ensure that the saturated pixels have been identified in the
1830  SAT mask. It should also be called after `assembleCcd`, since
1831  saturated regions may cross amplifier boundaries.
1832 
1833  Parameters
1834  ----------
1835  exposure : `lsst.afw.image.Exposure`
1836  Exposure to process.
1837 
1838  See Also
1839  --------
1840  lsst.ip.isr.isrTask.saturationDetection
1841  lsst.ip.isr.isrFunctions.interpolateFromMask
1842  """
1843  isrFunctions.interpolateFromMask(
1844  maskedImage=ccdExposure.getMaskedImage(),
1845  fwhm=self.config.fwhm,
1846  growFootprints=self.config.growSaturationFootprintSize,
1847  maskName=self.config.saturatedMaskName,
1848  )
1849 

◆ 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 2047 of file isrTask.py.

2047  def setValidPolygonIntersect(self, ccdExposure, fpPolygon):
2048  """!Set the valid polygon as the intersection of fpPolygon and the ccd corners.
2049 
2050  Parameters
2051  ----------
2052  ccdExposure : `lsst.afw.image.Exposure`
2053  Exposure to process.
2054  fpPolygon : `lsst.afw.geom.Polygon`
2055  Polygon in focal plane coordinates.
2056  """
2057  # Get ccd corners in focal plane coordinates
2058  ccd = ccdExposure.getDetector()
2059  fpCorners = ccd.getCorners(FOCAL_PLANE)
2060  ccdPolygon = Polygon(fpCorners)
2061 
2062  # Get intersection of ccd corners with fpPolygon
2063  intersect = ccdPolygon.intersectionSingle(fpPolygon)
2064 
2065  # Transform back to pixel positions and build new polygon
2066  ccdPoints = ccd.transform(intersect, FOCAL_PLANE, PIXELS)
2067  validPolygon = Polygon(ccdPoints)
2068  ccdExposure.getInfo().setValidPolygon(validPolygon)
2069 

◆ 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 1850 of file isrTask.py.

1850  def suspectDetection(self, exposure, amp):
1851  """!Detect suspect pixels and mask them using mask plane config.suspectMaskName, in place.
1852 
1853  Parameters
1854  ----------
1855  exposure : `lsst.afw.image.Exposure`
1856  Exposure to process. Only the amplifier DataSec is processed.
1857  amp : `lsst.afw.table.AmpInfoCatalog`
1858  Amplifier detector data.
1859 
1860  See Also
1861  --------
1862  lsst.ip.isr.isrFunctions.makeThresholdMask
1863 
1864  Notes
1865  -----
1866  Suspect pixels are pixels whose value is greater than amp.getSuspectLevel().
1867  This is intended to indicate pixels that may be affected by unknown systematics;
1868  for example if non-linearity corrections above a certain level are unstable
1869  then that would be a useful value for suspectLevel. A value of `nan` indicates
1870  that no such level exists and no pixels are to be masked as suspicious.
1871  """
1872  suspectLevel = amp.getSuspectLevel()
1873  if math.isnan(suspectLevel):
1874  return
1875 
1876  maskedImage = exposure.getMaskedImage()
1877  dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
1878  isrFunctions.makeThresholdMask(
1879  maskedImage=dataView,
1880  threshold=suspectLevel,
1881  growFootprints=0,
1882  maskName=self.config.suspectMaskName,
1883  )
1884 

◆ 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 1665 of file isrTask.py.

1665  def updateVariance(self, ampExposure, amp, overscanImage=None):
1666  """Set the variance plane using the amplifier gain and read noise
1667 
1668  The read noise is calculated from the ``overscanImage`` if the
1669  ``doEmpiricalReadNoise`` option is set in the configuration; otherwise
1670  the value from the amplifier data is used.
1671 
1672  Parameters
1673  ----------
1674  ampExposure : `lsst.afw.image.Exposure`
1675  Exposure to process.
1676  amp : `lsst.afw.table.AmpInfoRecord` or `FakeAmp`
1677  Amplifier detector data.
1678  overscanImage : `lsst.afw.image.MaskedImage`, optional.
1679  Image of overscan, required only for empirical read noise.
1680 
1681  See also
1682  --------
1683  lsst.ip.isr.isrFunctions.updateVariance
1684  """
1685  maskPlanes = [self.config.saturatedMaskName, self.config.suspectMaskName]
1686  gain = amp.getGain()
1687 
1688  if math.isnan(gain):
1689  gain = 1.0
1690  self.log.warn("Gain set to NAN! Updating to 1.0 to generate Poisson variance.")
1691  elif gain <= 0:
1692  patchedGain = 1.0
1693  self.log.warn("Gain for amp %s == %g <= 0; setting to %f" %
1694  (amp.getName(), gain, patchedGain))
1695  gain = patchedGain
1696 
1697  if self.config.doEmpiricalReadNoise and overscanImage is None:
1698  self.log.info("Overscan is none for EmpiricalReadNoise")
1699 
1700  if self.config.doEmpiricalReadNoise and overscanImage is not None:
1701  stats = afwMath.StatisticsControl()
1702  stats.setAndMask(overscanImage.mask.getPlaneBitMask(maskPlanes))
1703  readNoise = afwMath.makeStatistics(overscanImage, afwMath.STDEVCLIP, stats).getValue()
1704  self.log.info("Calculated empirical read noise for amp %s: %f", amp.getName(), readNoise)
1705  else:
1706  readNoise = amp.getReadNoise()
1707 
1708  isrFunctions.updateVariance(
1709  maskedImage=ampExposure.getMaskedImage(),
1710  gain=gain,
1711  readNoise=readNoise,
1712  )
1713 
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 693 of file isrTask.py.

◆ vignettePolygon

lsst.ip.isr.isrTask.IsrTask.vignettePolygon

Definition at line 1240 of file isrTask.py.


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