25 "attachTransmissionCurve",
27 "brighterFatterCorrection",
33 "fluxConservingBrighterFatterCorrection",
37 "illuminationCorrection",
38 "interpolateDefectList",
39 "interpolateFromMask",
41 "saturationCorrection",
44 "transposeMaskedImage",
45 "trimToMatchCalibBBox",
47 "widenSaturationTrails",
62from contextlib
import contextmanager
64from .defects
import Defects
68 """Make a double Gaussian PSF.
73 FWHM of double Gaussian smoothing kernel.
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.
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.
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.
120 if fallbackValue
is None:
121 fallbackValue = afwMath.makeStatistics(maskedImage.getImage(), afwMath.MEANCLIP).getValue()
122 if 'INTRP' not in maskedImage.getMask().getMaskPlaneDict():
123 maskedImage.getMask().addMaskPlane(
'INTRP')
124 measAlg.interpolateOverDefects(maskedImage, psf, defectList, fallbackValue,
True)
128def makeThresholdMask(maskedImage, threshold, growFootprints=1, maskName='SAT'):
129 """Mask pixels based on threshold detection.
134 Image to process. Only the mask plane is updated.
137 growFootprints : scalar, optional
138 Number of pixels to grow footprints of detected regions.
139 maskName : str, optional
140 Mask plane name,
or list of names to convert
144 defectList : `lsst.meas.algorithms.Defects`
145 Defect list constructed
from pixels above the threshold.
148 thresh = afwDetection.Threshold(threshold)
149 fs = afwDetection.FootprintSet(maskedImage, thresh)
151 if growFootprints > 0:
152 fs = afwDetection.FootprintSet(fs, rGrow=growFootprints, isotropic=
False)
153 fpList = fs.getFootprints()
156 mask = maskedImage.getMask()
157 bitmask = mask.getPlaneBitMask(maskName)
158 afwDetection.setMaskFromFootprintList(mask, fpList, bitmask)
160 return Defects.fromFootprintList(fpList)
163def growMasks(mask, radius=0, maskNameList=['BAD'], maskValue="BAD"):
164 """Grow a mask by an amount and add to the requested plane.
169 Mask image to process.
171 Amount to grow the mask.
172 maskNameList : `str` or `list` [`str`]
173 Mask names that should be grown.
175 Mask plane to assign the newly masked pixels to.
178 thresh = afwDetection.Threshold(mask.getPlaneBitMask(maskNameList), afwDetection.Threshold.BITMASK)
179 fpSet = afwDetection.FootprintSet(mask, thresh)
180 fpSet = afwDetection.FootprintSet(fpSet, rGrow=radius, isotropic=
False)
181 fpSet.setMask(mask, maskValue)
185 maskNameList=['SAT'], fallbackValue=None):
186 """Interpolate over defects identified by a particular set of mask planes.
193 FWHM of double Gaussian smoothing kernel.
194 growSaturatedFootprints : scalar, optional
195 Number of pixels to grow footprints for saturated pixels.
196 maskNameList : `List` of `str`, optional
198 fallbackValue : scalar, optional
199 Value of last resort
for interpolation.
201 mask = maskedImage.getMask()
203 if growSaturatedFootprints > 0
and "SAT" in maskNameList:
207 growMasks(mask, radius=growSaturatedFootprints, maskNameList=[
'SAT'], maskValue=
"SAT")
209 thresh = afwDetection.Threshold(mask.getPlaneBitMask(maskNameList), afwDetection.Threshold.BITMASK)
210 fpSet = afwDetection.FootprintSet(mask, thresh)
211 defectList = Defects.fromFootprintList(fpSet.getFootprints())
220 """Mark saturated pixels and optionally interpolate over them
227 Saturation level used as the detection threshold.
229 FWHM of double Gaussian smoothing kernel.
230 growFootprints : scalar, optional
231 Number of pixels to grow footprints of detected regions.
232 interpolate : Bool, optional
233 If
True, saturated pixels are interpolated over.
234 maskName : str, optional
236 fallbackValue : scalar, optional
237 Value of last resort
for interpolation.
239 defectList = makeThresholdMask(
240 maskedImage=maskedImage,
241 threshold=saturation,
242 growFootprints=growFootprints,
252 """Compute number of edge trim pixels to match the calibration data.
254 Use the dimension difference between the raw exposure and the
255 calibration exposure to compute the edge trim pixels. This trim
256 is applied symmetrically,
with the same number of pixels masked on
264 Calibration image to draw new bounding box
from.
269 ``rawMaskedImage`` trimmed to the appropriate size.
274 Raised
if ``rawMaskedImage`` cannot be symmetrically trimmed to
275 match ``calibMaskedImage``.
277 nx, ny = rawMaskedImage.getBBox().getDimensions() - calibMaskedImage.getBBox().getDimensions()
279 raise RuntimeError(
"Raw and calib maskedImages are trimmed differently in X and Y.")
281 raise RuntimeError(
"Calibration maskedImage is trimmed unevenly in X.")
283 raise RuntimeError(
"Calibration maskedImage is larger than raw data.")
287 replacementMaskedImage = rawMaskedImage[nEdge:-nEdge, nEdge:-nEdge, afwImage.LOCAL]
288 SourceDetectionTask.setEdgeBits(
290 replacementMaskedImage.getBBox(),
291 rawMaskedImage.getMask().getPlaneBitMask(
"EDGE")
294 replacementMaskedImage = rawMaskedImage
296 return replacementMaskedImage
300 """Apply bias correction in place.
305 Image to process. The image is modified by this method.
307 Bias image of the same size
as ``maskedImage``
308 trimToFit : `Bool`, optional
309 If
True, raw data
is symmetrically trimmed to match
315 Raised
if ``maskedImage``
and ``biasMaskedImage`` do
not have
322 if maskedImage.getBBox(afwImage.LOCAL) != biasMaskedImage.getBBox(afwImage.LOCAL):
323 raise RuntimeError(
"maskedImage bbox %s != biasMaskedImage bbox %s" %
324 (maskedImage.getBBox(afwImage.LOCAL), biasMaskedImage.getBBox(afwImage.LOCAL)))
325 maskedImage -= biasMaskedImage
328def darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False, trimToFit=False):
329 """Apply dark correction in place.
334 Image to process. The image is modified by this method.
336 Dark image of the same size
as ``maskedImage``.
338 Dark exposure time
for ``maskedImage``.
340 Dark exposure time
for ``darkMaskedImage``.
341 invert : `Bool`, optional
342 If
True, re-add the dark to an already corrected image.
343 trimToFit : `Bool`, optional
344 If
True, raw data
is symmetrically trimmed to match
350 Raised
if ``maskedImage``
and ``darkMaskedImage`` do
not have
355 The dark correction
is applied by calculating:
356 maskedImage -= dark * expScaling / darkScaling
361 if maskedImage.getBBox(afwImage.LOCAL) != darkMaskedImage.getBBox(afwImage.LOCAL):
362 raise RuntimeError(
"maskedImage bbox %s != darkMaskedImage bbox %s" %
363 (maskedImage.getBBox(afwImage.LOCAL), darkMaskedImage.getBBox(afwImage.LOCAL)))
365 scale = expScale / darkScale
367 maskedImage.scaledMinus(scale, darkMaskedImage)
369 maskedImage.scaledPlus(scale, darkMaskedImage)
373 """Set the variance plane based on the image plane.
378 Image to process. The variance plane is modified.
380 The amplifier gain
in electrons/ADU.
382 The amplifier read nmoise
in ADU/pixel.
384 var = maskedImage.getVariance()
385 var[:] = maskedImage.getImage()
390def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False, trimToFit=False):
391 """Apply flat correction in place.
396 Image to process. The image is modified.
398 Flat image of the same size
as ``maskedImage``
400 Flat scale computation method. Allowed values are
'MEAN',
402 userScale : scalar, optional
403 Scale to use
if ``scalingType=
'USER'``.
404 invert : `Bool`, optional
405 If
True, unflatten an already flattened image.
406 trimToFit : `Bool`, optional
407 If
True, raw data
is symmetrically trimmed to match
413 Raised
if ``maskedImage``
and ``flatMaskedImage`` do
not have
414 the same size
or if ``scalingType``
is not an allowed value.
419 if maskedImage.getBBox(afwImage.LOCAL) != flatMaskedImage.getBBox(afwImage.LOCAL):
420 raise RuntimeError(
"maskedImage bbox %s != flatMaskedImage bbox %s" %
421 (maskedImage.getBBox(afwImage.LOCAL), flatMaskedImage.getBBox(afwImage.LOCAL)))
427 if scalingType
in (
'MEAN',
'MEDIAN'):
428 scalingType = afwMath.stringToStatisticsProperty(scalingType)
429 flatScale = afwMath.makeStatistics(flatMaskedImage.image, scalingType).getValue()
430 elif scalingType ==
'USER':
431 flatScale = userScale
433 raise RuntimeError(
'%s : %s not implemented' % (
"flatCorrection", scalingType))
436 maskedImage.scaledDivides(1.0/flatScale, flatMaskedImage)
438 maskedImage.scaledMultiplies(1.0/flatScale, flatMaskedImage)
442 """Apply illumination correction in place.
447 Image to process. The image is modified.
449 Illumination correction image of the same size
as ``maskedImage``.
451 Scale factor
for the illumination correction.
452 trimToFit : `Bool`, optional
453 If
True, raw data
is symmetrically trimmed to match
459 Raised
if ``maskedImage``
and ``illumMaskedImage`` do
not have
465 if maskedImage.getBBox(afwImage.LOCAL) != illumMaskedImage.getBBox(afwImage.LOCAL):
466 raise RuntimeError(
"maskedImage bbox %s != illumMaskedImage bbox %s" %
467 (maskedImage.getBBox(afwImage.LOCAL), illumMaskedImage.getBBox(afwImage.LOCAL)))
469 maskedImage.scaledDivides(1.0/illumScale, illumMaskedImage)
473 """Apply brighter fatter correction in place for the image.
478 Exposure to have brighter-fatter correction applied. Modified
480 kernel : `numpy.ndarray`
481 Brighter-fatter kernel to apply.
483 Number of correction iterations to run.
485 Convergence threshold in terms of the sum of absolute
486 deviations between an iteration
and the previous one.
488 If
True, then the exposure values are scaled by the gain prior
490 gains : `dict` [`str`, `float`]
491 A dictionary, keyed by amplifier name, of the gains to use.
492 If gains
is None, the nominal gains
in the amplifier object are used.
497 Final difference between iterations achieved
in correction.
499 Number of iterations used to calculate correction.
503 This correction takes a kernel that has been derived
from flat
504 field images to redistribute the charge. The gradient of the
505 kernel
is the deflection field due to the accumulated charge.
507 Given the original image I(x)
and the kernel K(x) we can compute
508 the corrected image Ic(x) using the following equation:
510 Ic(x) = I(x) + 0.5*d/dx(I(x)*d/dx(int( dy*K(x-y)*I(y))))
512 To evaluate the derivative term we expand it
as follows:
514 0.5 * ( d/dx(I(x))*d/dx(int(dy*K(x-y)*I(y)))
515 + I(x)*d^2/dx^2(int(dy* K(x-y)*I(y))) )
517 Because we use the measured counts instead of the incident counts
518 we apply the correction iteratively to reconstruct the original
519 counts
and the correction. We stop iterating when the summed
520 difference between the current corrected image
and the one
from
521 the previous iteration
is below the threshold. We do
not require
522 convergence because the number of iterations
is too large a
523 computational cost. How we define the threshold still needs to be
524 evaluated, the current default was shown to work reasonably well
525 on a small set of images. For more information on the method see
526 DocuShare Document-19407.
528 The edges
as defined by the kernel are
not corrected because they
529 have spurious values due to the convolution.
531 image = exposure.getMaskedImage().getImage()
534 with gainContext(exposure, image, applyGain, gains):
536 kLx = numpy.shape(kernel)[0]
537 kLy = numpy.shape(kernel)[1]
538 kernelImage = afwImage.ImageD(kLx, kLy)
539 kernelImage.getArray()[:, :] = kernel
540 tempImage = image.clone()
542 nanIndex = numpy.isnan(tempImage.getArray())
543 tempImage.getArray()[nanIndex] = 0.
545 outImage = afwImage.ImageF(image.getDimensions())
546 corr = numpy.zeros_like(image.getArray())
547 prev_image = numpy.zeros_like(image.getArray())
548 convCntrl = afwMath.ConvolutionControl(
False,
True, 1)
549 fixedKernel = afwMath.FixedKernel(kernelImage)
561 for iteration
in range(maxIter):
563 afwMath.convolve(outImage, tempImage, fixedKernel, convCntrl)
564 tmpArray = tempImage.getArray()
565 outArray = outImage.getArray()
567 with numpy.errstate(invalid=
"ignore", over=
"ignore"):
569 gradTmp = numpy.gradient(tmpArray[startY:endY, startX:endX])
570 gradOut = numpy.gradient(outArray[startY:endY, startX:endX])
571 first = (gradTmp[0]*gradOut[0] + gradTmp[1]*gradOut[1])[1:-1, 1:-1]
574 diffOut20 = numpy.diff(outArray, 2, 0)[startY:endY, startX + 1:endX - 1]
575 diffOut21 = numpy.diff(outArray, 2, 1)[startY + 1:endY - 1, startX:endX]
576 second = tmpArray[startY + 1:endY - 1, startX + 1:endX - 1]*(diffOut20 + diffOut21)
578 corr[startY + 1:endY - 1, startX + 1:endX - 1] = 0.5*(first + second)
580 tmpArray[:, :] = image.getArray()[:, :]
581 tmpArray[nanIndex] = 0.
582 tmpArray[startY:endY, startX:endX] += corr[startY:endY, startX:endX]
585 diff = numpy.sum(numpy.abs(prev_image - tmpArray))
589 prev_image[:, :] = tmpArray[:, :]
591 image.getArray()[startY + 1:endY - 1, startX + 1:endX - 1] += \
592 corr[startY + 1:endY - 1, startX + 1:endX - 1]
594 return diff, iteration
598 """Take the input convolved deflection potential and the flux array
599 to compute and apply the flux transfer into the correction array.
604 Deflection potential, being the convolution of the flux F
with the
607 The array of flux values which act
as the source of the flux transfer.
608 correctionMode: `bool`
609 Defines
if applying correction (
True)
or generating sims (
False).
617 if cFunc.shape != fStep.shape:
618 raise RuntimeError(f
'transferFlux: array shapes do not match: {cFunc.shape}, {fStep.shape}')
630 corr = numpy.zeros_like(cFunc)
633 yDim, xDim = cFunc.shape
634 y = numpy.arange(yDim, dtype=int)
635 x = numpy.arange(xDim, dtype=int)
636 xc, yc = numpy.meshgrid(x, y)
642 diff = numpy.diff(cFunc, axis=ax)
645 gx = numpy.zeros_like(cFunc)
646 yDiff, xDiff = diff.shape
647 gx[:yDiff, :xDiff] += diff
652 for i, sel
in enumerate([gx > 0, gx < 0]):
668 flux = factor * fStep[sel]*gx[sel]
671 flux = factor * fStep[yPix, xPix]*gx[sel]
675 corr[yPix, xPix] += flux
682 gains=None, correctionMode=True):
683 """Apply brighter fatter correction in place for the image.
685 This version presents a modified version of the algorithm
686 found in ``lsst.ip.isr.isrFunctions.brighterFatterCorrection``
687 which conserves the image flux, resulting
in improved
688 correction of the cores of stars. The convolution has also been
689 modified to mitigate edge effects.
694 Exposure to have brighter-fatter correction applied. Modified
696 kernel : `numpy.ndarray`
697 Brighter-fatter kernel to apply.
699 Number of correction iterations to run.
701 Convergence threshold
in terms of the sum of absolute
702 deviations between an iteration
and the previous one.
704 If
True, then the exposure values are scaled by the gain prior
706 gains : `dict` [`str`, `float`]
707 A dictionary, keyed by amplifier name, of the gains to use.
708 If gains
is None, the nominal gains
in the amplifier object are used.
709 correctionMode : `Bool`
710 If
True (default) the function applies correction
for BFE. If
False,
711 the code can instead be used to generate a simulation of BFE (sign
712 change
in the direction of the effect)
717 Final difference between iterations achieved
in correction.
719 Number of iterations used to calculate correction.
723 Modified version of ``lsst.ip.isr.isrFunctions.brighterFatterCorrection``.
725 This correction takes a kernel that has been derived
from flat
726 field images to redistribute the charge. The gradient of the
727 kernel
is the deflection field due to the accumulated charge.
729 Given the original image I(x)
and the kernel K(x) we can compute
730 the corrected image Ic(x) using the following equation:
732 Ic(x) = I(x) + 0.5*d/dx(I(x)*d/dx(int( dy*K(x-y)*I(y))))
734 Improved algorithm at this step applies the divergence theorem to
735 obtain a pixelised correction.
737 Because we use the measured counts instead of the incident counts
738 we apply the correction iteratively to reconstruct the original
739 counts
and the correction. We stop iterating when the summed
740 difference between the current corrected image
and the one
from
741 the previous iteration
is below the threshold. We do
not require
742 convergence because the number of iterations
is too large a
743 computational cost. How we define the threshold still needs to be
744 evaluated, the current default was shown to work reasonably well
745 on a small set of images.
747 Edges are handled
in the convolution by padding. This
is still
not
748 a physical model
for the edge, but avoids discontinuity
in the correction.
750 Author of modified version: Lance.Miller
@physics.ox.ac.uk
753 image = exposure.getMaskedImage().getImage()
756 with gainContext(exposure, image, applyGain, gains):
759 kLy, kLx = kernel.shape
760 kernelImage = afwImage.ImageD(kLx, kLy)
761 kernelImage.getArray()[:, :] = kernel
762 tempImage = image.clone()
764 nanIndex = numpy.isnan(tempImage.getArray())
765 tempImage.getArray()[nanIndex] = 0.
767 outImage = afwImage.ImageF(image.getDimensions())
768 corr = numpy.zeros_like(image.getArray())
769 prevImage = numpy.zeros_like(image.getArray())
770 convCntrl = afwMath.ConvolutionControl(
False,
True, 1)
771 fixedKernel = afwMath.FixedKernel(kernelImage)
775 kLy = 2 * ((1+kLy)//2)
776 kLx = 2 * ((1+kLx)//2)
783 imYdimension, imXdimension = tempImage.array.shape
784 imean = numpy.mean(tempImage.getArray()[~nanIndex])
787 tempImage.array[nanIndex] = 0.
788 padArray = numpy.pad(tempImage.getArray(), ((0, kLy), (0, kLx)))
789 outImage = afwImage.ImageF(numpy.pad(outImage.getArray(), ((0, kLy), (0, kLx))))
791 padImage = afwImage.ImageF(padArray.shape[1], padArray.shape[0])
792 padImage.array[:] = padArray
794 for iteration
in range(maxIter):
798 afwMath.convolve(outImage, padImage, fixedKernel, convCntrl)
799 tmpArray = tempImage.getArray()
800 outArray = outImage.getArray()
803 outArray = outArray[:imYdimension, :imXdimension]
806 corr[...] =
transferFlux(outArray, tmpArray, correctionMode=correctionMode)
809 tmpArray[:, :] = image.getArray()[:, :]
811 tmpArray[nanIndex] = 0.
815 tempImage.array[nanIndex] = 0.
816 padArray = numpy.pad(tempImage.getArray(), ((0, kLy), (0, kLx)))
819 diff = numpy.sum(numpy.abs(prevImage - tmpArray))
823 prevImage[:, :] = tmpArray[:, :]
825 image.getArray()[:] += corr[:]
827 return diff, iteration
831def gainContext(exp, image, apply, gains=None):
832 """Context manager that applies and removes gain.
837 Exposure to apply/remove gain.
839 Image to apply/remove gain.
841 If True, apply
and remove the amplifier gain.
842 gains : `dict` [`str`, `float`]
843 A dictionary, keyed by amplifier name, of the gains to use.
844 If gains
is None, the nominal gains
in the amplifier object are used.
849 Exposure
with the gain applied.
853 if gains
and apply
is True:
854 ampNames = [amp.getName()
for amp
in exp.getDetector()]
855 for ampName
in ampNames:
856 if ampName
not in gains.keys():
857 raise RuntimeError(f
"Gains provided to gain context, but no entry found for amp {ampName}")
860 ccd = exp.getDetector()
862 sim = image.Factory(image, amp.getBBox())
864 gain = gains[amp.getName()]
873 ccd = exp.getDetector()
875 sim = image.Factory(image, amp.getBBox())
877 gain = gains[amp.getName()]
884 sensorTransmission=None, atmosphereTransmission=None):
885 """Attach a TransmissionCurve to an Exposure, given separate curves for
886 different components.
891 Exposure object to modify by attaching the product of all given
892 ``TransmissionCurves`` in post-assembly trimmed detector coordinates.
893 Must have a valid ``Detector`` attached that matches the detector
894 associated
with sensorTransmission.
896 A ``TransmissionCurve`` that represents the throughput of the optics,
897 to be evaluated
in focal-plane coordinates.
899 A ``TransmissionCurve`` that represents the throughput of the filter
900 itself, to be evaluated
in focal-plane coordinates.
902 A ``TransmissionCurve`` that represents the throughput of the sensor
903 itself, to be evaluated
in post-assembly trimmed detector coordinates.
905 A ``TransmissionCurve`` that represents the throughput of the
906 atmosphere, assumed to be spatially constant.
911 The TransmissionCurve attached to the exposure.
915 All ``TransmissionCurve`` arguments are optional;
if none are provided, the
916 attached ``TransmissionCurve`` will have unit transmission everywhere.
918 combined = afwImage.TransmissionCurve.makeIdentity()
919 if atmosphereTransmission
is not None:
920 combined *= atmosphereTransmission
921 if opticsTransmission
is not None:
922 combined *= opticsTransmission
923 if filterTransmission
is not None:
924 combined *= filterTransmission
925 detector = exposure.getDetector()
926 fpToPix = detector.getTransform(fromSys=camGeom.FOCAL_PLANE,
927 toSys=camGeom.PIXELS)
928 combined = combined.transformedBy(fpToPix)
929 if sensorTransmission
is not None:
930 combined *= sensorTransmission
931 exposure.getInfo().setTransmissionCurve(combined)
935def applyGains(exposure, normalizeGains=False, ptcGains=None):
936 """Scale an exposure by the amplifier gains.
941 Exposure to process. The image is modified.
942 normalizeGains : `Bool`, optional
943 If
True, then amplifiers are scaled to force the median of
944 each amplifier to equal the median of those medians.
945 ptcGains : `dict`[`str`], optional
946 Dictionary keyed by amp name containing the PTC gains.
948 ccd = exposure.getDetector()
949 ccdImage = exposure.getMaskedImage()
953 sim = ccdImage.Factory(ccdImage, amp.getBBox())
955 sim *= ptcGains[amp.getName()]
960 medians.append(numpy.median(sim.getImage().getArray()))
963 median = numpy.median(numpy.array(medians))
964 for index, amp
in enumerate(ccd):
965 sim = ccdImage.Factory(ccdImage, amp.getBBox())
966 if medians[index] != 0.0:
967 sim *= median/medians[index]
971 """Grow the saturation trails by an amount dependent on the width of the
977 Mask which will have the saturated areas grown.
981 for i
in range(1, 6):
983 for i
in range(6, 8):
985 for i
in range(8, 10):
989 if extraGrowMax <= 0:
992 saturatedBit = mask.getPlaneBitMask(
"SAT")
994 xmin, ymin = mask.getBBox().getMin()
995 width = mask.getWidth()
997 thresh = afwDetection.Threshold(saturatedBit, afwDetection.Threshold.BITMASK)
998 fpList = afwDetection.FootprintSet(mask, thresh).getFootprints()
1001 for s
in fp.getSpans():
1002 x0, x1 = s.getX0(), s.getX1()
1004 extraGrow = extraGrowDict.get(x1 - x0 + 1, extraGrowMax)
1007 x0 -= xmin + extraGrow
1008 x1 -= xmin - extraGrow
1015 mask.array[y, x0:x1+1] |= saturatedBit
1019 """Set all BAD areas of the chip to the average of the rest of the exposure
1024 Exposure to mask. The exposure mask is modified.
1025 badStatistic : `str`, optional
1026 Statistic to use to generate the replacement value
from the
1027 image data. Allowed values are
'MEDIAN' or 'MEANCLIP'.
1031 badPixelCount : scalar
1032 Number of bad pixels masked.
1033 badPixelValue : scalar
1034 Value substituted
for bad pixels.
1039 Raised
if `badStatistic`
is not an allowed value.
1041 if badStatistic ==
"MEDIAN":
1042 statistic = afwMath.MEDIAN
1043 elif badStatistic ==
"MEANCLIP":
1044 statistic = afwMath.MEANCLIP
1046 raise RuntimeError(
"Impossible method %s of bad region correction" % badStatistic)
1048 mi = exposure.getMaskedImage()
1050 BAD = mask.getPlaneBitMask(
"BAD")
1051 INTRP = mask.getPlaneBitMask(
"INTRP")
1053 sctrl = afwMath.StatisticsControl()
1054 sctrl.setAndMask(BAD)
1055 value = afwMath.makeStatistics(mi, statistic, sctrl).getValue()
1057 maskArray = mask.getArray()
1058 imageArray = mi.getImage().getArray()
1059 badPixels = numpy.logical_and((maskArray & BAD) > 0, (maskArray & INTRP) == 0)
1060 imageArray[:] = numpy.where(badPixels, value, imageArray)
1062 return badPixels.sum(), value
1065def checkFilter(exposure, filterList, log):
1066 """Check to see if an exposure is in a filter specified by a list.
1068 The goal of this is to provide a unified filter checking interface
1069 for all filter dependent stages.
1074 Exposure to examine.
1075 filterList : `list` [`str`]
1076 List of physical_filter names to check.
1077 log : `logging.Logger`
1078 Logger to handle messages.
1083 True if the exposure
's filter is contained in the list.
1085 if len(filterList) == 0:
1087 thisFilter = exposure.getFilter()
1088 if thisFilter
is None:
1089 log.warning(
"No FilterLabel attached to this exposure!")
1093 if thisPhysicalFilter
in filterList:
1095 elif thisFilter.bandLabel
in filterList:
1097 log.warning(
"Physical filter (%s) should be used instead of band %s for filter configurations"
1098 " (%s)", thisPhysicalFilter, thisFilter.bandLabel, filterList)
1105 """Get the physical filter label associated with the given filterLabel.
1107 If ``filterLabel`` is `
None`
or there
is no physicalLabel attribute
1108 associated
with the given ``filterLabel``, the returned label will be
1115 physical filter label.
1116 log : `logging.Logger`
1117 Logger to handle messages.
1121 physicalFilter : `str`
1122 The value returned by the physicalLabel attribute of ``filterLabel``
if
1123 it exists, otherwise set to \
"Unknown\".
1125 if filterLabel
is None:
1126 physicalFilter =
"Unknown"
1127 log.warning(
"filterLabel is None. Setting physicalFilter to \"Unknown\".")
1130 physicalFilter = filterLabel.physicalLabel
1131 except RuntimeError:
1132 log.warning(
"filterLabel has no physicalLabel attribute. Setting physicalFilter to \"Unknown\".")
1133 physicalFilter =
"Unknown"
1134 return physicalFilter
1138 """Count the number of pixels in a given mask plane.
1143 Masked image to examine.
1145 Name of the mask plane to examine.
1150 Number of pixels in the requested mask plane.
1152 maskBit = maskedIm.mask.getPlaneBitMask(maskPlane)
1153 nPix = numpy.where(numpy.bitwise_and(maskedIm.mask.array, maskBit))[0].flatten().size
def biasCorrection(maskedImage, biasMaskedImage, trimToFit=False)
def growMasks(mask, radius=0, maskNameList=['BAD'], maskValue="BAD")
def countMaskedPixels(maskedIm, maskPlane)
def darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False, trimToFit=False)
def saturationCorrection(maskedImage, saturation, fwhm, growFootprints=1, interpolate=True, maskName='SAT', fallbackValue=None)
def interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=None)
def illuminationCorrection(maskedImage, illumMaskedImage, illumScale, trimToFit=True)
def fluxConservingBrighterFatterCorrection(exposure, kernel, maxIter, threshold, applyGain, gains=None, correctionMode=True)
def brighterFatterCorrection(exposure, kernel, maxIter, threshold, applyGain, gains=None)
def setBadRegions(exposure, badStatistic="MEDIAN")
def applyGains(exposure, normalizeGains=False, ptcGains=None)
def getPhysicalFilter(filterLabel, log)
def trimToMatchCalibBBox(rawMaskedImage, calibMaskedImage)
def updateVariance(maskedImage, gain, readNoise)
def interpolateFromMask(maskedImage, fwhm, growSaturatedFootprints=1, maskNameList=['SAT'], fallbackValue=None)
def transferFlux(cFunc, fStep, correctionMode=True)
def transposeMaskedImage(maskedImage)
def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False, trimToFit=False)
def attachTransmissionCurve(exposure, opticsTransmission=None, filterTransmission=None, sensorTransmission=None, atmosphereTransmission=None)
def widenSaturationTrails(mask)