23 Apply intra-CCD crosstalk corrections 32 __all__ = [
"CrosstalkConfig",
"CrosstalkTask",
"subtractCrosstalk",
"writeCrosstalkCoeffs"]
36 """Configuration for intra-CCD crosstalk removal""" 37 minPixelToMask = Field(dtype=float, default=45000,
38 doc=
"Set crosstalk mask plane for pixels over this value")
39 crosstalkMaskPlane = Field(dtype=str, default=
"CROSSTALK", doc=
"Name for crosstalk mask plane")
43 """Apply intra-CCD crosstalk correction""" 44 ConfigClass = CrosstalkConfig
47 """Placeholder for crosstalk preparation method, e.g., for inter-CCD crosstalk. 51 lsst.obs.decam.crosstalk.DecamCrosstalkTask.prepCrosstalk 55 def run(self, exposure, crosstalkSources=None):
56 """Apply intra-CCD crosstalk correction 60 exposure : `lsst.afw.image.Exposure` 61 Exposure for which to remove crosstalk. 62 crosstalkSources : `defaultdict`, optional 63 Image data and crosstalk coefficients from other CCDs/amps that are 64 sources of crosstalk in exposure. 65 The default for intra-CCD crosstalk here is None. 67 detector = exposure.getDetector()
68 if not detector.hasCrosstalk():
69 self.log.warn(
"Crosstalk correction skipped: no crosstalk coefficients for detector")
71 self.log.info(
"Applying crosstalk correction")
73 crosstalkStr=self.config.crosstalkMaskPlane)
78 X_FLIP = {lsst.afw.table.LL:
False, lsst.afw.table.LR:
True,
79 lsst.afw.table.UL:
False, lsst.afw.table.UR:
True}
80 Y_FLIP = {lsst.afw.table.LL:
False, lsst.afw.table.LR:
False,
81 lsst.afw.table.UL:
True, lsst.afw.table.UR:
True}
85 """Return an image of the amp 87 The returned image will have the amp's readout corner in the 92 image : `lsst.afw.image.Image` or `lsst.afw.image.MaskedImage` 93 Image containing the amplifier of interest. 94 amp : `lsst.afw.table.AmpInfoRecord` 95 Amplifier information. 96 corner : `lsst.afw.table.ReadoutCorner` or `None` 97 Corner in which to put the amp's readout corner, or `None` for 100 The image is already trimmed. 101 This should no longer be needed once DM-15409 is resolved. 105 output : `lsst.afw.image.Image` 106 Image of the amplifier in the standard configuration. 108 output = image[amp.getBBox()
if isTrimmed
else amp.getRawDataBBox()]
109 ampCorner = amp.getReadoutCorner()
111 xFlip = X_FLIP[corner] ^ X_FLIP[ampCorner]
112 yFlip = Y_FLIP[corner] ^ Y_FLIP[ampCorner]
117 """Calculate median background in image 119 Getting a great background model isn't important for crosstalk correction, 120 since the crosstalk is at a low level. The median should be sufficient. 124 mi : `lsst.afw.image.MaskedImage` 125 MaskedImage for which to measure background. 126 badPixels : `list` of `str` 127 Mask planes to ignore. 132 Median background level. 136 stats.setAndMask(mask.getPlaneBitMask(badPixels))
140 def subtractCrosstalk(exposure, badPixels=["BAD"], minPixelToMask=45000, crosstalkStr="CROSSTALK"):
141 """Subtract the intra-CCD crosstalk from an exposure 143 We set the mask plane indicated by ``crosstalkStr`` in a target amplifier 144 for pixels in a source amplifier that exceed `minPixelToMask`. Note that 145 the correction is applied to all pixels in the amplifier, but only those 146 that have a substantial crosstalk are masked with ``crosstalkStr``. 148 The uncorrected image is used as a template for correction. This is good 149 enough if the crosstalk is small (e.g., coefficients < ~ 1e-3), but if it's 150 larger you may want to iterate. 154 exposure : `lsst.afw.image.Exposure` 155 Exposure for which to subtract crosstalk. 156 badPixels : `list` of `str` 157 Mask planes to ignore. 158 minPixelToMask : `float` 159 Minimum pixel value in source amplifier for which to set 160 ``crosstalkStr`` mask plane in target amplifier. 162 Mask plane name for pixels greatly modified by crosstalk. 164 mi = exposure.getMaskedImage()
167 ccd = exposure.getDetector()
169 coeffs = ccd.getCrosstalk()
170 assert coeffs.shape == (numAmps, numAmps)
173 crosstalkPlane = mask.addMaskPlane(crosstalkStr)
175 footprints.setMask(mask, crosstalkStr)
176 crosstalk = mask.getPlaneBitMask(crosstalkStr)
180 subtrahend = mi.Factory(mi.getBBox())
181 subtrahend.set((0, 0, 0))
182 for ii, iAmp
in enumerate(ccd):
183 iImage = subtrahend[iAmp.getRawDataBBox()]
184 for jj, jAmp
in enumerate(ccd):
186 assert coeffs[ii, jj] == 0.0
187 if coeffs[ii, jj] == 0.0:
190 jImage =
extractAmp(mi, jAmp, iAmp.getReadoutCorner())
191 jImage.getMask().getArray()[:] &= crosstalk
192 jImage -= backgrounds[jj]
194 iImage.scaledPlus(coeffs[ii, jj], jImage)
198 mask.clearMaskPlane(crosstalkPlane)
203 """Write a yaml file containing the crosstalk coefficients 205 The coeff array is indexed by [i, j] where i and j are amplifiers 206 corresponding to the amplifiers in det 210 outputFileName : `str` 211 Name of output yaml file 212 coeff : `numpy.array(namp, namp)` 213 numpy array of coefficients 214 det : `lsst.afw.cameraGeom.Detector` 215 Used to provide the list of amplifier names; 216 if None use ['0', '1', ...] 218 Name of CCD, used to index the yaml file 219 If all CCDs are identical could be the type (e.g. ITL) 221 Indent width to use when writing the yaml file 225 ampNames = [str(i)
for i
in range(coeff.shape[0])]
227 ampNames = [a.getName()
for a
in det]
229 assert coeff.shape == (len(ampNames), len(ampNames))
233 with open(outputFileName,
"w")
as fd:
234 print(indent*
" " +
"crosstalk :", file=fd)
236 print(indent*
" " +
"%s :" % crosstalkName, file=fd)
239 for i, ampNameI
in enumerate(ampNames):
240 print(indent*
" " +
"%s : {" % ampNameI, file=fd)
242 print(indent*
" ", file=fd, end=
'')
244 for j, ampNameJ
in enumerate(ampNames):
245 print(
"%s : %11.4e, " % (ampNameJ, coeff[i, j]), file=fd,
246 end=
'\n' + indent*
" " if j%4 == 3
else '')
def subtractCrosstalk(exposure, badPixels=["BAD"], minPixelToMask=45000, crosstalkStr="CROSSTALK")
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 extractAmp(image, amp, corner, isTrimmed=False)
def prepCrosstalk(self, dataRef)
def writeCrosstalkCoeffs(outputFileName, coeff, det=None, crosstalkName="Unknown", indent=2)