25 "attachTransmissionCurve",
27 "brighterFatterCorrection",
33 "fluxConservingBrighterFatterCorrection",
37 "illuminationCorrection",
38 "interpolateDefectList",
39 "interpolateFromMask",
41 "saturationCorrection",
44 "transposeMaskedImage",
45 "trimToMatchCalibBBox",
47 "widenSaturationTrails",
60from lsst.meas.algorithms.detection
import SourceDetectionTask
62from contextlib
import contextmanager
64from .defects
import Defects
68 """Make a double Gaussian PSF.
73 FWHM of double Gaussian smoothing kernel.
77 psf : `lsst.meas.algorithms.DoubleGaussianPsf`
78 The created smoothing kernel.
80 ksize = 4*int(fwhm) + 1
81 return measAlg.DoubleGaussianPsf(ksize, ksize, fwhm/(2*math.sqrt(2*math.log(2))))
85 """Make a transposed copy of a masked image.
89 maskedImage : `lsst.afw.image.MaskedImage`
94 transposed : `lsst.afw.image.MaskedImage`
95 The transposed copy of the input image.
97 transposed = maskedImage.Factory(
lsst.geom.Extent2I(maskedImage.getHeight(), maskedImage.getWidth()))
98 transposed.getImage().getArray()[:] = maskedImage.getImage().getArray().T
99 transposed.getMask().getArray()[:] = maskedImage.getMask().getArray().T
100 transposed.getVariance().getArray()[:] = maskedImage.getVariance().getArray().T
105 """Interpolate over defects specified in a defect list.
109 maskedImage : `lsst.afw.image.MaskedImage`
111 defectList : `lsst.meas.algorithms.Defects`
112 List of defects to interpolate over.
114 FWHM of double Gaussian smoothing kernel.
115 fallbackValue : scalar, optional
116 Fallback value if an interpolated value cannot be determined.
117 If None, then the clipped mean of the image is used.
121 The ``fwhm`` parameter is used to create a PSF, but the underlying
122 interpolation code (`lsst.meas.algorithms.interpolateOverDefects`) does
123 not currently make use of this information.
126 if fallbackValue
is None:
128 if 'INTRP' not in maskedImage.getMask().getMaskPlaneDict():
129 maskedImage.getMask().addMaskPlane(
'INTRP')
130 measAlg.interpolateOverDefects(maskedImage, psf, defectList, fallbackValue,
True)
135 """Mask pixels based on threshold detection.
139 maskedImage : `lsst.afw.image.MaskedImage`
140 Image to process. Only the mask plane is updated.
143 growFootprints : scalar, optional
144 Number of pixels to grow footprints of detected regions.
145 maskName : str, optional
146 Mask plane name, or list of names to convert
150 defectList : `lsst.meas.algorithms.Defects`
151 Defect list constructed from pixels above the threshold.
154 thresh = afwDetection.Threshold(threshold)
155 fs = afwDetection.FootprintSet(maskedImage, thresh)
157 if growFootprints > 0:
158 fs = afwDetection.FootprintSet(fs, rGrow=growFootprints, isotropic=
False)
159 fpList = fs.getFootprints()
162 mask = maskedImage.getMask()
163 bitmask = mask.getPlaneBitMask(maskName)
164 afwDetection.setMaskFromFootprintList(mask, fpList, bitmask)
166 return Defects.fromFootprintList(fpList)
169def growMasks(mask, radius=0, maskNameList=['BAD'], maskValue="BAD"):
170 """Grow a mask by an amount and add to the requested plane.
174 mask : `lsst.afw.image.Mask`
175 Mask image to process.
177 Amount to grow the mask.
178 maskNameList : `str` or `list` [`str`]
179 Mask names that should be grown.
181 Mask plane to assign the newly masked pixels to.
184 thresh = afwDetection.Threshold(mask.getPlaneBitMask(maskNameList), afwDetection.Threshold.BITMASK)
185 fpSet = afwDetection.FootprintSet(mask, thresh)
186 fpSet = afwDetection.FootprintSet(fpSet, rGrow=radius, isotropic=
False)
187 fpSet.setMask(mask, maskValue)
191 maskNameList=['SAT'], fallbackValue=None):
192 """Interpolate over defects identified by a particular set of mask planes.
196 maskedImage : `lsst.afw.image.MaskedImage`
199 FWHM of double Gaussian smoothing kernel.
200 growSaturatedFootprints : scalar, optional
201 Number of pixels to grow footprints for saturated pixels.
202 maskNameList : `List` of `str`, optional
204 fallbackValue : scalar, optional
205 Value of last resort for interpolation.
209 The ``fwhm`` parameter is used to create a PSF, but the underlying
210 interpolation code (`lsst.meas.algorithms.interpolateOverDefects`) does
211 not currently make use of this information.
213 mask = maskedImage.getMask()
215 if growSaturatedFootprints > 0
and "SAT" in maskNameList:
219 growMasks(mask, radius=growSaturatedFootprints, maskNameList=[
'SAT'], maskValue=
"SAT")
221 thresh = afwDetection.Threshold(mask.getPlaneBitMask(maskNameList), afwDetection.Threshold.BITMASK)
222 fpSet = afwDetection.FootprintSet(mask, thresh)
223 defectList = Defects.fromFootprintList(fpSet.getFootprints())
232 """Mark saturated pixels and optionally interpolate over them
236 maskedImage : `lsst.afw.image.MaskedImage`
239 Saturation level used as the detection threshold.
241 FWHM of double Gaussian smoothing kernel.
242 growFootprints : scalar, optional
243 Number of pixels to grow footprints of detected regions.
244 interpolate : Bool, optional
245 If True, saturated pixels are interpolated over.
246 maskName : str, optional
248 fallbackValue : scalar, optional
249 Value of last resort for interpolation.
253 The ``fwhm`` parameter is used to create a PSF, but the underlying
254 interpolation code (`lsst.meas.algorithms.interpolateOverDefects`) does
255 not currently make use of this information.
257 defectList = makeThresholdMask(
258 maskedImage=maskedImage,
259 threshold=saturation,
260 growFootprints=growFootprints,
270 """Compute number of edge trim pixels to match the calibration data.
272 Use the dimension difference between the raw exposure and the
273 calibration exposure to compute the edge trim pixels. This trim
274 is applied symmetrically, with the same number of pixels masked on
279 rawMaskedImage : `lsst.afw.image.MaskedImage`
281 calibMaskedImage : `lsst.afw.image.MaskedImage`
282 Calibration image to draw new bounding box from.
286 replacementMaskedImage : `lsst.afw.image.MaskedImage`
287 ``rawMaskedImage`` trimmed to the appropriate size.
292 Raised if ``rawMaskedImage`` cannot be symmetrically trimmed to
293 match ``calibMaskedImage``.
295 nx, ny = rawMaskedImage.getBBox().getDimensions() - calibMaskedImage.getBBox().getDimensions()
297 raise RuntimeError(
"Raw and calib maskedImages are trimmed differently in X and Y.")
299 raise RuntimeError(
"Calibration maskedImage is trimmed unevenly in X.")
301 raise RuntimeError(
"Calibration maskedImage is larger than raw data.")
305 replacementMaskedImage = rawMaskedImage[nEdge:-nEdge, nEdge:-nEdge, afwImage.LOCAL]
306 SourceDetectionTask.setEdgeBits(
308 replacementMaskedImage.getBBox(),
309 rawMaskedImage.getMask().getPlaneBitMask(
"EDGE")
312 replacementMaskedImage = rawMaskedImage
314 return replacementMaskedImage
318 """Apply bias correction in place.
322 maskedImage : `lsst.afw.image.MaskedImage`
323 Image to process. The image is modified by this method.
324 biasMaskedImage : `lsst.afw.image.MaskedImage`
325 Bias image of the same size as ``maskedImage``
326 trimToFit : `Bool`, optional
327 If True, raw data is symmetrically trimmed to match
333 Raised if ``maskedImage`` and ``biasMaskedImage`` do not have
340 if maskedImage.getBBox(afwImage.LOCAL) != biasMaskedImage.getBBox(afwImage.LOCAL):
341 raise RuntimeError(
"maskedImage bbox %s != biasMaskedImage bbox %s" %
342 (maskedImage.getBBox(afwImage.LOCAL), biasMaskedImage.getBBox(afwImage.LOCAL)))
343 maskedImage -= biasMaskedImage
346def darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False, trimToFit=False):
347 """Apply dark correction in place.
351 maskedImage : `lsst.afw.image.MaskedImage`
352 Image to process. The image is modified by this method.
353 darkMaskedImage : `lsst.afw.image.MaskedImage`
354 Dark image of the same size as ``maskedImage``.
356 Dark exposure time for ``maskedImage``.
358 Dark exposure time for ``darkMaskedImage``.
359 invert : `Bool`, optional
360 If True, re-add the dark to an already corrected image.
361 trimToFit : `Bool`, optional
362 If True, raw data is symmetrically trimmed to match
368 Raised if ``maskedImage`` and ``darkMaskedImage`` do not have
373 The dark correction is applied by calculating:
374 maskedImage -= dark * expScaling / darkScaling
379 if maskedImage.getBBox(afwImage.LOCAL) != darkMaskedImage.getBBox(afwImage.LOCAL):
380 raise RuntimeError(
"maskedImage bbox %s != darkMaskedImage bbox %s" %
381 (maskedImage.getBBox(afwImage.LOCAL), darkMaskedImage.getBBox(afwImage.LOCAL)))
383 scale = expScale / darkScale
385 maskedImage.scaledMinus(scale, darkMaskedImage)
387 maskedImage.scaledPlus(scale, darkMaskedImage)
391 """Set the variance plane based on the image plane.
395 maskedImage : `lsst.afw.image.MaskedImage`
396 Image to process. The variance plane is modified.
398 The amplifier gain in electrons/ADU.
400 The amplifier read nmoise in ADU/pixel.
402 var = maskedImage.getVariance()
403 var[:] = maskedImage.getImage()
408def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False, trimToFit=False):
409 """Apply flat correction in place.
413 maskedImage : `lsst.afw.image.MaskedImage`
414 Image to process. The image is modified.
415 flatMaskedImage : `lsst.afw.image.MaskedImage`
416 Flat image of the same size as ``maskedImage``
418 Flat scale computation method. Allowed values are 'MEAN',
420 userScale : scalar, optional
421 Scale to use if ``scalingType='USER'``.
422 invert : `Bool`, optional
423 If True, unflatten an already flattened image.
424 trimToFit : `Bool`, optional
425 If True, raw data is symmetrically trimmed to match
431 Raised if ``maskedImage`` and ``flatMaskedImage`` do not have
432 the same size or if ``scalingType`` is not an allowed value.
437 if maskedImage.getBBox(afwImage.LOCAL) != flatMaskedImage.getBBox(afwImage.LOCAL):
438 raise RuntimeError(
"maskedImage bbox %s != flatMaskedImage bbox %s" %
439 (maskedImage.getBBox(afwImage.LOCAL), flatMaskedImage.getBBox(afwImage.LOCAL)))
445 if scalingType
in (
'MEAN',
'MEDIAN'):
448 elif scalingType ==
'USER':
449 flatScale = userScale
451 raise RuntimeError(
'%s : %s not implemented' % (
"flatCorrection", scalingType))
454 maskedImage.scaledDivides(1.0/flatScale, flatMaskedImage)
456 maskedImage.scaledMultiplies(1.0/flatScale, flatMaskedImage)
460 """Apply illumination correction in place.
464 maskedImage : `lsst.afw.image.MaskedImage`
465 Image to process. The image is modified.
466 illumMaskedImage : `lsst.afw.image.MaskedImage`
467 Illumination correction image of the same size as ``maskedImage``.
469 Scale factor for the illumination correction.
470 trimToFit : `Bool`, optional
471 If True, raw data is symmetrically trimmed to match
477 Raised if ``maskedImage`` and ``illumMaskedImage`` do not have
483 if maskedImage.getBBox(afwImage.LOCAL) != illumMaskedImage.getBBox(afwImage.LOCAL):
484 raise RuntimeError(
"maskedImage bbox %s != illumMaskedImage bbox %s" %
485 (maskedImage.getBBox(afwImage.LOCAL), illumMaskedImage.getBBox(afwImage.LOCAL)))
487 maskedImage.scaledDivides(1.0/illumScale, illumMaskedImage)
491 """Apply brighter fatter correction in place for the image.
495 exposure : `lsst.afw.image.Exposure`
496 Exposure to have brighter-fatter correction applied. Modified
498 kernel : `numpy.ndarray`
499 Brighter-fatter kernel to apply.
501 Number of correction iterations to run.
503 Convergence threshold in terms of the sum of absolute
504 deviations between an iteration and the previous one.
506 If True, then the exposure values are scaled by the gain prior
508 gains : `dict` [`str`, `float`]
509 A dictionary, keyed by amplifier name, of the gains to use.
510 If gains is None, the nominal gains in the amplifier object are used.
515 Final difference between iterations achieved in correction.
517 Number of iterations used to calculate correction.
521 This correction takes a kernel that has been derived from flat
522 field images to redistribute the charge. The gradient of the
523 kernel is the deflection field due to the accumulated charge.
525 Given the original image I(x) and the kernel K(x) we can compute
526 the corrected image Ic(x) using the following equation:
528 Ic(x) = I(x) + 0.5*d/dx(I(x)*d/dx(int( dy*K(x-y)*I(y))))
530 To evaluate the derivative term we expand it as follows:
532 0.5 * ( d/dx(I(x))*d/dx(int(dy*K(x-y)*I(y)))
533 + I(x)*d^2/dx^2(int(dy* K(x-y)*I(y))) )
535 Because we use the measured counts instead of the incident counts
536 we apply the correction iteratively to reconstruct the original
537 counts and the correction. We stop iterating when the summed
538 difference between the current corrected image and the one from
539 the previous iteration is below the threshold. We do not require
540 convergence because the number of iterations is too large a
541 computational cost. How we define the threshold still needs to be
542 evaluated, the current default was shown to work reasonably well
543 on a small set of images. For more information on the method see
544 DocuShare Document-19407.
546 The edges as defined by the kernel are not corrected because they
547 have spurious values due to the convolution.
549 image = exposure.getMaskedImage().getImage()
552 with gainContext(exposure, image, applyGain, gains):
554 kLx = numpy.shape(kernel)[0]
555 kLy = numpy.shape(kernel)[1]
556 kernelImage = afwImage.ImageD(kLx, kLy)
557 kernelImage.getArray()[:, :] = kernel
558 tempImage = image.clone()
560 nanIndex = numpy.isnan(tempImage.getArray())
561 tempImage.getArray()[nanIndex] = 0.
563 outImage = afwImage.ImageF(image.getDimensions())
564 corr = numpy.zeros_like(image.getArray())
565 prev_image = numpy.zeros_like(image.getArray())
579 for iteration
in range(maxIter):
582 tmpArray = tempImage.getArray()
583 outArray = outImage.getArray()
585 with numpy.errstate(invalid=
"ignore", over=
"ignore"):
587 gradTmp = numpy.gradient(tmpArray[startY:endY, startX:endX])
588 gradOut = numpy.gradient(outArray[startY:endY, startX:endX])
589 first = (gradTmp[0]*gradOut[0] + gradTmp[1]*gradOut[1])[1:-1, 1:-1]
592 diffOut20 = numpy.diff(outArray, 2, 0)[startY:endY, startX + 1:endX - 1]
593 diffOut21 = numpy.diff(outArray, 2, 1)[startY + 1:endY - 1, startX:endX]
594 second = tmpArray[startY + 1:endY - 1, startX + 1:endX - 1]*(diffOut20 + diffOut21)
596 corr[startY + 1:endY - 1, startX + 1:endX - 1] = 0.5*(first + second)
598 tmpArray[:, :] = image.getArray()[:, :]
599 tmpArray[nanIndex] = 0.
600 tmpArray[startY:endY, startX:endX] += corr[startY:endY, startX:endX]
603 diff = numpy.sum(numpy.abs(prev_image - tmpArray))
607 prev_image[:, :] = tmpArray[:, :]
609 image.getArray()[startY + 1:endY - 1, startX + 1:endX - 1] += \
610 corr[startY + 1:endY - 1, startX + 1:endX - 1]
612 return diff, iteration
616 """Take the input convolved deflection potential and the flux array
617 to compute and apply the flux transfer into the correction array.
622 Deflection potential, being the convolution of the flux F with the
625 The array of flux values which act as the source of the flux transfer.
626 correctionMode: `bool`
627 Defines if applying correction (True) or generating sims (False).
635 if cFunc.shape != fStep.shape:
636 raise RuntimeError(f
'transferFlux: array shapes do not match: {cFunc.shape}, {fStep.shape}')
648 corr = numpy.zeros_like(cFunc)
651 yDim, xDim = cFunc.shape
652 y = numpy.arange(yDim, dtype=int)
653 x = numpy.arange(xDim, dtype=int)
654 xc, yc = numpy.meshgrid(x, y)
660 diff = numpy.diff(cFunc, axis=ax)
663 gx = numpy.zeros_like(cFunc)
664 yDiff, xDiff = diff.shape
665 gx[:yDiff, :xDiff] += diff
670 for i, sel
in enumerate([gx > 0, gx < 0]):
686 flux = factor * fStep[sel]*gx[sel]
689 flux = factor * fStep[yPix, xPix]*gx[sel]
693 corr[yPix, xPix] += flux
700 gains=None, correctionMode=True):
701 """Apply brighter fatter correction in place for the image.
703 This version presents a modified version of the algorithm
704 found in ``lsst.ip.isr.isrFunctions.brighterFatterCorrection``
705 which conserves the image flux, resulting in improved
706 correction of the cores of stars. The convolution has also been
707 modified to mitigate edge effects.
711 exposure : `lsst.afw.image.Exposure`
712 Exposure to have brighter-fatter correction applied. Modified
714 kernel : `numpy.ndarray`
715 Brighter-fatter kernel to apply.
717 Number of correction iterations to run.
719 Convergence threshold in terms of the sum of absolute
720 deviations between an iteration and the previous one.
722 If True, then the exposure values are scaled by the gain prior
724 gains : `dict` [`str`, `float`]
725 A dictionary, keyed by amplifier name, of the gains to use.
726 If gains is None, the nominal gains in the amplifier object are used.
727 correctionMode : `Bool`
728 If True (default) the function applies correction for BFE. If False,
729 the code can instead be used to generate a simulation of BFE (sign
730 change in the direction of the effect)
735 Final difference between iterations achieved in correction.
737 Number of iterations used to calculate correction.
741 Modified version of ``lsst.ip.isr.isrFunctions.brighterFatterCorrection``.
743 This correction takes a kernel that has been derived from flat
744 field images to redistribute the charge. The gradient of the
745 kernel is the deflection field due to the accumulated charge.
747 Given the original image I(x) and the kernel K(x) we can compute
748 the corrected image Ic(x) using the following equation:
750 Ic(x) = I(x) + 0.5*d/dx(I(x)*d/dx(int( dy*K(x-y)*I(y))))
752 Improved algorithm at this step applies the divergence theorem to
753 obtain a pixelised correction.
755 Because we use the measured counts instead of the incident counts
756 we apply the correction iteratively to reconstruct the original
757 counts and the correction. We stop iterating when the summed
758 difference between the current corrected image and the one from
759 the previous iteration is below the threshold. We do not require
760 convergence because the number of iterations is too large a
761 computational cost. How we define the threshold still needs to be
762 evaluated, the current default was shown to work reasonably well
763 on a small set of images.
765 Edges are handled in the convolution by padding. This is still not
766 a physical model for the edge, but avoids discontinuity in the correction.
768 Author of modified version: Lance.Miller@physics.ox.ac.uk
771 image = exposure.getMaskedImage().getImage()
774 with gainContext(exposure, image, applyGain, gains):
777 kLy, kLx = kernel.shape
778 kernelImage = afwImage.ImageD(kLx, kLy)
779 kernelImage.getArray()[:, :] = kernel
780 tempImage = image.clone()
782 nanIndex = numpy.isnan(tempImage.getArray())
783 tempImage.getArray()[nanIndex] = 0.
785 outImage = afwImage.ImageF(image.getDimensions())
786 corr = numpy.zeros_like(image.getArray())
787 prevImage = numpy.zeros_like(image.getArray())
793 kLy = 2 * ((1+kLy)//2)
794 kLx = 2 * ((1+kLx)//2)
801 imYdimension, imXdimension = tempImage.array.shape
802 imean = numpy.mean(tempImage.getArray()[~nanIndex])
805 tempImage.array[nanIndex] = 0.
806 padArray = numpy.pad(tempImage.getArray(), ((0, kLy), (0, kLx)))
807 outImage = afwImage.ImageF(numpy.pad(outImage.getArray(), ((0, kLy), (0, kLx))))
809 padImage = afwImage.ImageF(padArray.shape[1], padArray.shape[0])
810 padImage.array[:] = padArray
812 for iteration
in range(maxIter):
817 tmpArray = tempImage.getArray()
818 outArray = outImage.getArray()
821 outArray = outArray[:imYdimension, :imXdimension]
824 corr[...] =
transferFlux(outArray, tmpArray, correctionMode=correctionMode)
827 tmpArray[:, :] = image.getArray()[:, :]
829 tmpArray[nanIndex] = 0.
833 tempImage.array[nanIndex] = 0.
834 padArray = numpy.pad(tempImage.getArray(), ((0, kLy), (0, kLx)))
837 diff = numpy.sum(numpy.abs(prevImage - tmpArray))
841 prevImage[:, :] = tmpArray[:, :]
843 image.getArray()[:] += corr[:]
845 return diff, iteration
850 """Context manager that applies and removes gain.
854 exp : `lsst.afw.image.Exposure`
855 Exposure to apply/remove gain.
856 image : `lsst.afw.image.Image`
857 Image to apply/remove gain.
859 If True, apply and remove the amplifier gain.
860 gains : `dict` [`str`, `float`]
861 A dictionary, keyed by amplifier name, of the gains to use.
862 If gains is None, the nominal gains in the amplifier object are used.
866 exp : `lsst.afw.image.Exposure`
867 Exposure with the gain applied.
871 if gains
and apply
is True:
872 ampNames = [amp.getName()
for amp
in exp.getDetector()]
873 for ampName
in ampNames:
874 if ampName
not in gains.keys():
875 raise RuntimeError(f
"Gains provided to gain context, but no entry found for amp {ampName}")
878 ccd = exp.getDetector()
880 sim = image.Factory(image, amp.getBBox())
882 gain = gains[amp.getName()]
891 ccd = exp.getDetector()
893 sim = image.Factory(image, amp.getBBox())
895 gain = gains[amp.getName()]
902 sensorTransmission=None, atmosphereTransmission=None):
903 """Attach a TransmissionCurve to an Exposure, given separate curves for
904 different components.
908 exposure : `lsst.afw.image.Exposure`
909 Exposure object to modify by attaching the product of all given
910 ``TransmissionCurves`` in post-assembly trimmed detector coordinates.
911 Must have a valid ``Detector`` attached that matches the detector
912 associated with sensorTransmission.
913 opticsTransmission : `lsst.afw.image.TransmissionCurve`
914 A ``TransmissionCurve`` that represents the throughput of the optics,
915 to be evaluated in focal-plane coordinates.
916 filterTransmission : `lsst.afw.image.TransmissionCurve`
917 A ``TransmissionCurve`` that represents the throughput of the filter
918 itself, to be evaluated in focal-plane coordinates.
919 sensorTransmission : `lsst.afw.image.TransmissionCurve`
920 A ``TransmissionCurve`` that represents the throughput of the sensor
921 itself, to be evaluated in post-assembly trimmed detector coordinates.
922 atmosphereTransmission : `lsst.afw.image.TransmissionCurve`
923 A ``TransmissionCurve`` that represents the throughput of the
924 atmosphere, assumed to be spatially constant.
928 combined : `lsst.afw.image.TransmissionCurve`
929 The TransmissionCurve attached to the exposure.
933 All ``TransmissionCurve`` arguments are optional; if none are provided, the
934 attached ``TransmissionCurve`` will have unit transmission everywhere.
936 combined = afwImage.TransmissionCurve.makeIdentity()
937 if atmosphereTransmission
is not None:
938 combined *= atmosphereTransmission
939 if opticsTransmission
is not None:
940 combined *= opticsTransmission
941 if filterTransmission
is not None:
942 combined *= filterTransmission
943 detector = exposure.getDetector()
944 fpToPix = detector.getTransform(fromSys=camGeom.FOCAL_PLANE,
945 toSys=camGeom.PIXELS)
946 combined = combined.transformedBy(fpToPix)
947 if sensorTransmission
is not None:
948 combined *= sensorTransmission
949 exposure.getInfo().setTransmissionCurve(combined)
953def applyGains(exposure, normalizeGains=False, ptcGains=None):
954 """Scale an exposure by the amplifier gains.
958 exposure : `lsst.afw.image.Exposure`
959 Exposure to process. The image is modified.
960 normalizeGains : `Bool`, optional
961 If True, then amplifiers are scaled to force the median of
962 each amplifier to equal the median of those medians.
963 ptcGains : `dict`[`str`], optional
964 Dictionary keyed by amp name containing the PTC gains.
966 ccd = exposure.getDetector()
967 ccdImage = exposure.getMaskedImage()
971 sim = ccdImage.Factory(ccdImage, amp.getBBox())
973 sim *= ptcGains[amp.getName()]
978 medians.append(numpy.median(sim.getImage().getArray()))
981 median = numpy.median(numpy.array(medians))
982 for index, amp
in enumerate(ccd):
983 sim = ccdImage.Factory(ccdImage, amp.getBBox())
984 if medians[index] != 0.0:
985 sim *= median/medians[index]
989 """Grow the saturation trails by an amount dependent on the width of the
994 mask : `lsst.afw.image.Mask`
995 Mask which will have the saturated areas grown.
999 for i
in range(1, 6):
1000 extraGrowDict[i] = 0
1001 for i
in range(6, 8):
1002 extraGrowDict[i] = 1
1003 for i
in range(8, 10):
1004 extraGrowDict[i] = 3
1007 if extraGrowMax <= 0:
1010 saturatedBit = mask.getPlaneBitMask(
"SAT")
1012 xmin, ymin = mask.getBBox().getMin()
1013 width = mask.getWidth()
1015 thresh = afwDetection.Threshold(saturatedBit, afwDetection.Threshold.BITMASK)
1016 fpList = afwDetection.FootprintSet(mask, thresh).getFootprints()
1019 for s
in fp.getSpans():
1020 x0, x1 = s.getX0(), s.getX1()
1022 extraGrow = extraGrowDict.get(x1 - x0 + 1, extraGrowMax)
1025 x0 -= xmin + extraGrow
1026 x1 -= xmin - extraGrow
1033 mask.array[y, x0:x1+1] |= saturatedBit
1037 """Set all BAD areas of the chip to the average of the rest of the exposure
1041 exposure : `lsst.afw.image.Exposure`
1042 Exposure to mask. The exposure mask is modified.
1043 badStatistic : `str`, optional
1044 Statistic to use to generate the replacement value from the
1045 image data. Allowed values are 'MEDIAN' or 'MEANCLIP'.
1049 badPixelCount : scalar
1050 Number of bad pixels masked.
1051 badPixelValue : scalar
1052 Value substituted for bad pixels.
1057 Raised if `badStatistic` is not an allowed value.
1059 if badStatistic ==
"MEDIAN":
1060 statistic = afwMath.MEDIAN
1061 elif badStatistic ==
"MEANCLIP":
1062 statistic = afwMath.MEANCLIP
1064 raise RuntimeError(
"Impossible method %s of bad region correction" % badStatistic)
1066 mi = exposure.getMaskedImage()
1068 BAD = mask.getPlaneBitMask(
"BAD")
1069 INTRP = mask.getPlaneBitMask(
"INTRP")
1072 sctrl.setAndMask(BAD)
1075 maskArray = mask.getArray()
1076 imageArray = mi.getImage().getArray()
1077 badPixels = numpy.logical_and((maskArray & BAD) > 0, (maskArray & INTRP) == 0)
1078 imageArray[:] = numpy.where(badPixels, value, imageArray)
1080 return badPixels.sum(), value
1084 """Check to see if an exposure is in a filter specified by a list.
1086 The goal of this is to provide a unified filter checking interface
1087 for all filter dependent stages.
1091 exposure : `lsst.afw.image.Exposure`
1092 Exposure to examine.
1093 filterList : `list` [`str`]
1094 List of physical_filter names to check.
1095 log : `logging.Logger`
1096 Logger to handle messages.
1101 True if the exposure's filter is contained in the list.
1103 if len(filterList) == 0:
1105 thisFilter = exposure.getFilter()
1106 if thisFilter
is None:
1107 log.warning(
"No FilterLabel attached to this exposure!")
1111 if thisPhysicalFilter
in filterList:
1113 elif thisFilter.bandLabel
in filterList:
1115 log.warning(
"Physical filter (%s) should be used instead of band %s for filter configurations"
1116 " (%s)", thisPhysicalFilter, thisFilter.bandLabel, filterList)
1123 """Get the physical filter label associated with the given filterLabel.
1125 If ``filterLabel`` is `None` or there is no physicalLabel attribute
1126 associated with the given ``filterLabel``, the returned label will be
1131 filterLabel : `lsst.afw.image.FilterLabel`
1132 The `lsst.afw.image.FilterLabel` object from which to derive the
1133 physical filter label.
1134 log : `logging.Logger`
1135 Logger to handle messages.
1139 physicalFilter : `str`
1140 The value returned by the physicalLabel attribute of ``filterLabel`` if
1141 it exists, otherwise set to \"Unknown\".
1143 if filterLabel
is None:
1144 physicalFilter =
"Unknown"
1145 log.warning(
"filterLabel is None. Setting physicalFilter to \"Unknown\".")
1148 physicalFilter = filterLabel.physicalLabel
1149 except RuntimeError:
1150 log.warning(
"filterLabel has no physicalLabel attribute. Setting physicalFilter to \"Unknown\".")
1151 physicalFilter =
"Unknown"
1152 return physicalFilter
1156 """Count the number of pixels in a given mask plane.
1160 maskedIm : `~lsst.afw.image.MaskedImage`
1161 Masked image to examine.
1163 Name of the mask plane to examine.
1168 Number of pixels in the requested mask plane.
1170 maskBit = maskedIm.mask.getPlaneBitMask(maskPlane)
1171 nPix = numpy.where(numpy.bitwise_and(maskedIm.mask.array, maskBit))[0].flatten().size
Parameters to control convolution.
A kernel created from an Image.
Pass parameters to a Statistics object.
Statistics makeStatistics(lsst::afw::image::Image< Pixel > const &img, lsst::afw::image::Mask< image::MaskPixel > const &msk, int const flags, StatisticsControl const &sctrl=StatisticsControl())
Handle a watered-down front-end to the constructor (no variance)
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.
Property stringToStatisticsProperty(std::string const property)
Conversion function to switch a string to a Property (see Statistics.h)
setBadRegions(exposure, badStatistic="MEDIAN")
countMaskedPixels(maskedIm, maskPlane)
attachTransmissionCurve(exposure, opticsTransmission=None, filterTransmission=None, sensorTransmission=None, atmosphereTransmission=None)
updateVariance(maskedImage, gain, readNoise)
applyGains(exposure, normalizeGains=False, ptcGains=None)
flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False, trimToFit=False)
interpolateFromMask(maskedImage, fwhm, growSaturatedFootprints=1, maskNameList=['SAT'], fallbackValue=None)
illuminationCorrection(maskedImage, illumMaskedImage, illumScale, trimToFit=True)
growMasks(mask, radius=0, maskNameList=['BAD'], maskValue="BAD")
gainContext(exp, image, apply, gains=None)
widenSaturationTrails(mask)
transferFlux(cFunc, fStep, correctionMode=True)
biasCorrection(maskedImage, biasMaskedImage, trimToFit=False)
checkFilter(exposure, filterList, log)
darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False, trimToFit=False)
fluxConservingBrighterFatterCorrection(exposure, kernel, maxIter, threshold, applyGain, gains=None, correctionMode=True)
brighterFatterCorrection(exposure, kernel, maxIter, threshold, applyGain, gains=None)
interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=None)
makeThresholdMask(maskedImage, threshold, growFootprints=1, maskName='SAT')
transposeMaskedImage(maskedImage)
getPhysicalFilter(filterLabel, log)
saturationCorrection(maskedImage, saturation, fwhm, growFootprints=1, interpolate=True, maskName='SAT', fallbackValue=None)
trimToMatchCalibBBox(rawMaskedImage, calibMaskedImage)