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. 82 Raised if called for a detector that does not have a 85 detector = exposure.getDetector()
86 if not detector.hasCrosstalk():
87 raise RuntimeError(
"Attempted to correct crosstalk without crosstalk coefficients")
88 self.log.info(
"Applying crosstalk correction")
90 crosstalkStr=self.config.crosstalkMaskPlane)
95 X_FLIP = {lsst.afw.table.LL:
False, lsst.afw.table.LR:
True,
96 lsst.afw.table.UL:
False, lsst.afw.table.UR:
True}
97 Y_FLIP = {lsst.afw.table.LL:
False, lsst.afw.table.LR:
False,
98 lsst.afw.table.UL:
True, lsst.afw.table.UR:
True}
102 def run(self, exposure, crosstalkSources=None):
103 self.log.info(
"Not performing any crosstalk correction")
107 """Return an image of the amp 109 The returned image will have the amp's readout corner in the 114 image : `lsst.afw.image.Image` or `lsst.afw.image.MaskedImage` 115 Image containing the amplifier of interest. 116 amp : `lsst.afw.table.AmpInfoRecord` 117 Amplifier information. 118 corner : `lsst.afw.table.ReadoutCorner` or `None` 119 Corner in which to put the amp's readout corner, or `None` for 122 The image is already trimmed. 123 This should no longer be needed once DM-15409 is resolved. 127 output : `lsst.afw.image.Image` 128 Image of the amplifier in the standard configuration. 130 output = image[amp.getBBox()
if isTrimmed
else amp.getRawDataBBox()]
131 ampCorner = amp.getReadoutCorner()
133 xFlip = X_FLIP[corner] ^ X_FLIP[ampCorner]
134 yFlip = Y_FLIP[corner] ^ Y_FLIP[ampCorner]
139 """Calculate median background in image 141 Getting a great background model isn't important for crosstalk correction, 142 since the crosstalk is at a low level. The median should be sufficient. 146 mi : `lsst.afw.image.MaskedImage` 147 MaskedImage for which to measure background. 148 badPixels : `list` of `str` 149 Mask planes to ignore. 154 Median background level. 158 stats.setAndMask(mask.getPlaneBitMask(badPixels))
162 def subtractCrosstalk(exposure, badPixels=["BAD"], minPixelToMask=45000, crosstalkStr="CROSSTALK"):
163 """Subtract the intra-CCD crosstalk from an exposure 165 We set the mask plane indicated by ``crosstalkStr`` in a target amplifier 166 for pixels in a source amplifier that exceed `minPixelToMask`. Note that 167 the correction is applied to all pixels in the amplifier, but only those 168 that have a substantial crosstalk are masked with ``crosstalkStr``. 170 The uncorrected image is used as a template for correction. This is good 171 enough if the crosstalk is small (e.g., coefficients < ~ 1e-3), but if it's 172 larger you may want to iterate. 176 exposure : `lsst.afw.image.Exposure` 177 Exposure for which to subtract crosstalk. 178 badPixels : `list` of `str` 179 Mask planes to ignore. 180 minPixelToMask : `float` 181 Minimum pixel value in source amplifier for which to set 182 ``crosstalkStr`` mask plane in target amplifier. 184 Mask plane name for pixels greatly modified by crosstalk. 186 mi = exposure.getMaskedImage()
189 ccd = exposure.getDetector()
191 coeffs = ccd.getCrosstalk()
192 assert coeffs.shape == (numAmps, numAmps)
195 crosstalkPlane = mask.addMaskPlane(crosstalkStr)
197 footprints.setMask(mask, crosstalkStr)
198 crosstalk = mask.getPlaneBitMask(crosstalkStr)
202 subtrahend = mi.Factory(mi.getBBox())
203 subtrahend.set((0, 0, 0))
204 for ii, iAmp
in enumerate(ccd):
205 iImage = subtrahend[iAmp.getRawDataBBox()]
206 for jj, jAmp
in enumerate(ccd):
208 assert coeffs[ii, jj] == 0.0
209 if coeffs[ii, jj] == 0.0:
212 jImage =
extractAmp(mi, jAmp, iAmp.getReadoutCorner())
213 jImage.getMask().getArray()[:] &= crosstalk
214 jImage -= backgrounds[jj]
216 iImage.scaledPlus(coeffs[ii, jj], jImage)
220 mask.clearMaskPlane(crosstalkPlane)
225 """Write a yaml file containing the crosstalk coefficients 227 The coeff array is indexed by [i, j] where i and j are amplifiers 228 corresponding to the amplifiers in det 232 outputFileName : `str` 233 Name of output yaml file 234 coeff : `numpy.array(namp, namp)` 235 numpy array of coefficients 236 det : `lsst.afw.cameraGeom.Detector` 237 Used to provide the list of amplifier names; 238 if None use ['0', '1', ...] 240 Name of CCD, used to index the yaml file 241 If all CCDs are identical could be the type (e.g. ITL) 243 Indent width to use when writing the yaml file 247 ampNames = [str(i)
for i
in range(coeff.shape[0])]
249 ampNames = [a.getName()
for a
in det]
251 assert coeff.shape == (len(ampNames), len(ampNames))
255 with open(outputFileName,
"w")
as fd:
256 print(indent*
" " +
"crosstalk :", file=fd)
258 print(indent*
" " +
"%s :" % crosstalkName, file=fd)
261 for i, ampNameI
in enumerate(ampNames):
262 print(indent*
" " +
"%s : {" % ampNameI, file=fd)
264 print(indent*
" ", file=fd, end=
'')
266 for j, ampNameJ
in enumerate(ampNames):
267 print(
"%s : %11.4e, " % (ampNameJ, coeff[i, j]), file=fd,
268 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)