lsst.ip.isr  15.0-3-g26e257c
crosstalk.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008-2017 AURA/LSST.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <https://www.lsstcorp.org/LegalNotices/>.
21 #
22 """
23 Apply intra-CCD crosstalk corrections
24 """
25 from __future__ import absolute_import, division, print_function
26 
27 import lsst.afw.math
28 import lsst.afw.table
29 import lsst.afw.detection
30 from lsst.pex.config import Config, Field
31 from lsst.pipe.base import Task
32 
33 __all__ = ["CrosstalkConfig", "CrosstalkTask", "subtractCrosstalk"]
34 
35 
36 class CrosstalkConfig(Config):
37  """Configuration for intra-CCD crosstalk removal"""
38  minPixelToMask = Field(dtype=float, default=45000,
39  doc="Set crosstalk mask plane for pixels over this value")
40  crosstalkMaskPlane = Field(dtype=str, default="CROSSTALK", doc="Name for crosstalk mask plane")
41 
42 
43 class CrosstalkTask(Task):
44  """Apply intra-CCD crosstalk correction"""
45  ConfigClass = CrosstalkConfig
46 
47  def prepCrosstalk(self, dataRef):
48  """Placeholder for crosstalk preparation method, e.g., for inter-CCD crosstalk.
49 
50  See also
51  --------
52  lsst.obs.decam.crosstalk.DecamCrosstalkTask.prepCrosstalk
53  """
54  return
55 
56  def run(self, exposure, crosstalkSources=None):
57  """Apply intra-CCD crosstalk correction
58 
59  Parameters
60  ----------
61  exposure : `lsst.afw.image.Exposure`
62  Exposure for which to remove crosstalk.
63  crosstalkSources : `defaultdict`, optional
64  Image data and crosstalk coefficients from other CCDs/amps that are
65  sources of crosstalk in exposure.
66  The default for intra-CCD crosstalk here is None.
67  """
68  detector = exposure.getDetector()
69  if not detector.hasCrosstalk():
70  self.log.warn("Crosstalk correction skipped: no crosstalk coefficients for detector")
71  return
72  self.log.info("Applying crosstalk correction")
73  numAmps = len(exposure.getDetector())
74  subtractCrosstalk(exposure, minPixelToMask=self.config.minPixelToMask,
75  crosstalkStr=self.config.crosstalkMaskPlane)
76 
77 
78 # Flips required to get the corner to the lower-left
79 # (an arbitrary choice; flips are relative, so the choice of reference here is not important)
80 X_FLIP = {lsst.afw.table.LL: False, lsst.afw.table.LR: True,
81  lsst.afw.table.UL: False, lsst.afw.table.UR: True}
82 Y_FLIP = {lsst.afw.table.LL: False, lsst.afw.table.LR: False,
83  lsst.afw.table.UL: True, lsst.afw.table.UR: True}
84 
85 
86 def extractAmp(image, amp, corner):
87  """Return an image of the amp
88 
89  The returned image will have the amp's readout corner in the
90  nominated `corner`.
91 
92  Parameters
93  ----------
94  image : `lsst.afw.image.Image` or `lsst.afw.image.MaskedImage`
95  Image containing the amplifier of interest.
96  amp : `lsst.afw.table.AmpInfoRecord`
97  Amplifier information.
98  corner : `lsst.afw.table.ReadoutCorner` or `None`
99  Corner in which to put the amp's readout corner, or `None` for
100  no flipping.
101 
102  Returns
103  -------
104  output : `lsst.afw.image.Image`
105  Image of the amplifier in the standard configuration.
106  """
107  output = image.Factory(image, amp.getBBox())
108  ampCorner = amp.getReadoutCorner()
109  # Flipping is necessary only if the desired configuration doesn't match what we currently have
110  xFlip = X_FLIP[corner] ^ X_FLIP[ampCorner]
111  yFlip = Y_FLIP[corner] ^ Y_FLIP[ampCorner]
112  return lsst.afw.math.flipImage(output, xFlip, yFlip)
113 
114 
115 def calculateBackground(mi, badPixels=["BAD"]):
116  """Calculate median background in image
117 
118  Getting a great background model isn't important for crosstalk correction,
119  since the crosstalk is at a low level. The median should be sufficient.
120 
121  Parameters
122  ----------
123  mi : `lsst.afw.image.MaskedImage`
124  MaskedImage for which to measure background.
125  badPixels : `list` of `str`
126  Mask planes to ignore.
127 
128  Returns
129  -------
130  bg : `float`
131  Median background level.
132  """
133  mask = mi.getMask()
135  stats.setAndMask(mask.getPlaneBitMask(badPixels))
136  return lsst.afw.math.makeStatistics(mi, lsst.afw.math.MEDIAN, stats).getValue()
137 
138 
139 def subtractCrosstalk(exposure, badPixels=["BAD"], minPixelToMask=45000, crosstalkStr="CROSSTALK"):
140  """Subtract the intra-CCD crosstalk from an exposure
141 
142  We set the mask plane indicated by ``crosstalkStr`` in a target amplifier
143  for pixels in a source amplifier that exceed `minPixelToMask`. Note that
144  the correction is applied to all pixels in the amplifier, but only those
145  that have a substantial crosstalk are masked with ``crosstalkStr``.
146 
147  The uncorrected image is used as a template for correction. This is good
148  enough if the crosstalk is small (e.g., coefficients < ~ 1e-3), but if it's
149  larger you may want to iterate.
150 
151  Parameters
152  ----------
153  exposure : `lsst.afw.image.Exposure`
154  Exposure for which to subtract crosstalk.
155  badPixels : `list` of `str`
156  Mask planes to ignore.
157  minPixelToMask : `float`
158  Minimum pixel value in source amplifier for which to set
159  ``crosstalkStr`` mask plane in target amplifier.
160  crosstalkStr : `str`
161  Mask plane name for pixels greatly modified by crosstalk.
162  """
163  mi = exposure.getMaskedImage()
164  mask = mi.getMask()
165 
166  ccd = exposure.getDetector()
167  numAmps = len(ccd)
168  coeffs = ccd.getCrosstalk()
169  assert coeffs.shape == (numAmps, numAmps)
170 
171  # Set the crosstalkStr bit for the bright pixels (those which will have significant crosstalk correction)
172  crosstalkPlane = mask.addMaskPlane(crosstalkStr)
173  footprints = lsst.afw.detection.FootprintSet(mi, lsst.afw.detection.Threshold(minPixelToMask))
174  footprints.setMask(mask, crosstalkStr)
175  crosstalk = mask.getPlaneBitMask(crosstalkStr)
176 
177  backgrounds = [calculateBackground(mi.Factory(mi, amp.getBBox()), badPixels) for amp in ccd]
178 
179  subtrahend = mi.Factory(mi.getBBox())
180  subtrahend.set((0, 0, 0))
181  for ii, iAmp in enumerate(ccd):
182  iImage = subtrahend.Factory(subtrahend, iAmp.getBBox())
183  for jj, jAmp in enumerate(ccd):
184  if ii == jj:
185  assert coeffs[ii, jj] == 0.0
186  if coeffs[ii, jj] == 0.0:
187  continue
188 
189  jImage = extractAmp(mi, jAmp, iAmp.getReadoutCorner())
190  jImage.getMask().getArray()[:] &= crosstalk # Remove all other masks
191  jImage -= backgrounds[jj]
192 
193  iImage.scaledPlus(coeffs[ii, jj], jImage)
194 
195  # Set crosstalkStr bit only for those pixels that have been significantly modified (i.e., those
196  # masked as such in 'subtrahend'), not necessarily those that are bright originally.
197  mask.clearMaskPlane(crosstalkPlane)
198  mi -= subtrahend # also sets crosstalkStr bit for bright pixels
def subtractCrosstalk(exposure, badPixels=["BAD"], minPixelToMask=45000, crosstalkStr="CROSSTALK")
Definition: crosstalk.py:139
def extractAmp(image, amp, corner)
Definition: crosstalk.py:86
std::shared_ptr< ImageT > flipImage(ImageT const &inImage, bool flipLR, bool flipTB)
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
def calculateBackground(mi, badPixels=["BAD"])
Definition: crosstalk.py:115
def run(self, exposure, crosstalkSources=None)
Definition: crosstalk.py:56
def prepCrosstalk(self, dataRef)
Definition: crosstalk.py:47