25 "attachTransmissionCurve",
27 "brighterFatterCorrection",
29 "compareCameraKeywords",
34 "fluxConservingBrighterFatterCorrection",
42 "illuminationCorrection",
43 "interpolateDefectList",
44 "interpolateFromMask",
46 "saturationCorrection",
49 "transposeMaskedImage",
50 "trimToMatchCalibBBox",
52 "widenSaturationTrails",
54 "getExposureReadNoises",
63import lsst.afw.image
as afwImage
72from contextlib
import contextmanager
74from .defects
import Defects
78 """Make a double Gaussian PSF.
83 FWHM of double Gaussian smoothing kernel.
87 psf : `lsst.meas.algorithms.DoubleGaussianPsf`
88 The created smoothing kernel.
90 ksize = 4*int(fwhm) + 1
91 return measAlg.DoubleGaussianPsf(ksize, ksize, fwhm/(2*math.sqrt(2*math.log(2))))
95 """Make a transposed copy of a masked image.
99 maskedImage : `lsst.afw.image.MaskedImage`
104 transposed : `lsst.afw.image.MaskedImage`
105 The transposed copy of the input image.
107 transposed = maskedImage.Factory(
lsst.geom.Extent2I(maskedImage.getHeight(), maskedImage.getWidth()))
108 transposed.getImage().getArray()[:] = maskedImage.getImage().getArray().T
109 transposed.getMask().getArray()[:] = maskedImage.getMask().getArray().T
110 transposed.getVariance().getArray()[:] = maskedImage.getVariance().getArray().T
115 maskNameList=None, useLegacyInterp=True):
116 """Interpolate over defects specified in a defect list.
120 maskedImage : `lsst.afw.image.MaskedImage`
122 defectList : `lsst.meas.algorithms.Defects`
123 List of defects to interpolate over.
125 FWHM of double Gaussian smoothing kernel.
126 fallbackValue : scalar, optional
127 Fallback value if an interpolated value cannot be determined.
128 If None, then the clipped mean of the image is used.
129 maskNameList : `list [string]`
130 List of the defects to interpolate over (used for GP interpolator).
131 useLegacyInterp : `bool`
132 Use the legacy interpolation (polynomial interpolation) if True. Use
133 Gaussian Process interpolation if False.
137 The ``fwhm`` parameter is used to create a PSF, but the underlying
138 interpolation code (`lsst.meas.algorithms.interpolateOverDefects`) does
139 not currently make use of this information in legacy Interpolation, but use
140 if for the Gaussian Process as an estimation of the correlation lenght.
143 if fallbackValue
is None:
145 if 'INTRP' not in maskedImage.getMask().getMaskPlaneDict():
146 maskedImage.getMask().addMaskPlane(
'INTRP')
157 kwargs = {
"bin_spacing": 20,
158 "threshold_dynamic_binning": 2000,
159 "threshold_subdivide": 20000}
162 measAlg.interpolateOverDefects(maskedImage, psf, defectList,
163 fallbackValue=fallbackValue,
164 useFallbackValueAtEdge=
True,
166 useLegacyInterp=useLegacyInterp,
167 maskNameList=maskNameList, **kwargs)
172 """Mask pixels based on threshold detection.
176 maskedImage : `lsst.afw.image.MaskedImage`
177 Image to process. Only the mask plane is updated.
180 growFootprints : scalar, optional
181 Number of pixels to grow footprints of detected regions.
182 maskName : str, optional
183 Mask plane name, or list of names to convert
187 defectList : `lsst.meas.algorithms.Defects`
188 Defect list constructed from pixels above the threshold.
191 thresh = afwDetection.Threshold(threshold)
192 fs = afwDetection.FootprintSet(maskedImage, thresh)
194 if growFootprints > 0:
195 fs = afwDetection.FootprintSet(fs, rGrow=growFootprints, isotropic=
False)
196 fpList = fs.getFootprints()
199 mask = maskedImage.getMask()
200 bitmask = mask.getPlaneBitMask(maskName)
201 afwDetection.setMaskFromFootprintList(mask, fpList, bitmask)
203 return Defects.fromFootprintList(fpList)
206def growMasks(mask, radius=0, maskNameList=['BAD'], maskValue="BAD"):
207 """Grow a mask by an amount and add to the requested plane.
211 mask : `lsst.afw.image.Mask`
212 Mask image to process.
214 Amount to grow the mask.
215 maskNameList : `str` or `list` [`str`]
216 Mask names that should be grown.
218 Mask plane to assign the newly masked pixels to.
221 spans = SpanSet.fromMask(mask, mask.getPlaneBitMask(maskNameList))
224 spans = spans.dilated(radius, Stencil.MANHATTAN)
225 spans = spans.clippedTo(mask.getBBox())
226 spans.setMask(mask, mask.getPlaneBitMask(maskValue))
230 e2vEdgeBleedSatMaxArea=100000,
231 e2vEdgeBleedYMax=350,
232 saturatedMaskName="SAT", log=None):
233 """Mask edge bleeds in E2V detectors.
237 exposure : `lsst.afw.image.Exposure`
238 Exposure to apply masking to.
239 e2vEdgeBleedSatMinArea : `int`, optional
240 Minimum limit of saturated cores footprint area.
241 e2vEdgeBleedSatMaxArea : `int`, optional
242 Maximum limit of saturated cores footprint area.
243 e2vEdgeBleedYMax: `float`, optional
244 Height of edge bleed masking.
245 saturatedMaskName : `str`, optional
246 Mask name for saturation.
247 log : `logging.Logger`, optional
248 Logger to handle messages.
251 log = log
if log
else logging.getLogger(__name__)
253 maskedImage = exposure.maskedImage
254 saturatedBit = maskedImage.mask.getPlaneBitMask(saturatedMaskName)
256 thresh = afwDetection.Threshold(saturatedBit, afwDetection.Threshold.BITMASK)
258 fpList = afwDetection.FootprintSet(exposure.mask, thresh).getFootprints()
260 satAreas = numpy.asarray([fp.getArea()
for fp
in fpList])
261 largeAreas, = numpy.where((satAreas >= e2vEdgeBleedSatMinArea)
262 & (satAreas < e2vEdgeBleedSatMaxArea))
263 for largeAreasIndex
in largeAreas:
264 fpCore = fpList[largeAreasIndex]
265 xCore, yCore = fpCore.getCentroid()
269 for amp
in exposure.getDetector():
270 if amp.getBBox().contains(xCore, yCore):
271 ampName = amp.getName()
272 if ampName[:2] ==
'C0':
275 if fpCore.getBBox().getMinY() == 0:
283 log.info(
"Found E2V edge bleed in amp %s, column %d.", ampName, xCore)
284 maskedImage.mask[amp.getBBox()].array[:e2vEdgeBleedYMax, :] |= saturatedBit
288 fpCore, itlEdgeBleedSatMinArea=10000,
289 itlEdgeBleedSatMaxArea=100000,
290 itlEdgeBleedThreshold=5000.,
291 itlEdgeBleedModelConstant=0.02,
292 saturatedMaskName="SAT", log=None):
293 """Mask edge bleeds in ITL detectors.
297 ccdExposure : `lsst.afw.image.Exposure`
298 Exposure to apply masking to.
299 badAmpDict : `dict` [`str`, `bool`]
300 Dictionary of amplifiers, keyed by name, value is True if
301 amplifier is fully masked.
302 fpCore : `lsst.afw.detection._detection.Footprint`
303 Footprint of saturated core.
304 itlEdgeBleedThreshold : `float`, optional
305 Threshold above median sky background for edge bleed detection
307 itlEdgeBleedModelConstant : `float`, optional
308 Constant in the decaying exponential in the edge bleed masking.
309 saturatedMaskName : `str`, optional
310 Mask name for saturation.
311 log : `logging.Logger`, optional
312 Logger to handle messages.
315 log = log
if log
else logging.getLogger(__name__)
318 satLevel = numpy.nanmedian([ccdExposure.metadata[f
"LSST ISR SATURATION LEVEL {amp.getName()}"]
319 for amp
in ccdExposure.getDetector()
if not badAmpDict[amp.getName()]])
323 xCore, yCore = fpCore.getCentroid()
325 yCoreFP = int(yCore) - fpCore.getBBox().getMinY()
329 checkCoreNbRow = fpCore.getSpans().asArray()[yCoreFP, :]
332 indexSwitchFalse = []
333 if checkCoreNbRow[0]:
337 indexSwitchTrue.append(0)
342 for i, value
in enumerate(checkCoreNbRow):
345 indexSwitchTrue.append(i)
350 indexSwitchFalse.append(i)
358 xEdgesCores.append(int((indexSwitchTrue[1] + indexSwitchFalse[0])/2))
359 xEdgesCores.append(fpCore.getSpans().asArray().shape[1])
361 for i
in range(nbCore):
362 subfp = fpCore.getSpans().asArray()[:, xEdgesCores[i]:xEdgesCores[i+1]]
363 xCoreFP = int(xEdgesCores[i] + numpy.argmax(numpy.sum(subfp, axis=0)))
365 xCore = xCoreFP + fpCore.getBBox().getMinX()
368 if subfp.shape[0] <= 200:
369 yCoreFP = int(numpy.argmax(numpy.sum(subfp, axis=1)))
371 yCoreFP = int(numpy.argmax(numpy.sum(subfp[100:-100, :],
373 yCoreFP = 100+yCoreFP
376 widthSat = numpy.sum(subfp[int(yCoreFP), :])
378 subfpArea = numpy.sum(subfp)
379 if subfpArea > itlEdgeBleedSatMinArea
and subfpArea < itlEdgeBleedSatMaxArea:
382 itlEdgeBleedThreshold,
383 itlEdgeBleedModelConstant,
384 saturatedMaskName, log)
388 "Too many (%d) cores in saturated footprint to mask edge bleeds.",
393 xCore, yCore = fpCore.getCentroid()
395 yCoreFP = yCore - fpCore.getBBox().getMinY()
397 widthSat = numpy.sum(fpCore.getSpans().asArray()[int(yCoreFP), :])
399 satLevel, widthSat, itlEdgeBleedThreshold,
400 itlEdgeBleedModelConstant, saturatedMaskName, log)
405 itlEdgeBleedThreshold=5000.,
406 itlEdgeBleedModelConstant=0.03,
407 saturatedMaskName="SAT", log=None):
408 """Apply ITL edge bleed masking model.
412 ccdExposure : `lsst.afw.image.Exposure`
413 Exposure to apply masking to.
415 X coordinate of the saturated core.
417 Minimum saturation level of the detector.
419 Width of the saturated core.
420 itlEdgeBleedThreshold : `float`, optional
421 Threshold above median sky background for edge bleed detection
423 itlEdgeBleedModelConstant : `float`, optional
424 Constant in the decaying exponential in the edge bleed masking.
425 saturatedMaskName : `str`, optional
426 Mask name for saturation.
427 log : `logging.Logger`, optional
428 Logger to handle messages.
430 log = log
if log
else logging.getLogger(__name__)
432 maskedImage = ccdExposure.maskedImage
433 xmax = maskedImage.image.array.shape[1]
434 saturatedBit = maskedImage.mask.getPlaneBitMask(saturatedMaskName)
436 for amp
in ccdExposure.getDetector():
442 yBox = amp.getBBox().getCenter()[1]
443 if amp.getBBox().contains(xCore, yBox):
446 ampName = amp.getName()
456 if ampName[:2] ==
'C1':
457 sliceImage = maskedImage.image.array[:200, :]
458 sliceMask = maskedImage.mask.array[:200, :]
459 elif ampName[:2] ==
'C0':
460 sliceImage = numpy.flipud(maskedImage.image.array[-200:, :])
461 sliceMask = numpy.flipud(maskedImage.mask.array[-200:, :])
473 lowerRangeSmall = int(xCore)-5
474 upperRangeSmall = int(xCore)+5
475 if lowerRangeSmall < 0:
477 if upperRangeSmall > xmax:
478 upperRangeSmall = xmax
479 ampImageBG = numpy.median(maskedImage[amp.getBBox()].image.array)
480 edgeMedian = numpy.median(sliceImage[:50, lowerRangeSmall:upperRangeSmall])
481 if edgeMedian > (ampImageBG + itlEdgeBleedThreshold):
483 log.info(
"Found ITL edge bleed in amp %s, column %d.", ampName, xCore)
492 subImageXMin = int(xCore)-250
493 subImageXMax = int(xCore)+250
496 elif subImageXMax > xmax:
499 subImage = sliceImage[:100, subImageXMin:subImageXMax]
500 maxWidthEdgeBleed = numpy.max(numpy.sum(subImage > 0.45*satLevel,
505 edgeBleedHalfWidth = \
506 int(((maxWidthEdgeBleed)*numpy.exp(-itlEdgeBleedModelConstant*y)
508 lowerRange = int(xCore)-edgeBleedHalfWidth
509 upperRange = int(xCore)+edgeBleedHalfWidth
515 if upperRange > xmax:
517 sliceMask[y, lowerRange:upperRange] |= saturatedBit
521 """Mask columns presenting saturation sag in saturated footprints in
526 ccdExposure : `lsst.afw.image.Exposure`
527 Exposure to apply masking to.
528 fpCore : `lsst.afw.detection._detection.Footprint`
529 Footprint of saturated core.
530 saturatedMaskName : `str`, optional
531 Mask name for saturation.
536 maskedImage = ccdExposure.maskedImage
537 saturatedBit = maskedImage.mask.getPlaneBitMask(saturatedMaskName)
539 cc = numpy.sum(fpCore.getSpans().asArray(), axis=0)
542 columnsToMaskFP = numpy.where(cc > fpCore.getSpans().asArray().shape[0]/5.)
544 columnsToMask = [x + int(fpCore.getBBox().getMinX())
for x
in columnsToMaskFP]
545 maskedImage.mask.array[:, columnsToMask] |= saturatedBit
548def maskITLDip(exposure, detectorConfig, maskPlaneNames=["SUSPECT", "ITL_DIP"], log=None):
549 """Add mask bits according to the ITL dip model.
553 exposure : `lsst.afw.image.Exposure`
554 Exposure to do ITL dip masking.
555 detectorConfig : `lsst.ip.isr.overscanAmpConfig.OverscanDetectorConfig`
556 Configuration for this detector.
557 maskPlaneNames : `list [`str`], optional
558 Name of the ITL Dip mask planes.
559 log : `logging.Logger`, optional
560 If not set, a default logger will be used.
562 if detectorConfig.itlDipBackgroundFraction == 0.0:
567 log = logging.getLogger(__name__)
569 thresh = afwDetection.Threshold(
570 exposure.mask.getPlaneBitMask(
"SAT"),
571 afwDetection.Threshold.BITMASK,
573 fpList = afwDetection.FootprintSet(exposure.mask, thresh).getFootprints()
575 heights = numpy.asarray([fp.getBBox().getHeight()
for fp
in fpList])
577 largeHeights, = numpy.where(heights >= detectorConfig.itlDipMinHeight)
579 if len(largeHeights) == 0:
583 approxBackground = numpy.median(exposure.image.array)
584 maskValue = exposure.mask.getPlaneBitMask(maskPlaneNames)
586 maskBak = exposure.mask.array.copy()
589 for index
in largeHeights:
591 center = fp.getCentroid()
593 nSat = numpy.sum(fp.getSpans().asArray(), axis=0)
594 width = numpy.sum(nSat > detectorConfig.itlDipMinHeight)
596 if width < detectorConfig.itlDipMinWidth:
599 width = numpy.clip(width,
None, detectorConfig.itlDipMaxWidth)
601 dipMax = detectorConfig.itlDipBackgroundFraction * approxBackground * width
604 if dipMax < detectorConfig.itlDipMinBackgroundNoiseFraction * numpy.sqrt(approxBackground):
607 minCol = int(center.getX() - (detectorConfig.itlDipWidthScale * width) / 2.)
608 maxCol = int(center.getX() + (detectorConfig.itlDipWidthScale * width) / 2.)
609 minCol = numpy.clip(minCol, 0,
None)
610 maxCol = numpy.clip(maxCol,
None, exposure.mask.array.shape[1] - 1)
613 "Found ITL dip (width %d; bkg %.2f); masking column %d to %d.",
620 exposure.mask.array[:, minCol: maxCol + 1] |= maskValue
622 nMaskedCols += (maxCol - minCol + 1)
624 if nMaskedCols > detectorConfig.itlDipMaxColsPerImage:
626 "Too many (%d) columns would be masked on this image from dip masking; restoring original mask.",
629 exposure.mask.array[:, :] = maskBak
633 maskNameList=['SAT'], fallbackValue=None, useLegacyInterp=True):
634 """Interpolate over defects identified by a particular set of mask planes.
638 maskedImage : `lsst.afw.image.MaskedImage`
641 FWHM of double Gaussian smoothing kernel.
642 growSaturatedFootprints : scalar, optional
643 Number of pixels to grow footprints for saturated pixels.
644 maskNameList : `List` of `str`, optional
646 fallbackValue : scalar, optional
647 Value of last resort for interpolation.
651 The ``fwhm`` parameter is used to create a PSF, but the underlying
652 interpolation code (`lsst.meas.algorithms.interpolateOverDefects`) does
653 not currently make use of this information.
655 mask = maskedImage.getMask()
657 if growSaturatedFootprints > 0
and "SAT" in maskNameList:
661 growMasks(mask, radius=growSaturatedFootprints, maskNameList=[
'SAT'], maskValue=
"SAT")
663 thresh = afwDetection.Threshold(mask.getPlaneBitMask(maskNameList), afwDetection.Threshold.BITMASK)
664 fpSet = afwDetection.FootprintSet(mask, thresh)
665 defectList = Defects.fromFootprintList(fpSet.getFootprints())
668 maskNameList=maskNameList, useLegacyInterp=useLegacyInterp)
674 fallbackValue=None, useLegacyInterp=True):
675 """Mark saturated pixels and optionally interpolate over them
679 maskedImage : `lsst.afw.image.MaskedImage`
682 Saturation level used as the detection threshold.
684 FWHM of double Gaussian smoothing kernel.
685 growFootprints : scalar, optional
686 Number of pixels to grow footprints of detected regions.
687 interpolate : Bool, optional
688 If True, saturated pixels are interpolated over.
689 maskName : str, optional
691 fallbackValue : scalar, optional
692 Value of last resort for interpolation.
696 The ``fwhm`` parameter is used to create a PSF, but the underlying
697 interpolation code (`lsst.meas.algorithms.interpolateOverDefects`) does
698 not currently make use of this information.
701 maskedImage=maskedImage,
702 threshold=saturation,
703 growFootprints=growFootprints,
708 maskNameList=[maskName], useLegacyInterp=useLegacyInterp)
714 """Compute number of edge trim pixels to match the calibration data.
716 Use the dimension difference between the raw exposure and the
717 calibration exposure to compute the edge trim pixels. This trim
718 is applied symmetrically, with the same number of pixels masked on
723 rawMaskedImage : `lsst.afw.image.MaskedImage`
725 calibMaskedImage : `lsst.afw.image.MaskedImage`
726 Calibration image to draw new bounding box from.
730 replacementMaskedImage : `lsst.afw.image.MaskedImage`
731 ``rawMaskedImage`` trimmed to the appropriate size.
736 Raised if ``rawMaskedImage`` cannot be symmetrically trimmed to
737 match ``calibMaskedImage``.
739 nx, ny = rawMaskedImage.getBBox().getDimensions() - calibMaskedImage.getBBox().getDimensions()
741 raise RuntimeError(
"Raw and calib maskedImages are trimmed differently in X and Y.")
743 raise RuntimeError(
"Calibration maskedImage is trimmed unevenly in X.")
745 raise RuntimeError(
"Calibration maskedImage is larger than raw data.")
749 replacementMaskedImage = rawMaskedImage[nEdge:-nEdge, nEdge:-nEdge, afwImage.LOCAL]
750 SourceDetectionTask.setEdgeBits(
752 replacementMaskedImage.getBBox(),
753 rawMaskedImage.getMask().getPlaneBitMask(
"EDGE")
756 replacementMaskedImage = rawMaskedImage
758 return replacementMaskedImage
762 """Apply bias correction in place.
766 maskedImage : `lsst.afw.image.MaskedImage`
767 Image to process. The image is modified by this method.
768 biasMaskedImage : `lsst.afw.image.MaskedImage`
769 Bias image of the same size as ``maskedImage``
770 trimToFit : `Bool`, optional
771 If True, raw data is symmetrically trimmed to match
777 Raised if ``maskedImage`` and ``biasMaskedImage`` do not have
784 if maskedImage.getBBox(afwImage.LOCAL) != biasMaskedImage.getBBox(afwImage.LOCAL):
785 raise RuntimeError(
"maskedImage bbox %s != biasMaskedImage bbox %s" %
786 (maskedImage.getBBox(afwImage.LOCAL), biasMaskedImage.getBBox(afwImage.LOCAL)))
787 maskedImage -= biasMaskedImage
790def darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False, trimToFit=False):
791 """Apply dark correction in place.
795 maskedImage : `lsst.afw.image.MaskedImage`
796 Image to process. The image is modified by this method.
797 darkMaskedImage : `lsst.afw.image.MaskedImage`
798 Dark image of the same size as ``maskedImage``.
800 Dark exposure time for ``maskedImage``.
802 Dark exposure time for ``darkMaskedImage``.
803 invert : `Bool`, optional
804 If True, re-add the dark to an already corrected image.
805 trimToFit : `Bool`, optional
806 If True, raw data is symmetrically trimmed to match
812 Raised if ``maskedImage`` and ``darkMaskedImage`` do not have
817 The dark correction is applied by calculating:
818 maskedImage -= dark * expScaling / darkScaling
823 if maskedImage.getBBox(afwImage.LOCAL) != darkMaskedImage.getBBox(afwImage.LOCAL):
824 raise RuntimeError(
"maskedImage bbox %s != darkMaskedImage bbox %s" %
825 (maskedImage.getBBox(afwImage.LOCAL), darkMaskedImage.getBBox(afwImage.LOCAL)))
827 scale = expScale / darkScale
829 maskedImage.scaledMinus(scale, darkMaskedImage)
831 maskedImage.scaledPlus(scale, darkMaskedImage)
835 """Set the variance plane based on the image plane.
837 The maskedImage must have units of `adu` (if gain != 1.0) or
838 electron (if gain == 1.0). This routine will always produce a
839 variance plane in the same units as the image.
843 maskedImage : `lsst.afw.image.MaskedImage`
844 Image to process. The variance plane is modified.
846 The amplifier gain in electron/adu.
848 The amplifier read noise in electron/pixel.
849 replace : `bool`, optional
850 Replace the current variance? If False, the image
851 variance will be added to the current variance plane.
853 var = maskedImage.variance
855 var[:, :] = maskedImage.image
857 var[:, :] += maskedImage.image
859 var += (readNoise/gain)**2
862def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False, trimToFit=False):
863 """Apply flat correction in place.
867 maskedImage : `lsst.afw.image.MaskedImage`
868 Image to process. The image is modified.
869 flatMaskedImage : `lsst.afw.image.MaskedImage`
870 Flat image of the same size as ``maskedImage``
872 Flat scale computation method. Allowed values are 'MEAN',
874 userScale : scalar, optional
875 Scale to use if ``scalingType='USER'``.
876 invert : `Bool`, optional
877 If True, unflatten an already flattened image.
878 trimToFit : `Bool`, optional
879 If True, raw data is symmetrically trimmed to match
885 Raised if ``maskedImage`` and ``flatMaskedImage`` do not have
886 the same size or if ``scalingType`` is not an allowed value.
891 if maskedImage.getBBox(afwImage.LOCAL) != flatMaskedImage.getBBox(afwImage.LOCAL):
892 raise RuntimeError(
"maskedImage bbox %s != flatMaskedImage bbox %s" %
893 (maskedImage.getBBox(afwImage.LOCAL), flatMaskedImage.getBBox(afwImage.LOCAL)))
899 if scalingType
in (
'MEAN',
'MEDIAN'):
902 elif scalingType ==
'USER':
903 flatScale = userScale
905 raise RuntimeError(
'%s : %s not implemented' % (
"flatCorrection", scalingType))
908 maskedImage.scaledDivides(1.0/flatScale, flatMaskedImage)
910 maskedImage.scaledMultiplies(1.0/flatScale, flatMaskedImage)
914 """Apply illumination correction in place.
918 maskedImage : `lsst.afw.image.MaskedImage`
919 Image to process. The image is modified.
920 illumMaskedImage : `lsst.afw.image.MaskedImage`
921 Illumination correction image of the same size as ``maskedImage``.
923 Scale factor for the illumination correction.
924 trimToFit : `Bool`, optional
925 If True, raw data is symmetrically trimmed to match
931 Raised if ``maskedImage`` and ``illumMaskedImage`` do not have
937 if maskedImage.getBBox(afwImage.LOCAL) != illumMaskedImage.getBBox(afwImage.LOCAL):
938 raise RuntimeError(
"maskedImage bbox %s != illumMaskedImage bbox %s" %
939 (maskedImage.getBBox(afwImage.LOCAL), illumMaskedImage.getBBox(afwImage.LOCAL)))
941 maskedImage.scaledDivides(1.0/illumScale, illumMaskedImage)
945 """Apply brighter fatter correction in place for the image.
949 exposure : `lsst.afw.image.Exposure`
950 Exposure to have brighter-fatter correction applied. Modified
952 kernel : `numpy.ndarray`
953 Brighter-fatter kernel to apply.
955 Number of correction iterations to run.
957 Convergence threshold in terms of the sum of absolute
958 deviations between an iteration and the previous one.
960 If True, then the exposure values are scaled by the gain prior
962 gains : `dict` [`str`, `float`]
963 A dictionary, keyed by amplifier name, of the gains to use.
964 If gains is None, the nominal gains in the amplifier object are used.
969 Final difference between iterations achieved in correction.
971 Number of iterations used to calculate correction.
975 This correction takes a kernel that has been derived from flat
976 field images to redistribute the charge. The gradient of the
977 kernel is the deflection field due to the accumulated charge.
979 Given the original image I(x) and the kernel K(x) we can compute
980 the corrected image Ic(x) using the following equation:
982 Ic(x) = I(x) + 0.5*d/dx(I(x)*d/dx(int( dy*K(x-y)*I(y))))
984 To evaluate the derivative term we expand it as follows:
986 0.5 * ( d/dx(I(x))*d/dx(int(dy*K(x-y)*I(y)))
987 + I(x)*d^2/dx^2(int(dy* K(x-y)*I(y))) )
989 Because we use the measured counts instead of the incident counts
990 we apply the correction iteratively to reconstruct the original
991 counts and the correction. We stop iterating when the summed
992 difference between the current corrected image and the one from
993 the previous iteration is below the threshold. We do not require
994 convergence because the number of iterations is too large a
995 computational cost. How we define the threshold still needs to be
996 evaluated, the current default was shown to work reasonably well
997 on a small set of images. For more information on the method see
998 DocuShare Document-19407.
1000 The edges as defined by the kernel are not corrected because they
1001 have spurious values due to the convolution.
1003 image = exposure.getMaskedImage().getImage()
1006 with gainContext(exposure, image, applyGain, gains):
1008 kLx = numpy.shape(kernel)[0]
1009 kLy = numpy.shape(kernel)[1]
1010 kernelImage = afwImage.ImageD(kLx, kLy)
1011 kernelImage.getArray()[:, :] = kernel
1012 tempImage = afwImage.ImageD(image, deep=
True)
1014 nanIndex = numpy.isnan(tempImage.getArray())
1015 tempImage.getArray()[nanIndex] = 0.
1017 corr = numpy.zeros(image.array.shape, dtype=numpy.float64)
1018 prev_image = numpy.zeros(image.array.shape, dtype=numpy.float64)
1030 for iteration
in range(maxIter):
1032 outArray = scipy.signal.convolve(
1038 tmpArray = tempImage.getArray()
1040 with numpy.errstate(invalid=
"ignore", over=
"ignore"):
1042 gradTmp = numpy.gradient(tmpArray[startY:endY, startX:endX])
1043 gradOut = numpy.gradient(outArray[startY:endY, startX:endX])
1044 first = (gradTmp[0]*gradOut[0] + gradTmp[1]*gradOut[1])[1:-1, 1:-1]
1047 diffOut20 = numpy.diff(outArray, 2, 0)[startY:endY, startX + 1:endX - 1]
1048 diffOut21 = numpy.diff(outArray, 2, 1)[startY + 1:endY - 1, startX:endX]
1049 second = tmpArray[startY + 1:endY - 1, startX + 1:endX - 1]*(diffOut20 + diffOut21)
1051 corr[startY + 1:endY - 1, startX + 1:endX - 1] = 0.5*(first + second)
1053 tmpArray[:, :] = image.getArray()[:, :]
1054 tmpArray[nanIndex] = 0.
1055 tmpArray[startY:endY, startX:endX] += corr[startY:endY, startX:endX]
1058 diff = numpy.sum(numpy.abs(prev_image - tmpArray), dtype=numpy.float64)
1060 if diff < threshold:
1062 prev_image[:, :] = tmpArray[:, :]
1064 image.getArray()[startY + 1:endY - 1, startX + 1:endX - 1] += \
1065 corr[startY + 1:endY - 1, startX + 1:endX - 1]
1067 return diff, iteration
1071 """Take the input convolved deflection potential and the flux array
1072 to compute and apply the flux transfer into the correction array.
1076 cFunc: `numpy.array`
1077 Deflection potential, being the convolution of the flux F with the
1079 fStep: `numpy.array`
1080 The array of flux values which act as the source of the flux transfer.
1081 correctionMode: `bool`
1082 Defines if applying correction (True) or generating sims (False).
1087 BFE correction array
1090 if cFunc.shape != fStep.shape:
1091 raise RuntimeError(f
'transferFlux: array shapes do not match: {cFunc.shape}, {fStep.shape}')
1103 corr = numpy.zeros(cFunc.shape, dtype=numpy.float64)
1106 yDim, xDim = cFunc.shape
1107 y = numpy.arange(yDim, dtype=int)
1108 x = numpy.arange(xDim, dtype=int)
1109 xc, yc = numpy.meshgrid(x, y)
1115 diff = numpy.diff(cFunc, axis=ax)
1118 gx = numpy.zeros(cFunc.shape, dtype=numpy.float64)
1119 yDiff, xDiff = diff.shape
1120 gx[:yDiff, :xDiff] += diff
1125 for i, sel
in enumerate([gx > 0, gx < 0]):
1126 xSelPixels = xc[sel]
1127 ySelPixels = yc[sel]
1141 flux = factor * fStep[sel]*gx[sel]
1144 flux = factor * fStep[yPix, xPix]*gx[sel]
1148 corr[yPix, xPix] += flux
1155 gains=None, correctionMode=True):
1156 """Apply brighter fatter correction in place for the image.
1158 This version presents a modified version of the algorithm
1159 found in ``lsst.ip.isr.isrFunctions.brighterFatterCorrection``
1160 which conserves the image flux, resulting in improved
1161 correction of the cores of stars. The convolution has also been
1162 modified to mitigate edge effects.
1166 exposure : `lsst.afw.image.Exposure`
1167 Exposure to have brighter-fatter correction applied. Modified
1169 kernel : `numpy.ndarray`
1170 Brighter-fatter kernel to apply.
1172 Number of correction iterations to run.
1174 Convergence threshold in terms of the sum of absolute
1175 deviations between an iteration and the previous one.
1177 If True, then the exposure values are scaled by the gain prior
1179 gains : `dict` [`str`, `float`]
1180 A dictionary, keyed by amplifier name, of the gains to use.
1181 If gains is None, the nominal gains in the amplifier object are used.
1182 correctionMode : `Bool`
1183 If True (default) the function applies correction for BFE. If False,
1184 the code can instead be used to generate a simulation of BFE (sign
1185 change in the direction of the effect)
1190 Final difference between iterations achieved in correction.
1192 Number of iterations used to calculate correction.
1196 Modified version of ``lsst.ip.isr.isrFunctions.brighterFatterCorrection``.
1198 This correction takes a kernel that has been derived from flat
1199 field images to redistribute the charge. The gradient of the
1200 kernel is the deflection field due to the accumulated charge.
1202 Given the original image I(x) and the kernel K(x) we can compute
1203 the corrected image Ic(x) using the following equation:
1205 Ic(x) = I(x) + 0.5*d/dx(I(x)*d/dx(int( dy*K(x-y)*I(y))))
1207 Improved algorithm at this step applies the divergence theorem to
1208 obtain a pixelised correction.
1210 Because we use the measured counts instead of the incident counts
1211 we apply the correction iteratively to reconstruct the original
1212 counts and the correction. We stop iterating when the summed
1213 difference between the current corrected image and the one from
1214 the previous iteration is below the threshold. We do not require
1215 convergence because the number of iterations is too large a
1216 computational cost. How we define the threshold still needs to be
1217 evaluated, the current default was shown to work reasonably well
1218 on a small set of images.
1220 Edges are handled in the convolution by padding. This is still not
1221 a physical model for the edge, but avoids discontinuity in the correction.
1223 Author of modified version: Lance.Miller@physics.ox.ac.uk
1226 image = exposure.getMaskedImage().getImage()
1229 with gainContext(exposure, image, applyGain, gains):
1232 kLy, kLx = kernel.shape
1233 kernelImage = afwImage.ImageD(kLx, kLy)
1234 kernelImage.getArray()[:, :] = kernel
1235 tempImage = afwImage.ImageD(image, deep=
True)
1237 nanIndex = numpy.isnan(tempImage.getArray())
1238 tempImage.getArray()[nanIndex] = 0.
1240 outImage = afwImage.ImageD(image.getDimensions())
1241 corr = numpy.zeros(image.array.shape, dtype=numpy.float64)
1242 prevImage = numpy.zeros(image.array.shape, dtype=numpy.float64)
1248 kLy = 2 * ((1+kLy)//2)
1249 kLx = 2 * ((1+kLx)//2)
1256 imYdimension, imXdimension = tempImage.array.shape
1257 imean = numpy.mean(tempImage.getArray()[~nanIndex], dtype=numpy.float64)
1260 tempImage.array[nanIndex] = 0.0
1261 padArray = numpy.pad(tempImage.getArray(), ((0, kLy), (0, kLx)))
1262 outImage = afwImage.ImageD(numpy.pad(outImage.getArray(), ((0, kLy), (0, kLx))))
1264 padImage = afwImage.ImageD(padArray.shape[1], padArray.shape[0])
1265 padImage.array[:] = padArray
1267 for iteration
in range(maxIter):
1272 tmpArray = tempImage.getArray()
1273 outArray = outImage.getArray()
1276 outArray = outArray[:imYdimension, :imXdimension]
1279 corr[...] =
transferFlux(outArray, tmpArray, correctionMode=correctionMode)
1282 tmpArray[:, :] = image.getArray()[:, :]
1284 tmpArray[nanIndex] = 0.
1288 tempImage.array[nanIndex] = 0.
1289 padArray = numpy.pad(tempImage.getArray(), ((0, kLy), (0, kLx)))
1292 diff = numpy.sum(numpy.abs(prevImage - tmpArray), dtype=numpy.float64)
1294 if diff < threshold:
1296 prevImage[:, :] = tmpArray[:, :]
1298 image.getArray()[:] += corr[:]
1300 return diff, iteration
1304def gainContext(exp, image, apply, gains=None, invert=False, isTrimmed=True):
1305 """Context manager that applies and removes gain.
1309 exp : `lsst.afw.image.Exposure`
1310 Exposure to apply/remove gain.
1311 image : `lsst.afw.image.Image`
1312 Image to apply/remove gain.
1314 If True, apply and remove the amplifier gain.
1315 gains : `dict` [`str`, `float`], optional
1316 A dictionary, keyed by amplifier name, of the gains to use.
1317 If gains is None, the nominal gains in the amplifier object are used.
1318 invert : `bool`, optional
1319 Invert the gains (e.g. convert electrons to adu temporarily)?
1320 isTrimmed : `bool`, optional
1321 Is this a trimmed exposure?
1325 exp : `lsst.afw.image.Exposure`
1326 Exposure with the gain applied.
1330 if gains
and apply
is True:
1331 ampNames = [amp.getName()
for amp
in exp.getDetector()]
1332 for ampName
in ampNames:
1333 if ampName
not in gains.keys():
1334 raise RuntimeError(f
"Gains provided to gain context, but no entry found for amp {ampName}")
1337 ccd = exp.getDetector()
1339 sim = image.Factory(image, amp.getBBox()
if isTrimmed
else amp.getRawBBox())
1341 gain = gains[amp.getName()]
1343 gain = amp.getGain()
1353 ccd = exp.getDetector()
1355 sim = image.Factory(image, amp.getBBox()
if isTrimmed
else amp.getRawBBox())
1357 gain = gains[amp.getName()]
1359 gain = amp.getGain()
1367 sensorTransmission=None, atmosphereTransmission=None):
1368 """Attach a TransmissionCurve to an Exposure, given separate curves for
1369 different components.
1373 exposure : `lsst.afw.image.Exposure`
1374 Exposure object to modify by attaching the product of all given
1375 ``TransmissionCurves`` in post-assembly trimmed detector coordinates.
1376 Must have a valid ``Detector`` attached that matches the detector
1377 associated with sensorTransmission.
1378 opticsTransmission : `lsst.afw.image.TransmissionCurve`
1379 A ``TransmissionCurve`` that represents the throughput of the optics,
1380 to be evaluated in focal-plane coordinates.
1381 filterTransmission : `lsst.afw.image.TransmissionCurve`
1382 A ``TransmissionCurve`` that represents the throughput of the filter
1383 itself, to be evaluated in focal-plane coordinates.
1384 sensorTransmission : `lsst.afw.image.TransmissionCurve`
1385 A ``TransmissionCurve`` that represents the throughput of the sensor
1386 itself, to be evaluated in post-assembly trimmed detector coordinates.
1387 atmosphereTransmission : `lsst.afw.image.TransmissionCurve`
1388 A ``TransmissionCurve`` that represents the throughput of the
1389 atmosphere, assumed to be spatially constant.
1393 combined : `lsst.afw.image.TransmissionCurve`
1394 The TransmissionCurve attached to the exposure.
1398 All ``TransmissionCurve`` arguments are optional; if none are provided, the
1399 attached ``TransmissionCurve`` will have unit transmission everywhere.
1401 combined = afwImage.TransmissionCurve.makeIdentity()
1402 if atmosphereTransmission
is not None:
1403 combined *= atmosphereTransmission
1404 if opticsTransmission
is not None:
1405 combined *= opticsTransmission
1406 if filterTransmission
is not None:
1407 combined *= filterTransmission
1408 detector = exposure.getDetector()
1409 fpToPix = detector.getTransform(fromSys=camGeom.FOCAL_PLANE,
1410 toSys=camGeom.PIXELS)
1411 combined = combined.transformedBy(fpToPix)
1412 if sensorTransmission
is not None:
1413 combined *= sensorTransmission
1414 exposure.getInfo().setTransmissionCurve(combined)
1418def applyGains(exposure, normalizeGains=False, ptcGains=None, isTrimmed=True):
1419 """Scale an exposure by the amplifier gains.
1423 exposure : `lsst.afw.image.Exposure`
1424 Exposure to process. The image is modified.
1425 normalizeGains : `Bool`, optional
1426 If True, then amplifiers are scaled to force the median of
1427 each amplifier to equal the median of those medians.
1428 ptcGains : `dict`[`str`], optional
1429 Dictionary keyed by amp name containing the PTC gains.
1430 isTrimmed : `bool`, optional
1431 Is the input image trimmed?
1433 ccd = exposure.getDetector()
1434 ccdImage = exposure.getMaskedImage()
1439 sim = ccdImage.Factory(ccdImage, amp.getBBox())
1441 sim = ccdImage.Factory(ccdImage, amp.getRawBBox())
1443 sim *= ptcGains[amp.getName()]
1445 sim *= amp.getGain()
1448 medians.append(numpy.median(sim.getImage().getArray()))
1451 median = numpy.median(numpy.array(medians))
1452 for index, amp
in enumerate(ccd):
1454 sim = ccdImage.Factory(ccdImage, amp.getBBox())
1456 sim = ccdImage.Factory(ccdImage, amp.getRawBBox())
1457 if medians[index] != 0.0:
1458 sim *= median/medians[index]
1462 """Grow the saturation trails by an amount dependent on the width of the
1467 mask : `lsst.afw.image.Mask`
1468 Mask which will have the saturated areas grown.
1472 for i
in range(1, 6):
1473 extraGrowDict[i] = 0
1474 for i
in range(6, 8):
1475 extraGrowDict[i] = 1
1476 for i
in range(8, 10):
1477 extraGrowDict[i] = 3
1480 if extraGrowMax <= 0:
1483 saturatedBit = mask.getPlaneBitMask(
"SAT")
1485 xmin, ymin = mask.getBBox().getMin()
1486 width = mask.getWidth()
1488 thresh = afwDetection.Threshold(saturatedBit, afwDetection.Threshold.BITMASK)
1489 fpList = afwDetection.FootprintSet(mask, thresh).getFootprints()
1492 for s
in fp.getSpans():
1493 x0, x1 = s.getX0(), s.getX1()
1495 extraGrow = extraGrowDict.get(x1 - x0 + 1, extraGrowMax)
1498 x0 -= xmin + extraGrow
1499 x1 -= xmin - extraGrow
1506 mask.array[y, x0:x1+1] |= saturatedBit
1510 """Set all BAD areas of the chip to the average of the rest of the exposure
1514 exposure : `lsst.afw.image.Exposure`
1515 Exposure to mask. The exposure mask is modified.
1516 badStatistic : `str`, optional
1517 Statistic to use to generate the replacement value from the
1518 image data. Allowed values are 'MEDIAN' or 'MEANCLIP'.
1522 badPixelCount : scalar
1523 Number of bad pixels masked.
1524 badPixelValue : scalar
1525 Value substituted for bad pixels.
1530 Raised if `badStatistic` is not an allowed value.
1532 if badStatistic ==
"MEDIAN":
1533 statistic = afwMath.MEDIAN
1534 elif badStatistic ==
"MEANCLIP":
1535 statistic = afwMath.MEANCLIP
1537 raise RuntimeError(
"Impossible method %s of bad region correction" % badStatistic)
1539 mi = exposure.getMaskedImage()
1541 BAD = mask.getPlaneBitMask(
"BAD")
1542 INTRP = mask.getPlaneBitMask(
"INTRP")
1545 sctrl.setAndMask(BAD)
1548 maskArray = mask.getArray()
1549 imageArray = mi.getImage().getArray()
1550 badPixels = numpy.logical_and((maskArray & BAD) > 0, (maskArray & INTRP) == 0)
1551 imageArray[:] = numpy.where(badPixels, value, imageArray)
1553 return badPixels.sum(), value
1557 """Check to see if an exposure is in a filter specified by a list.
1559 The goal of this is to provide a unified filter checking interface
1560 for all filter dependent stages.
1564 exposure : `lsst.afw.image.Exposure`
1565 Exposure to examine.
1566 filterList : `list` [`str`]
1567 List of physical_filter names to check.
1568 log : `logging.Logger`
1569 Logger to handle messages.
1574 True if the exposure's filter is contained in the list.
1576 if len(filterList) == 0:
1578 thisFilter = exposure.getFilter()
1579 if thisFilter
is None:
1580 log.warning(
"No FilterLabel attached to this exposure!")
1584 if thisPhysicalFilter
in filterList:
1586 elif thisFilter.bandLabel
in filterList:
1588 log.warning(
"Physical filter (%s) should be used instead of band %s for filter configurations"
1589 " (%s)", thisPhysicalFilter, thisFilter.bandLabel, filterList)
1596 """Get the physical filter label associated with the given filterLabel.
1598 If ``filterLabel`` is `None` or there is no physicalLabel attribute
1599 associated with the given ``filterLabel``, the returned label will be
1604 filterLabel : `lsst.afw.image.FilterLabel`
1605 The `lsst.afw.image.FilterLabel` object from which to derive the
1606 physical filter label.
1607 log : `logging.Logger`
1608 Logger to handle messages.
1612 physicalFilter : `str`
1613 The value returned by the physicalLabel attribute of ``filterLabel`` if
1614 it exists, otherwise set to \"Unknown\".
1616 if filterLabel
is None:
1617 physicalFilter =
"Unknown"
1618 log.warning(
"filterLabel is None. Setting physicalFilter to \"Unknown\".")
1621 physicalFilter = filterLabel.physicalLabel
1622 except RuntimeError:
1623 log.warning(
"filterLabel has no physicalLabel attribute. Setting physicalFilter to \"Unknown\".")
1624 physicalFilter =
"Unknown"
1625 return physicalFilter
1629 """Count the number of pixels in a given mask plane.
1633 maskedIm : `~lsst.afw.image.MaskedImage`
1634 Masked image to examine.
1636 Name of the mask plane to examine.
1641 Number of pixels in the requested mask plane.
1643 maskBit = maskedIm.mask.getPlaneBitMask(maskPlane)
1644 nPix = numpy.where(numpy.bitwise_and(maskedIm.mask.array, maskBit))[0].flatten().size
1649 """Get the per-amplifier gains used for this exposure.
1653 exposure : `lsst.afw.image.Exposure`
1654 The exposure to find gains for.
1658 gains : `dict` [`str` `float`]
1659 Dictionary of gain values, keyed by amplifier name.
1660 Returns empty dict when detector is None.
1662 det = exposure.getDetector()
1666 metadata = exposure.getMetadata()
1669 ampName = amp.getName()
1671 if (key1 := f
"LSST ISR GAIN {ampName}")
in metadata:
1672 gains[ampName] = metadata[key1]
1673 elif (key2 := f
"LSST GAIN {ampName}")
in metadata:
1674 gains[ampName] = metadata[key2]
1676 gains[ampName] = amp.getGain()
1681 """Get the per-amplifier read noise used for this exposure.
1685 exposure : `lsst.afw.image.Exposure`
1686 The exposure to find read noise for.
1690 readnoises : `dict` [`str` `float`]
1691 Dictionary of read noise values, keyed by amplifier name.
1692 Returns empty dict when detector is None.
1694 det = exposure.getDetector()
1698 metadata = exposure.getMetadata()
1701 ampName = amp.getName()
1703 if (key1 := f
"LSST ISR READNOISE {ampName}")
in metadata:
1704 readnoises[ampName] = metadata[key1]
1705 elif (key2 := f
"LSST READNOISE {ampName}")
in metadata:
1706 readnoises[ampName] = metadata[key2]
1708 readnoises[ampName] = amp.getReadNoise()
1713 """Check if the unused pixels (pre-/over-scan pixels) have
1714 been trimmed from an exposure.
1718 exposure : `lsst.afw.image.Exposure`
1719 The exposure to check.
1724 True if the image is trimmed, else False.
1726 return exposure.getDetector().getBBox() == exposure.getBBox()
1730 """Check if the unused pixels (pre-/over-scan pixels) have
1731 been trimmed from an image
1735 image : `lsst.afw.image.Image`
1737 detector : `lsst.afw.cameraGeom.Detector`
1738 The detector associated with the image.
1743 True if the image is trimmed, else False.
1745 return detector.getBBox() == image.getBBox()
1749 doRaiseOnCalibMismatch,
1750 cameraKeywordsToCompare,
1756 """Compare header keywords to confirm camera states match.
1760 doRaiseOnCalibMismatch : `bool`
1761 Raise on calibration mismatch? Otherwise, log a warning.
1762 cameraKeywordsToCompare : `list` [`str`]
1763 List of camera keywords to compare.
1764 exposureMetadata : `lsst.daf.base.PropertyList`
1765 Header for the exposure being processed.
1766 calib : `lsst.afw.image.Exposure` or `lsst.ip.isr.IsrCalib`
1767 Calibration to be applied.
1769 Calib type for log message.
1770 log : `logging.Logger`, optional
1771 Logger to handle messages.
1774 calibMetadata = calib.metadata
1775 except AttributeError:
1778 log = log
if log
else logging.getLogger(__name__)
1780 missingKeywords = []
1781 for keyword
in cameraKeywordsToCompare:
1782 exposureValue = exposureMetadata.get(keyword,
None)
1783 if exposureValue
is None:
1784 log.debug(
"Sequencer keyword %s not found in exposure metadata.", keyword)
1787 calibValue = calibMetadata.get(keyword,
None)
1790 if calibValue
is None:
1791 missingKeywords.append(keyword)
1794 if exposureValue != calibValue:
1795 if doRaiseOnCalibMismatch:
1797 "Sequencer mismatch for %s [%s]: exposure: %s calib: %s",
1805 "Sequencer mismatch for %s [%s]: exposure: %s calib: %s",
1811 exposureMetadata[f
"ISR {calibName.upper()} SEQUENCER MISMATCH"] =
True
1815 "Calibration %s missing keywords %s, which were not checked.",
1817 ",".join(missingKeywords),
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)
gainContext(exp, image, apply, gains=None, invert=False, isTrimmed=True)
compareCameraKeywords(doRaiseOnCalibMismatch, cameraKeywordsToCompare, exposureMetadata, calib, calibName, log=None)
getExposureGains(exposure)
interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=None, maskNameList=None, useLegacyInterp=True)
setBadRegions(exposure, badStatistic="MEDIAN")
countMaskedPixels(maskedIm, maskPlane)
maskITLEdgeBleed(ccdExposure, badAmpDict, fpCore, itlEdgeBleedSatMinArea=10000, itlEdgeBleedSatMaxArea=100000, itlEdgeBleedThreshold=5000., itlEdgeBleedModelConstant=0.02, saturatedMaskName="SAT", log=None)
attachTransmissionCurve(exposure, opticsTransmission=None, filterTransmission=None, sensorTransmission=None, atmosphereTransmission=None)
flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False, trimToFit=False)
illuminationCorrection(maskedImage, illumMaskedImage, illumScale, trimToFit=True)
growMasks(mask, radius=0, maskNameList=['BAD'], maskValue="BAD")
maskITLSatSag(ccdExposure, fpCore, saturatedMaskName="SAT")
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)
maskE2VEdgeBleed(exposure, e2vEdgeBleedSatMinArea=10000, e2vEdgeBleedSatMaxArea=100000, e2vEdgeBleedYMax=350, saturatedMaskName="SAT", log=None)
brighterFatterCorrection(exposure, kernel, maxIter, threshold, applyGain, gains=None)
makeThresholdMask(maskedImage, threshold, growFootprints=1, maskName='SAT')
getExposureReadNoises(exposure)
transposeMaskedImage(maskedImage)
interpolateFromMask(maskedImage, fwhm, growSaturatedFootprints=1, maskNameList=['SAT'], fallbackValue=None, useLegacyInterp=True)
isTrimmedImage(image, detector)
maskITLDip(exposure, detectorConfig, maskPlaneNames=["SUSPECT", "ITL_DIP"], log=None)
updateVariance(maskedImage, gain, readNoise, replace=True)
applyGains(exposure, normalizeGains=False, ptcGains=None, isTrimmed=True)
getPhysicalFilter(filterLabel, log)
_applyMaskITLEdgeBleed(ccdExposure, xCore, satLevel, widthSat, itlEdgeBleedThreshold=5000., itlEdgeBleedModelConstant=0.03, saturatedMaskName="SAT", log=None)
saturationCorrection(maskedImage, saturation, fwhm, growFootprints=1, interpolate=True, maskName='SAT', fallbackValue=None, useLegacyInterp=True)
isTrimmedExposure(exposure)
trimToMatchCalibBBox(rawMaskedImage, calibMaskedImage)