543 def run(self, exposure, ctiCalib, gains=None):
544 """Correct deferred charge/CTI issues.
548 exposure : `lsst.afw.image.Exposure`
549 Exposure to correct the deferred charge on.
550 ctiCalib : `lsst.ip.isr.DeferredChargeCalib`
551 Calibration object containing the charge transfer
553 gains : `dict` [`str`, `float`]
554 A dictionary, keyed by amplifier name, of the gains to
555 use. If gains is None, the nominal gains in the amplifier
560 exposure : `lsst.afw.image.Exposure`
561 The corrected exposure.
563 image = exposure.getMaskedImage().image
564 detector = exposure.getDetector()
570 if self.config.useGains:
572 gains = {amp.getName(): amp.getGain()
for amp
in detector.getAmplifiers()}
574 with gainContext(exposure, image, self.config.useGains, gains):
575 for amp
in detector.getAmplifiers():
576 ampName = amp.getName()
578 ampImage = image[amp.getRawBBox()]
579 if self.config.zeroUnusedPixels:
582 ampImage[amp.getRawParallelOverscanBBox()].array[:, :] = 0.0
583 ampImage[amp.getRawSerialPrescanBBox()].array[:, :] = 0.0
588 ampData = self.
flipData(ampImage.array, amp)
590 if ctiCalib.driftScale[ampName] > 0.0:
592 ctiCalib.driftScale[ampName],
593 ctiCalib.decayTime[ampName],
594 self.config.nPixelOffsetCorrection)
596 correctedAmpData = ampData.copy()
599 ctiCalib.serialTraps[ampName],
600 ctiCalib.globalCti[ampName],
601 self.config.nPixelTrapCorrection)
604 correctedAmpData = self.
flipData(correctedAmpData, amp)
605 image[amp.getRawBBox()].array[:, :] = correctedAmpData[:, :]
611 """Flip data array such that readout corner is at lower-left.
615 ampData : `numpy.ndarray`, (nx, ny)
617 amp : `lsst.afw.cameraGeom.Amplifier`
618 Amplifier to get readout corner information.
622 ampData : `numpy.ndarray`, (nx, ny)
625 X_FLIP = {ReadoutCorner.LL:
False,
626 ReadoutCorner.LR:
True,
627 ReadoutCorner.UL:
False,
628 ReadoutCorner.UR:
True}
629 Y_FLIP = {ReadoutCorner.LL:
False,
630 ReadoutCorner.LR:
False,
631 ReadoutCorner.UL:
True,
632 ReadoutCorner.UR:
True}
634 if X_FLIP[amp.getReadoutCorner()]:
635 ampData = np.fliplr(ampData)
636 if Y_FLIP[amp.getReadoutCorner()]:
637 ampData = np.flipud(ampData)
643 r"""Remove CTI effects from local offsets.
645 This implements equation 10 of Snyder+21. For an image with
646 CTI, s'(m, n), the correction factor is equal to the maximum
651 {A_L s'(m, n - j) exp(-j t / \tau_L)}_j=0^jmax
655 inputArr : `numpy.ndarray`, (nx, ny)
656 Input image data to correct.
657 drift_scale : `float`
658 Drift scale (Snyder+21 A_L value) to use in correction.
660 Decay time (Snyder+21 \tau_L) of the correction.
661 num_previous_pixels : `int`, optional
662 Number of previous pixels to use for correction. As the
663 CTI has an exponential decay, this essentially truncates
664 the correction where that decay scales the input charge to
669 outputArr : `numpy.ndarray`, (nx, ny)
670 Corrected image data.
672 r = np.exp(-1/decay_time)
673 Ny, Nx = inputArr.shape
676 offset = np.zeros((num_previous_pixels, Ny, Nx))
677 offset[0, :, :] = drift_scale*np.maximum(0, inputArr)
680 for n
in range(1, num_previous_pixels):
681 offset[n, :, n:] = drift_scale*np.maximum(0, inputArr[:, :-n])*(r**n)
683 Linv = np.amax(offset, axis=0)
684 outputArr = inputArr - Linv
690 r"""Apply localized trapping inverse operator to pixel signals.
692 This implements equation 13 of Snyder+21. For an image with
693 CTI, s'(m, n), the correction factor is equal to the maximum
698 {A_L s'(m, n - j) exp(-j t / \tau_L)}_j=0^jmax
702 inputArr : `numpy.ndarray`, (nx, ny)
703 Input image data to correct.
704 trap : `lsst.ip.isr.SerialTrap`
705 Serial trap describing the capture and release of charge.
707 Mean charge transfer inefficiency, b from Snyder+21.
708 num_previous_pixels : `int`, optional
709 Number of previous pixels to use for correction.
713 outputArr : `numpy.ndarray`, (nx, ny)
714 Corrected image data.
717 Ny, Nx = inputArr.shape
719 r = np.exp(-1/trap.emission_time)
722 trap_occupancy = np.zeros((num_previous_pixels, Ny, Nx))
723 for n
in range(num_previous_pixels):
724 trap_occupancy[n, :, n+1:] = trap.capture(np.maximum(0, inputArr))[:, :-(n+1)]*(r**n)
725 trap_occupancy = np.amax(trap_occupancy, axis=0)
728 C = trap.capture(np.maximum(0, inputArr)) - trap_occupancy*r
732 R = np.zeros(inputArr.shape)
733 R[:, 1:] = trap_occupancy[:, 1:]*(1-r)
736 outputArr = inputArr - a*T