24 from deprecated.sphinx
import deprecated
38 from contextlib
import contextmanager
42 """Make a double Gaussian PSF. 47 FWHM of double Gaussian smoothing kernel. 51 psf : `lsst.meas.algorithms.DoubleGaussianPsf` 52 The created smoothing kernel. 54 ksize = 4*int(fwhm) + 1
55 return measAlg.DoubleGaussianPsf(ksize, ksize, fwhm/(2*math.sqrt(2*math.log(2))))
59 """Make a transposed copy of a masked image. 63 maskedImage : `lsst.afw.image.MaskedImage` 68 transposed : `lsst.afw.image.MaskedImage` 69 The transposed copy of the input image. 71 transposed = maskedImage.Factory(
lsst.geom.Extent2I(maskedImage.getHeight(), maskedImage.getWidth()))
72 transposed.getImage().getArray()[:] = maskedImage.getImage().getArray().T
73 transposed.getMask().getArray()[:] = maskedImage.getMask().getArray().T
74 transposed.getVariance().getArray()[:] = maskedImage.getVariance().getArray().T
79 """Interpolate over defects specified in a defect list. 83 maskedImage : `lsst.afw.image.MaskedImage` 85 defectList : `lsst.meas.algorithms.Defects` 86 List of defects to interpolate over. 88 FWHM of double Gaussian smoothing kernel. 89 fallbackValue : scalar, optional 90 Fallback value if an interpolated value cannot be determined. 91 If None, then the clipped mean of the image is used. 94 if fallbackValue
is None:
96 if 'INTRP' not in maskedImage.getMask().getMaskPlaneDict():
97 maskedImage.getMask().addMaskPlane(
'INTRP')
98 measAlg.interpolateOverDefects(maskedImage, psf, defectList, fallbackValue,
True)
103 """Mask pixels based on threshold detection. 107 maskedImage : `lsst.afw.image.MaskedImage` 108 Image to process. Only the mask plane is updated. 111 growFootprints : scalar, optional 112 Number of pixels to grow footprints of detected regions. 113 maskName : str, optional 114 Mask plane name, or list of names to convert 118 defectList : `lsst.meas.algorithms.Defects` 119 Defect list constructed from pixels above the threshold. 122 thresh = afwDetection.Threshold(threshold)
123 fs = afwDetection.FootprintSet(maskedImage, thresh)
125 if growFootprints > 0:
126 fs = afwDetection.FootprintSet(fs, rGrow=growFootprints, isotropic=
False)
127 fpList = fs.getFootprints()
130 mask = maskedImage.getMask()
131 bitmask = mask.getPlaneBitMask(maskName)
132 afwDetection.setMaskFromFootprintList(mask, fpList, bitmask)
134 return measAlg.Defects.fromFootprintList(fpList)
138 maskNameList=['SAT'], fallbackValue=None):
139 """Interpolate over defects identified by a particular set of mask planes. 143 maskedImage : `lsst.afw.image.MaskedImage` 146 FWHM of double Gaussian smoothing kernel. 147 growSaturatedFootprints : scalar, optional 148 Number of pixels to grow footprints for saturated pixels. 149 maskNameList : `List` of `str`, optional 151 fallbackValue : scalar, optional 152 Value of last resort for interpolation. 154 mask = maskedImage.getMask()
156 if growSaturatedFootprints > 0
and "SAT" in maskNameList:
157 thresh = afwDetection.Threshold(mask.getPlaneBitMask(
"SAT"), afwDetection.Threshold.BITMASK)
158 fpSet = afwDetection.FootprintSet(mask, thresh)
161 fpSet = afwDetection.FootprintSet(fpSet, rGrow=growSaturatedFootprints, isotropic=
False)
162 fpSet.setMask(mask,
"SAT")
164 thresh = afwDetection.Threshold(mask.getPlaneBitMask(maskNameList), afwDetection.Threshold.BITMASK)
165 fpSet = afwDetection.FootprintSet(mask, thresh)
166 defectList = measAlg.Defects.fromFootprintList(fpSet.getFootprints())
173 def saturationCorrection(maskedImage, saturation, fwhm, growFootprints=1, interpolate=True, maskName='SAT',
175 """Mark saturated pixels and optionally interpolate over them 179 maskedImage : `lsst.afw.image.MaskedImage` 182 Saturation level used as the detection threshold. 184 FWHM of double Gaussian smoothing kernel. 185 growFootprints : scalar, optional 186 Number of pixels to grow footprints of detected regions. 187 interpolate : Bool, optional 188 If True, saturated pixels are interpolated over. 189 maskName : str, optional 191 fallbackValue : scalar, optional 192 Value of last resort for interpolation. 195 maskedImage=maskedImage,
196 threshold=saturation,
197 growFootprints=growFootprints,
207 """Compute number of edge trim pixels to match the calibration data. 209 Use the dimension difference between the raw exposure and the 210 calibration exposure to compute the edge trim pixels. This trim 211 is applied symmetrically, with the same number of pixels masked on 216 rawMaskedImage : `lsst.afw.image.MaskedImage` 218 calibMaskedImage : `lsst.afw.image.MaskedImage` 219 Calibration image to draw new bounding box from. 223 replacementMaskedImage : `lsst.afw.image.MaskedImage` 224 ``rawMaskedImage`` trimmed to the appropriate size 228 Rasied if ``rawMaskedImage`` cannot be symmetrically trimmed to 229 match ``calibMaskedImage``. 231 nx, ny = rawMaskedImage.getBBox().getDimensions() - calibMaskedImage.getBBox().getDimensions()
233 raise RuntimeError(
"Raw and calib maskedImages are trimmed differently in X and Y.")
235 raise RuntimeError(
"Calibration maskedImage is trimmed unevenly in X.")
237 raise RuntimeError(
"Calibration maskedImage is larger than raw data.")
241 replacementMaskedImage = rawMaskedImage[nEdge:-nEdge, nEdge:-nEdge, afwImage.LOCAL]
242 SourceDetectionTask.setEdgeBits(
244 replacementMaskedImage.getBBox(),
245 rawMaskedImage.getMask().getPlaneBitMask(
"EDGE")
248 replacementMaskedImage = rawMaskedImage
250 return replacementMaskedImage
254 """Apply bias correction in place. 258 maskedImage : `lsst.afw.image.MaskedImage` 259 Image to process. The image is modified by this method. 260 biasMaskedImage : `lsst.afw.image.MaskedImage` 261 Bias image of the same size as ``maskedImage`` 262 trimToFit : `Bool`, optional 263 If True, raw data is symmetrically trimmed to match 269 Raised if ``maskedImage`` and ``biasMaskedImage`` do not have 276 if maskedImage.getBBox(afwImage.LOCAL) != biasMaskedImage.getBBox(afwImage.LOCAL):
277 raise RuntimeError(
"maskedImage bbox %s != biasMaskedImage bbox %s" %
278 (maskedImage.getBBox(afwImage.LOCAL), biasMaskedImage.getBBox(afwImage.LOCAL)))
279 maskedImage -= biasMaskedImage
282 def darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False, trimToFit=False):
283 """Apply dark correction in place. 287 maskedImage : `lsst.afw.image.MaskedImage` 288 Image to process. The image is modified by this method. 289 darkMaskedImage : `lsst.afw.image.MaskedImage` 290 Dark image of the same size as ``maskedImage``. 292 Dark exposure time for ``maskedImage``. 294 Dark exposure time for ``darkMaskedImage``. 295 invert : `Bool`, optional 296 If True, re-add the dark to an already corrected image. 297 trimToFit : `Bool`, optional 298 If True, raw data is symmetrically trimmed to match 304 Raised if ``maskedImage`` and ``darkMaskedImage`` do not have 309 The dark correction is applied by calculating: 310 maskedImage -= dark * expScaling / darkScaling 315 if maskedImage.getBBox(afwImage.LOCAL) != darkMaskedImage.getBBox(afwImage.LOCAL):
316 raise RuntimeError(
"maskedImage bbox %s != darkMaskedImage bbox %s" %
317 (maskedImage.getBBox(afwImage.LOCAL), darkMaskedImage.getBBox(afwImage.LOCAL)))
319 scale = expScale / darkScale
321 maskedImage.scaledMinus(scale, darkMaskedImage)
323 maskedImage.scaledPlus(scale, darkMaskedImage)
327 """Set the variance plane based on the image plane. 331 maskedImage : `lsst.afw.image.MaskedImage` 332 Image to process. The variance plane is modified. 334 The amplifier gain in electrons/ADU. 336 The amplifier read nmoise in ADU/pixel. 338 var = maskedImage.getVariance()
339 var[:] = maskedImage.getImage()
344 def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False, trimToFit=False):
345 """Apply flat correction in place. 349 maskedImage : `lsst.afw.image.MaskedImage` 350 Image to process. The image is modified. 351 flatMaskedImage : `lsst.afw.image.MaskedImage` 352 Flat image of the same size as ``maskedImage`` 354 Flat scale computation method. Allowed values are 'MEAN', 356 userScale : scalar, optional 357 Scale to use if ``scalingType``='USER'. 358 invert : `Bool`, optional 359 If True, unflatten an already flattened image. 360 trimToFit : `Bool`, optional 361 If True, raw data is symmetrically trimmed to match 367 Raised if ``maskedImage`` and ``flatMaskedImage`` do not have 368 the same size or if ``scalingType`` is not an allowed value. 373 if maskedImage.getBBox(afwImage.LOCAL) != flatMaskedImage.getBBox(afwImage.LOCAL):
374 raise RuntimeError(
"maskedImage bbox %s != flatMaskedImage bbox %s" %
375 (maskedImage.getBBox(afwImage.LOCAL), flatMaskedImage.getBBox(afwImage.LOCAL)))
380 if scalingType
in (
'MEAN',
'MEDIAN'):
383 elif scalingType ==
'USER':
384 flatScale = userScale
386 raise RuntimeError(
'%s : %s not implemented' % (
"flatCorrection", scalingType))
389 maskedImage.scaledDivides(1.0/flatScale, flatMaskedImage)
391 maskedImage.scaledMultiplies(1.0/flatScale, flatMaskedImage)
395 """Apply illumination correction in place. 399 maskedImage : `lsst.afw.image.MaskedImage` 400 Image to process. The image is modified. 401 illumMaskedImage : `lsst.afw.image.MaskedImage` 402 Illumination correction image of the same size as ``maskedImage``. 404 Scale factor for the illumination correction. 405 trimToFit : `Bool`, optional 406 If True, raw data is symmetrically trimmed to match 412 Raised if ``maskedImage`` and ``illumMaskedImage`` do not have 418 if maskedImage.getBBox(afwImage.LOCAL) != illumMaskedImage.getBBox(afwImage.LOCAL):
419 raise RuntimeError(
"maskedImage bbox %s != illumMaskedImage bbox %s" %
420 (maskedImage.getBBox(afwImage.LOCAL), illumMaskedImage.getBBox(afwImage.LOCAL)))
422 maskedImage.scaledDivides(1.0/illumScale, illumMaskedImage)
425 def overscanCorrection(ampMaskedImage, overscanImage, fitType='MEDIAN', order=1, collapseRej=3.0,
426 statControl=None, overscanIsInt=True):
427 """Apply overscan correction in place. 431 ampMaskedImage : `lsst.afw.image.MaskedImage` 432 Image of amplifier to correct; modified. 433 overscanImage : `lsst.afw.image.Image` or `lsst.afw.image.MaskedImage` 434 Image of overscan; modified. 436 Type of fit for overscan correction. May be one of: 438 - ``MEAN``: use mean of overscan. 439 - ``MEANCLIP``: use clipped mean of overscan. 440 - ``MEDIAN``: use median of overscan. 441 - ``POLY``: fit with ordinary polynomial. 442 - ``CHEB``: fit with Chebyshev polynomial. 443 - ``LEG``: fit with Legendre polynomial. 444 - ``NATURAL_SPLINE``: fit with natural spline. 445 - ``CUBIC_SPLINE``: fit with cubic spline. 446 - ``AKIMA_SPLINE``: fit with Akima spline. 449 Polynomial order or number of spline knots; ignored unless 450 ``fitType`` indicates a polynomial or spline. 451 statControl : `lsst.afw.math.StatisticsControl` 452 Statistics control object. In particular, we pay attention to numSigmaClip 453 overscanIsInt : `bool` 454 Treat the overscan region as consisting of integers, even if it's been 455 converted to float. E.g. handle ties properly. 459 result : `lsst.pipe.base.Struct` 460 Result struct with components: 462 - ``imageFit``: Value(s) removed from image (scalar or 463 `lsst.afw.image.Image`) 464 - ``overscanFit``: Value(s) removed from overscan (scalar or 465 `lsst.afw.image.Image`) 466 - ``overscanImage``: Overscan corrected overscan region 467 (`lsst.afw.image.Image`) 471 Raised if ``fitType`` is not an allowed value. 475 The ``ampMaskedImage`` and ``overscanImage`` are modified, with the fit 476 subtracted. Note that the ``overscanImage`` should not be a subimage of 477 the ``ampMaskedImage``, to avoid being subtracted twice. 479 Debug plots are available for the SPLINE fitTypes by setting the 480 `debug.display` for `name` == "lsst.ip.isr.isrFunctions". These 481 plots show the scatter plot of the overscan data (collapsed along 482 the perpendicular dimension) as a function of position on the CCD 483 (normalized between +/-1). 485 ampImage = ampMaskedImage.getImage()
486 if statControl
is None:
489 numSigmaClip = statControl.getNumSigmaClip()
491 if fitType
in (
'MEAN',
'MEANCLIP'):
494 overscanFit = offImage
495 elif fitType
in (
'MEDIAN',):
498 if hasattr(overscanImage,
"image"):
499 imageI = overscanImage.image.convertI()
500 overscanImageI = afwImage.MaskedImageI(imageI, overscanImage.mask, overscanImage.variance)
502 overscanImageI = overscanImage.convertI()
504 overscanImageI = overscanImage
508 overscanFit = offImage
512 elif fitType
in (
'POLY',
'CHEB',
'LEG',
'NATURAL_SPLINE',
'CUBIC_SPLINE',
'AKIMA_SPLINE'):
513 if hasattr(overscanImage,
"getImage"):
514 biasArray = overscanImage.getImage().getArray()
515 biasArray = numpy.ma.masked_where(overscanImage.getMask().getArray() & statControl.getAndMask(),
518 biasArray = overscanImage.getArray()
520 shortInd = numpy.argmin(biasArray.shape)
523 biasArray = numpy.transpose(biasArray)
526 percentiles = numpy.percentile(biasArray, [25.0, 50.0, 75.0], axis=1)
527 medianBiasArr = percentiles[1]
528 stdevBiasArr = 0.74*(percentiles[2] - percentiles[0])
529 diff = numpy.abs(biasArray - medianBiasArr[:, numpy.newaxis])
530 biasMaskedArr = numpy.ma.masked_where(diff > numSigmaClip*stdevBiasArr[:, numpy.newaxis], biasArray)
531 collapsed = numpy.mean(biasMaskedArr, axis=1)
532 if collapsed.mask.sum() > 0:
533 collapsed.data[collapsed.mask] = numpy.mean(biasArray.data[collapsed.mask], axis=1)
534 del biasArray, percentiles, stdevBiasArr, diff, biasMaskedArr
537 collapsed = numpy.transpose(collapsed)
540 indices = 2.0*numpy.arange(num)/float(num) - 1.0
542 if fitType
in (
'POLY',
'CHEB',
'LEG'):
544 poly = numpy.polynomial
545 fitter, evaler = {
"POLY": (poly.polynomial.polyfit, poly.polynomial.polyval),
546 "CHEB": (poly.chebyshev.chebfit, poly.chebyshev.chebval),
547 "LEG": (poly.legendre.legfit, poly.legendre.legval),
550 coeffs =
fitter(indices, collapsed, order)
551 fitBiasArr = evaler(indices, coeffs)
552 elif 'SPLINE' in fitType:
561 collapsedMask = collapsed.mask
563 if collapsedMask == numpy.ma.nomask:
564 collapsedMask = numpy.array(len(collapsed)*[numpy.ma.nomask])
568 numPerBin, binEdges = numpy.histogram(indices, bins=numBins,
569 weights=1-collapsedMask.astype(int))
572 with numpy.errstate(invalid=
"ignore"):
573 values = numpy.histogram(indices, bins=numBins,
574 weights=collapsed.data*~collapsedMask)[0]/numPerBin
575 binCenters = numpy.histogram(indices, bins=numBins,
576 weights=indices*~collapsedMask)[0]/numPerBin
578 values.astype(float)[numPerBin > 0],
580 fitBiasArr = numpy.array([interp.interpolate(i)
for i
in indices])
584 import matplotlib.pyplot
as plot
585 figure = plot.figure(1)
587 axes = figure.add_axes((0.1, 0.1, 0.8, 0.8))
588 axes.plot(indices[~collapsedMask], collapsed[~collapsedMask],
'k+')
589 if collapsedMask.sum() > 0:
590 axes.plot(indices[collapsedMask], collapsed.data[collapsedMask],
'b+')
591 axes.plot(indices, fitBiasArr,
'r-')
592 plot.xlabel(
"centered/scaled position along overscan region")
593 plot.ylabel(
"pixel value/fit value")
595 prompt =
"Press Enter or c to continue [chp]... " 597 ans = input(prompt).lower()
598 if ans
in (
"",
"c",):
604 print(
"h[elp] c[ontinue] p[db]")
607 offImage = ampImage.Factory(ampImage.getDimensions())
608 offArray = offImage.getArray()
609 overscanFit = afwImage.ImageF(overscanImage.getDimensions())
610 overscanArray = overscanFit.getArray()
612 offArray[:, :] = fitBiasArr[:, numpy.newaxis]
613 overscanArray[:, :] = fitBiasArr[:, numpy.newaxis]
615 offArray[:, :] = fitBiasArr[numpy.newaxis, :]
616 overscanArray[:, :] = fitBiasArr[numpy.newaxis, :]
624 mask = ampMaskedImage.getMask()
625 maskArray = mask.getArray()
if shortInd == 1
else mask.getArray().transpose()
626 suspect = mask.getPlaneBitMask(
"SUSPECT")
628 if collapsed.mask == numpy.ma.nomask:
632 for low
in range(num):
633 if not collapsed.mask[low]:
636 maskArray[:low, :] |= suspect
637 for high
in range(1, num):
638 if not collapsed.mask[-high]:
641 maskArray[-high:, :] |= suspect
644 raise pexExcept.Exception(
'%s : %s an invalid overscan type' % (
"overscanCorrection", fitType))
646 overscanImage -= overscanFit
647 return Struct(imageFit=offImage, overscanFit=overscanFit, overscanImage=overscanImage)
651 """Apply brighter fatter correction in place for the image. 655 exposure : `lsst.afw.image.Exposure` 656 Exposure to have brighter-fatter correction applied. Modified 658 kernel : `numpy.ndarray` 659 Brighter-fatter kernel to apply. 661 Number of correction iterations to run. 663 Convergence threshold in terms of the sum of absolute 664 deviations between an iteration and the previous one. 666 If True, then the exposure values are scaled by the gain prior 668 gains : `dict` [`str`, `float`] 669 A dictionary, keyed by amplifier name, of the gains to use. 670 If gains is None, the nominal gains in the amplifier object are used. 675 Final difference between iterations achieved in correction. 677 Number of iterations used to calculate correction. 681 This correction takes a kernel that has been derived from flat 682 field images to redistribute the charge. The gradient of the 683 kernel is the deflection field due to the accumulated charge. 685 Given the original image I(x) and the kernel K(x) we can compute 686 the corrected image Ic(x) using the following equation: 688 Ic(x) = I(x) + 0.5*d/dx(I(x)*d/dx(int( dy*K(x-y)*I(y)))) 690 To evaluate the derivative term we expand it as follows: 692 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))) ) 694 Because we use the measured counts instead of the incident counts 695 we apply the correction iteratively to reconstruct the original 696 counts and the correction. We stop iterating when the summed 697 difference between the current corrected image and the one from 698 the previous iteration is below the threshold. We do not require 699 convergence because the number of iterations is too large a 700 computational cost. How we define the threshold still needs to be 701 evaluated, the current default was shown to work reasonably well 702 on a small set of images. For more information on the method see 703 DocuShare Document-19407. 705 The edges as defined by the kernel are not corrected because they 706 have spurious values due to the convolution. 708 image = exposure.getMaskedImage().getImage()
711 with
gainContext(exposure, image, applyGain, gains):
713 kLx = numpy.shape(kernel)[0]
714 kLy = numpy.shape(kernel)[1]
715 kernelImage = afwImage.ImageD(kLx, kLy)
716 kernelImage.getArray()[:, :] = kernel
717 tempImage = image.clone()
719 nanIndex = numpy.isnan(tempImage.getArray())
720 tempImage.getArray()[nanIndex] = 0.
722 outImage = afwImage.ImageF(image.getDimensions())
723 corr = numpy.zeros_like(image.getArray())
724 prev_image = numpy.zeros_like(image.getArray())
736 for iteration
in range(maxIter):
739 tmpArray = tempImage.getArray()
740 outArray = outImage.getArray()
742 with numpy.errstate(invalid=
"ignore", over=
"ignore"):
744 gradTmp = numpy.gradient(tmpArray[startY:endY, startX:endX])
745 gradOut = numpy.gradient(outArray[startY:endY, startX:endX])
746 first = (gradTmp[0]*gradOut[0] + gradTmp[1]*gradOut[1])[1:-1, 1:-1]
749 diffOut20 = numpy.diff(outArray, 2, 0)[startY:endY, startX + 1:endX - 1]
750 diffOut21 = numpy.diff(outArray, 2, 1)[startY + 1:endY - 1, startX:endX]
751 second = tmpArray[startY + 1:endY - 1, startX + 1:endX - 1]*(diffOut20 + diffOut21)
753 corr[startY + 1:endY - 1, startX + 1:endX - 1] = 0.5*(first + second)
755 tmpArray[:, :] = image.getArray()[:, :]
756 tmpArray[nanIndex] = 0.
757 tmpArray[startY:endY, startX:endX] += corr[startY:endY, startX:endX]
760 diff = numpy.sum(numpy.abs(prev_image - tmpArray))
764 prev_image[:, :] = tmpArray[:, :]
766 image.getArray()[startY + 1:endY - 1, startX + 1:endX - 1] += \
767 corr[startY + 1:endY - 1, startX + 1:endX - 1]
769 return diff, iteration
774 """Context manager that applies and removes gain. 778 exp : `lsst.afw.image.Exposure` 779 Exposure to apply/remove gain. 780 image : `lsst.afw.image.Image` 781 Image to apply/remove gain. 783 If True, apply and remove the amplifier gain. 784 gains : `dict` [`str`, `float`] 785 A dictionary, keyed by amplifier name, of the gains to use. 786 If gains is None, the nominal gains in the amplifier object are used. 790 exp : `lsst.afw.image.Exposure` 791 Exposure with the gain applied. 795 if gains
and apply
is True:
796 ampNames = [amp.getName()
for amp
in exp.getDetector()]
797 for ampName
in ampNames:
798 if ampName
not in gains.keys():
799 raise RuntimeError(f
"Gains provided to gain context, but no entry found for amp {ampName}")
802 ccd = exp.getDetector()
804 sim = image.Factory(image, amp.getBBox())
806 gain = gains[amp.getName()]
815 ccd = exp.getDetector()
817 sim = image.Factory(image, amp.getBBox())
819 gain = gains[amp.getName()]
826 sensorTransmission=None, atmosphereTransmission=None):
827 """Attach a TransmissionCurve to an Exposure, given separate curves for 828 different components. 832 exposure : `lsst.afw.image.Exposure` 833 Exposure object to modify by attaching the product of all given 834 ``TransmissionCurves`` in post-assembly trimmed detector coordinates. 835 Must have a valid ``Detector`` attached that matches the detector 836 associated with sensorTransmission. 837 opticsTransmission : `lsst.afw.image.TransmissionCurve` 838 A ``TransmissionCurve`` that represents the throughput of the optics, 839 to be evaluated in focal-plane coordinates. 840 filterTransmission : `lsst.afw.image.TransmissionCurve` 841 A ``TransmissionCurve`` that represents the throughput of the filter 842 itself, to be evaluated in focal-plane coordinates. 843 sensorTransmission : `lsst.afw.image.TransmissionCurve` 844 A ``TransmissionCurve`` that represents the throughput of the sensor 845 itself, to be evaluated in post-assembly trimmed detector coordinates. 846 atmosphereTransmission : `lsst.afw.image.TransmissionCurve` 847 A ``TransmissionCurve`` that represents the throughput of the 848 atmosphere, assumed to be spatially constant. 852 combined : `lsst.afw.image.TransmissionCurve` 853 The TransmissionCurve attached to the exposure. 857 All ``TransmissionCurve`` arguments are optional; if none are provided, the 858 attached ``TransmissionCurve`` will have unit transmission everywhere. 860 combined = afwImage.TransmissionCurve.makeIdentity()
861 if atmosphereTransmission
is not None:
862 combined *= atmosphereTransmission
863 if opticsTransmission
is not None:
864 combined *= opticsTransmission
865 if filterTransmission
is not None:
866 combined *= filterTransmission
867 detector = exposure.getDetector()
868 fpToPix = detector.getTransform(fromSys=camGeom.FOCAL_PLANE,
869 toSys=camGeom.PIXELS)
870 combined = combined.transformedBy(fpToPix)
871 if sensorTransmission
is not None:
872 combined *= sensorTransmission
873 exposure.getInfo().setTransmissionCurve(combined)
877 @deprecated(reason=
"Camera geometry-based SkyWcs are now set when reading raws. To be removed after v19.",
878 category=FutureWarning)
880 """!Update the WCS in exposure with a distortion model based on camera 885 exposure : `lsst.afw.image.Exposure` 886 Exposure to process. Must contain a Detector and WCS. The 887 exposure is modified. 888 camera : `lsst.afw.cameraGeom.Camera` 894 Raised if ``exposure`` is lacking a Detector or WCS, or if 898 Add a model for optical distortion based on geometry found in ``camera`` 899 and the ``exposure``'s detector. The raw input exposure is assumed 900 have a TAN WCS that has no compensation for optical distortion. 901 Two other possibilities are: 902 - The raw input exposure already has a model for optical distortion, 903 as is the case for raw DECam data. 904 In that case you should set config.doAddDistortionModel False. 905 - The raw input exposure has a model for distortion, but it has known 906 deficiencies severe enough to be worth fixing (e.g. because they 907 cause problems for fitting a better WCS). In that case you should 908 override this method with a version suitable for your raw data. 911 wcs = exposure.getWcs()
913 raise RuntimeError(
"exposure has no WCS")
915 raise RuntimeError(
"camera is None")
916 detector = exposure.getDetector()
918 raise RuntimeError(
"exposure has no Detector")
919 pixelToFocalPlane = detector.getTransform(camGeom.PIXELS, camGeom.FOCAL_PLANE)
920 focalPlaneToFieldAngle = camera.getTransformMap().getTransform(camGeom.FOCAL_PLANE,
923 exposure.setWcs(distortedWcs)
927 """Scale an exposure by the amplifier gains. 931 exposure : `lsst.afw.image.Exposure` 932 Exposure to process. The image is modified. 933 normalizeGains : `Bool`, optional 934 If True, then amplifiers are scaled to force the median of 935 each amplifier to equal the median of those medians. 937 ccd = exposure.getDetector()
938 ccdImage = exposure.getMaskedImage()
942 sim = ccdImage.Factory(ccdImage, amp.getBBox())
946 medians.append(numpy.median(sim.getImage().getArray()))
949 median = numpy.median(numpy.array(medians))
950 for index, amp
in enumerate(ccd):
951 sim = ccdImage.Factory(ccdImage, amp.getBBox())
952 if medians[index] != 0.0:
953 sim *= median/medians[index]
957 """Grow the saturation trails by an amount dependent on the width of the trail. 961 mask : `lsst.afw.image.Mask` 962 Mask which will have the saturated areas grown. 966 for i
in range(1, 6):
968 for i
in range(6, 8):
970 for i
in range(8, 10):
974 if extraGrowMax <= 0:
977 saturatedBit = mask.getPlaneBitMask(
"SAT")
979 xmin, ymin = mask.getBBox().getMin()
980 width = mask.getWidth()
982 thresh = afwDetection.Threshold(saturatedBit, afwDetection.Threshold.BITMASK)
983 fpList = afwDetection.FootprintSet(mask, thresh).getFootprints()
986 for s
in fp.getSpans():
987 x0, x1 = s.getX0(), s.getX1()
989 extraGrow = extraGrowDict.get(x1 - x0 + 1, extraGrowMax)
992 x0 -= xmin + extraGrow
993 x1 -= xmin - extraGrow
1000 mask.array[y, x0:x1+1] |= saturatedBit
1004 """Set all BAD areas of the chip to the average of the rest of the exposure 1008 exposure : `lsst.afw.image.Exposure` 1009 Exposure to mask. The exposure mask is modified. 1010 badStatistic : `str`, optional 1011 Statistic to use to generate the replacement value from the 1012 image data. Allowed values are 'MEDIAN' or 'MEANCLIP'. 1016 badPixelCount : scalar 1017 Number of bad pixels masked. 1018 badPixelValue : scalar 1019 Value substituted for bad pixels. 1024 Raised if `badStatistic` is not an allowed value. 1026 if badStatistic ==
"MEDIAN":
1027 statistic = afwMath.MEDIAN
1028 elif badStatistic ==
"MEANCLIP":
1029 statistic = afwMath.MEANCLIP
1031 raise RuntimeError(
"Impossible method %s of bad region correction" % badStatistic)
1033 mi = exposure.getMaskedImage()
1035 BAD = mask.getPlaneBitMask(
"BAD")
1036 INTRP = mask.getPlaneBitMask(
"INTRP")
1039 sctrl.setAndMask(BAD)
1042 maskArray = mask.getArray()
1043 imageArray = mi.getImage().getArray()
1044 badPixels = numpy.logical_and((maskArray & BAD) > 0, (maskArray & INTRP) == 0)
1045 imageArray[:] = numpy.where(badPixels, value, imageArray)
1047 return badPixels.sum(), value
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")
Parameters to control convolution.
def interpolateFromMask(maskedImage, fwhm, growSaturatedFootprints=1, maskNameList=['SAT'], fallbackValue=None)
def interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=None)
Provides consistent interface for LSST exceptions.
def brighterFatterCorrection(exposure, kernel, maxIter, threshold, applyGain, gains=None)
def transposeMaskedImage(maskedImage)
Fit spatial kernel using approximate fluxes for candidates, and solving a linear system of equations...
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.
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
def gainContext(exp, image, apply, gains=None)
def illuminationCorrection(maskedImage, illumMaskedImage, illumScale, trimToFit=True)
def updateVariance(maskedImage, gain, readNoise)
def overscanCorrection(ampMaskedImage, overscanImage, fitType='MEDIAN', order=1, collapseRej=3.0, statControl=None, overscanIsInt=True)
A kernel created from an Image.