37 from contextlib
import contextmanager
41 """Make a double Gaussian PSF. 46 FWHM of double Gaussian smoothing kernel. 50 psf : `lsst.meas.algorithms.DoubleGaussianPsf` 51 The created smoothing kernel. 53 ksize = 4*
int(fwhm) + 1
54 return measAlg.DoubleGaussianPsf(ksize, ksize, fwhm/(2*math.sqrt(2*math.log(2))))
58 """Make a transposed copy of a masked image. 62 maskedImage : `lsst.afw.image.MaskedImage` 67 transposed : `lsst.afw.image.MaskedImage` 68 The transposed copy of the input image. 70 transposed = maskedImage.Factory(
afwGeom.Extent2I(maskedImage.getHeight(), maskedImage.getWidth()))
71 transposed.getImage().getArray()[:] = maskedImage.getImage().getArray().T
72 transposed.getMask().getArray()[:] = maskedImage.getMask().getArray().T
73 transposed.getVariance().getArray()[:] = maskedImage.getVariance().getArray().T
78 """Interpolate over defects specified in a defect list. 82 maskedImage : `lsst.afw.image.MaskedImage` 85 List of defects to interpolate over. 87 FWHM of double Gaussian smoothing kernel. 88 fallbackValue : scalar, optional 89 Fallback value if an interpolated value cannot be determined. 90 If None, then the clipped mean of the image is used. 93 if fallbackValue
is None:
95 if 'INTRP' not in maskedImage.getMask().getMaskPlaneDict():
96 maskedImage.getMask.addMaskPlane(
'INTRP')
97 measAlg.interpolateOverDefects(maskedImage, psf, defectList, fallbackValue,
True)
101 """Compute a defect list from a footprint list, optionally growing the footprints. 105 fpList : `list` of `lsst.afw.detection.Footprint` 106 Footprint list to process. 110 defectList : `list` of `lsst.afw.meas.algorithms.Defect` 115 for bbox
in afwDetection.footprintToBBoxList(fp):
116 defect = measAlg.Defect(bbox)
117 defectList.append(defect)
122 """Make a transposed copy of a defect list. 126 defectList : `list` of `lsst.afw.meas.algorithms.Defect` 127 Input list of defects. 131 retDefectList : `list` of `lsst.afw.meas.algorithms.Defect` 132 Transposed list of defects. 135 for defect
in defectList:
136 bbox = defect.getBBox()
139 retDefectList.append(measAlg.Defect(nbbox))
144 """Set mask plane based on a defect list. 148 maskedImage : `lsst.afw.image.MaskedImage` 149 Image to process. Only the mask plane is updated. 150 defectList : `list` of `lsst.afw.meas.algorithms.Defect` 152 maskName : str, optional 153 Mask plane name to use. 156 mask = maskedImage.getMask()
157 bitmask = mask.getPlaneBitMask(maskName)
158 for defect
in defectList:
159 bbox = defect.getBBox()
164 """Compute a defect list from a specified mask plane. 168 maskedImage : `lsst.afw.image.MaskedImage` 170 maskName : str or `list` 171 Mask plane name, or list of names to convert. 175 defectList : `list` of `lsst.afw.meas.algorithms.Defect` 176 Defect list constructed from masked pixels. 178 mask = maskedImage.getMask()
179 thresh = afwDetection.Threshold(mask.getPlaneBitMask(maskName), afwDetection.Threshold.BITMASK)
180 fpList = afwDetection.FootprintSet(mask, thresh).getFootprints()
185 """Mask pixels based on threshold detection. 189 maskedImage : `lsst.afw.image.MaskedImage` 190 Image to process. Only the mask plane is updated. 193 growFootprints : scalar, optional 194 Number of pixels to grow footprints of detected regions. 195 maskName : str, optional 196 Mask plane name, or list of names to convert 200 defectList : `list` of `lsst.afw.meas.algorithms.Defect` 201 Defect list constructed from pixels above the threshold. 204 thresh = afwDetection.Threshold(threshold)
205 fs = afwDetection.FootprintSet(maskedImage, thresh)
207 if growFootprints > 0:
208 fs = afwDetection.FootprintSet(fs, growFootprints)
209 fpList = fs.getFootprints()
212 mask = maskedImage.getMask()
213 bitmask = mask.getPlaneBitMask(maskName)
214 afwDetection.setMaskFromFootprintList(mask, fpList, bitmask)
220 """Interpolate over defects identified by a particular mask plane. 224 maskedImage : `lsst.afw.image.MaskedImage` 227 FWHM of double Gaussian smoothing kernel. 228 growFootprints : scalar, optional 229 Number of pixels to grow footprints of detected regions. 230 maskName : str, optional 232 fallbackValue : scalar, optional 233 Value of last resort for interpolation. 235 mask = maskedImage.getMask()
236 thresh = afwDetection.Threshold(mask.getPlaneBitMask(maskName), afwDetection.Threshold.BITMASK)
237 fpSet = afwDetection.FootprintSet(mask, thresh)
238 if growFootprints > 0:
239 fpSet = afwDetection.FootprintSet(fpSet, rGrow=growFootprints, isotropic=
False)
242 fpSet.setMask(mask, maskName)
247 def saturationCorrection(maskedImage, saturation, fwhm, growFootprints=1, interpolate=True, maskName='SAT',
249 """Mark saturated pixels and optionally interpolate over them 253 maskedImage : `lsst.afw.image.MaskedImage` 256 Saturation level used as the detection threshold. 258 FWHM of double Gaussian smoothing kernel. 259 growFootprints : scalar, optional 260 Number of pixels to grow footprints of detected regions. 261 interpolate : Bool, optional 262 If True, saturated pixels are interpolated over. 263 maskName : str, optional 265 fallbackValue : scalar, optional 266 Value of last resort for interpolation. 269 maskedImage=maskedImage,
270 threshold=saturation,
271 growFootprints=growFootprints,
279 """Compute number of edge trim pixels to match the calibration data. 281 Use the dimension difference between the raw exposure and the 282 calibration exposure to compute the edge trim pixels. This trim 283 is applied symmetrically, with the same number of pixels masked on 288 rawMaskedImage : `lsst.afw.image.MaskedImage` 290 calibMaskedImage : `lsst.afw.image.MaskedImage` 291 Calibration image to draw new bounding box from. 295 replacementMaskedImage : `lsst.afw.image.MaskedImage` 296 ``rawMaskedImage`` trimmed to the appropriate size 300 Rasied if ``rawMaskedImage`` cannot be symmetrically trimmed to 301 match ``calibMaskedImage``. 303 nx, ny = rawMaskedImage.getBBox().getDimensions() - calibMaskedImage.getBBox().getDimensions()
305 raise RuntimeError(
"Raw and calib maskedImages are trimmed differently in X and Y.")
307 raise RuntimeError(
"Calibration maskedImage is trimmed unevenly in X.")
309 raise RuntimeError(
"Calibration maskedImage is larger than raw data.")
313 replacementMaskedImage = rawMaskedImage[nEdge:-nEdge, nEdge:-nEdge, afwImage.LOCAL]
314 SourceDetectionTask.setEdgeBits(
316 replacementMaskedImage.getBBox(),
317 rawMaskedImage.getMask().getPlaneBitMask(
"EDGE")
320 replacementMaskedImage = rawMaskedImage
322 return replacementMaskedImage
326 """Apply bias correction in place. 330 maskedImage : `lsst.afw.image.MaskedImage` 331 Image to process. The image is modified by this method. 332 biasMaskedImage : `lsst.afw.image.MaskedImage` 333 Bias image of the same size as ``maskedImage`` 334 trimToFit : `Bool`, optional 335 If True, raw data is symmetrically trimmed to match 341 Raised if ``maskedImage`` and ``biasMaskedImage`` do not have 348 if maskedImage.getBBox(afwImage.LOCAL) != biasMaskedImage.getBBox(afwImage.LOCAL):
349 raise RuntimeError(
"maskedImage bbox %s != biasMaskedImage bbox %s" %
350 (maskedImage.getBBox(afwImage.LOCAL), biasMaskedImage.getBBox(afwImage.LOCAL)))
351 maskedImage -= biasMaskedImage
354 def darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False, trimToFit=False):
355 """Apply dark correction in place. 359 maskedImage : `lsst.afw.image.MaskedImage` 360 Image to process. The image is modified by this method. 361 darkMaskedImage : `lsst.afw.image.MaskedImage` 362 Dark image of the same size as ``maskedImage``. 364 Dark exposure time for ``maskedImage``. 366 Dark exposure time for ``darkMaskedImage``. 367 invert : `Bool`, optional 368 If True, re-add the dark to an already corrected image. 369 trimToFit : `Bool`, optional 370 If True, raw data is symmetrically trimmed to match 376 Raised if ``maskedImage`` and ``darkMaskedImage`` do not have 381 The dark correction is applied by calculating: 382 maskedImage -= dark * expScaling / darkScaling 387 if maskedImage.getBBox(afwImage.LOCAL) != darkMaskedImage.getBBox(afwImage.LOCAL):
388 raise RuntimeError(
"maskedImage bbox %s != darkMaskedImage bbox %s" %
389 (maskedImage.getBBox(afwImage.LOCAL), darkMaskedImage.getBBox(afwImage.LOCAL)))
391 scale = expScale / darkScale
393 maskedImage.scaledMinus(scale, darkMaskedImage)
395 maskedImage.scaledPlus(scale, darkMaskedImage)
399 """Set the variance plane based on the image plane. 403 maskedImage : `lsst.afw.image.MaskedImage` 404 Image to process. The variance plane is modified. 406 The amplifier gain in electrons/ADU. 408 The amplifier read nmoise in ADU/pixel. 410 var = maskedImage.getVariance()
411 var[:] = maskedImage.getImage()
416 def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False, trimToFit=False):
417 """Apply flat correction in place. 421 maskedImage : `lsst.afw.image.MaskedImage` 422 Image to process. The image is modified. 423 flatMaskedImage : `lsst.afw.image.MaskedImage` 424 Flat image of the same size as ``maskedImage`` 426 Flat scale computation method. Allowed values are 'MEAN', 428 userScale : scalar, optional 429 Scale to use if ``scalingType``='USER'. 430 invert : `Bool`, optional 431 If True, unflatten an already flattened image. 432 trimToFit : `Bool`, optional 433 If True, raw data is symmetrically trimmed to match 439 Raised if ``maskedImage`` and ``flatMaskedImage`` do not have 442 Raised if ``scalingType`` is not an allowed value. 447 if maskedImage.getBBox(afwImage.LOCAL) != flatMaskedImage.getBBox(afwImage.LOCAL):
448 raise RuntimeError(
"maskedImage bbox %s != flatMaskedImage bbox %s" %
449 (maskedImage.getBBox(afwImage.LOCAL), flatMaskedImage.getBBox(afwImage.LOCAL)))
454 if scalingType
in (
'MEAN',
'MEDIAN'):
457 elif scalingType ==
'USER':
458 flatScale = userScale
463 maskedImage.scaledDivides(1.0/flatScale, flatMaskedImage)
465 maskedImage.scaledMultiplies(1.0/flatScale, flatMaskedImage)
469 """Apply illumination correction in place. 473 maskedImage : `lsst.afw.image.MaskedImage` 474 Image to process. The image is modified. 475 illumMaskedImage : `lsst.afw.image.MaskedImage` 476 Illumination correction image of the same size as ``maskedImage``. 478 Scale factor for the illumination correction. 483 Raised if ``maskedImage`` and ``illumMaskedImage`` do not have 486 if maskedImage.getBBox(afwImage.LOCAL) != illumMaskedImage.getBBox(afwImage.LOCAL):
487 raise RuntimeError(
"maskedImage bbox %s != illumMaskedImage bbox %s" %
488 (maskedImage.getBBox(afwImage.LOCAL), illumMaskedImage.getBBox(afwImage.LOCAL)))
490 maskedImage.scaledDivides(1./illumScale, illumMaskedImage)
493 def overscanCorrection(ampMaskedImage, overscanImage, fitType='MEDIAN', order=1, collapseRej=3.0,
494 statControl=None, overscanIsInt=True):
495 """Apply overscan correction in place. 499 ampMaskedImage : `lsst.afw.image.MaskedImage` 500 Image of amplifier to correct; modified. 501 overscanImage : `lsst.afw.image.Image` or `lsst.afw.image.MaskedImage` 502 Image of overscan; modified. 504 Type of fit for overscan correction. May be one of: 506 - ``MEAN``: use mean of overscan. 507 - ``MEANCLIP``: use clipped mean of overscan. 508 - ``MEDIAN``: use median of overscan. 509 - ``POLY``: fit with ordinary polynomial. 510 - ``CHEB``: fit with Chebyshev polynomial. 511 - ``LEG``: fit with Legendre polynomial. 512 - ``NATURAL_SPLINE``: fit with natural spline. 513 - ``CUBIC_SPLINE``: fit with cubic spline. 514 - ``AKIMA_SPLINE``: fit with Akima spline. 517 Polynomial order or number of spline knots; ignored unless 518 ``fitType`` indicates a polynomial or spline. 519 statControl : `lsst.afw.math.StatisticsControl` 520 Statistics control object. In particular, we pay attention to numSigmaClip 521 overscanIsInt : `bool` 522 Treat the overscan region as consisting of integers, even if it's been 523 converted to float. E.g. handle ties properly. 527 result : `lsst.pipe.base.Struct` 528 Result struct with components: 530 - ``imageFit``: Value(s) removed from image (scalar or 531 `lsst.afw.image.Image`) 532 - ``overscanFit``: Value(s) removed from overscan (scalar or 533 `lsst.afw.image.Image`) 534 - ``overscanImage``: Overscan corrected overscan region 535 (`lsst.afw.image.Image`) 539 Raised if ``fitType`` is not an allowed value. 543 The ``ampMaskedImage`` and ``overscanImage`` are modified, with the fit 544 subtracted. Note that the ``overscanImage`` should not be a subimage of 545 the ``ampMaskedImage``, to avoid being subtracted twice. 547 Debug plots are available for the SPLINE fitTypes by setting the 548 `debug.display` for `name` == "lsst.ip.isr.isrFunctions". These 549 plots show the scatter plot of the overscan data (collapsed along 550 the perpendicular dimension) as a function of position on the CCD 551 (normalized between +/-1). 553 ampImage = ampMaskedImage.getImage()
554 if statControl
is None:
557 numSigmaClip = statControl.getNumSigmaClip()
559 if fitType
in (
'MEAN',
'MEANCLIP'):
562 overscanFit = offImage
563 elif fitType
in (
'MEDIAN',):
566 if hasattr(overscanImage,
"image"):
567 imageI = overscanImage.image.convertI()
568 overscanImageI = afwImage.MaskedImageI(imageI, overscanImage.mask, overscanImage.variance)
570 overscanImageI = overscanImage.convertI()
572 overscanImageI = overscanImage
576 overscanFit = offImage
580 elif fitType
in (
'POLY',
'CHEB',
'LEG',
'NATURAL_SPLINE',
'CUBIC_SPLINE',
'AKIMA_SPLINE'):
581 if hasattr(overscanImage,
"getImage"):
582 biasArray = overscanImage.getImage().getArray()
583 biasArray = numpy.ma.masked_where(overscanImage.getMask().getArray() & statControl.getAndMask(),
586 biasArray = overscanImage.getArray()
588 shortInd = numpy.argmin(biasArray.shape)
591 biasArray = numpy.transpose(biasArray)
594 percentiles = numpy.percentile(biasArray, [25.0, 50.0, 75.0], axis=1)
595 medianBiasArr = percentiles[1]
596 stdevBiasArr = 0.74*(percentiles[2] - percentiles[0])
597 diff = numpy.abs(biasArray - medianBiasArr[:, numpy.newaxis])
598 biasMaskedArr = numpy.ma.masked_where(diff > numSigmaClip*stdevBiasArr[:, numpy.newaxis], biasArray)
599 collapsed = numpy.mean(biasMaskedArr, axis=1)
600 if collapsed.mask.sum() > 0:
601 collapsed.data[collapsed.mask] = numpy.mean(biasArray.data[collapsed.mask], axis=1)
602 del biasArray, percentiles, stdevBiasArr, diff, biasMaskedArr
605 collapsed = numpy.transpose(collapsed)
608 indices = 2.0*numpy.arange(num)/
float(num) - 1.0
610 if fitType
in (
'POLY',
'CHEB',
'LEG'):
612 poly = numpy.polynomial
613 fitter, evaler = {
"POLY": (poly.polynomial.polyfit, poly.polynomial.polyval),
614 "CHEB": (poly.chebyshev.chebfit, poly.chebyshev.chebval),
615 "LEG": (poly.legendre.legfit, poly.legendre.legval),
618 coeffs =
fitter(indices, collapsed, order)
619 fitBiasArr = evaler(indices, coeffs)
620 elif 'SPLINE' in fitType:
629 collapsedMask = collapsed.mask
631 if collapsedMask == numpy.ma.nomask:
632 collapsedMask = numpy.array(len(collapsed)*[numpy.ma.nomask])
636 numPerBin, binEdges = numpy.histogram(indices, bins=numBins,
637 weights=1-collapsedMask.astype(int))
640 with numpy.errstate(invalid=
"ignore"):
641 values = numpy.histogram(indices, bins=numBins,
642 weights=collapsed.data*~collapsedMask)[0]/numPerBin
643 binCenters = numpy.histogram(indices, bins=numBins,
644 weights=indices*~collapsedMask)[0]/numPerBin
646 values.astype(float)[numPerBin > 0],
648 fitBiasArr = numpy.array([interp.interpolate(i)
for i
in indices])
652 import matplotlib.pyplot
as plot
653 figure = plot.figure(1)
655 axes = figure.add_axes((0.1, 0.1, 0.8, 0.8))
656 axes.plot(indices[~collapsedMask], collapsed[~collapsedMask],
'k+')
657 if collapsedMask.sum() > 0:
658 axes.plot(indices[collapsedMask], collapsed.data[collapsedMask],
'b+')
659 axes.plot(indices, fitBiasArr,
'r-')
660 plot.xlabel(
"centered/scaled position along overscan region")
661 plot.ylabel(
"pixel value/fit value")
663 prompt =
"Press Enter or c to continue [chp]... " 665 ans = input(prompt).lower()
666 if ans
in (
"",
"c",):
672 print(
"h[elp] c[ontinue] p[db]")
675 offImage = ampImage.Factory(ampImage.getDimensions())
676 offArray = offImage.getArray()
677 overscanFit = afwImage.ImageF(overscanImage.getDimensions())
678 overscanArray = overscanFit.getArray()
680 offArray[:, :] = fitBiasArr[:, numpy.newaxis]
681 overscanArray[:, :] = fitBiasArr[:, numpy.newaxis]
683 offArray[:, :] = fitBiasArr[numpy.newaxis, :]
684 overscanArray[:, :] = fitBiasArr[numpy.newaxis, :]
692 mask = ampMaskedImage.getMask()
693 maskArray = mask.getArray()
if shortInd == 1
else mask.getArray().transpose()
694 suspect = mask.getPlaneBitMask(
"SUSPECT")
696 if collapsed.mask == numpy.ma.nomask:
700 for low
in range(num):
701 if not collapsed.mask[low]:
704 maskArray[:low, :] |= suspect
705 for high
in range(1, num):
706 if not collapsed.mask[-high]:
709 maskArray[-high:, :] |= suspect
712 raise pexExcept.Exception(
'%s : %s an invalid overscan type' % (
"overscanCorrection", fitType))
714 overscanImage -= overscanFit
715 return Struct(imageFit=offImage, overscanFit=overscanFit, overscanImage=overscanImage)
719 """Apply brighter fatter correction in place for the image. 723 exposure : `lsst.afw.image.Exposure` 724 Exposure to have brighter-fatter correction applied. Modified 726 kernel : `numpy.ndarray` 727 Brighter-fatter kernel to apply. 729 Number of correction iterations to run. 731 Convergence threshold in terms of the sum of absolute 732 deviations between an iteration and the previous one. 734 If True, then the exposure values are scaled by the gain prior 739 This correction takes a kernel that has been derived from flat 740 field images to redistribute the charge. The gradient of the 741 kernel is the deflection field due to the accumulated charge. 743 Given the original image I(x) and the kernel K(x) we can compute 744 the corrected image Ic(x) using the following equation: 746 Ic(x) = I(x) + 0.5*d/dx(I(x)*d/dx(int( dy*K(x-y)*I(y)))) 748 To evaluate the derivative term we expand it as follows: 750 0.5 * ( d/dx(I(x))*d/dx(int(dy*K(x-y)*I(y))) + I(x)*d^2/dx^2(int(dy* K(x-y)*I(y))) ) 752 Because we use the measured counts instead of the incident counts 753 we apply the correction iteratively to reconstruct the original 754 counts and the correction. We stop iterating when the summed 755 difference between the current corrected image and the one from 756 the previous iteration is below the threshold. We do not require 757 convergence because the number of iterations is too large a 758 computational cost. How we define the threshold still needs to be 759 evaluated, the current default was shown to work reasonably well 760 on a small set of images. For more information on the method see 761 DocuShare Document-19407. 763 The edges as defined by the kernel are not corrected because they 764 have spurious values due to the convolution. 766 image = exposure.getMaskedImage().getImage()
771 kLx = numpy.shape(kernel)[0]
772 kLy = numpy.shape(kernel)[1]
773 kernelImage = afwImage.ImageD(kLx, kLy)
774 kernelImage.getArray()[:, :] = kernel
775 tempImage = image.clone()
777 nanIndex = numpy.isnan(tempImage.getArray())
778 tempImage.getArray()[nanIndex] = 0.
780 outImage = afwImage.ImageF(image.getDimensions())
781 corr = numpy.zeros_like(image.getArray())
782 prev_image = numpy.zeros_like(image.getArray())
794 for iteration
in range(maxIter):
797 tmpArray = tempImage.getArray()
798 outArray = outImage.getArray()
800 with numpy.errstate(invalid=
"ignore", over=
"ignore"):
802 gradTmp = numpy.gradient(tmpArray[startY:endY, startX:endX])
803 gradOut = numpy.gradient(outArray[startY:endY, startX:endX])
804 first = (gradTmp[0]*gradOut[0] + gradTmp[1]*gradOut[1])[1:-1, 1:-1]
807 diffOut20 = numpy.diff(outArray, 2, 0)[startY:endY, startX + 1:endX - 1]
808 diffOut21 = numpy.diff(outArray, 2, 1)[startY + 1:endY - 1, startX:endX]
809 second = tmpArray[startY + 1:endY - 1, startX + 1:endX - 1]*(diffOut20 + diffOut21)
811 corr[startY + 1:endY - 1, startX + 1:endX - 1] = 0.5*(first + second)
813 tmpArray[:, :] = image.getArray()[:, :]
814 tmpArray[nanIndex] = 0.
815 tmpArray[startY:endY, startX:endX] += corr[startY:endY, startX:endX]
818 diff = numpy.sum(numpy.abs(prev_image - tmpArray))
822 prev_image[:, :] = tmpArray[:, :]
829 image.getArray()[startY + 1:endY - 1, startX + 1:endX - 1] += \
830 corr[startY + 1:endY - 1, startX + 1:endX - 1]
835 """Context manager that applies and removes gain. 839 exp : `lsst.afw.image.Exposure` 840 Exposure to apply/remove gain. 841 image : `lsst.afw.image.Image` 842 Image to apply/remove gain. 844 If True, apply and remove the amplifier gain. 848 exp : `lsst.afw.image.Exposure` 849 Exposure with the gain applied. 852 ccd = exp.getDetector()
854 sim = image.Factory(image, amp.getBBox())
861 ccd = exp.getDetector()
863 sim = image.Factory(image, amp.getBBox())
868 sensorTransmission=None, atmosphereTransmission=None):
869 """Attach a TransmissionCurve to an Exposure, given separate curves for 870 different components. 874 exposure : `lsst.afw.image.Exposure` 875 Exposure object to modify by attaching the product of all given 876 ``TransmissionCurves`` in post-assembly trimmed detector coordinates. 877 Must have a valid ``Detector`` attached that matches the detector 878 associated with sensorTransmission. 879 opticsTransmission : `lsst.afw.image.TransmissionCurve` 880 A ``TransmissionCurve`` that represents the throughput of the optics, 881 to be evaluated in focal-plane coordinates. 882 filterTransmission : `lsst.afw.image.TransmissionCurve` 883 A ``TransmissionCurve`` that represents the throughput of the filter 884 itself, to be evaluated in focal-plane coordinates. 885 sensorTransmission : `lsst.afw.image.TransmissionCurve` 886 A ``TransmissionCurve`` that represents the throughput of the sensor 887 itself, to be evaluated in post-assembly trimmed detector coordinates. 888 atmosphereTransmission : `lsst.afw.image.TransmissionCurve` 889 A ``TransmissionCurve`` that represents the throughput of the 890 atmosphere, assumed to be spatially constant. 894 combined : `lsst.afw.image.TransmissionCurve` 895 The TransmissionCurve attached to the exposure. 899 All ``TransmissionCurve`` arguments are optional; if none are provided, the 900 attached ``TransmissionCurve`` will have unit transmission everywhere. 902 combined = afwImage.TransmissionCurve.makeIdentity()
903 if atmosphereTransmission
is not None:
904 combined *= atmosphereTransmission
905 if opticsTransmission
is not None:
906 combined *= opticsTransmission
907 if filterTransmission
is not None:
908 combined *= filterTransmission
909 detector = exposure.getDetector()
910 fpToPix = detector.getTransform(fromSys=camGeom.FOCAL_PLANE,
911 toSys=camGeom.PIXELS)
912 combined = combined.transformedBy(fpToPix)
913 if sensorTransmission
is not None:
914 combined *= sensorTransmission
915 exposure.getInfo().setTransmissionCurve(combined)
920 """!Update the WCS in exposure with a distortion model based on camera 925 exposure : `lsst.afw.image.Exposure` 926 Exposure to process. Must contain a Detector and WCS. The 927 exposure is modified. 928 camera : `lsst.afw.cameraGeom.Camera` 934 Raised if ``exposure`` is lacking a Detector or WCS, or if 938 Add a model for optical distortion based on geometry found in ``camera`` 939 and the ``exposure``'s detector. The raw input exposure is assumed 940 have a TAN WCS that has no compensation for optical distortion. 941 Two other possibilities are: 942 - The raw input exposure already has a model for optical distortion, 943 as is the case for raw DECam data. 944 In that case you should set config.doAddDistortionModel False. 945 - The raw input exposure has a model for distortion, but it has known 946 deficiencies severe enough to be worth fixing (e.g. because they 947 cause problems for fitting a better WCS). In that case you should 948 override this method with a version suitable for your raw data. 951 wcs = exposure.getWcs()
953 raise RuntimeError(
"exposure has no WCS")
955 raise RuntimeError(
"camera is None")
956 detector = exposure.getDetector()
958 raise RuntimeError(
"exposure has no Detector")
959 pixelToFocalPlane = detector.getTransform(camGeom.PIXELS, camGeom.FOCAL_PLANE)
960 focalPlaneToFieldAngle = camera.getTransformMap().getTransform(camGeom.FOCAL_PLANE,
963 exposure.setWcs(distortedWcs)
967 """Scale an exposure by the amplifier gains. 971 exposure : `lsst.afw.image.Exposure` 972 Exposure to process. The image is modified. 973 normalizeGains : `Bool`, optional 974 If True, then amplifiers are scaled to force the median of 975 each amplifier to equal the median of those medians. 977 ccd = exposure.getDetector()
978 ccdImage = exposure.getMaskedImage()
982 sim = ccdImage.Factory(ccdImage, amp.getBBox())
986 medians.append(numpy.median(sim.getImage().getArray()))
989 median = numpy.median(numpy.array(medians))
990 for index, amp
in enumerate(ccd):
991 sim = ccdImage.Factory(ccdImage, amp.getDataSec())
992 sim *= median/medians[index]
996 """Grow the saturation trails by an amount dependent on the width of the trail. 1000 mask : `lsst.afw.image.Mask` 1001 Mask which will have the saturated areas grown. 1005 for i
in range(1, 6):
1006 extraGrowDict[i] = 0
1007 for i
in range(6, 8):
1008 extraGrowDict[i] = 1
1009 for i
in range(8, 10):
1010 extraGrowDict[i] = 3
1013 if extraGrowMax <= 0:
1016 saturatedBit = mask.getPlaneBitMask(
"SAT")
1018 xmin, ymin = mask.getBBox().getMin()
1019 width = mask.getWidth()
1021 thresh = afwDetection.Threshold(saturatedBit, afwDetection.Threshold.BITMASK)
1022 fpList = afwDetection.FootprintSet(mask, thresh).getFootprints()
1025 for s
in fp.getSpans():
1026 x0, x1 = s.getX0(), s.getX1()
1028 extraGrow = extraGrowDict.get(x1 - x0 + 1, extraGrowMax)
1031 x0 -= xmin + extraGrow
1032 x1 -= xmin - extraGrow
1039 mask.array[y, x0:x1+1] |= saturatedBit
1043 """Set all BAD areas of the chip to the average of the rest of the exposure 1047 exposure : `lsst.afw.image.Exposure` 1048 Exposure to mask. The exposure mask is modified. 1049 badStatistic : `str`, optional 1050 Statistic to use to generate the replacement value from the 1051 image data. Allowed values are 'MEDIAN' or 'MEANCLIP'. 1055 badPixelCount : scalar 1056 Number of bad pixels masked. 1057 badPixelValue : scalar 1058 Value substituted for bad pixels. 1063 Raised if `badStatistic` is not an allowed value. 1065 if badStatistic ==
"MEDIAN":
1066 statistic = afwMath.MEDIAN
1067 elif badStatistic ==
"MEANCLIP":
1068 statistic = afwMath.MEANCLIP
1070 raise RuntimeError(
"Impossible method %s of bad region correction" % badStatistic)
1072 mi = exposure.getMaskedImage()
1074 BAD = mask.getPlaneBitMask(
"BAD")
1075 INTRP = mask.getPlaneBitMask(
"INTRP")
1078 sctrl.setAndMask(BAD)
1081 maskArray = mask.getArray()
1082 imageArray = mi.getImage().getArray()
1083 badPixels = numpy.logical_and((maskArray & BAD) > 0, (maskArray & INTRP) == 0)
1084 imageArray[:] = numpy.where(badPixels, value, imageArray)
1086 return badPixels.sum(), value
def illuminationCorrection(maskedImage, illumMaskedImage, illumScale)
Interpolate::Style stringToInterpStyle(std::string const &style)
Conversion function to switch a string to an Interpolate::Style.
def addDistortionModel(exposure, camera)
Update the WCS in exposure with a distortion model based on camera geometry.
def saturationCorrection(maskedImage, saturation, fwhm, growFootprints=1, interpolate=True, maskName='SAT', fallbackValue=None)
def setBadRegions(exposure, badStatistic="MEDIAN")
def brighterFatterCorrection(exposure, kernel, maxIter, threshold, applyGain)
def transposeDefectList(defectList)
A compact representation of a collection of pixels.
Parameters to control convolution.
def getDefectListFromMask(maskedImage, maskName)
def interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=None)
def defectListFromFootprintList(fpList)
Provides consistent interface for LSST exceptions.
def transposeMaskedImage(maskedImage)
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
def interpolateFromMask(maskedImage, fwhm, growFootprints=1, maskName='SAT', fallbackValue=None)
def trimToMatchCalibBBox(rawMaskedImage, calibMaskedImage)
Statistics makeStatistics(lsst::afw::math::MaskedVector< EntryT > const &mv, std::vector< WeightPixel > const &vweights, int const flags, StatisticsControl const &sctrl=StatisticsControl())
The makeStatistics() overload to handle lsst::afw::math::MaskedVector<>
def attachTransmissionCurve(exposure, opticsTransmission=None, filterTransmission=None, sensorTransmission=None, atmosphereTransmission=None)
Property stringToStatisticsProperty(std::string const property)
Conversion function to switch a string to a Property (see Statistics.h)
Pass parameters to a Statistics object.
def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False, trimToFit=False)
def makeThresholdMask(maskedImage, threshold, growFootprints=1, maskName='SAT')
std::shared_ptr< Interpolate > makeInterpolate(ndarray::Array< double const, 1 > const &x, ndarray::Array< double const, 1 > const &y, Interpolate::Style const style=Interpolate::AKIMA_SPLINE)
def widenSaturationTrails(mask)
def applyGains(exposure, normalizeGains=False)
def darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False, trimToFit=False)
def makeDistortedTanWcs(tanWcs, pixelToFocalPlane, focalPlaneToFieldAngle)
def biasCorrection(maskedImage, biasMaskedImage, trimToFit=False)
void convolve(OutImageT &convolvedImage, InImageT const &inImage, KernelT const &kernel, bool doNormalize, bool doCopyEdge=false)
Old, deprecated version of convolve.
def gainContext(exp, image, apply)
def updateVariance(maskedImage, gain, readNoise)
An integer coordinate rectangle.
def overscanCorrection(ampMaskedImage, overscanImage, fitType='MEDIAN', order=1, collapseRej=3.0, statControl=None, overscanIsInt=True)
A kernel created from an Image.
def maskPixelsFromDefectList(maskedImage, defectList, maskName='BAD')