800 gains=None, correctionMode=True):
801 """Apply brighter fatter correction in place for the image.
802
803 This version presents a modified version of the algorithm
804 found in ``lsst.ip.isr.isrFunctions.brighterFatterCorrection``
805 which conserves the image flux, resulting in improved
806 correction of the cores of stars. The convolution has also been
807 modified to mitigate edge effects.
808
809 Parameters
810 ----------
811 exposure : `lsst.afw.image.Exposure`
812 Exposure to have brighter-fatter correction applied. Modified
813 by this method.
814 kernel : `np.ndarray`
815 Brighter-fatter kernel to apply.
816 maxIter : scalar
817 Number of correction iterations to run.
818 threshold : scalar
819 Convergence threshold in terms of the sum of absolute
820 deviations between an iteration and the previous one.
821 applyGain : `Bool`
822 If True, then the exposure values are scaled by the gain prior
823 to correction.
824 gains : `dict` [`str`, `float`]
825 A dictionary, keyed by amplifier name, of the gains to use.
826 If gains is None, the nominal gains in the amplifier object are used.
827 correctionMode : `Bool`
828 If True (default) the function applies correction for BFE. If False,
829 the code can instead be used to generate a simulation of BFE (sign
830 change in the direction of the effect)
831
832 Returns
833 -------
834 diff : `float`
835 Final difference between iterations achieved in correction.
836 iteration : `int`
837 Number of iterations used to calculate correction.
838
839 Notes
840 -----
841 Modified version of ``lsst.ip.isr.isrFunctions.brighterFatterCorrection``.
842
843 This correction takes a kernel that has been derived from flat
844 field images to redistribute the charge. The gradient of the
845 kernel is the deflection field due to the accumulated charge.
846
847 Given the original image I(x) and the kernel K(x) we can compute
848 the corrected image Ic(x) using the following equation:
849
850 Ic(x) = I(x) + 0.5*d/dx(I(x)*d/dx(int( dy*K(x-y)*I(y))))
851
852 Improved algorithm at this step applies the divergence theorem to
853 obtain a pixelised correction.
854
855 Because we use the measured counts instead of the incident counts
856 we apply the correction iteratively to reconstruct the original
857 counts and the correction. We stop iterating when the summed
858 difference between the current corrected image and the one from
859 the previous iteration is below the threshold. We do not require
860 convergence because the number of iterations is too large a
861 computational cost. How we define the threshold still needs to be
862 evaluated, the current default was shown to work reasonably well
863 on a small set of images.
864
865 Edges are handled in the convolution by padding. This is still not
866 a physical model for the edge, but avoids discontinuity in the correction.
867
868 Author of modified version: Lance.Miller@physics.ox.ac.uk
869 (see DM-38555).
870 """
871 image = exposure.getMaskedImage().getImage()
872
873
874 with gainContext(exposure, image, applyGain, gains):
875
876
877 kLy, kLx = kernel.shape
878 kernelImage = afwImage.ImageD(kLx, kLy)
879 kernelImage.getArray()[:, :] = kernel
880 tempImage = afwImage.ImageD(image, deep=True)
881
882 nanIndex = np.isnan(tempImage.getArray())
883 tempImage.getArray()[nanIndex] = 0.
884
885 outImage = afwImage.ImageD(image.getDimensions())
886 corr = np.zeros(image.array.shape, dtype=np.float64)
887 prevImage = np.zeros(image.array.shape, dtype=np.float64)
890
891
892
893 kLy = 2 * ((1+kLy)//2)
894 kLx = 2 * ((1+kLx)//2)
895
896
897
898
899
900
901 imYdimension, imXdimension = tempImage.array.shape
902 imean = np.mean(tempImage.getArray()[~nanIndex], dtype=np.float64)
903
904 tempImage -= imean
905 tempImage.array[nanIndex] = 0.0
906 padArray = np.pad(tempImage.getArray(), ((0, kLy), (0, kLx)))
907 outImage = afwImage.ImageD(np.pad(outImage.getArray(), ((0, kLy), (0, kLx))))
908
909 padImage = afwImage.ImageD(padArray.shape[1], padArray.shape[0])
910 padImage.array[:] = padArray
911
912 for iteration in range(maxIter):
913
914
915
917 tmpArray = tempImage.getArray()
918 outArray = outImage.getArray()
919
920
921 outArray = outArray[:imYdimension, :imXdimension]
922
923
924 corr[...] = transferFlux(outArray, tmpArray, correctionMode=correctionMode)
925
926
927 tmpArray[:, :] = image.getArray()[:, :]
928 tmpArray += corr
929 tmpArray[nanIndex] = 0.
930
931
932 tmpArray -= imean
933 tempImage.array[nanIndex] = 0.
934 padArray = np.pad(tempImage.getArray(), ((0, kLy), (0, kLx)))
935
936 if iteration > 0:
937 diff = np.sum(np.abs(prevImage - tmpArray), dtype=np.float64)
938
939 if diff < threshold:
940 break
941 prevImage[:, :] = tmpArray[:, :]
942
943 image.getArray()[:] += corr[:]
944
945 return diff, iteration
Parameters to control convolution.
A kernel created from an Image.
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, ConvolutionControl const &convolutionControl=ConvolutionControl())
Convolve an Image or MaskedImage with a Kernel, setting pixels of an existing output image.