Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DM-10299: Apply crosstalk correction in decam #50

Merged
merged 3 commits into from
Mar 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 17 additions & 9 deletions python/lsst/ip/isr/crosstalk.py
Expand Up @@ -24,19 +24,14 @@
"""
from __future__ import absolute_import, division, print_function

__all__ = ["CrosstalkConfig", "CrosstalkTask", "subtractCrosstalk"]

from builtins import zip

import numpy as np

import lsst.afw.math
import lsst.afw.table
import lsst.afw.detection

from lsst.pex.config import Config, Field, ListField
from lsst.pex.config import Config, Field
from lsst.pipe.base import Task

__all__ = ["CrosstalkConfig", "CrosstalkTask", "subtractCrosstalk"]


class CrosstalkConfig(Config):
"""Configuration for intra-CCD crosstalk removal"""
Expand All @@ -49,13 +44,26 @@ class CrosstalkTask(Task):
"""Apply intra-CCD crosstalk correction"""
ConfigClass = CrosstalkConfig

def run(self, exposure):
def prepCrosstalk(self, dataRef):
"""Placeholder for crosstalk preparation method, e.g., for inter-CCD crosstalk.

See also
--------
lsst.obs.decam.crosstalk.DecamCrosstalkTask.prepCrosstalk
"""
return

def run(self, exposure, crosstalkSources=None):
"""Apply intra-CCD crosstalk correction

Parameters
----------
exposure : `lsst.afw.image.Exposure`
Exposure for which to remove crosstalk.
crosstalkSources : `defaultdict`, optional
Image data and crosstalk coefficients from other CCDs/amps that are
sources of crosstalk in exposure.
The default for intra-CCD crosstalk here is None.
"""
detector = exposure.getDetector()
if not detector.hasCrosstalk():
Expand Down
55 changes: 43 additions & 12 deletions python/lsst/ip/isr/isrTask.py
Expand Up @@ -81,6 +81,21 @@ class IsrTaskConfig(pexConfig.Config):
doc="Persist postISRCCD?",
default=True,
)
biasDataProductName = pexConfig.Field(
dtype=str,
doc="Name of the bias data product",
default="bias",
)
darkDataProductName = pexConfig.Field(
dtype=str,
doc="Name of the dark data product",
default="dark",
)
flatDataProductName = pexConfig.Field(
dtype=str,
doc="Name of the flat data product",
default="flat",
)
assembleCcd = pexConfig.ConfigurableField(
target=AssembleCcdTask,
doc="CCD assembly task",
Expand Down Expand Up @@ -392,14 +407,22 @@ def readIsrData(self, dataRef, rawExposure):
"""
ccd = rawExposure.getDetector()

biasExposure = self.getIsrExposure(dataRef, "bias") if self.config.doBias else None
biasExposure = self.getIsrExposure(dataRef, self.config.biasDataProductName) \
if self.config.doBias else None
# immediate=True required for functors and linearizers are functors; see ticket DM-6515
linearizer = dataRef.get("linearizer", immediate=True) if self.doLinearize(ccd) else None
darkExposure = self.getIsrExposure(dataRef, "dark") if self.config.doDark else None
flatExposure = self.getIsrExposure(dataRef, "flat") if self.config.doFlat else None
darkExposure = self.getIsrExposure(dataRef, self.config.darkDataProductName) \
if self.config.doDark else None
flatExposure = self.getIsrExposure(dataRef, self.config.flatDataProductName) \
if self.config.doFlat else None
brighterFatterKernel = dataRef.get("brighterFatterKernel") if self.config.doBrighterFatter else None
defectList = dataRef.get("defects") if self.config.doDefect else None

if self.config.doCrosstalk:
crosstalkSources = self.crosstalk.prepCrosstalk(dataRef)
else:
crosstalkSources = None

if self.config.doFringe and self.fringe.checkFilter(rawExposure):
fringeStruct = self.fringe.readFringes(dataRef, assembler=self.assembleCcd
if self.config.doAssembleIsrExposures else None)
Expand Down Expand Up @@ -433,13 +456,15 @@ def readIsrData(self, dataRef, rawExposure):
filterTransmission=filterTransmission,
sensorTransmission=sensorTransmission,
atmosphereTransmission=atmosphereTransmission,
crosstalkSources=crosstalkSources,
)

@pipeBase.timeMethod
def run(self, ccdExposure, bias=None, linearizer=None, dark=None, flat=None, defects=None,
fringes=None, bfKernel=None, camera=None,
opticsTransmission=None, filterTransmission=None,
sensorTransmission=None, atmosphereTransmission=None):
sensorTransmission=None, atmosphereTransmission=None,
crosstalkSources=None):
"""!Perform instrument signature removal on an exposure

Steps include:
Expand All @@ -462,6 +487,7 @@ def run(self, ccdExposure, bias=None, linearizer=None, dark=None, flat=None, def
@param[in] filterTransmission a TransmissionCurve for the filter
@param[in] sensorTransmission a TransmissionCurve for the sensor
@param[in] atmosphereTransmission a TransmissionCurve for the atmosphere
@param[in] crosstalkSources a defaultdict used for DECam inter-CCD crosstalk

@return a pipeBase.Struct with field:
- exposure
Expand Down Expand Up @@ -505,6 +531,9 @@ def run(self, ccdExposure, bias=None, linearizer=None, dark=None, flat=None, def
self.suspectDetection(ccdExposure, amp)
self.overscanCorrection(ccdExposure, amp)

if self.config.doCrosstalk:
self.crosstalk.run(ccdExposure, crosstalkSources)

if self.config.doAssembleCcd:
ccdExposure = self.assembleCcd.assembleCcd(ccdExposure)
if self.config.expectWcs and not ccdExposure.getWcs():
Expand All @@ -516,9 +545,6 @@ def run(self, ccdExposure, bias=None, linearizer=None, dark=None, flat=None, def
if self.doLinearize(ccd):
linearizer(image=ccdExposure.getMaskedImage().getImage(), detector=ccd, log=self.log)

if self.config.doCrosstalk:
self.crosstalk.run(ccdExposure)

for amp in ccd:
# if ccdExposure is one amp, check for coverage to prevent performing ops multiple times
if ccdExposure.getBBox().contains(amp.getBBox()):
Expand Down Expand Up @@ -589,16 +615,21 @@ def run(self, ccdExposure, bias=None, linearizer=None, dark=None, flat=None, def

@pipeBase.timeMethod
def runDataRef(self, sensorRef):
"""!Perform instrument signature removal on a ButlerDataRef of a Sensor
"""Perform instrument signature removal on a ButlerDataRef of a Sensor

- Read in necessary detrending/isr/calibration data
- Process raw exposure in run()
- Persist the ISR-corrected exposure as "postISRCCD" if config.doWrite is True

@param[in] sensorRef daf.persistence.butlerSubset.ButlerDataRef of the
detector data to be processed
@return a pipeBase.Struct with fields:
- exposure: the exposure after application of ISR
Parameters
----------
sensorRef : `daf.persistence.butlerSubset.ButlerDataRef`
DataRef of the detector data to be processed

Returns
-------
result : `pipeBase.Struct`
Struct contains field "exposure," which is the exposure after application of ISR
"""
self.log.info("Performing ISR on sensor %s" % (sensorRef.dataId))
ccdExposure = sensorRef.get('raw')
Expand Down