LSST Applications g0265f82a02+0e5473021a,g02d81e74bb+f5613e8b4f,g1470d8bcf6+190ad2ba91,g14a832a312+311607e4ab,g2079a07aa2+86d27d4dc4,g2305ad1205+a8e3196225,g295015adf3+b67ee847e5,g2bbee38e9b+0e5473021a,g337abbeb29+0e5473021a,g3ddfee87b4+a761f810f3,g487adcacf7+17c8fdbcbd,g50ff169b8f+96c6868917,g52b1c1532d+585e252eca,g591dd9f2cf+65b5bd823e,g5a732f18d5+53520f316c,g64a986408d+f5613e8b4f,g6c1bc301e9+51106c2951,g858d7b2824+f5613e8b4f,g8a8a8dda67+585e252eca,g99cad8db69+6729933424,g9ddcbc5298+9a081db1e4,ga1e77700b3+15fc3df1f7,ga8c6da7877+ef4e3a5875,gb0e22166c9+60f28cb32d,gb6a65358fc+0e5473021a,gba4ed39666+c2a2e4ac27,gbb8dafda3b+e9bba80f27,gc120e1dc64+eee469a5e5,gc28159a63d+0e5473021a,gcf0d15dbbd+a761f810f3,gdaeeff99f8+f9a426f77a,ge6526c86ff+d4c1d4bfef,ge79ae78c31+0e5473021a,gee10cc3b42+585e252eca,gf1cff7945b+f5613e8b4f,w.2024.16
LSST Data Management Base Package
Loading...
Searching...
No Matches
Public Member Functions | Static Public Member Functions | Public Attributes | Static Public Attributes | Static Protected Attributes | List of all members
lsst.ip.isr.isrTask.IsrTask Class Reference
Inheritance diagram for lsst.ip.isr.isrTask.IsrTask:

Public Member Functions

 __init__ (self, **kwargs)
 
 runQuantum (self, butlerQC, inputRefs, outputRefs)
 
 run (self, ccdExposure, *camera=None, bias=None, linearizer=None, crosstalk=None, crosstalkSources=None, dark=None, flat=None, ptc=None, bfKernel=None, bfGains=None, defects=None, fringes=pipeBase.Struct(fringes=None), opticsTransmission=None, filterTransmission=None, sensorTransmission=None, atmosphereTransmission=None, detectorNum=None, strayLightData=None, illumMaskedImage=None, deferredChargeCalib=None)
 
 defineEffectivePtc (self, ptcDataset, detector, bfGains, overScans, metadata)
 
 ensureExposure (self, inputExp, camera=None, detectorNum=None)
 
 compareCameraKeywords (self, exposureMetadata, calib, calibName)
 
 convertIntToFloat (self, exposure)
 
 maskAmplifier (self, ccdExposure, amp, defects)
 
 overscanCorrection (self, ccdExposure, amp)
 
 updateVariance (self, ampExposure, amp, ptcDataset)
 
 maskNegativeVariance (self, exposure)
 
 darkCorrection (self, exposure, darkExposure, invert=False)
 
 doLinearize (self, detector)
 
 flatCorrection (self, exposure, flatExposure, invert=False)
 
 saturationDetection (self, exposure, amp)
 
 saturationInterpolation (self, exposure)
 
 suspectDetection (self, exposure, amp)
 
 maskDefect (self, exposure, defectBaseList)
 
 maskEdges (self, exposure, numEdgePixels=0, maskPlane="SUSPECT", level='DETECTOR')
 
 maskAndInterpolateDefects (self, exposure, defectBaseList)
 
 maskNan (self, exposure)
 
 maskAndInterpolateNan (self, exposure)
 
 measureBackground (self, exposure, IsrQaConfig=None)
 
 roughZeroPoint (self, exposure)
 
 flatContext (self, exp, flat, dark=None)
 
 makeBinnedImages (self, exposure)
 
 debugView (self, exposure, stepname)
 

Static Public Member Functions

 extractCalibDate (calib)
 

Public Attributes

 vignettePolygon
 

Static Public Attributes

 ConfigClass = IsrTaskConfig
 

Static Protected Attributes

str _DefaultName = "isr"
 

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 __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 984 of file isrTask.py.

Constructor & Destructor Documentation

◆ __init__()

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

Definition at line 1011 of file isrTask.py.

1011 def __init__(self, **kwargs):
1012 super().__init__(**kwargs)
1013 self.makeSubtask("assembleCcd")
1014 self.makeSubtask("crosstalk")
1015 self.makeSubtask("strayLight")
1016 self.makeSubtask("fringe")
1017 self.makeSubtask("masking")
1018 self.makeSubtask("overscan")
1019 self.makeSubtask("vignette")
1020 self.makeSubtask("ampOffset")
1021 self.makeSubtask("deferredChargeCorrection")
1022 self.makeSubtask("isrStats")
1023

Member Function Documentation

◆ compareCameraKeywords()

lsst.ip.isr.isrTask.IsrTask.compareCameraKeywords ( self,
exposureMetadata,
calib,
calibName )
Compare header keywords to confirm camera states match.

Parameters
----------
exposureMetadata : `lsst.daf.base.PropertySet`
    Header for the exposure being processed.
calib : `lsst.afw.image.Exposure` or `lsst.ip.isr.IsrCalib`
    Calibration to be applied.
calibName : `str`
    Calib type for log message.

Definition at line 1995 of file isrTask.py.

1995 def compareCameraKeywords(self, exposureMetadata, calib, calibName):
1996 """Compare header keywords to confirm camera states match.
1997
1998 Parameters
1999 ----------
2000 exposureMetadata : `lsst.daf.base.PropertySet`
2001 Header for the exposure being processed.
2002 calib : `lsst.afw.image.Exposure` or `lsst.ip.isr.IsrCalib`
2003 Calibration to be applied.
2004 calibName : `str`
2005 Calib type for log message.
2006 """
2007 try:
2008 calibMetadata = calib.getMetadata()
2009 except AttributeError:
2010 return
2011 for keyword in self.config.cameraKeywordsToCompare:
2012 if keyword in exposureMetadata and keyword in calibMetadata:
2013 if exposureMetadata[keyword] != calibMetadata[keyword]:
2014 if self.config.doRaiseOnCalibMismatch:
2015 raise RuntimeError("Sequencer mismatch for %s [%s]: exposure: %s calib: %s",
2016 calibName, keyword,
2017 exposureMetadata[keyword], calibMetadata[keyword])
2018 else:
2019 self.log.warning("Sequencer mismatch for %s [%s]: exposure: %s calib: %s",
2020 calibName, keyword,
2021 exposureMetadata[keyword], calibMetadata[keyword])
2022 else:
2023 self.log.debug("Sequencer keyword %s not found.", keyword)
2024

◆ convertIntToFloat()

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

2025 def convertIntToFloat(self, exposure):
2026 """Convert exposure image from uint16 to float.
2027
2028 If the exposure does not need to be converted, the input is
2029 immediately returned. For exposures that are converted to use
2030 floating point pixels, the variance is set to unity and the
2031 mask to zero.
2032
2033 Parameters
2034 ----------
2035 exposure : `lsst.afw.image.Exposure`
2036 The raw exposure to be converted.
2037
2038 Returns
2039 -------
2040 newexposure : `lsst.afw.image.Exposure`
2041 The input ``exposure``, converted to floating point pixels.
2042
2043 Raises
2044 ------
2045 RuntimeError
2046 Raised if the exposure type cannot be converted to float.
2047
2048 """
2049 if isinstance(exposure, afwImage.ExposureF):
2050 # Nothing to be done
2051 self.log.debug("Exposure already of type float.")
2052 return exposure
2053 if not hasattr(exposure, "convertF"):
2054 raise RuntimeError("Unable to convert exposure (%s) to float." % type(exposure))
2055
2056 newexposure = exposure.convertF()
2057 newexposure.variance[:] = 1
2058 newexposure.mask[:] = 0x0
2059
2060 return newexposure
2061

◆ darkCorrection()

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

2280 def darkCorrection(self, exposure, darkExposure, invert=False):
2281 """Apply dark correction in place.
2282
2283 Parameters
2284 ----------
2285 exposure : `lsst.afw.image.Exposure`
2286 Exposure to process.
2287 darkExposure : `lsst.afw.image.Exposure`
2288 Dark exposure of the same size as ``exposure``.
2289 invert : `Bool`, optional
2290 If True, re-add the dark to an already corrected image.
2291
2292 Raises
2293 ------
2294 RuntimeError
2295 Raised if either ``exposure`` or ``darkExposure`` do not
2296 have their dark time defined.
2297
2298 See Also
2299 --------
2300 lsst.ip.isr.isrFunctions.darkCorrection
2301 """
2302 expScale = exposure.getInfo().getVisitInfo().getDarkTime()
2303 if math.isnan(expScale):
2304 raise RuntimeError("Exposure darktime is NAN.")
2305 if darkExposure.getInfo().getVisitInfo() is not None \
2306 and not math.isnan(darkExposure.getInfo().getVisitInfo().getDarkTime()):
2307 darkScale = darkExposure.getInfo().getVisitInfo().getDarkTime()
2308 else:
2309 # DM-17444: darkExposure.getInfo.getVisitInfo() is None
2310 # so getDarkTime() does not exist.
2311 self.log.warning("darkExposure.getInfo().getVisitInfo() does not exist. Using darkScale = 1.0.")
2312 darkScale = 1.0
2313
2314 isrFunctions.darkCorrection(
2315 maskedImage=exposure.getMaskedImage(),
2316 darkMaskedImage=darkExposure.getMaskedImage(),
2317 expScale=expScale,
2318 darkScale=darkScale,
2319 invert=invert,
2320 trimToFit=self.config.doTrimToMatchCalib
2321 )
2322

◆ debugView()

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

2725 def debugView(self, exposure, stepname):
2726 """Utility function to examine ISR exposure at different stages.
2727
2728 Parameters
2729 ----------
2730 exposure : `lsst.afw.image.Exposure`
2731 Exposure to view.
2732 stepname : `str`
2733 State of processing to view.
2734 """
2735 frame = getDebugFrame(self._display, stepname)
2736 if frame:
2737 display = getDisplay(frame)
2738 display.scale('asinh', 'zscale')
2739 display.mtv(exposure)
2740 prompt = "Press Enter to continue [c]... "
2741 while True:
2742 ans = input(prompt).lower()
2743 if ans in ("", "c",):
2744 break
2745
2746

◆ defineEffectivePtc()

lsst.ip.isr.isrTask.IsrTask.defineEffectivePtc ( self,
ptcDataset,
detector,
bfGains,
overScans,
metadata )
Define an effective Photon Transfer Curve dataset
with nominal gains and noise.

Parameters
----------
ptcDataset : `lsst.ip.isr.PhotonTransferCurveDataset`
    Input Photon Transfer Curve dataset.
detector : `lsst.afw.cameraGeom.Detector`
    Detector object.
bfGains : `dict`
    Gains from running the brighter-fatter code.
    A dict keyed by amplifier name for the detector
    in question.
ovserScans : `list` [`lsst.pipe.base.Struct`]
    List of overscanResults structures
metadata : `lsst.daf.base.PropertyList`
    Exposure metadata to update gain and noise provenance.

Returns
-------
effectivePtc : `lsst.ip.isr.PhotonTransferCurveDataset`
    PTC dataset containing gains and readout noise
    values to be used throughout
    Instrument Signature Removal.

Definition at line 1783 of file isrTask.py.

1783 def defineEffectivePtc(self, ptcDataset, detector, bfGains, overScans, metadata):
1784 """Define an effective Photon Transfer Curve dataset
1785 with nominal gains and noise.
1786
1787 Parameters
1788 ----------
1789 ptcDataset : `lsst.ip.isr.PhotonTransferCurveDataset`
1790 Input Photon Transfer Curve dataset.
1791 detector : `lsst.afw.cameraGeom.Detector`
1792 Detector object.
1793 bfGains : `dict`
1794 Gains from running the brighter-fatter code.
1795 A dict keyed by amplifier name for the detector
1796 in question.
1797 ovserScans : `list` [`lsst.pipe.base.Struct`]
1798 List of overscanResults structures
1799 metadata : `lsst.daf.base.PropertyList`
1800 Exposure metadata to update gain and noise provenance.
1801
1802 Returns
1803 -------
1804 effectivePtc : `lsst.ip.isr.PhotonTransferCurveDataset`
1805 PTC dataset containing gains and readout noise
1806 values to be used throughout
1807 Instrument Signature Removal.
1808 """
1809 amps = detector.getAmplifiers()
1810 ampNames = [amp.getName() for amp in amps]
1811 detName = detector.getName()
1812 effectivePtc = PhotonTransferCurveDataset(ampNames, 'EFFECTIVE_PTC', 1)
1813 boolGainMismatch = False
1814 doWarningPtcValidation = True
1815
1816 for amp, overscanResults in zip(amps, overScans):
1817 ampName = amp.getName()
1818 # Gain:
1819 # Try first with the PTC gains.
1820 gainProvenanceString = "amp"
1821 if self.config.usePtcGains:
1822 gain = ptcDataset.gain[ampName]
1823 gainProvenanceString = "ptc"
1824 self.log.debug("Using gain from Photon Transfer Curve.")
1825 else:
1826 # Try then with the amplifier gain.
1827 # We already have a detector at this point. If there was no
1828 # detector to begin with, one would have been created with
1829 # self.config.gain and self.config.noise. Same comment
1830 # applies for the noise block below.
1831 gain = amp.getGain()
1832
1833 # Check if the gain up to this point differs from the
1834 # gain in bfGains. If so, raise or warn, accordingly.
1835 if not boolGainMismatch and bfGains is not None and ampName in bfGains:
1836 bfGain = bfGains[ampName]
1837 if not math.isclose(gain, bfGain, rel_tol=1e-4):
1838 if self.config.doRaiseOnCalibMismatch:
1839 raise RuntimeError("Gain mismatch for det %s amp %s: "
1840 "(gain (%s): %s, bfGain: %s)",
1841 detName, ampName, gainProvenanceString,
1842 gain, bfGain)
1843 else:
1844 self.log.warning("Gain mismatch for det %s amp %s: "
1845 "(gain (%s): %s, bfGain: %s)",
1846 detName, ampName, gainProvenanceString,
1847 gain, bfGain)
1848 boolGainMismatch = True
1849
1850 # Noise:
1851 # Try first with the empirical noise from the overscan.
1852 noiseProvenanceString = "amp"
1853 if self.config.doEmpiricalReadNoise and overscanResults is not None:
1854 noiseProvenanceString = "serial overscan"
1855 if isinstance(overscanResults.residualSigma, float):
1856 # Only serial overscan was run
1857 noise = overscanResults.residualSigma
1858 else:
1859 # Both serial and parallel overscan were
1860 # run. Only report noise from serial here.
1861 noise = overscanResults.residualSigma[0]
1862 elif self.config.usePtcReadNoise:
1863 # Try then with the PTC noise.
1864 noise = ptcDataset.noise[ampName]
1865 noiseProvenanceString = "ptc"
1866 self.log.debug("Using noise from Photon Transfer Curve.")
1867 else:
1868 # Finally, try with the amplifier noise.
1869 # We already have a detector at this point. If there
1870 # was no detector to begin with, one would have
1871 # been created with self.config.gain and
1872 # self.config.noise.
1873 noise = amp.getReadNoise()
1874
1875 if math.isnan(gain):
1876 gain = 1.0
1877 self.log.warning("Gain for amp %s set to NAN! Updating to"
1878 " 1.0 to generate Poisson variance.", ampName)
1879 elif gain <= 0:
1880 patchedGain = 1.0
1881 self.log.warning("Gain for amp %s == %g <= 0; setting to %f.",
1882 ampName, gain, patchedGain)
1883 gain = patchedGain
1884
1885 # PTC Turnoff:
1886 # Copy it over from the input PTC if it's positive. If it's a nan
1887 # set it to a high value.
1888 if ptcDataset is not None:
1889 ptcTurnoff = ptcDataset.ptcTurnoff[ampName]
1890 else:
1891 ptcTurnoff = 2e19
1892
1893 if (isinstance(ptcTurnoff, numbers.Real) and ptcTurnoff > 0):
1894 effectivePtc.ptcTurnoff[ampName] = ptcTurnoff
1895 elif math.isnan(ptcTurnoff):
1896 effectivePtc.ptcTurnoff[ampName] = 2e19
1897
1898 effectivePtc.gain[ampName] = gain
1899 effectivePtc.noise[ampName] = noise
1900 # Make sure noise,turnoff, and gain make sense
1901 effectivePtc.validateGainNoiseTurnoffValues(ampName, doWarn=doWarningPtcValidation)
1902 doWarningPtcValidation = False
1903
1904 metadata[f"LSST GAIN {amp.getName()}"] = effectivePtc.gain[ampName]
1905 metadata[f"LSST READNOISE {amp.getName()}"] = effectivePtc.noise[ampName]
1906
1907 self.log.info("Det: %s - Noise provenance: %s, Gain provenance: %s",
1908 detName,
1909 noiseProvenanceString,
1910 gainProvenanceString)
1911 metadata["LSST ISR GAIN SOURCE"] = gainProvenanceString
1912 metadata["LSST ISR NOISE SOURCE"] = noiseProvenanceString
1913
1914 return effectivePtc
1915

◆ doLinearize()

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

2323 def doLinearize(self, detector):
2324 """Check if linearization is needed for the detector cameraGeom.
2325
2326 Checks config.doLinearize and the linearity type of the first
2327 amplifier.
2328
2329 Parameters
2330 ----------
2331 detector : `lsst.afw.cameraGeom.Detector`
2332 Detector to get linearity type from.
2333
2334 Returns
2335 -------
2336 doLinearize : `Bool`
2337 If True, linearization should be performed.
2338 """
2339 return self.config.doLinearize and \
2340 detector.getAmplifiers()[0].getLinearityType() != NullLinearityType
2341

◆ ensureExposure()

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

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` image-type.
    The input data structure obtained from Butler.
    Can be  `lsst.afw.image.Exposure`,
    `lsst.afw.image.DecoratedImageU`,
    or `lsst.afw.image.ImageF`
camera : `lsst.afw.cameraGeom.camera`, optional
    The camera associated with the image.  Used to find the appropriate
    detector if detector is not already set.
detectorNum : `int`, optional
    The detector in the camera to attach, if the detector is not
    already set.

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

1916 def ensureExposure(self, inputExp, camera=None, detectorNum=None):
1917 """Ensure that the data returned by Butler is a fully constructed exp.
1918
1919 ISR requires exposure-level image data for historical reasons, so if we
1920 did not recieve that from Butler, construct it from what we have,
1921 modifying the input in place.
1922
1923 Parameters
1924 ----------
1925 inputExp : `lsst.afw.image` image-type.
1926 The input data structure obtained from Butler.
1927 Can be `lsst.afw.image.Exposure`,
1928 `lsst.afw.image.DecoratedImageU`,
1929 or `lsst.afw.image.ImageF`
1930 camera : `lsst.afw.cameraGeom.camera`, optional
1931 The camera associated with the image. Used to find the appropriate
1932 detector if detector is not already set.
1933 detectorNum : `int`, optional
1934 The detector in the camera to attach, if the detector is not
1935 already set.
1936
1937 Returns
1938 -------
1939 inputExp : `lsst.afw.image.Exposure`
1940 The re-constructed exposure, with appropriate detector parameters.
1941
1942 Raises
1943 ------
1944 TypeError
1945 Raised if the input data cannot be used to construct an exposure.
1946 """
1947 if isinstance(inputExp, afwImage.DecoratedImageU):
1949 elif isinstance(inputExp, afwImage.ImageF):
1951 elif isinstance(inputExp, afwImage.MaskedImageF):
1952 inputExp = afwImage.makeExposure(inputExp)
1953 elif isinstance(inputExp, afwImage.Exposure):
1954 pass
1955 elif inputExp is None:
1956 # Assume this will be caught by the setup if it is a problem.
1957 return inputExp
1958 else:
1959 raise TypeError("Input Exposure is not known type in isrTask.ensureExposure: %s." %
1960 (type(inputExp), ))
1961
1962 if inputExp.getDetector() is None:
1963 if camera is None or detectorNum is None:
1964 raise RuntimeError('Must supply both a camera and detector number when using exposures '
1965 'without a detector set.')
1966 inputExp.setDetector(camera[detectorNum])
1967
1968 return inputExp
1969
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.
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:484

◆ extractCalibDate()

lsst.ip.isr.isrTask.IsrTask.extractCalibDate ( calib)
static
Extract common calibration metadata values that will be written to
output header.

Parameters
----------
calib : `lsst.afw.image.Exposure` or `lsst.ip.isr.IsrCalib`
    Calibration to pull date information from.

Returns
-------
dateString : `str`
    Calibration creation date string to add to header.

Definition at line 1971 of file isrTask.py.

1971 def extractCalibDate(calib):
1972 """Extract common calibration metadata values that will be written to
1973 output header.
1974
1975 Parameters
1976 ----------
1977 calib : `lsst.afw.image.Exposure` or `lsst.ip.isr.IsrCalib`
1978 Calibration to pull date information from.
1979
1980 Returns
1981 -------
1982 dateString : `str`
1983 Calibration creation date string to add to header.
1984 """
1985 if hasattr(calib, "getMetadata"):
1986 if 'CALIB_CREATION_DATE' in calib.getMetadata():
1987 return " ".join((calib.getMetadata().get("CALIB_CREATION_DATE", "Unknown"),
1988 calib.getMetadata().get("CALIB_CREATION_TIME", "Unknown")))
1989 else:
1990 return " ".join((calib.getMetadata().get("CALIB_CREATE_DATE", "Unknown"),
1991 calib.getMetadata().get("CALIB_CREATE_TIME", "Unknown")))
1992 else:
1993 return "Unknown Unknown"
1994

◆ flatContext()

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

2673 def flatContext(self, exp, flat, dark=None):
2674 """Context manager that applies and removes flats and darks,
2675 if the task is configured to apply them.
2676
2677 Parameters
2678 ----------
2679 exp : `lsst.afw.image.Exposure`
2680 Exposure to process.
2681 flat : `lsst.afw.image.Exposure`
2682 Flat exposure the same size as ``exp``.
2683 dark : `lsst.afw.image.Exposure`, optional
2684 Dark exposure the same size as ``exp``.
2685
2686 Yields
2687 ------
2688 exp : `lsst.afw.image.Exposure`
2689 The flat and dark corrected exposure.
2690 """
2691 if self.config.doDark and dark is not None:
2692 self.darkCorrection(exp, dark)
2693 if self.config.doFlat:
2694 self.flatCorrection(exp, flat)
2695 try:
2696 yield exp
2697 finally:
2698 if self.config.doFlat:
2699 self.flatCorrection(exp, flat, invert=True)
2700 if self.config.doDark and dark is not None:
2701 self.darkCorrection(exp, dark, invert=True)
2702

◆ flatCorrection()

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

2342 def flatCorrection(self, exposure, flatExposure, invert=False):
2343 """Apply flat correction in place.
2344
2345 Parameters
2346 ----------
2347 exposure : `lsst.afw.image.Exposure`
2348 Exposure to process.
2349 flatExposure : `lsst.afw.image.Exposure`
2350 Flat exposure of the same size as ``exposure``.
2351 invert : `Bool`, optional
2352 If True, unflatten an already flattened image.
2353
2354 See Also
2355 --------
2356 lsst.ip.isr.isrFunctions.flatCorrection
2357 """
2358 isrFunctions.flatCorrection(
2359 maskedImage=exposure.getMaskedImage(),
2360 flatMaskedImage=flatExposure.getMaskedImage(),
2361 scalingType=self.config.flatScalingType,
2362 userScale=self.config.flatUserScale,
2363 invert=invert,
2364 trimToFit=self.config.doTrimToMatchCalib
2365 )
2366

◆ makeBinnedImages()

lsst.ip.isr.isrTask.IsrTask.makeBinnedImages ( self,
exposure )
Make visualizeVisit style binned exposures.

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

Returns
-------
bin1 : `lsst.afw.image.Exposure`
    Binned exposure using binFactor1.
bin2 : `lsst.afw.image.Exposure`
    Binned exposure using binFactor2.

Definition at line 2703 of file isrTask.py.

2703 def makeBinnedImages(self, exposure):
2704 """Make visualizeVisit style binned exposures.
2705
2706 Parameters
2707 ----------
2708 exposure : `lsst.afw.image.Exposure`
2709 Exposure to bin.
2710
2711 Returns
2712 -------
2713 bin1 : `lsst.afw.image.Exposure`
2714 Binned exposure using binFactor1.
2715 bin2 : `lsst.afw.image.Exposure`
2716 Binned exposure using binFactor2.
2717 """
2718 mi = exposure.getMaskedImage()
2719
2720 bin1 = afwMath.binImage(mi, self.config.binFactor1)
2721 bin2 = afwMath.binImage(mi, self.config.binFactor2)
2722
2723 return bin1, bin2
2724
std::shared_ptr< ImageT > binImage(ImageT const &inImage, int const binX, int const binY, lsst::afw::math::Property const flags=lsst::afw::math::MEAN)
Definition binImage.cc:44

◆ maskAmplifier()

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.cameraGeom.Amplifier`
    Catalog of parameters defining the amplifier on this
    exposure to mask.
defects : `lsst.ip.isr.Defects`
    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 2062 of file isrTask.py.

2062 def maskAmplifier(self, ccdExposure, amp, defects):
2063 """Identify bad amplifiers, saturated and suspect pixels.
2064
2065 Parameters
2066 ----------
2067 ccdExposure : `lsst.afw.image.Exposure`
2068 Input exposure to be masked.
2069 amp : `lsst.afw.cameraGeom.Amplifier`
2070 Catalog of parameters defining the amplifier on this
2071 exposure to mask.
2072 defects : `lsst.ip.isr.Defects`
2073 List of defects. Used to determine if the entire
2074 amplifier is bad.
2075
2076 Returns
2077 -------
2078 badAmp : `Bool`
2079 If this is true, the entire amplifier area is covered by
2080 defects and unusable.
2081
2082 """
2083 maskedImage = ccdExposure.getMaskedImage()
2084
2085 badAmp = False
2086
2087 # Check if entire amp region is defined as a defect
2088 # NB: need to use amp.getBBox() for correct comparison with current
2089 # defects definition.
2090 if defects is not None:
2091 badAmp = bool(sum([v.getBBox().contains(amp.getBBox()) for v in defects]))
2092
2093 # In the case of a bad amp, we will set mask to "BAD"
2094 # (here use amp.getRawBBox() for correct association with pixels in
2095 # current ccdExposure).
2096 if badAmp:
2097 dataView = afwImage.MaskedImageF(maskedImage, amp.getRawBBox(),
2098 afwImage.PARENT)
2099 maskView = dataView.getMask()
2100 maskView |= maskView.getPlaneBitMask("BAD")
2101 del maskView
2102 return badAmp
2103
2104 # Mask remaining defects after assembleCcd() to allow for defects that
2105 # cross amplifier boundaries. Saturation and suspect pixels can be
2106 # masked now, though.
2107 limits = dict()
2108 if self.config.doSaturation and not badAmp:
2109 # Set to the default from the camera model.
2110 limits.update({self.config.saturatedMaskName: amp.getSaturation()})
2111 # And update if it is set in the config.
2112 if math.isfinite(self.config.saturation):
2113 limits.update({self.config.saturatedMaskName: self.config.saturation})
2114 if self.config.doSuspect and not badAmp:
2115 limits.update({self.config.suspectMaskName: amp.getSuspectLevel()})
2116
2117 for maskName, maskThreshold in limits.items():
2118 if not math.isnan(maskThreshold):
2119 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
2120 isrFunctions.makeThresholdMask(
2121 maskedImage=dataView,
2122 threshold=maskThreshold,
2123 growFootprints=0,
2124 maskName=maskName
2125 )
2126
2127 # Determine if we've fully masked this amplifier with SUSPECT and
2128 # SAT pixels.
2129 maskView = afwImage.Mask(maskedImage.getMask(), amp.getRawDataBBox(),
2130 afwImage.PARENT)
2131 maskVal = maskView.getPlaneBitMask([self.config.saturatedMaskName,
2132 self.config.suspectMaskName])
2133 if numpy.all(maskView.getArray() & maskVal > 0):
2134 badAmp = True
2135 maskView |= maskView.getPlaneBitMask("BAD")
2136
2137 return badAmp
2138
Represent a 2-dimensional array of bitmask pixels.
Definition Mask.h:77

◆ maskAndInterpolateDefects()

lsst.ip.isr.isrTask.IsrTask.maskAndInterpolateDefects ( self,
exposure,
defectBaseList )
Mask and interpolate defects using mask plane "BAD", in place.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to process.
defectBaseList : defects-like
    List of defects to mask and interpolate. Can be
    `lsst.ip.isr.Defects` or `list` of `lsst.afw.image.DefectBase`.

See Also
--------
lsst.ip.isr.isrTask.maskDefect

Definition at line 2510 of file isrTask.py.

2510 def maskAndInterpolateDefects(self, exposure, defectBaseList):
2511 """Mask and interpolate defects using mask plane "BAD", in place.
2512
2513 Parameters
2514 ----------
2515 exposure : `lsst.afw.image.Exposure`
2516 Exposure to process.
2517 defectBaseList : defects-like
2518 List of defects to mask and interpolate. Can be
2519 `lsst.ip.isr.Defects` or `list` of `lsst.afw.image.DefectBase`.
2520
2521 See Also
2522 --------
2523 lsst.ip.isr.isrTask.maskDefect
2524 """
2525 self.maskDefect(exposure, defectBaseList)
2526 self.maskEdges(exposure, numEdgePixels=self.config.numEdgeSuspect,
2527 maskPlane="SUSPECT", level=self.config.edgeMaskLevel)
2528 isrFunctions.interpolateFromMask(
2529 maskedImage=exposure.getMaskedImage(),
2530 fwhm=self.config.fwhm,
2531 growSaturatedFootprints=0,
2532 maskNameList=["BAD"],
2533 )
2534

◆ maskAndInterpolateNan()

lsst.ip.isr.isrTask.IsrTask.maskAndInterpolateNan ( self,
exposure )
"Mask and interpolate NaN/infs using mask plane "UNMASKEDNAN",
in place.

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

See Also
--------
lsst.ip.isr.isrTask.maskNan

Definition at line 2561 of file isrTask.py.

2561 def maskAndInterpolateNan(self, exposure):
2562 """"Mask and interpolate NaN/infs using mask plane "UNMASKEDNAN",
2563 in place.
2564
2565 Parameters
2566 ----------
2567 exposure : `lsst.afw.image.Exposure`
2568 Exposure to process.
2569
2570 See Also
2571 --------
2572 lsst.ip.isr.isrTask.maskNan
2573 """
2574 self.maskNan(exposure)
2575 isrFunctions.interpolateFromMask(
2576 maskedImage=exposure.getMaskedImage(),
2577 fwhm=self.config.fwhm,
2578 growSaturatedFootprints=0,
2579 maskNameList=["UNMASKEDNAN"],
2580 )
2581

◆ maskDefect()

lsst.ip.isr.isrTask.IsrTask.maskDefect ( self,
exposure,
defectBaseList )
Mask defects using mask plane "BAD", in place.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to process.
defectBaseList : defect-type
    List of defects to mask. Can be of type  `lsst.ip.isr.Defects`
    or `list` of `lsst.afw.image.DefectBase`.

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

Definition at line 2452 of file isrTask.py.

2452 def maskDefect(self, exposure, defectBaseList):
2453 """Mask defects using mask plane "BAD", in place.
2454
2455 Parameters
2456 ----------
2457 exposure : `lsst.afw.image.Exposure`
2458 Exposure to process.
2459 defectBaseList : defect-type
2460 List of defects to mask. Can be of type `lsst.ip.isr.Defects`
2461 or `list` of `lsst.afw.image.DefectBase`.
2462
2463 Notes
2464 -----
2465 Call this after CCD assembly, since defects may cross amplifier
2466 boundaries.
2467 """
2468 maskedImage = exposure.getMaskedImage()
2469 if not isinstance(defectBaseList, Defects):
2470 # Promotes DefectBase to Defect
2471 defectList = Defects(defectBaseList)
2472 else:
2473 defectList = defectBaseList
2474 defectList.maskPixels(maskedImage, maskName="BAD")
2475

◆ maskEdges()

lsst.ip.isr.isrTask.IsrTask.maskEdges ( self,
exposure,
numEdgePixels = 0,
maskPlane = "SUSPECT",
level = 'DETECTOR' )
Mask edge pixels with applicable mask plane.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to process.
numEdgePixels : `int`, optional
    Number of edge pixels to mask.
maskPlane : `str`, optional
    Mask plane name to use.
level : `str`, optional
    Level at which to mask edges.

Definition at line 2476 of file isrTask.py.

2476 def maskEdges(self, exposure, numEdgePixels=0, maskPlane="SUSPECT", level='DETECTOR'):
2477 """Mask edge pixels with applicable mask plane.
2478
2479 Parameters
2480 ----------
2481 exposure : `lsst.afw.image.Exposure`
2482 Exposure to process.
2483 numEdgePixels : `int`, optional
2484 Number of edge pixels to mask.
2485 maskPlane : `str`, optional
2486 Mask plane name to use.
2487 level : `str`, optional
2488 Level at which to mask edges.
2489 """
2490 maskedImage = exposure.getMaskedImage()
2491 maskBitMask = maskedImage.getMask().getPlaneBitMask(maskPlane)
2492
2493 if numEdgePixels > 0:
2494 if level == 'DETECTOR':
2495 boxes = [maskedImage.getBBox()]
2496 elif level == 'AMP':
2497 boxes = [amp.getBBox() for amp in exposure.getDetector()]
2498
2499 for box in boxes:
2500 # This makes a bbox numEdgeSuspect pixels smaller than the
2501 # image on each side
2502 subImage = maskedImage[box]
2503 box.grow(-numEdgePixels)
2504 # Mask pixels outside box
2505 SourceDetectionTask.setEdgeBits(
2506 subImage,
2507 box,
2508 maskBitMask)
2509

◆ maskNan()

lsst.ip.isr.isrTask.IsrTask.maskNan ( self,
exposure )
Mask NaNs using mask plane "UNMASKEDNAN", in place.

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

Notes
-----
We mask over all non-finite values (NaN, inf), including those
that are masked with other bits (because those may or may not be
interpolated over later, and we want to remove all NaN/infs).
Despite this behaviour, the "UNMASKEDNAN" mask plane is used to
preserve the historical name.

Definition at line 2535 of file isrTask.py.

2535 def maskNan(self, exposure):
2536 """Mask NaNs using mask plane "UNMASKEDNAN", in place.
2537
2538 Parameters
2539 ----------
2540 exposure : `lsst.afw.image.Exposure`
2541 Exposure to process.
2542
2543 Notes
2544 -----
2545 We mask over all non-finite values (NaN, inf), including those
2546 that are masked with other bits (because those may or may not be
2547 interpolated over later, and we want to remove all NaN/infs).
2548 Despite this behaviour, the "UNMASKEDNAN" mask plane is used to
2549 preserve the historical name.
2550 """
2551 maskedImage = exposure.getMaskedImage()
2552
2553 # Find and mask NaNs
2554 maskedImage.getMask().addMaskPlane("UNMASKEDNAN")
2555 maskVal = maskedImage.getMask().getPlaneBitMask("UNMASKEDNAN")
2556 numNans = maskNans(maskedImage, maskVal)
2557 self.metadata["NUMNANS"] = numNans
2558 if numNans > 0:
2559 self.log.warning("There were %d unmasked NaNs.", numNans)
2560

◆ maskNegativeVariance()

lsst.ip.isr.isrTask.IsrTask.maskNegativeVariance ( self,
exposure )
Identify and mask pixels with negative variance values.

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

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

Definition at line 2264 of file isrTask.py.

2264 def maskNegativeVariance(self, exposure):
2265 """Identify and mask pixels with negative variance values.
2266
2267 Parameters
2268 ----------
2269 exposure : `lsst.afw.image.Exposure`
2270 Exposure to process.
2271
2272 See Also
2273 --------
2274 lsst.ip.isr.isrFunctions.updateVariance
2275 """
2276 maskPlane = exposure.getMask().getPlaneBitMask(self.config.negativeVarianceMaskName)
2277 bad = numpy.where(exposure.getVariance().getArray() <= 0.0)
2278 exposure.mask.array[bad] |= maskPlane
2279

◆ measureBackground()

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

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

2582 def measureBackground(self, exposure, IsrQaConfig=None):
2583 """Measure the image background in subgrids, for quality control.
2584
2585 Parameters
2586 ----------
2587 exposure : `lsst.afw.image.Exposure`
2588 Exposure to process.
2589 IsrQaConfig : `lsst.ip.isr.isrQa.IsrQaConfig`
2590 Configuration object containing parameters on which background
2591 statistics and subgrids to use.
2592 """
2593 if IsrQaConfig is not None:
2594 statsControl = afwMath.StatisticsControl(IsrQaConfig.flatness.clipSigma,
2595 IsrQaConfig.flatness.nIter)
2596 maskVal = exposure.getMaskedImage().getMask().getPlaneBitMask(["BAD", "SAT", "DETECTED"])
2597 statsControl.setAndMask(maskVal)
2598 maskedImage = exposure.getMaskedImage()
2599 stats = afwMath.makeStatistics(maskedImage, afwMath.MEDIAN | afwMath.STDEVCLIP, statsControl)
2600 skyLevel = stats.getValue(afwMath.MEDIAN)
2601 skySigma = stats.getValue(afwMath.STDEVCLIP)
2602 self.log.info("Flattened sky level: %f +/- %f.", skyLevel, skySigma)
2603 metadata = exposure.getMetadata()
2604 metadata["SKYLEVEL"] = skyLevel
2605 metadata["SKYSIGMA"] = skySigma
2606
2607 # calcluating flatlevel over the subgrids
2608 stat = afwMath.MEANCLIP if IsrQaConfig.flatness.doClip else afwMath.MEAN
2609 meshXHalf = int(IsrQaConfig.flatness.meshX/2.)
2610 meshYHalf = int(IsrQaConfig.flatness.meshY/2.)
2611 nX = int((exposure.getWidth() + meshXHalf) / IsrQaConfig.flatness.meshX)
2612 nY = int((exposure.getHeight() + meshYHalf) / IsrQaConfig.flatness.meshY)
2613 skyLevels = numpy.zeros((nX, nY))
2614
2615 for j in range(nY):
2616 yc = meshYHalf + j * IsrQaConfig.flatness.meshY
2617 for i in range(nX):
2618 xc = meshXHalf + i * IsrQaConfig.flatness.meshX
2619
2620 xLLC = xc - meshXHalf
2621 yLLC = yc - meshYHalf
2622 xURC = xc + meshXHalf - 1
2623 yURC = yc + meshYHalf - 1
2624
2625 bbox = lsst.geom.Box2I(lsst.geom.Point2I(xLLC, yLLC), lsst.geom.Point2I(xURC, yURC))
2626 miMesh = maskedImage.Factory(exposure.getMaskedImage(), bbox, afwImage.LOCAL)
2627
2628 skyLevels[i, j] = afwMath.makeStatistics(miMesh, stat, statsControl).getValue()
2629
2630 good = numpy.where(numpy.isfinite(skyLevels))
2631 skyMedian = numpy.median(skyLevels[good])
2632 flatness = (skyLevels[good] - skyMedian) / skyMedian
2633 flatness_rms = numpy.std(flatness)
2634 flatness_pp = flatness.max() - flatness.min() if len(flatness) > 0 else numpy.nan
2635
2636 self.log.info("Measuring sky levels in %dx%d grids: %f.", nX, nY, skyMedian)
2637 self.log.info("Sky flatness in %dx%d grids - pp: %f rms: %f.",
2638 nX, nY, flatness_pp, flatness_rms)
2639
2640 metadata["FLATNESS_PP"] = float(flatness_pp)
2641 metadata["FLATNESS_RMS"] = float(flatness_rms)
2642 metadata["FLATNESS_NGRIDS"] = '%dx%d' % (nX, nY)
2643 metadata["FLATNESS_MESHX"] = IsrQaConfig.flatness.meshX
2644 metadata["FLATNESS_MESHY"] = IsrQaConfig.flatness.meshY
2645
Pass parameters to a Statistics object.
Definition Statistics.h:83
An integer coordinate rectangle.
Definition Box.h:55
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Handle a watered-down front-end to the constructor (no variance)
Definition Statistics.h:361

◆ overscanCorrection()

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.overscan.OverscanTask`, which is called here
after the amplifier is preprocessed.

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

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

    ``imageFit``
        Value or fit subtracted from the amplifier image data.
        (scalar or `lsst.afw.image.Image`)
    ``overscanFit``
        Value or fit subtracted from the overscan image data.
        (scalar or `lsst.afw.image.Image`)
    ``overscanImage``
        Image of the overscan region with the overscan
        correction applied. This quantity is used to estimate
        the amplifier read noise empirically.
        (`lsst.afw.image.Image`)
    ``edgeMask``
        Mask of the suspect pixels. (`lsst.afw.image.Mask`)
    ``overscanMean``
        Median overscan fit value. (`float`)
    ``overscanSigma``
        Clipped standard deviation of the overscan after
        correction. (`float`)

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

See Also
--------
lsst.ip.isr.overscan.OverscanTask

Definition at line 2139 of file isrTask.py.

2139 def overscanCorrection(self, ccdExposure, amp):
2140 """Apply overscan correction in place.
2141
2142 This method does initial pixel rejection of the overscan
2143 region. The overscan can also be optionally segmented to
2144 allow for discontinuous overscan responses to be fit
2145 separately. The actual overscan subtraction is performed by
2146 the `lsst.ip.isr.overscan.OverscanTask`, which is called here
2147 after the amplifier is preprocessed.
2148
2149 Parameters
2150 ----------
2151 ccdExposure : `lsst.afw.image.Exposure`
2152 Exposure to have overscan correction performed.
2153 amp : `lsst.afw.cameraGeom.Amplifer`
2154 The amplifier to consider while correcting the overscan.
2155
2156 Returns
2157 -------
2158 overscanResults : `lsst.pipe.base.Struct`
2159 Result struct with components:
2160
2161 ``imageFit``
2162 Value or fit subtracted from the amplifier image data.
2163 (scalar or `lsst.afw.image.Image`)
2164 ``overscanFit``
2165 Value or fit subtracted from the overscan image data.
2166 (scalar or `lsst.afw.image.Image`)
2167 ``overscanImage``
2168 Image of the overscan region with the overscan
2169 correction applied. This quantity is used to estimate
2170 the amplifier read noise empirically.
2171 (`lsst.afw.image.Image`)
2172 ``edgeMask``
2173 Mask of the suspect pixels. (`lsst.afw.image.Mask`)
2174 ``overscanMean``
2175 Median overscan fit value. (`float`)
2176 ``overscanSigma``
2177 Clipped standard deviation of the overscan after
2178 correction. (`float`)
2179
2180 Raises
2181 ------
2182 RuntimeError
2183 Raised if the ``amp`` does not contain raw pixel information.
2184
2185 See Also
2186 --------
2187 lsst.ip.isr.overscan.OverscanTask
2188 """
2189 if amp.getRawHorizontalOverscanBBox().isEmpty():
2190 self.log.info("ISR_OSCAN: No overscan region. Not performing overscan correction.")
2191 return None
2192
2193 # Perform overscan correction on subregions.
2194 overscanResults = self.overscan.run(ccdExposure, amp)
2195
2196 metadata = ccdExposure.getMetadata()
2197 ampName = amp.getName()
2198
2199 keyBase = "LSST ISR OVERSCAN"
2200 # Updated quantities
2201 if isinstance(overscanResults.overscanMean, float):
2202 # Serial overscan correction only:
2203 metadata[f"{keyBase} SERIAL MEAN {ampName}"] = overscanResults.overscanMean
2204 metadata[f"{keyBase} SERIAL MEDIAN {ampName}"] = overscanResults.overscanMedian
2205 metadata[f"{keyBase} SERIAL STDEV {ampName}"] = overscanResults.overscanSigma
2206
2207 metadata[f"{keyBase} RESIDUAL SERIAL MEAN {ampName}"] = overscanResults.residualMean
2208 metadata[f"{keyBase} RESIDUAL SERIAL MEDIAN {ampName}"] = overscanResults.residualMedian
2209 metadata[f"{keyBase} RESIDUAL SERIAL STDEV {ampName}"] = overscanResults.residualSigma
2210 elif isinstance(overscanResults.overscanMean, tuple):
2211 # Both serial and parallel overscan have run:
2212 metadata[f"{keyBase} SERIAL MEAN {ampName}"] = overscanResults.overscanMean[0]
2213 metadata[f"{keyBase} SERIAL MEDIAN {ampName}"] = overscanResults.overscanMedian[0]
2214 metadata[f"{keyBase} SERIAL STDEV {ampName}"] = overscanResults.overscanSigma[0]
2215
2216 metadata[f"{keyBase} PARALLEL MEAN {ampName}"] = overscanResults.overscanMean[1]
2217 metadata[f"{keyBase} PARALLEL MEDIAN {ampName}"] = overscanResults.overscanMedian[1]
2218 metadata[f"{keyBase} PARALLEL STDEV {ampName}"] = overscanResults.overscanSigma[1]
2219
2220 metadata[f"{keyBase} RESIDUAL SERIAL MEAN {ampName}"] = overscanResults.residualMean[0]
2221 metadata[f"{keyBase} RESIDUAL SERIAL MEDIAN {ampName}"] = overscanResults.residualMedian[0]
2222 metadata[f"{keyBase} RESIDUAL SERIAL STDEV {ampName}"] = overscanResults.residualSigma[0]
2223
2224 metadata[f"{keyBase} RESIDUAL PARALLEL MEAN {ampName}"] = overscanResults.residualMean[1]
2225 metadata[f"{keyBase} RESIDUAL PARALLEL MEDIAN {ampName}"] = overscanResults.residualMedian[1]
2226 metadata[f"{keyBase} RESIDUAL PARALLEL STDEV {ampName}"] = overscanResults.residualSigma[1]
2227 else:
2228 self.log.warning("Unexpected type for overscan values; none added to header.")
2229
2230 return overscanResults
2231

◆ roughZeroPoint()

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

2646 def roughZeroPoint(self, exposure):
2647 """Set an approximate magnitude zero point for the exposure.
2648
2649 Parameters
2650 ----------
2651 exposure : `lsst.afw.image.Exposure`
2652 Exposure to process.
2653 """
2654 filterLabel = exposure.getFilter()
2655 physicalFilter = isrFunctions.getPhysicalFilter(filterLabel, self.log)
2656
2657 if physicalFilter in self.config.fluxMag0T1:
2658 fluxMag0 = self.config.fluxMag0T1[physicalFilter]
2659 else:
2660 self.log.warning("No rough magnitude zero point defined for filter %s.", physicalFilter)
2661 fluxMag0 = self.config.defaultFluxMag0T1
2662
2663 expTime = exposure.getInfo().getVisitInfo().getExposureTime()
2664 if not expTime > 0: # handle NaN as well as <= 0
2665 self.log.warning("Non-positive exposure time; skipping rough zero point.")
2666 return
2667
2668 self.log.info("Setting rough magnitude zero point for filter %s: %f",
2669 physicalFilter, 2.5*math.log10(fluxMag0*expTime))
2670 exposure.setPhotoCalib(afwImage.makePhotoCalibFromCalibZeroPoint(fluxMag0*expTime, 0.0))
2671
std::shared_ptr< PhotoCalib > makePhotoCalibFromCalibZeroPoint(double instFluxMag0, double instFluxMag0Err)
Construct a PhotoCalib from the deprecated Calib-style instFluxMag0/instFluxMag0Err values.

◆ run()

lsst.ip.isr.isrTask.IsrTask.run ( self,
ccdExposure,
* camera = None,
bias = None,
linearizer = None,
crosstalk = None,
crosstalkSources = None,
dark = None,
flat = None,
ptc = None,
bfKernel = None,
bfGains = None,
defects = None,
fringes = pipeBase.Struct(fringes=None),
opticsTransmission = None,
filterTransmission = None,
sensorTransmission = None,
atmosphereTransmission = None,
detectorNum = None,
strayLightData = None,
illumMaskedImage = None,
deferredChargeCalib = 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. Required if
    one or more of ``ccdExposure``, ``bias``, ``dark``, or
    ``flat`` does not have an associated detector.
bias : `lsst.afw.image.Exposure`, optional
    Bias calibration frame.
linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional
    Functor for linearization.
crosstalk : `lsst.ip.isr.crosstalk.CrosstalkCalib`, optional
    Calibration for crosstalk.
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.
ptc : `lsst.ip.isr.PhotonTransferCurveDataset`, optional
    Photon transfer curve dataset, with, e.g., gains
    and read noise.
bfKernel : `numpy.ndarray`, optional
    Brighter-fatter kernel.
bfGains : `dict` of `float`, optional
    Gains used to override the detector's nominal gains for the
    brighter-fatter correction. A dict keyed by amplifier name for
    the detector in question.
defects : `lsst.ip.isr.Defects`, optional
    List of defects.
fringes : `lsst.pipe.base.Struct`, optional
    Struct containing the fringe correction data, with
    elements:

    ``fringes``
        fringe calibration frame (`lsst.afw.image.Exposure`)
    ``seed``
        random seed derived from the ``ccdExposureId`` for random
        number generator (`numpy.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.
strayLightData : `object`, optional
    Opaque object containing calibration information for stray-light
    correction.  If `None`, no correction will be performed.
illumMaskedImage : `lsst.afw.image.MaskedImage`, optional
    Illumination correction image.

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

    ``exposure``
        The fully ISR corrected exposure.
        (`lsst.afw.image.Exposure`)
    ``outputExposure``
        An alias for ``exposure``. (`lsst.afw.image.Exposure`)
    ``ossThumb``
        Thumbnail image of the exposure after overscan subtraction.
        (`numpy.ndarray`)
    ``flattenedThumb``
        Thumbnail image of the exposure after flat-field correction.
        (`numpy.ndarray`)
    ``outputStatistics``
        Values of the additional statistics calculated.

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

1185 ):
1186 """Perform instrument signature removal on an exposure.
1187
1188 Steps included in the ISR processing, in order performed, are:
1189
1190 - saturation and suspect pixel masking
1191 - overscan subtraction
1192 - CCD assembly of individual amplifiers
1193 - bias subtraction
1194 - variance image construction
1195 - linearization of non-linear response
1196 - crosstalk masking
1197 - brighter-fatter correction
1198 - dark subtraction
1199 - fringe correction
1200 - stray light subtraction
1201 - flat correction
1202 - masking of known defects and camera specific features
1203 - vignette calculation
1204 - appending transmission curve and distortion model
1205
1206 Parameters
1207 ----------
1208 ccdExposure : `lsst.afw.image.Exposure`
1209 The raw exposure that is to be run through ISR. The
1210 exposure is modified by this method.
1211 camera : `lsst.afw.cameraGeom.Camera`, optional
1212 The camera geometry for this exposure. Required if
1213 one or more of ``ccdExposure``, ``bias``, ``dark``, or
1214 ``flat`` does not have an associated detector.
1215 bias : `lsst.afw.image.Exposure`, optional
1216 Bias calibration frame.
1217 linearizer : `lsst.ip.isr.linearize.LinearizeBase`, optional
1218 Functor for linearization.
1219 crosstalk : `lsst.ip.isr.crosstalk.CrosstalkCalib`, optional
1220 Calibration for crosstalk.
1221 crosstalkSources : `list`, optional
1222 List of possible crosstalk sources.
1223 dark : `lsst.afw.image.Exposure`, optional
1224 Dark calibration frame.
1225 flat : `lsst.afw.image.Exposure`, optional
1226 Flat calibration frame.
1227 ptc : `lsst.ip.isr.PhotonTransferCurveDataset`, optional
1228 Photon transfer curve dataset, with, e.g., gains
1229 and read noise.
1230 bfKernel : `numpy.ndarray`, optional
1231 Brighter-fatter kernel.
1232 bfGains : `dict` of `float`, optional
1233 Gains used to override the detector's nominal gains for the
1234 brighter-fatter correction. A dict keyed by amplifier name for
1235 the detector in question.
1236 defects : `lsst.ip.isr.Defects`, optional
1237 List of defects.
1238 fringes : `lsst.pipe.base.Struct`, optional
1239 Struct containing the fringe correction data, with
1240 elements:
1241
1242 ``fringes``
1243 fringe calibration frame (`lsst.afw.image.Exposure`)
1244 ``seed``
1245 random seed derived from the ``ccdExposureId`` for random
1246 number generator (`numpy.uint32`)
1247 opticsTransmission: `lsst.afw.image.TransmissionCurve`, optional
1248 A ``TransmissionCurve`` that represents the throughput of the,
1249 optics, to be evaluated in focal-plane coordinates.
1250 filterTransmission : `lsst.afw.image.TransmissionCurve`
1251 A ``TransmissionCurve`` that represents the throughput of the
1252 filter itself, to be evaluated in focal-plane coordinates.
1253 sensorTransmission : `lsst.afw.image.TransmissionCurve`
1254 A ``TransmissionCurve`` that represents the throughput of the
1255 sensor itself, to be evaluated in post-assembly trimmed detector
1256 coordinates.
1257 atmosphereTransmission : `lsst.afw.image.TransmissionCurve`
1258 A ``TransmissionCurve`` that represents the throughput of the
1259 atmosphere, assumed to be spatially constant.
1260 detectorNum : `int`, optional
1261 The integer number for the detector to process.
1262 strayLightData : `object`, optional
1263 Opaque object containing calibration information for stray-light
1264 correction. If `None`, no correction will be performed.
1265 illumMaskedImage : `lsst.afw.image.MaskedImage`, optional
1266 Illumination correction image.
1267
1268 Returns
1269 -------
1270 result : `lsst.pipe.base.Struct`
1271 Result struct with component:
1272
1273 ``exposure``
1274 The fully ISR corrected exposure.
1275 (`lsst.afw.image.Exposure`)
1276 ``outputExposure``
1277 An alias for ``exposure``. (`lsst.afw.image.Exposure`)
1278 ``ossThumb``
1279 Thumbnail image of the exposure after overscan subtraction.
1280 (`numpy.ndarray`)
1281 ``flattenedThumb``
1282 Thumbnail image of the exposure after flat-field correction.
1283 (`numpy.ndarray`)
1284 ``outputStatistics``
1285 Values of the additional statistics calculated.
1286
1287 Raises
1288 ------
1289 RuntimeError
1290 Raised if a configuration option is set to `True`, but the
1291 required calibration data has not been specified.
1292
1293 Notes
1294 -----
1295 The current processed exposure can be viewed by setting the
1296 appropriate `lsstDebug` entries in the ``debug.display``
1297 dictionary. The names of these entries correspond to some of
1298 the `IsrTaskConfig` Boolean options, with the value denoting the
1299 frame to use. The exposure is shown inside the matching
1300 option check and after the processing of that step has
1301 finished. The steps with debug points are:
1302
1303 * doAssembleCcd
1304 * doBias
1305 * doCrosstalk
1306 * doBrighterFatter
1307 * doDark
1308 * doFringe
1309 * doStrayLight
1310 * doFlat
1311
1312 In addition, setting the ``postISRCCD`` entry displays the
1313 exposure after all ISR processing has finished.
1314 """
1315
1316 ccdExposure = self.ensureExposure(ccdExposure, camera, detectorNum)
1317 bias = self.ensureExposure(bias, camera, detectorNum)
1318 dark = self.ensureExposure(dark, camera, detectorNum)
1319 flat = self.ensureExposure(flat, camera, detectorNum)
1320
1321 ccd = ccdExposure.getDetector()
1322 filterLabel = ccdExposure.getFilter()
1323 physicalFilter = isrFunctions.getPhysicalFilter(filterLabel, self.log)
1324
1325 if not ccd:
1326 assert not self.config.doAssembleCcd, "You need a Detector to run assembleCcd."
1327 ccd = [FakeAmp(ccdExposure, self.config)]
1328
1329 # Validate Input
1330 if self.config.doBias and bias is None:
1331 raise RuntimeError("Must supply a bias exposure if config.doBias=True.")
1332 if self.doLinearize(ccd) and linearizer is None:
1333 raise RuntimeError("Must supply a linearizer if config.doLinearize=True for this detector.")
1334 if self.config.doBrighterFatter and bfKernel is None:
1335 raise RuntimeError("Must supply a kernel if config.doBrighterFatter=True.")
1336 if self.config.doDark and dark is None:
1337 raise RuntimeError("Must supply a dark exposure if config.doDark=True.")
1338 if self.config.doFlat and flat is None:
1339 raise RuntimeError("Must supply a flat exposure if config.doFlat=True.")
1340 if self.config.doDefect and defects is None:
1341 raise RuntimeError("Must supply defects if config.doDefect=True.")
1342 if (self.config.doFringe and physicalFilter in self.fringe.config.filters
1343 and fringes.fringes is None):
1344 # The `fringes` object needs to be a pipeBase.Struct, as
1345 # we use it as a `dict` for the parameters of
1346 # `FringeTask.run()`. The `fringes.fringes` `list` may
1347 # not be `None` if `doFringe=True`. Otherwise, raise.
1348 raise RuntimeError("Must supply fringe exposure as a pipeBase.Struct.")
1349 if (self.config.doIlluminationCorrection and physicalFilter in self.config.illumFilters
1350 and illumMaskedImage is None):
1351 raise RuntimeError("Must supply an illumcor if config.doIlluminationCorrection=True.")
1352 if (self.config.doDeferredCharge and deferredChargeCalib is None):
1353 raise RuntimeError("Must supply a deferred charge calibration if config.doDeferredCharge=True.")
1354 if (self.config.usePtcGains and ptc is None):
1355 raise RuntimeError("No ptcDataset provided to use PTC gains.")
1356 if (self.config.usePtcReadNoise and ptc is None):
1357 raise RuntimeError("No ptcDataset provided to use PTC read noise.")
1358
1359 # Validate that the inputs match the exposure configuration.
1360 exposureMetadata = ccdExposure.getMetadata()
1361 if self.config.doBias:
1362 self.compareCameraKeywords(exposureMetadata, bias, "bias")
1363 if self.config.doBrighterFatter:
1364 self.compareCameraKeywords(exposureMetadata, bfKernel, "brighter-fatter")
1365 if self.config.doCrosstalk:
1366 self.compareCameraKeywords(exposureMetadata, crosstalk, "crosstalk")
1367 if self.config.doDark:
1368 self.compareCameraKeywords(exposureMetadata, dark, "dark")
1369 if self.config.doDefect:
1370 self.compareCameraKeywords(exposureMetadata, defects, "defects")
1371 if self.config.doDeferredCharge:
1372 self.compareCameraKeywords(exposureMetadata, deferredChargeCalib, "CTI")
1373 if self.config.doFlat:
1374 self.compareCameraKeywords(exposureMetadata, flat, "flat")
1375 if (self.config.doFringe and physicalFilter in self.fringe.config.filters):
1376 self.compareCameraKeywords(exposureMetadata, fringes.fringes, "fringe")
1377 if (self.config.doIlluminationCorrection and physicalFilter in self.config.illumFilters):
1378 self.compareCameraKeywords(exposureMetadata, illumMaskedImage, "illumination")
1379 if self.doLinearize(ccd):
1380 self.compareCameraKeywords(exposureMetadata, linearizer, "linearizer")
1381 if self.config.usePtcGains or self.config.usePtcReadNoise:
1382 self.compareCameraKeywords(exposureMetadata, ptc, "PTC")
1383 if self.config.doStrayLight:
1384 self.compareCameraKeywords(exposureMetadata, strayLightData, "straylight")
1385
1386 # Start in ADU. Update units to electrons when gain is applied:
1387 # updateVariance, applyGains
1388 # Check if needed during/after BFE correction, CTI correction.
1389 exposureMetadata["LSST ISR UNITS"] = "ADU"
1390
1391 # Begin ISR processing.
1392 if self.config.doConvertIntToFloat:
1393 self.log.info("Converting exposure to floating point values.")
1394 ccdExposure = self.convertIntToFloat(ccdExposure)
1395
1396 if self.config.doBias and self.config.doBiasBeforeOverscan:
1397 self.log.info("Applying bias correction.")
1398 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1399 trimToFit=self.config.doTrimToMatchCalib)
1400 self.debugView(ccdExposure, "doBias")
1401
1402 # Amplifier level processing.
1403 overscans = []
1404
1405 if self.config.doOverscan and self.config.overscan.doParallelOverscan:
1406 # This will attempt to mask bleed pixels across all amplifiers.
1407 self.overscan.maskParallelOverscan(ccdExposure, ccd)
1408
1409 for amp in ccd:
1410 # if ccdExposure is one amp,
1411 # check for coverage to prevent performing ops multiple times
1412 if ccdExposure.getBBox().contains(amp.getBBox()):
1413 # Check for fully masked bad amplifiers,
1414 # and generate masks for SUSPECT and SATURATED values.
1415 badAmp = self.maskAmplifier(ccdExposure, amp, defects)
1416
1417 if self.config.doOverscan and not badAmp:
1418 # Overscan correction on amp-by-amp basis.
1419 overscanResults = self.overscanCorrection(ccdExposure, amp)
1420 self.log.debug("Corrected overscan for amplifier %s.", amp.getName())
1421 if overscanResults is not None and \
1422 self.config.qa is not None and self.config.qa.saveStats is True:
1423 if isinstance(overscanResults.overscanMean, float):
1424 # Only serial overscan was run
1425 mean = overscanResults.overscanMean
1426 sigma = overscanResults.overscanSigma
1427 residMean = overscanResults.residualMean
1428 residSigma = overscanResults.residualSigma
1429 else:
1430 # Both serial and parallel overscan were
1431 # run. Only report serial here.
1432 mean = overscanResults.overscanMean[0]
1433 sigma = overscanResults.overscanSigma[0]
1434 residMean = overscanResults.residualMean[0]
1435 residSigma = overscanResults.residualSigma[0]
1436
1437 self.metadata[f"FIT MEDIAN {amp.getName()}"] = mean
1438 self.metadata[f"FIT STDEV {amp.getName()}"] = sigma
1439 self.log.debug(" Overscan stats for amplifer %s: %f +/- %f",
1440 amp.getName(), mean, sigma)
1441
1442 self.metadata[f"RESIDUAL MEDIAN {amp.getName()}"] = residMean
1443 self.metadata[f"RESIDUAL STDEV {amp.getName()}"] = residSigma
1444 self.log.debug(" Overscan stats for amplifer %s after correction: %f +/- %f",
1445 amp.getName(), residMean, residSigma)
1446
1447 ccdExposure.getMetadata().set('OVERSCAN', "Overscan corrected")
1448 else:
1449 if badAmp:
1450 self.log.warning("Amplifier %s is bad.", amp.getName())
1451 overscanResults = None
1452
1453 overscans.append(overscanResults if overscanResults is not None else None)
1454 else:
1455 self.log.info("Skipped OSCAN for %s.", amp.getName())
1456
1457 # Define an effective PTC that will contain the gain and readout
1458 # noise to be used throughout the ISR task.
1459 ptc = self.defineEffectivePtc(ptc, ccd, bfGains, overscans, exposureMetadata)
1460
1461 if self.config.doDeferredCharge:
1462 self.log.info("Applying deferred charge/CTI correction.")
1463 self.deferredChargeCorrection.run(ccdExposure, deferredChargeCalib)
1464 self.debugView(ccdExposure, "doDeferredCharge")
1465
1466 if self.config.doCrosstalk and self.config.doCrosstalkBeforeAssemble:
1467 self.log.info("Applying crosstalk correction.")
1468 self.crosstalk.run(ccdExposure, crosstalk=crosstalk,
1469 crosstalkSources=crosstalkSources, camera=camera)
1470 self.debugView(ccdExposure, "doCrosstalk")
1471
1472 if self.config.doAssembleCcd:
1473 self.log.info("Assembling CCD from amplifiers.")
1474 ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
1475
1476 if self.config.expectWcs and not ccdExposure.getWcs():
1477 self.log.warning("No WCS found in input exposure.")
1478 self.debugView(ccdExposure, "doAssembleCcd")
1479
1480 ossThumb = None
1481 if self.config.qa.doThumbnailOss:
1482 ossThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1483
1484 if self.config.doBias and not self.config.doBiasBeforeOverscan:
1485 self.log.info("Applying bias correction.")
1486 isrFunctions.biasCorrection(ccdExposure.getMaskedImage(), bias.getMaskedImage(),
1487 trimToFit=self.config.doTrimToMatchCalib)
1488 self.debugView(ccdExposure, "doBias")
1489
1490 if self.config.doVariance:
1491 for amp in ccd:
1492 if ccdExposure.getBBox().contains(amp.getBBox()):
1493 self.log.debug("Constructing variance map for amplifer %s.", amp.getName())
1494 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1495 self.updateVariance(ampExposure, amp, ptc)
1496
1497 if self.config.qa is not None and self.config.qa.saveStats is True:
1498 qaStats = afwMath.makeStatistics(ampExposure.getVariance(),
1499 afwMath.MEDIAN | afwMath.STDEVCLIP)
1500 self.metadata[f"ISR VARIANCE {amp.getName()} MEDIAN"] = \
1501 qaStats.getValue(afwMath.MEDIAN)
1502 self.metadata[f"ISR VARIANCE {amp.getName()} STDEV"] = \
1503 qaStats.getValue(afwMath.STDEVCLIP)
1504 self.log.debug(" Variance stats for amplifer %s: %f +/- %f.",
1505 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1506 qaStats.getValue(afwMath.STDEVCLIP))
1507 if self.config.maskNegativeVariance:
1508 self.maskNegativeVariance(ccdExposure)
1509
1510 if self.doLinearize(ccd):
1511 self.log.info("Applying linearizer.")
1512 linearizer.applyLinearity(image=ccdExposure.getMaskedImage().getImage(),
1513 detector=ccd, log=self.log)
1514
1515 if self.config.doCrosstalk and not self.config.doCrosstalkBeforeAssemble:
1516 self.log.info("Applying crosstalk correction.")
1517 self.crosstalk.run(ccdExposure, crosstalk=crosstalk,
1518 crosstalkSources=crosstalkSources, isTrimmed=True)
1519 self.debugView(ccdExposure, "doCrosstalk")
1520
1521 # Masking block. Optionally mask known defects, NAN/inf pixels,
1522 # widen trails, and do anything else the camera needs. Saturated and
1523 # suspect pixels have already been masked.
1524 if self.config.doDefect:
1525 self.log.info("Masking defects.")
1526 self.maskDefect(ccdExposure, defects)
1527
1528 if self.config.numEdgeSuspect > 0:
1529 self.log.info("Masking edges as SUSPECT.")
1530 self.maskEdges(ccdExposure, numEdgePixels=self.config.numEdgeSuspect,
1531 maskPlane="SUSPECT", level=self.config.edgeMaskLevel)
1532
1533 if self.config.doNanMasking:
1534 self.log.info("Masking non-finite (NAN, inf) value pixels.")
1535 self.maskNan(ccdExposure)
1536
1537 if self.config.doWidenSaturationTrails:
1538 self.log.info("Widening saturation trails.")
1539 isrFunctions.widenSaturationTrails(ccdExposure.getMaskedImage().getMask())
1540
1541 if self.config.doCameraSpecificMasking:
1542 self.log.info("Masking regions for camera specific reasons.")
1543 self.masking.run(ccdExposure)
1544
1545 if self.config.doBrighterFatter:
1546 # We need to apply flats and darks before we can interpolate, and
1547 # we need to interpolate before we do B-F, but we do B-F without
1548 # the flats and darks applied so we can work in units of electrons
1549 # or holes. This context manager applies and then removes the darks
1550 # and flats.
1551 #
1552 # We also do not want to interpolate values here, so operate on
1553 # temporary images so we can apply only the BF-correction and roll
1554 # back the interpolation.
1555 interpExp = ccdExposure.clone()
1556 with self.flatContext(interpExp, flat, dark):
1557 isrFunctions.interpolateFromMask(
1558 maskedImage=interpExp.getMaskedImage(),
1559 fwhm=self.config.fwhm,
1560 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1561 maskNameList=list(self.config.brighterFatterMaskListToInterpolate)
1562 )
1563 bfExp = interpExp.clone()
1564
1565 self.log.info("Applying brighter-fatter correction using kernel type %s / gains %s.",
1566 type(bfKernel), type(bfGains))
1567 if self.config.doFluxConservingBrighterFatterCorrection:
1568 bfResults = isrFunctions.fluxConservingBrighterFatterCorrection(
1569 bfExp,
1570 bfKernel,
1571 self.config.brighterFatterMaxIter,
1572 self.config.brighterFatterThreshold,
1573 self.config.brighterFatterApplyGain,
1574 bfGains
1575 )
1576 else:
1577 bfResults = isrFunctions.brighterFatterCorrection(
1578 bfExp,
1579 bfKernel,
1580 self.config.brighterFatterMaxIter,
1581 self.config.brighterFatterThreshold,
1582 self.config.brighterFatterApplyGain,
1583 bfGains
1584 )
1585 if bfResults[1] == self.config.brighterFatterMaxIter - 1:
1586 self.log.warning("Brighter-fatter correction did not converge, final difference %f.",
1587 bfResults[0])
1588 else:
1589 self.log.info("Finished brighter-fatter correction in %d iterations.",
1590 bfResults[1])
1591 image = ccdExposure.getMaskedImage().getImage()
1592 bfCorr = bfExp.getMaskedImage().getImage()
1593 bfCorr -= interpExp.getMaskedImage().getImage()
1594 image += bfCorr
1595
1596 # Applying the brighter-fatter correction applies a
1597 # convolution to the science image. At the edges this
1598 # convolution may not have sufficient valid pixels to
1599 # produce a valid correction. Mark pixels within the size
1600 # of the brighter-fatter kernel as EDGE to warn of this
1601 # fact.
1602 self.log.info("Ensuring image edges are masked as EDGE to the brighter-fatter kernel size.")
1603 self.maskEdges(ccdExposure, numEdgePixels=numpy.max(bfKernel.shape) // 2,
1604 maskPlane="EDGE")
1605
1606 if self.config.brighterFatterMaskGrowSize > 0:
1607 self.log.info("Growing masks to account for brighter-fatter kernel convolution.")
1608 for maskPlane in self.config.brighterFatterMaskListToInterpolate:
1609 isrFunctions.growMasks(ccdExposure.getMask(),
1610 radius=self.config.brighterFatterMaskGrowSize,
1611 maskNameList=maskPlane,
1612 maskValue=maskPlane)
1613
1614 self.debugView(ccdExposure, "doBrighterFatter")
1615
1616 if self.config.doDark:
1617 self.log.info("Applying dark correction.")
1618 self.darkCorrection(ccdExposure, dark)
1619 self.debugView(ccdExposure, "doDark")
1620
1621 if self.config.doFringe and not self.config.fringeAfterFlat:
1622 self.log.info("Applying fringe correction before flat.")
1623 self.fringe.run(ccdExposure, **fringes.getDict())
1624 self.debugView(ccdExposure, "doFringe")
1625
1626 if self.config.doStrayLight and self.strayLight.check(ccdExposure):
1627 self.log.info("Checking strayLight correction.")
1628 self.strayLight.run(ccdExposure, strayLightData)
1629 self.debugView(ccdExposure, "doStrayLight")
1630
1631 if self.config.doFlat:
1632 self.log.info("Applying flat correction.")
1633 self.flatCorrection(ccdExposure, flat)
1634 self.debugView(ccdExposure, "doFlat")
1635
1636 if self.config.doApplyGains:
1637 self.log.info("Applying gain correction instead of flat.")
1638 isrFunctions.applyGains(ccdExposure, self.config.normalizeGains,
1639 ptcGains=ptc.gain)
1640 exposureMetadata["LSST ISR UNITS"] = "electrons"
1641
1642 if self.config.doFringe and self.config.fringeAfterFlat:
1643 self.log.info("Applying fringe correction after flat.")
1644 self.fringe.run(ccdExposure, **fringes.getDict())
1645
1646 if self.config.doVignette:
1647 if self.config.doMaskVignettePolygon:
1648 self.log.info("Constructing, attaching, and masking vignette polygon.")
1649 else:
1650 self.log.info("Constructing and attaching vignette polygon.")
1651 self.vignettePolygon = self.vignette.run(
1652 exposure=ccdExposure, doUpdateMask=self.config.doMaskVignettePolygon,
1653 vignetteValue=self.config.vignetteValue, log=self.log)
1654
1655 if self.config.doAttachTransmissionCurve:
1656 self.log.info("Adding transmission curves.")
1657 isrFunctions.attachTransmissionCurve(ccdExposure, opticsTransmission=opticsTransmission,
1658 filterTransmission=filterTransmission,
1659 sensorTransmission=sensorTransmission,
1660 atmosphereTransmission=atmosphereTransmission)
1661
1662 flattenedThumb = None
1663 if self.config.qa.doThumbnailFlattened:
1664 flattenedThumb = isrQa.makeThumbnail(ccdExposure, isrQaConfig=self.config.qa)
1665
1666 if self.config.doIlluminationCorrection and physicalFilter in self.config.illumFilters:
1667 self.log.info("Performing illumination correction.")
1668 isrFunctions.illuminationCorrection(ccdExposure.getMaskedImage(),
1669 illumMaskedImage, illumScale=self.config.illumScale,
1670 trimToFit=self.config.doTrimToMatchCalib)
1671
1672 preInterpExp = None
1673 if self.config.doSaveInterpPixels:
1674 preInterpExp = ccdExposure.clone()
1675
1676 # Reset and interpolate bad pixels.
1677 #
1678 # Large contiguous bad regions (which should have the BAD mask
1679 # bit set) should have their values set to the image median.
1680 # This group should include defects and bad amplifiers. As the
1681 # area covered by these defects are large, there's little
1682 # reason to expect that interpolation would provide a more
1683 # useful value.
1684 #
1685 # Smaller defects can be safely interpolated after the larger
1686 # regions have had their pixel values reset. This ensures
1687 # that the remaining defects adjacent to bad amplifiers (as an
1688 # example) do not attempt to interpolate extreme values.
1689 if self.config.doSetBadRegions:
1690 badPixelCount, badPixelValue = isrFunctions.setBadRegions(ccdExposure)
1691 if badPixelCount > 0:
1692 self.log.info("Set %d BAD pixels to %f.", badPixelCount, badPixelValue)
1693
1694 if self.config.doInterpolate:
1695 self.log.info("Interpolating masked pixels.")
1696 isrFunctions.interpolateFromMask(
1697 maskedImage=ccdExposure.getMaskedImage(),
1698 fwhm=self.config.fwhm,
1699 growSaturatedFootprints=self.config.growSaturationFootprintSize,
1700 maskNameList=list(self.config.maskListToInterpolate)
1701 )
1702
1703 self.roughZeroPoint(ccdExposure)
1704
1705 # correct for amp offsets within the CCD
1706 if self.config.doAmpOffset:
1707 self.log.info("Correcting amp offsets.")
1708 self.ampOffset.run(ccdExposure)
1709
1710 if self.config.doMeasureBackground:
1711 self.log.info("Measuring background level.")
1712 self.measureBackground(ccdExposure, self.config.qa)
1713
1714 if self.config.qa is not None and self.config.qa.saveStats is True:
1715 for amp in ccd:
1716 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1717 qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1718 afwMath.MEDIAN | afwMath.STDEVCLIP)
1719 self.metadata[f"ISR BACKGROUND {amp.getName()} MEDIAN"] = qaStats.getValue(afwMath.MEDIAN)
1720 self.metadata[f"ISR BACKGROUND {amp.getName()} STDEV"] = \
1721 qaStats.getValue(afwMath.STDEVCLIP)
1722 self.log.debug(" Background stats for amplifer %s: %f +/- %f",
1723 amp.getName(), qaStats.getValue(afwMath.MEDIAN),
1724 qaStats.getValue(afwMath.STDEVCLIP))
1725
1726 # Calculate standard image quality statistics
1727 if self.config.doStandardStatistics:
1728 metadata = ccdExposure.getMetadata()
1729 for amp in ccd:
1730 ampExposure = ccdExposure.Factory(ccdExposure, amp.getBBox())
1731 ampName = amp.getName()
1732 metadata[f"LSST ISR MASK SAT {ampName}"] = isrFunctions.countMaskedPixels(
1733 ampExposure.getMaskedImage(),
1734 [self.config.saturatedMaskName]
1735 )
1736 metadata[f"LSST ISR MASK BAD {ampName}"] = isrFunctions.countMaskedPixels(
1737 ampExposure.getMaskedImage(),
1738 ["BAD"]
1739 )
1740 qaStats = afwMath.makeStatistics(ampExposure.getImage(),
1741 afwMath.MEAN | afwMath.MEDIAN | afwMath.STDEVCLIP)
1742
1743 metadata[f"LSST ISR FINAL MEAN {ampName}"] = qaStats.getValue(afwMath.MEAN)
1744 metadata[f"LSST ISR FINAL MEDIAN {ampName}"] = qaStats.getValue(afwMath.MEDIAN)
1745 metadata[f"LSST ISR FINAL STDEV {ampName}"] = qaStats.getValue(afwMath.STDEVCLIP)
1746
1747 k1 = f"LSST ISR FINAL MEDIAN {ampName}"
1748 k2 = f"LSST ISR OVERSCAN SERIAL MEDIAN {ampName}"
1749 if self.config.doOverscan and k1 in metadata and k2 in metadata:
1750 metadata[f"LSST ISR LEVEL {ampName}"] = metadata[k1] - metadata[k2]
1751 else:
1752 metadata[f"LSST ISR LEVEL {ampName}"] = numpy.nan
1753
1754 # calculate additional statistics.
1755 outputStatistics = None
1756 if self.config.doCalculateStatistics:
1757 outputStatistics = self.isrStats.run(ccdExposure, overscanResults=overscans,
1758 bias=bias, dark=dark, flat=flat, ptc=ptc).results
1759
1760 # do any binning.
1761 outputBin1Exposure = None
1762 outputBin2Exposure = None
1763 if self.config.doBinnedExposures:
1764 outputBin1Exposure, outputBin2Exposure = self.makeBinnedImages(ccdExposure)
1765
1766 self.debugView(ccdExposure, "postISRCCD")
1767
1768 return pipeBase.Struct(
1769 exposure=ccdExposure,
1770 ossThumb=ossThumb,
1771 flattenedThumb=flattenedThumb,
1772
1773 outputBin1Exposure=outputBin1Exposure,
1774 outputBin2Exposure=outputBin2Exposure,
1775
1776 preInterpExposure=preInterpExp,
1777 outputExposure=ccdExposure,
1778 outputOssThumbnail=ossThumb,
1779 outputFlattenedThumbnail=flattenedThumb,
1780 outputStatistics=outputStatistics,
1781 )
1782
daf::base::PropertySet * set
Definition fits.cc:931

◆ runQuantum()

lsst.ip.isr.isrTask.IsrTask.runQuantum ( self,
butlerQC,
inputRefs,
outputRefs )

Definition at line 1024 of file isrTask.py.

1024 def runQuantum(self, butlerQC, inputRefs, outputRefs):
1025 inputs = butlerQC.get(inputRefs)
1026
1027 try:
1028 inputs['detectorNum'] = inputRefs.ccdExposure.dataId['detector']
1029 except Exception as e:
1030 raise ValueError("Failure to find valid detectorNum value for Dataset %s: %s." %
1031 (inputRefs, e))
1032
1033 detector = inputs['ccdExposure'].getDetector()
1034
1035 # This is use for header provenance.
1036 additionalInputDates = {}
1037
1038 if self.config.doCrosstalk is True:
1039 # Crosstalk sources need to be defined by the pipeline
1040 # yaml if they exist.
1041 if 'crosstalk' in inputs and inputs['crosstalk'] is not None:
1042 if not isinstance(inputs['crosstalk'], CrosstalkCalib):
1043 inputs['crosstalk'] = CrosstalkCalib.fromTable(inputs['crosstalk'])
1044 else:
1045 coeffVector = (self.config.crosstalk.crosstalkValues
1046 if self.config.crosstalk.useConfigCoefficients else None)
1047 crosstalkCalib = CrosstalkCalib().fromDetector(detector, coeffVector=coeffVector)
1048 inputs['crosstalk'] = crosstalkCalib
1049 if inputs['crosstalk'].interChip and len(inputs['crosstalk'].interChip) > 0:
1050 if 'crosstalkSources' not in inputs:
1051 self.log.warning("No crosstalkSources found for chip with interChip terms!")
1052
1053 if self.doLinearize(detector) is True:
1054 if 'linearizer' in inputs:
1055 if isinstance(inputs['linearizer'], dict):
1056 linearizer = linearize.Linearizer(detector=detector, log=self.log)
1057 linearizer.fromYaml(inputs['linearizer'])
1058 self.log.warning("Dictionary linearizers will be deprecated in DM-28741.")
1059 elif isinstance(inputs['linearizer'], numpy.ndarray):
1060 linearizer = linearize.Linearizer(table=inputs.get('linearizer', None),
1061 detector=detector,
1062 log=self.log)
1063 self.log.warning("Bare lookup table linearizers will be deprecated in DM-28741.")
1064 else:
1065 linearizer = inputs['linearizer']
1066 self.log.info("Loading linearizer from the Butler.")
1067 linearizer.log = self.log
1068 inputs['linearizer'] = linearizer
1069 else:
1070 inputs['linearizer'] = linearize.Linearizer(detector=detector, log=self.log)
1071 self.log.info("Constructing linearizer from cameraGeom information.")
1072
1073 if self.config.doDefect is True:
1074 if "defects" in inputs and inputs['defects'] is not None:
1075 # defects is loaded as a BaseCatalog with columns
1076 # x0, y0, width, height. Masking expects a list of defects
1077 # defined by their bounding box
1078 if not isinstance(inputs["defects"], Defects):
1079 inputs["defects"] = Defects.fromTable(inputs["defects"])
1080
1081 # Load the correct style of brighter-fatter kernel, and repack
1082 # the information as a numpy array.
1083 brighterFatterSource = None
1084 if self.config.doBrighterFatter:
1085 brighterFatterKernel = inputs.pop('newBFKernel', None)
1086 if brighterFatterKernel is None:
1087 # This type of kernel must be in (y, x) index
1088 # ordering, as it used directly as the .array
1089 # component of the afwImage kernel.
1090 brighterFatterKernel = inputs.get('bfKernel', None)
1091 brighterFatterSource = 'bfKernel'
1092 additionalInputDates[brighterFatterSource] = self.extractCalibDate(brighterFatterKernel)
1093
1094 if brighterFatterKernel is None:
1095 # This was requested by the config, but none were found.
1096 raise RuntimeError("No brighter-fatter kernel was supplied.")
1097 elif not isinstance(brighterFatterKernel, numpy.ndarray):
1098 # This is a ISR calib kernel. These kernels are
1099 # generated in (x, y) index ordering, and need to be
1100 # transposed to be used directly as the .array
1101 # component of the afwImage kernel. This is done
1102 # explicitly below when setting the ``bfKernel``
1103 # input.
1104 brighterFatterSource = 'newBFKernel'
1105 additionalInputDates[brighterFatterSource] = self.extractCalibDate(brighterFatterKernel)
1106
1107 detName = detector.getName()
1108 level = brighterFatterKernel.level
1109
1110 # This is expected to be a dictionary of amp-wise gains.
1111 inputs['bfGains'] = brighterFatterKernel.gain
1112 if self.config.brighterFatterLevel == 'DETECTOR':
1113 kernel = None
1114 if level == 'DETECTOR':
1115 if detName in brighterFatterKernel.detKernels:
1116 kernel = brighterFatterKernel.detKernels[detName]
1117 else:
1118 raise RuntimeError("Failed to extract kernel from new-style BF kernel.")
1119 elif level == 'AMP':
1120 self.log.warning("Making DETECTOR level kernel from AMP based brighter "
1121 "fatter kernels.")
1122 brighterFatterKernel.makeDetectorKernelFromAmpwiseKernels(detName)
1123 kernel = brighterFatterKernel.detKernels[detName]
1124 if kernel is None:
1125 raise RuntimeError("Could not identify brighter-fatter kernel!")
1126 # Do the one single transpose here so the kernel
1127 # can be directly loaded into the afwImage .array
1128 # component.
1129 inputs['bfKernel'] = numpy.transpose(kernel)
1130 elif self.config.brighterFatterLevel == 'AMP':
1131 raise NotImplementedError("Per-amplifier brighter-fatter correction not implemented")
1132
1133 if self.config.doFringe is True and self.fringe.checkFilter(inputs['ccdExposure']):
1134 expId = inputs['ccdExposure'].info.id
1135 inputs['fringes'] = self.fringe.loadFringes(inputs['fringes'],
1136 expId=expId,
1137 assembler=self.assembleCcd
1138 if self.config.doAssembleIsrExposures else None)
1139 else:
1140 inputs['fringes'] = pipeBase.Struct(fringes=None)
1141
1142 if self.config.doStrayLight is True and self.strayLight.checkFilter(inputs['ccdExposure']):
1143 if 'strayLightData' not in inputs:
1144 inputs['strayLightData'] = None
1145
1146 if self.config.doHeaderProvenance:
1147 # Add calibration provenanace info to header.
1148 exposureMetadata = inputs['ccdExposure'].getMetadata()
1149
1150 # These inputs change name during this step. These should
1151 # have matching entries in the additionalInputDates dict.
1152 additionalInputs = []
1153 if self.config.doBrighterFatter:
1154 additionalInputs.append(brighterFatterSource)
1155
1156 for inputName in sorted(list(inputs.keys()) + additionalInputs):
1157 reference = getattr(inputRefs, inputName, None)
1158 if reference is not None and hasattr(reference, "run"):
1159 runKey = f"LSST CALIB RUN {inputName.upper()}"
1160 runValue = reference.run
1161 idKey = f"LSST CALIB UUID {inputName.upper()}"
1162 idValue = str(reference.id)
1163 dateKey = f"LSST CALIB DATE {inputName.upper()}"
1164
1165 if inputName in additionalInputDates:
1166 dateValue = additionalInputDates[inputName]
1167 else:
1168 dateValue = self.extractCalibDate(inputs[inputName])
1169
1170 exposureMetadata[runKey] = runValue
1171 exposureMetadata[idKey] = idValue
1172 exposureMetadata[dateKey] = dateValue
1173
1174 outputs = self.run(**inputs)
1175 butlerQC.put(outputs, outputRefs)
1176

◆ saturationDetection()

lsst.ip.isr.isrTask.IsrTask.saturationDetection ( self,
exposure,
amp )
Detect and mask saturated pixels in config.saturatedMaskName.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to process.  Only the amplifier DataSec is processed.
amp : `lsst.afw.cameraGeom.Amplifier`
    Amplifier detector data.

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

Definition at line 2367 of file isrTask.py.

2367 def saturationDetection(self, exposure, amp):
2368 """Detect and mask saturated pixels in config.saturatedMaskName.
2369
2370 Parameters
2371 ----------
2372 exposure : `lsst.afw.image.Exposure`
2373 Exposure to process. Only the amplifier DataSec is processed.
2374 amp : `lsst.afw.cameraGeom.Amplifier`
2375 Amplifier detector data.
2376
2377 See Also
2378 --------
2379 lsst.ip.isr.isrFunctions.makeThresholdMask
2380 """
2381 if not math.isnan(amp.getSaturation()):
2382 maskedImage = exposure.getMaskedImage()
2383 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
2384 isrFunctions.makeThresholdMask(
2385 maskedImage=dataView,
2386 threshold=amp.getSaturation(),
2387 growFootprints=0,
2388 maskName=self.config.saturatedMaskName,
2389 )
2390

◆ saturationInterpolation()

lsst.ip.isr.isrTask.IsrTask.saturationInterpolation ( self,
exposure )
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 2391 of file isrTask.py.

2391 def saturationInterpolation(self, exposure):
2392 """Interpolate over saturated pixels, in place.
2393
2394 This method should be called after `saturationDetection`, to
2395 ensure that the saturated pixels have been identified in the
2396 SAT mask. It should also be called after `assembleCcd`, since
2397 saturated regions may cross amplifier boundaries.
2398
2399 Parameters
2400 ----------
2401 exposure : `lsst.afw.image.Exposure`
2402 Exposure to process.
2403
2404 See Also
2405 --------
2406 lsst.ip.isr.isrTask.saturationDetection
2407 lsst.ip.isr.isrFunctions.interpolateFromMask
2408 """
2409 isrFunctions.interpolateFromMask(
2410 maskedImage=exposure.getMaskedImage(),
2411 fwhm=self.config.fwhm,
2412 growSaturatedFootprints=self.config.growSaturationFootprintSize,
2413 maskNameList=list(self.config.saturatedMaskName),
2414 )
2415

◆ suspectDetection()

lsst.ip.isr.isrTask.IsrTask.suspectDetection ( self,
exposure,
amp )
Detect and mask suspect pixels in config.suspectMaskName.

Parameters
----------
exposure : `lsst.afw.image.Exposure`
    Exposure to process.  Only the amplifier DataSec is processed.
amp : `lsst.afw.cameraGeom.Amplifier`
    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 2416 of file isrTask.py.

2416 def suspectDetection(self, exposure, amp):
2417 """Detect and mask suspect pixels in config.suspectMaskName.
2418
2419 Parameters
2420 ----------
2421 exposure : `lsst.afw.image.Exposure`
2422 Exposure to process. Only the amplifier DataSec is processed.
2423 amp : `lsst.afw.cameraGeom.Amplifier`
2424 Amplifier detector data.
2425
2426 See Also
2427 --------
2428 lsst.ip.isr.isrFunctions.makeThresholdMask
2429
2430 Notes
2431 -----
2432 Suspect pixels are pixels whose value is greater than
2433 amp.getSuspectLevel(). This is intended to indicate pixels that may be
2434 affected by unknown systematics; for example if non-linearity
2435 corrections above a certain level are unstable then that would be a
2436 useful value for suspectLevel. A value of `nan` indicates that no such
2437 level exists and no pixels are to be masked as suspicious.
2438 """
2439 suspectLevel = amp.getSuspectLevel()
2440 if math.isnan(suspectLevel):
2441 return
2442
2443 maskedImage = exposure.getMaskedImage()
2444 dataView = maskedImage.Factory(maskedImage, amp.getRawBBox())
2445 isrFunctions.makeThresholdMask(
2446 maskedImage=dataView,
2447 threshold=suspectLevel,
2448 growFootprints=0,
2449 maskName=self.config.suspectMaskName,
2450 )
2451

◆ updateVariance()

lsst.ip.isr.isrTask.IsrTask.updateVariance ( self,
ampExposure,
amp,
ptcDataset )
Set the variance plane using the 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.cameraGeom.Amplifier` or `FakeAmp`
    Amplifier detector data.
ptcDataset : `lsst.ip.isr.PhotonTransferCurveDataset`
    Effective PTC dataset containing the gains and read noise.

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

Definition at line 2232 of file isrTask.py.

2232 def updateVariance(self, ampExposure, amp, ptcDataset):
2233 """Set the variance plane using the gain and read noise
2234
2235 The read noise is calculated from the ``overscanImage`` if the
2236 ``doEmpiricalReadNoise`` option is set in the configuration; otherwise
2237 the value from the amplifier data is used.
2238
2239 Parameters
2240 ----------
2241 ampExposure : `lsst.afw.image.Exposure`
2242 Exposure to process.
2243 amp : `lsst.afw.cameraGeom.Amplifier` or `FakeAmp`
2244 Amplifier detector data.
2245 ptcDataset : `lsst.ip.isr.PhotonTransferCurveDataset`
2246 Effective PTC dataset containing the gains and read noise.
2247
2248 See also
2249 --------
2250 lsst.ip.isr.isrFunctions.updateVariance
2251 """
2252 ampName = amp.getName()
2253 # At this point, the effective PTC should have
2254 # gain and noise values.
2255 gain = ptcDataset.gain[ampName]
2256 readNoise = ptcDataset.noise[ampName]
2257
2258 isrFunctions.updateVariance(
2259 maskedImage=ampExposure.getMaskedImage(),
2260 gain=gain,
2261 readNoise=readNoise,
2262 )
2263

Member Data Documentation

◆ _DefaultName

str lsst.ip.isr.isrTask.IsrTask._DefaultName = "isr"
staticprotected

Definition at line 1009 of file isrTask.py.

◆ ConfigClass

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

Definition at line 1008 of file isrTask.py.

◆ vignettePolygon

lsst.ip.isr.isrTask.IsrTask.vignettePolygon

Definition at line 1651 of file isrTask.py.


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