23 Apply intra-CCD crosstalk corrections 32 __all__ = [
"CrosstalkConfig",
"CrosstalkTask",
"subtractCrosstalk",
"writeCrosstalkCoeffs"]
36 """Configuration for intra-CCD crosstalk removal""" 37 minPixelToMask = Field(
39 doc=
"Set crosstalk mask plane for pixels over this value",
42 crosstalkMaskPlane = Field(
44 doc=
"Name for crosstalk mask plane",
50 """Apply intra-CCD crosstalk correction""" 51 ConfigClass = CrosstalkConfig
54 """Placeholder for crosstalk preparation method, e.g., for inter-CCD crosstalk. 58 dataRef : `daf.persistence.butlerSubset.ButlerDataRef` 59 Butler reference of the detector data to be processed. 63 lsst.obs.decam.crosstalk.DecamCrosstalkTask.prepCrosstalk 67 def run(self, exposure, crosstalkSources=None):
68 """Apply intra-CCD crosstalk correction 72 exposure : `lsst.afw.image.Exposure` 73 Exposure for which to remove crosstalk. 74 crosstalkSources : `defaultdict`, optional 75 Image data and crosstalk coefficients from other CCDs/amps that are 76 sources of crosstalk in exposure. 77 The default for intra-CCD crosstalk here is None. 79 detector = exposure.getDetector()
80 if not detector.hasCrosstalk():
81 self.log.warn(
"Crosstalk correction skipped: no crosstalk coefficients for detector")
83 self.log.info(
"Applying crosstalk correction")
85 crosstalkStr=self.config.crosstalkMaskPlane)
90 X_FLIP = {lsst.afw.table.LL:
False, lsst.afw.table.LR:
True,
91 lsst.afw.table.UL:
False, lsst.afw.table.UR:
True}
92 Y_FLIP = {lsst.afw.table.LL:
False, lsst.afw.table.LR:
False,
93 lsst.afw.table.UL:
True, lsst.afw.table.UR:
True}
97 def run(self, exposure, crosstalkSources=None):
98 self.log.info(
"Not performing any crosstalk correction")
102 """Return an image of the amp 104 The returned image will have the amp's readout corner in the 109 image : `lsst.afw.image.Image` or `lsst.afw.image.MaskedImage` 110 Image containing the amplifier of interest. 111 amp : `lsst.afw.table.AmpInfoRecord` 112 Amplifier information. 113 corner : `lsst.afw.table.ReadoutCorner` or `None` 114 Corner in which to put the amp's readout corner, or `None` for 117 The image is already trimmed. 118 This should no longer be needed once DM-15409 is resolved. 122 output : `lsst.afw.image.Image` 123 Image of the amplifier in the standard configuration. 125 output = image[amp.getBBox()
if isTrimmed
else amp.getRawDataBBox()]
126 ampCorner = amp.getReadoutCorner()
128 xFlip = X_FLIP[corner] ^ X_FLIP[ampCorner]
129 yFlip = Y_FLIP[corner] ^ Y_FLIP[ampCorner]
134 """Calculate median background in image 136 Getting a great background model isn't important for crosstalk correction, 137 since the crosstalk is at a low level. The median should be sufficient. 141 mi : `lsst.afw.image.MaskedImage` 142 MaskedImage for which to measure background. 143 badPixels : `list` of `str` 144 Mask planes to ignore. 149 Median background level. 153 stats.setAndMask(mask.getPlaneBitMask(badPixels))
157 def subtractCrosstalk(exposure, badPixels=["BAD"], minPixelToMask=45000, crosstalkStr="CROSSTALK"):
158 """Subtract the intra-CCD crosstalk from an exposure 160 We set the mask plane indicated by ``crosstalkStr`` in a target amplifier 161 for pixels in a source amplifier that exceed `minPixelToMask`. Note that 162 the correction is applied to all pixels in the amplifier, but only those 163 that have a substantial crosstalk are masked with ``crosstalkStr``. 165 The uncorrected image is used as a template for correction. This is good 166 enough if the crosstalk is small (e.g., coefficients < ~ 1e-3), but if it's 167 larger you may want to iterate. 171 exposure : `lsst.afw.image.Exposure` 172 Exposure for which to subtract crosstalk. 173 badPixels : `list` of `str` 174 Mask planes to ignore. 175 minPixelToMask : `float` 176 Minimum pixel value in source amplifier for which to set 177 ``crosstalkStr`` mask plane in target amplifier. 179 Mask plane name for pixels greatly modified by crosstalk. 181 mi = exposure.getMaskedImage()
184 ccd = exposure.getDetector()
186 coeffs = ccd.getCrosstalk()
187 assert coeffs.shape == (numAmps, numAmps)
190 crosstalkPlane = mask.addMaskPlane(crosstalkStr)
192 footprints.setMask(mask, crosstalkStr)
193 crosstalk = mask.getPlaneBitMask(crosstalkStr)
197 subtrahend = mi.Factory(mi.getBBox())
198 subtrahend.set((0, 0, 0))
199 for ii, iAmp
in enumerate(ccd):
200 iImage = subtrahend[iAmp.getRawDataBBox()]
201 for jj, jAmp
in enumerate(ccd):
203 assert coeffs[ii, jj] == 0.0
204 if coeffs[ii, jj] == 0.0:
207 jImage =
extractAmp(mi, jAmp, iAmp.getReadoutCorner())
208 jImage.getMask().getArray()[:] &= crosstalk
209 jImage -= backgrounds[jj]
211 iImage.scaledPlus(coeffs[ii, jj], jImage)
215 mask.clearMaskPlane(crosstalkPlane)
220 """Write a yaml file containing the crosstalk coefficients 222 The coeff array is indexed by [i, j] where i and j are amplifiers 223 corresponding to the amplifiers in det 227 outputFileName : `str` 228 Name of output yaml file 229 coeff : `numpy.array(namp, namp)` 230 numpy array of coefficients 231 det : `lsst.afw.cameraGeom.Detector` 232 Used to provide the list of amplifier names; 233 if None use ['0', '1', ...] 235 Name of CCD, used to index the yaml file 236 If all CCDs are identical could be the type (e.g. ITL) 238 Indent width to use when writing the yaml file 242 ampNames = [str(i)
for i
in range(coeff.shape[0])]
244 ampNames = [a.getName()
for a
in det]
246 assert coeff.shape == (len(ampNames), len(ampNames))
250 with open(outputFileName,
"w")
as fd:
251 print(indent*
" " +
"crosstalk :", file=fd)
253 print(indent*
" " +
"%s :" % crosstalkName, file=fd)
256 for i, ampNameI
in enumerate(ampNames):
257 print(indent*
" " +
"%s : {" % ampNameI, file=fd)
259 print(indent*
" ", file=fd, end=
'')
261 for j, ampNameJ
in enumerate(ampNames):
262 print(
"%s : %11.4e, " % (ampNameJ, coeff[i, j]), file=fd,
263 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)
def run(self, exposure, crosstalkSources=None)
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)