23 Apply intra-CCD crosstalk corrections 25 from __future__
import absolute_import, division, print_function
33 __all__ = [
"CrosstalkConfig",
"CrosstalkTask",
"subtractCrosstalk"]
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")
44 """Apply intra-CCD crosstalk correction""" 45 ConfigClass = CrosstalkConfig
48 """Placeholder for crosstalk preparation method, e.g., for inter-CCD crosstalk. 52 lsst.obs.decam.crosstalk.DecamCrosstalkTask.prepCrosstalk 56 def run(self, exposure, crosstalkSources=None):
57 """Apply intra-CCD crosstalk correction 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. 68 detector = exposure.getDetector()
69 if not detector.hasCrosstalk():
70 self.log.warn(
"Crosstalk correction skipped: no crosstalk coefficients for detector")
72 self.log.info(
"Applying crosstalk correction")
73 numAmps = len(exposure.getDetector())
75 crosstalkStr=self.config.crosstalkMaskPlane)
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}
87 """Return an image of the amp 89 The returned image will have the amp's readout corner in the 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 104 output : `lsst.afw.image.Image` 105 Image of the amplifier in the standard configuration. 107 output = image.Factory(image, amp.getBBox())
108 ampCorner = amp.getReadoutCorner()
110 xFlip = X_FLIP[corner] ^ X_FLIP[ampCorner]
111 yFlip = Y_FLIP[corner] ^ Y_FLIP[ampCorner]
116 """Calculate median background in image 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. 123 mi : `lsst.afw.image.MaskedImage` 124 MaskedImage for which to measure background. 125 badPixels : `list` of `str` 126 Mask planes to ignore. 131 Median background level. 135 stats.setAndMask(mask.getPlaneBitMask(badPixels))
139 def subtractCrosstalk(exposure, badPixels=["BAD"], minPixelToMask=45000, crosstalkStr="CROSSTALK"):
140 """Subtract the intra-CCD crosstalk from an exposure 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``. 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. 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. 161 Mask plane name for pixels greatly modified by crosstalk. 163 mi = exposure.getMaskedImage()
166 ccd = exposure.getDetector()
168 coeffs = ccd.getCrosstalk()
169 assert coeffs.shape == (numAmps, numAmps)
172 crosstalkPlane = mask.addMaskPlane(crosstalkStr)
174 footprints.setMask(mask, crosstalkStr)
175 crosstalk = mask.getPlaneBitMask(crosstalkStr)
177 backgrounds = [
calculateBackground(mi.Factory(mi, amp.getBBox()), badPixels)
for amp
in ccd]
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):
185 assert coeffs[ii, jj] == 0.0
186 if coeffs[ii, jj] == 0.0:
189 jImage =
extractAmp(mi, jAmp, iAmp.getReadoutCorner())
190 jImage.getMask().getArray()[:] &= crosstalk
191 jImage -= backgrounds[jj]
193 iImage.scaledPlus(coeffs[ii, jj], jImage)
197 mask.clearMaskPlane(crosstalkPlane)
def subtractCrosstalk(exposure, badPixels=["BAD"], minPixelToMask=45000, crosstalkStr="CROSSTALK")
def extractAmp(image, amp, corner)
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"])
def run(self, exposure, crosstalkSources=None)
def prepCrosstalk(self, dataRef)