536 def run(self, exposure, ctiCalib, gains=None):
537 """Correct deferred charge/CTI issues.
541 exposure : `lsst.afw.image.Exposure`
542 Exposure to correct the deferred charge on.
543 ctiCalib : `lsst.ip.isr.DeferredChargeCalib`
544 Calibration object containing the charge transfer
546 gains : `dict` [`str`, `float`]
547 A dictionary, keyed by amplifier name, of the gains to
548 use. If gains is None, the nominal gains in the amplifier
553 exposure : `lsst.afw.image.Exposure`
554 The corrected exposure.
556 image = exposure.getMaskedImage().image
557 detector = exposure.getDetector()
563 if self.config.useGains:
565 gains = {amp.getName(): amp.getGain()
for amp
in detector.getAmplifiers()}
567 with gainContext(exposure, image, self.config.useGains, gains):
568 for amp
in detector.getAmplifiers():
569 ampName = amp.getName()
571 ampImage = image[amp.getRawBBox()]
572 if self.config.zeroUnusedPixels:
575 ampImage[amp.getRawParallelOverscanBBox()].array[:, :] = 0.0
576 ampImage[amp.getRawSerialPrescanBBox()].array[:, :] = 0.0
581 ampData = self.
flipData(ampImage.array, amp)
583 if ctiCalib.driftScale[ampName] > 0.0:
585 ctiCalib.driftScale[ampName],
586 ctiCalib.decayTime[ampName],
587 self.config.nPixelOffsetCorrection)
589 correctedAmpData = ampData.copy()
592 ctiCalib.serialTraps[ampName],
593 ctiCalib.globalCti[ampName],
594 self.config.nPixelTrapCorrection)
597 correctedAmpData = self.
flipData(correctedAmpData, amp)
598 image[amp.getRawBBox()].array[:, :] = correctedAmpData[:, :]
604 """Flip data array such that readout corner is at lower-left.
608 ampData : `numpy.ndarray`, (nx, ny)
610 amp : `lsst.afw.cameraGeom.Amplifier`
611 Amplifier to get readout corner information.
615 ampData : `numpy.ndarray`, (nx, ny)
618 X_FLIP = {ReadoutCorner.LL:
False,
619 ReadoutCorner.LR:
True,
620 ReadoutCorner.UL:
False,
621 ReadoutCorner.UR:
True}
622 Y_FLIP = {ReadoutCorner.LL:
False,
623 ReadoutCorner.LR:
False,
624 ReadoutCorner.UL:
True,
625 ReadoutCorner.UR:
True}
627 if X_FLIP[amp.getReadoutCorner()]:
628 ampData = np.fliplr(ampData)
629 if Y_FLIP[amp.getReadoutCorner()]:
630 ampData = np.flipud(ampData)
636 r"""Remove CTI effects from local offsets.
638 This implements equation 10 of Snyder+21. For an image with
639 CTI, s'(m, n), the correction factor is equal to the maximum
644 {A_L s'(m, n - j) exp(-j t / \tau_L)}_j=0^jmax
648 inputArr : `numpy.ndarray`, (nx, ny)
649 Input image data to correct.
650 drift_scale : `float`
651 Drift scale (Snyder+21 A_L value) to use in correction.
653 Decay time (Snyder+21 \tau_L) of the correction.
654 num_previous_pixels : `int`, optional
655 Number of previous pixels to use for correction. As the
656 CTI has an exponential decay, this essentially truncates
657 the correction where that decay scales the input charge to
662 outputArr : `numpy.ndarray`, (nx, ny)
663 Corrected image data.
665 r = np.exp(-1/decay_time)
666 Ny, Nx = inputArr.shape
669 offset = np.zeros((num_previous_pixels, Ny, Nx))
670 offset[0, :, :] = drift_scale*np.maximum(0, inputArr)
673 for n
in range(1, num_previous_pixels):
674 offset[n, :, n:] = drift_scale*np.maximum(0, inputArr[:, :-n])*(r**n)
676 Linv = np.amax(offset, axis=0)
677 outputArr = inputArr - Linv
683 r"""Apply localized trapping inverse operator to pixel signals.
685 This implements equation 13 of Snyder+21. For an image with
686 CTI, s'(m, n), the correction factor is equal to the maximum
691 {A_L s'(m, n - j) exp(-j t / \tau_L)}_j=0^jmax
695 inputArr : `numpy.ndarray`, (nx, ny)
696 Input image data to correct.
697 trap : `lsst.ip.isr.SerialTrap`
698 Serial trap describing the capture and release of charge.
700 Mean charge transfer inefficiency, b from Snyder+21.
701 num_previous_pixels : `int`, optional
702 Number of previous pixels to use for correction.
706 outputArr : `numpy.ndarray`, (nx, ny)
707 Corrected image data.
710 Ny, Nx = inputArr.shape
712 r = np.exp(-1/trap.emission_time)
715 trap_occupancy = np.zeros((num_previous_pixels, Ny, Nx))
716 for n
in range(num_previous_pixels):
717 trap_occupancy[n, :, n+1:] = trap.capture(np.maximum(0, inputArr))[:, :-(n+1)]*(r**n)
718 trap_occupancy = np.amax(trap_occupancy, axis=0)
721 C = trap.capture(np.maximum(0, inputArr)) - trap_occupancy*r
725 R = np.zeros(inputArr.shape)
726 R[:, 1:] = trap_occupancy[:, 1:]*(1-r)
729 outputArr = inputArr - a*T