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:
95 fallbackValue = afwMath.makeStatistics(maskedImage.getImage(), afwMath.MEANCLIP).getValue()
96 if 'INTRP' not in maskedImage.getMask().getMaskPlaneDict():
97 maskedImage.getMask().addMaskPlane(
'INTRP')
98 measAlg.interpolateOverDefects(maskedImage, psf, defectList, fallbackValue,
True)
102 @deprecated(reason=
"Replaced by Defects.fromFootPrintList() (will be removed after v18)",
103 category=FutureWarning)
105 """Compute a defect list from a footprint list, optionally growing the footprints. 109 fpList : `list` of `lsst.afw.detection.Footprint` 110 Footprint list to process. 114 defectList : `lsst.meas.algorithms.Defects` 117 return measAlg.Defects.fromFootprintList(fpList)
119 @deprecated(reason=
"Replaced by Defects.transpose() (will be removed after v18)",
120 category=FutureWarning)
122 """Make a transposed copy of a defect list. 126 defectList : `lsst.meas.algorithms.Defects` 127 Input list of defects. 131 retDefectList : `lsst.meas.algorithms.Defects` 132 Transposed list of defects. 134 if isinstance(defectList, measAlg.Defects):
135 return defectList.transpose()
136 return measAlg.Defects(defectList).transpose()
139 @deprecated(reason=
"Replaced by Defects.maskPixels() (will be removed after v18)",
140 category=FutureWarning)
142 """Set mask plane based on a defect list. 146 maskedImage : `lsst.afw.image.MaskedImage` 147 Image to process. Only the mask plane is updated. 148 defectList : `lsst.meas.algorithms.Defects` 150 maskName : str, optional 151 Mask plane name to use. 153 return lsst.meas.algorithms.Defects(defectList).maskPixels(maskedImage, maskName=maskName)
156 @deprecated(reason=
"Replaced by Defects.fromMask() (will be removed after v18)",
157 category=FutureWarning)
159 """Compute a defect list from a specified mask plane. 163 maskedImage : `lsst.afw.image.MaskedImage` 165 maskName : `str` or `list` 166 Mask plane name, or list of names to convert. 170 defectList : `lsst.meas.algorithms.Defects` 171 Defect list constructed from masked pixels. 173 return measAlg.Defects.fromMask(maskedImage, maskName)
177 """Mask pixels based on threshold detection. 181 maskedImage : `lsst.afw.image.MaskedImage` 182 Image to process. Only the mask plane is updated. 185 growFootprints : scalar, optional 186 Number of pixels to grow footprints of detected regions. 187 maskName : str, optional 188 Mask plane name, or list of names to convert 192 defectList : `lsst.meas.algorithms.Defects` 193 Defect list constructed from pixels above the threshold. 196 thresh = afwDetection.Threshold(threshold)
197 fs = afwDetection.FootprintSet(maskedImage, thresh)
199 if growFootprints > 0:
200 fs = afwDetection.FootprintSet(fs, rGrow=growFootprints, isotropic=
False)
201 fpList = fs.getFootprints()
204 mask = maskedImage.getMask()
205 bitmask = mask.getPlaneBitMask(maskName)
206 afwDetection.setMaskFromFootprintList(mask, fpList, bitmask)
208 return measAlg.Defects.fromFootprintList(fpList)
212 maskNameList=['SAT'], fallbackValue=None):
213 """Interpolate over defects identified by a particular set of mask planes. 217 maskedImage : `lsst.afw.image.MaskedImage` 220 FWHM of double Gaussian smoothing kernel. 221 growSaturatedFootprints : scalar, optional 222 Number of pixels to grow footprints for saturated pixels. 223 maskNameList : `List` of `str`, optional 225 fallbackValue : scalar, optional 226 Value of last resort for interpolation. 228 mask = maskedImage.getMask()
230 if growSaturatedFootprints > 0
and "SAT" in maskNameList:
231 thresh = afwDetection.Threshold(mask.getPlaneBitMask(
"SAT"), afwDetection.Threshold.BITMASK)
232 fpSet = afwDetection.FootprintSet(mask, thresh)
235 fpSet = afwDetection.FootprintSet(fpSet, rGrow=growSaturatedFootprints, isotropic=
False)
236 fpSet.setMask(mask,
"SAT")
238 thresh = afwDetection.Threshold(mask.getPlaneBitMask(maskNameList), afwDetection.Threshold.BITMASK)
239 fpSet = afwDetection.FootprintSet(mask, thresh)
240 defectList = measAlg.Defects.fromFootprintList(fpSet.getFootprints())
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,
281 """Compute number of edge trim pixels to match the calibration data. 283 Use the dimension difference between the raw exposure and the 284 calibration exposure to compute the edge trim pixels. This trim 285 is applied symmetrically, with the same number of pixels masked on 290 rawMaskedImage : `lsst.afw.image.MaskedImage` 292 calibMaskedImage : `lsst.afw.image.MaskedImage` 293 Calibration image to draw new bounding box from. 297 replacementMaskedImage : `lsst.afw.image.MaskedImage` 298 ``rawMaskedImage`` trimmed to the appropriate size 302 Rasied if ``rawMaskedImage`` cannot be symmetrically trimmed to 303 match ``calibMaskedImage``. 305 nx, ny = rawMaskedImage.getBBox().getDimensions() - calibMaskedImage.getBBox().getDimensions()
307 raise RuntimeError(
"Raw and calib maskedImages are trimmed differently in X and Y.")
309 raise RuntimeError(
"Calibration maskedImage is trimmed unevenly in X.")
311 raise RuntimeError(
"Calibration maskedImage is larger than raw data.")
315 replacementMaskedImage = rawMaskedImage[nEdge:-nEdge, nEdge:-nEdge, afwImage.LOCAL]
316 SourceDetectionTask.setEdgeBits(
318 replacementMaskedImage.getBBox(),
319 rawMaskedImage.getMask().getPlaneBitMask(
"EDGE")
322 replacementMaskedImage = rawMaskedImage
324 return replacementMaskedImage
328 """Apply bias correction in place. 332 maskedImage : `lsst.afw.image.MaskedImage` 333 Image to process. The image is modified by this method. 334 biasMaskedImage : `lsst.afw.image.MaskedImage` 335 Bias image of the same size as ``maskedImage`` 336 trimToFit : `Bool`, optional 337 If True, raw data is symmetrically trimmed to match 343 Raised if ``maskedImage`` and ``biasMaskedImage`` do not have 350 if maskedImage.getBBox(afwImage.LOCAL) != biasMaskedImage.getBBox(afwImage.LOCAL):
351 raise RuntimeError(
"maskedImage bbox %s != biasMaskedImage bbox %s" %
352 (maskedImage.getBBox(afwImage.LOCAL), biasMaskedImage.getBBox(afwImage.LOCAL)))
353 maskedImage -= biasMaskedImage
356 def darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False, trimToFit=False):
357 """Apply dark correction in place. 361 maskedImage : `lsst.afw.image.MaskedImage` 362 Image to process. The image is modified by this method. 363 darkMaskedImage : `lsst.afw.image.MaskedImage` 364 Dark image of the same size as ``maskedImage``. 366 Dark exposure time for ``maskedImage``. 368 Dark exposure time for ``darkMaskedImage``. 369 invert : `Bool`, optional 370 If True, re-add the dark to an already corrected image. 371 trimToFit : `Bool`, optional 372 If True, raw data is symmetrically trimmed to match 378 Raised if ``maskedImage`` and ``darkMaskedImage`` do not have 383 The dark correction is applied by calculating: 384 maskedImage -= dark * expScaling / darkScaling 389 if maskedImage.getBBox(afwImage.LOCAL) != darkMaskedImage.getBBox(afwImage.LOCAL):
390 raise RuntimeError(
"maskedImage bbox %s != darkMaskedImage bbox %s" %
391 (maskedImage.getBBox(afwImage.LOCAL), darkMaskedImage.getBBox(afwImage.LOCAL)))
393 scale = expScale / darkScale
395 maskedImage.scaledMinus(scale, darkMaskedImage)
397 maskedImage.scaledPlus(scale, darkMaskedImage)
401 """Set the variance plane based on the image plane. 405 maskedImage : `lsst.afw.image.MaskedImage` 406 Image to process. The variance plane is modified. 408 The amplifier gain in electrons/ADU. 410 The amplifier read nmoise in ADU/pixel. 412 var = maskedImage.getVariance()
413 var[:] = maskedImage.getImage()
418 def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False, trimToFit=False):
419 """Apply flat correction in place. 423 maskedImage : `lsst.afw.image.MaskedImage` 424 Image to process. The image is modified. 425 flatMaskedImage : `lsst.afw.image.MaskedImage` 426 Flat image of the same size as ``maskedImage`` 428 Flat scale computation method. Allowed values are 'MEAN', 430 userScale : scalar, optional 431 Scale to use if ``scalingType``='USER'. 432 invert : `Bool`, optional 433 If True, unflatten an already flattened image. 434 trimToFit : `Bool`, optional 435 If True, raw data is symmetrically trimmed to match 441 Raised if ``maskedImage`` and ``flatMaskedImage`` do not have 442 the same size or 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'):
455 scalingType = afwMath.stringToStatisticsProperty(scalingType)
456 flatScale = afwMath.makeStatistics(flatMaskedImage.image, scalingType).getValue()
457 elif scalingType ==
'USER':
458 flatScale = userScale
460 raise RuntimeError(
'%s : %s not implemented' % (
"flatCorrection", scalingType))
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. 479 trimToFit : `Bool`, optional 480 If True, raw data is symmetrically trimmed to match 486 Raised if ``maskedImage`` and ``illumMaskedImage`` do not have 492 if maskedImage.getBBox(afwImage.LOCAL) != illumMaskedImage.getBBox(afwImage.LOCAL):
493 raise RuntimeError(
"maskedImage bbox %s != illumMaskedImage bbox %s" %
494 (maskedImage.getBBox(afwImage.LOCAL), illumMaskedImage.getBBox(afwImage.LOCAL)))
496 maskedImage.scaledDivides(1.0/illumScale, illumMaskedImage)
499 def overscanCorrection(ampMaskedImage, overscanImage, fitType='MEDIAN', order=1, collapseRej=3.0,
500 statControl=None, overscanIsInt=True):
501 """Apply overscan correction in place. 505 ampMaskedImage : `lsst.afw.image.MaskedImage` 506 Image of amplifier to correct; modified. 507 overscanImage : `lsst.afw.image.Image` or `lsst.afw.image.MaskedImage` 508 Image of overscan; modified. 510 Type of fit for overscan correction. May be one of: 512 - ``MEAN``: use mean of overscan. 513 - ``MEANCLIP``: use clipped mean of overscan. 514 - ``MEDIAN``: use median of overscan. 515 - ``POLY``: fit with ordinary polynomial. 516 - ``CHEB``: fit with Chebyshev polynomial. 517 - ``LEG``: fit with Legendre polynomial. 518 - ``NATURAL_SPLINE``: fit with natural spline. 519 - ``CUBIC_SPLINE``: fit with cubic spline. 520 - ``AKIMA_SPLINE``: fit with Akima spline. 523 Polynomial order or number of spline knots; ignored unless 524 ``fitType`` indicates a polynomial or spline. 525 statControl : `lsst.afw.math.StatisticsControl` 526 Statistics control object. In particular, we pay attention to numSigmaClip 527 overscanIsInt : `bool` 528 Treat the overscan region as consisting of integers, even if it's been 529 converted to float. E.g. handle ties properly. 533 result : `lsst.pipe.base.Struct` 534 Result struct with components: 536 - ``imageFit``: Value(s) removed from image (scalar or 537 `lsst.afw.image.Image`) 538 - ``overscanFit``: Value(s) removed from overscan (scalar or 539 `lsst.afw.image.Image`) 540 - ``overscanImage``: Overscan corrected overscan region 541 (`lsst.afw.image.Image`) 545 Raised if ``fitType`` is not an allowed value. 549 The ``ampMaskedImage`` and ``overscanImage`` are modified, with the fit 550 subtracted. Note that the ``overscanImage`` should not be a subimage of 551 the ``ampMaskedImage``, to avoid being subtracted twice. 553 Debug plots are available for the SPLINE fitTypes by setting the 554 `debug.display` for `name` == "lsst.ip.isr.isrFunctions". These 555 plots show the scatter plot of the overscan data (collapsed along 556 the perpendicular dimension) as a function of position on the CCD 557 (normalized between +/-1). 559 ampImage = ampMaskedImage.getImage()
560 if statControl
is None:
561 statControl = afwMath.StatisticsControl()
563 numSigmaClip = statControl.getNumSigmaClip()
565 if fitType
in (
'MEAN',
'MEANCLIP'):
566 fitType = afwMath.stringToStatisticsProperty(fitType)
567 offImage = afwMath.makeStatistics(overscanImage, fitType, statControl).getValue()
568 overscanFit = offImage
569 elif fitType
in (
'MEDIAN',):
572 if hasattr(overscanImage,
"image"):
573 imageI = overscanImage.image.convertI()
574 overscanImageI = afwImage.MaskedImageI(imageI, overscanImage.mask, overscanImage.variance)
576 overscanImageI = overscanImage.convertI()
578 overscanImageI = overscanImage
580 fitType = afwMath.stringToStatisticsProperty(fitType)
581 offImage = afwMath.makeStatistics(overscanImageI, fitType, statControl).getValue()
582 overscanFit = offImage
586 elif fitType
in (
'POLY',
'CHEB',
'LEG',
'NATURAL_SPLINE',
'CUBIC_SPLINE',
'AKIMA_SPLINE'):
587 if hasattr(overscanImage,
"getImage"):
588 biasArray = overscanImage.getImage().getArray()
589 biasArray = numpy.ma.masked_where(overscanImage.getMask().getArray() & statControl.getAndMask(),
592 biasArray = overscanImage.getArray()
594 shortInd = numpy.argmin(biasArray.shape)
597 biasArray = numpy.transpose(biasArray)
600 percentiles = numpy.percentile(biasArray, [25.0, 50.0, 75.0], axis=1)
601 medianBiasArr = percentiles[1]
602 stdevBiasArr = 0.74*(percentiles[2] - percentiles[0])
603 diff = numpy.abs(biasArray - medianBiasArr[:, numpy.newaxis])
604 biasMaskedArr = numpy.ma.masked_where(diff > numSigmaClip*stdevBiasArr[:, numpy.newaxis], biasArray)
605 collapsed = numpy.mean(biasMaskedArr, axis=1)
606 if collapsed.mask.sum() > 0:
607 collapsed.data[collapsed.mask] = numpy.mean(biasArray.data[collapsed.mask], axis=1)
608 del biasArray, percentiles, stdevBiasArr, diff, biasMaskedArr
611 collapsed = numpy.transpose(collapsed)
614 indices = 2.0*numpy.arange(num)/float(num) - 1.0
616 if fitType
in (
'POLY',
'CHEB',
'LEG'):
618 poly = numpy.polynomial
619 fitter, evaler = {
"POLY": (poly.polynomial.polyfit, poly.polynomial.polyval),
620 "CHEB": (poly.chebyshev.chebfit, poly.chebyshev.chebval),
621 "LEG": (poly.legendre.legfit, poly.legendre.legval),
624 coeffs = fitter(indices, collapsed, order)
625 fitBiasArr = evaler(indices, coeffs)
626 elif 'SPLINE' in fitType:
635 collapsedMask = collapsed.mask
637 if collapsedMask == numpy.ma.nomask:
638 collapsedMask = numpy.array(len(collapsed)*[numpy.ma.nomask])
642 numPerBin, binEdges = numpy.histogram(indices, bins=numBins,
643 weights=1-collapsedMask.astype(int))
646 with numpy.errstate(invalid=
"ignore"):
647 values = numpy.histogram(indices, bins=numBins,
648 weights=collapsed.data*~collapsedMask)[0]/numPerBin
649 binCenters = numpy.histogram(indices, bins=numBins,
650 weights=indices*~collapsedMask)[0]/numPerBin
651 interp = afwMath.makeInterpolate(binCenters.astype(float)[numPerBin > 0],
652 values.astype(float)[numPerBin > 0],
653 afwMath.stringToInterpStyle(fitType))
654 fitBiasArr = numpy.array([interp.interpolate(i)
for i
in indices])
658 import matplotlib.pyplot
as plot
659 figure = plot.figure(1)
661 axes = figure.add_axes((0.1, 0.1, 0.8, 0.8))
662 axes.plot(indices[~collapsedMask], collapsed[~collapsedMask],
'k+')
663 if collapsedMask.sum() > 0:
664 axes.plot(indices[collapsedMask], collapsed.data[collapsedMask],
'b+')
665 axes.plot(indices, fitBiasArr,
'r-')
666 plot.xlabel(
"centered/scaled position along overscan region")
667 plot.ylabel(
"pixel value/fit value")
669 prompt =
"Press Enter or c to continue [chp]... " 671 ans = input(prompt).lower()
672 if ans
in (
"",
"c",):
678 print(
"h[elp] c[ontinue] p[db]")
681 offImage = ampImage.Factory(ampImage.getDimensions())
682 offArray = offImage.getArray()
683 overscanFit = afwImage.ImageF(overscanImage.getDimensions())
684 overscanArray = overscanFit.getArray()
686 offArray[:, :] = fitBiasArr[:, numpy.newaxis]
687 overscanArray[:, :] = fitBiasArr[:, numpy.newaxis]
689 offArray[:, :] = fitBiasArr[numpy.newaxis, :]
690 overscanArray[:, :] = fitBiasArr[numpy.newaxis, :]
698 mask = ampMaskedImage.getMask()
699 maskArray = mask.getArray()
if shortInd == 1
else mask.getArray().transpose()
700 suspect = mask.getPlaneBitMask(
"SUSPECT")
702 if collapsed.mask == numpy.ma.nomask:
706 for low
in range(num):
707 if not collapsed.mask[low]:
710 maskArray[:low, :] |= suspect
711 for high
in range(1, num):
712 if not collapsed.mask[-high]:
715 maskArray[-high:, :] |= suspect
718 raise pexExcept.Exception(
'%s : %s an invalid overscan type' % (
"overscanCorrection", fitType))
720 overscanImage -= overscanFit
721 return Struct(imageFit=offImage, overscanFit=overscanFit, overscanImage=overscanImage)
725 """Apply brighter fatter correction in place for the image. 729 exposure : `lsst.afw.image.Exposure` 730 Exposure to have brighter-fatter correction applied. Modified 732 kernel : `numpy.ndarray` 733 Brighter-fatter kernel to apply. 735 Number of correction iterations to run. 737 Convergence threshold in terms of the sum of absolute 738 deviations between an iteration and the previous one. 740 If True, then the exposure values are scaled by the gain prior 746 Final difference between iterations achieved in correction. 748 Number of iterations used to calculate correction. 752 This correction takes a kernel that has been derived from flat 753 field images to redistribute the charge. The gradient of the 754 kernel is the deflection field due to the accumulated charge. 756 Given the original image I(x) and the kernel K(x) we can compute 757 the corrected image Ic(x) using the following equation: 759 Ic(x) = I(x) + 0.5*d/dx(I(x)*d/dx(int( dy*K(x-y)*I(y)))) 761 To evaluate the derivative term we expand it as follows: 763 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))) ) 765 Because we use the measured counts instead of the incident counts 766 we apply the correction iteratively to reconstruct the original 767 counts and the correction. We stop iterating when the summed 768 difference between the current corrected image and the one from 769 the previous iteration is below the threshold. We do not require 770 convergence because the number of iterations is too large a 771 computational cost. How we define the threshold still needs to be 772 evaluated, the current default was shown to work reasonably well 773 on a small set of images. For more information on the method see 774 DocuShare Document-19407. 776 The edges as defined by the kernel are not corrected because they 777 have spurious values due to the convolution. 779 image = exposure.getMaskedImage().getImage()
784 kLx = numpy.shape(kernel)[0]
785 kLy = numpy.shape(kernel)[1]
786 kernelImage = afwImage.ImageD(kLx, kLy)
787 kernelImage.getArray()[:, :] = kernel
788 tempImage = image.clone()
790 nanIndex = numpy.isnan(tempImage.getArray())
791 tempImage.getArray()[nanIndex] = 0.
793 outImage = afwImage.ImageF(image.getDimensions())
794 corr = numpy.zeros_like(image.getArray())
795 prev_image = numpy.zeros_like(image.getArray())
796 convCntrl = afwMath.ConvolutionControl(
False,
True, 1)
797 fixedKernel = afwMath.FixedKernel(kernelImage)
807 for iteration
in range(maxIter):
809 afwMath.convolve(outImage, tempImage, fixedKernel, convCntrl)
810 tmpArray = tempImage.getArray()
811 outArray = outImage.getArray()
813 with numpy.errstate(invalid=
"ignore", over=
"ignore"):
815 gradTmp = numpy.gradient(tmpArray[startY:endY, startX:endX])
816 gradOut = numpy.gradient(outArray[startY:endY, startX:endX])
817 first = (gradTmp[0]*gradOut[0] + gradTmp[1]*gradOut[1])[1:-1, 1:-1]
820 diffOut20 = numpy.diff(outArray, 2, 0)[startY:endY, startX + 1:endX - 1]
821 diffOut21 = numpy.diff(outArray, 2, 1)[startY + 1:endY - 1, startX:endX]
822 second = tmpArray[startY + 1:endY - 1, startX + 1:endX - 1]*(diffOut20 + diffOut21)
824 corr[startY + 1:endY - 1, startX + 1:endX - 1] = 0.5*(first + second)
826 tmpArray[:, :] = image.getArray()[:, :]
827 tmpArray[nanIndex] = 0.
828 tmpArray[startY:endY, startX:endX] += corr[startY:endY, startX:endX]
831 diff = numpy.sum(numpy.abs(prev_image - tmpArray))
835 prev_image[:, :] = tmpArray[:, :]
837 image.getArray()[startY + 1:endY - 1, startX + 1:endX - 1] += \
838 corr[startY + 1:endY - 1, startX + 1:endX - 1]
840 return diff, iteration
845 """Context manager that applies and removes gain. 849 exp : `lsst.afw.image.Exposure` 850 Exposure to apply/remove gain. 851 image : `lsst.afw.image.Image` 852 Image to apply/remove gain. 854 If True, apply and remove the amplifier gain. 858 exp : `lsst.afw.image.Exposure` 859 Exposure with the gain applied. 862 ccd = exp.getDetector()
864 sim = image.Factory(image, amp.getBBox())
871 ccd = exp.getDetector()
873 sim = image.Factory(image, amp.getBBox())
878 sensorTransmission=None, atmosphereTransmission=None):
879 """Attach a TransmissionCurve to an Exposure, given separate curves for 880 different components. 884 exposure : `lsst.afw.image.Exposure` 885 Exposure object to modify by attaching the product of all given 886 ``TransmissionCurves`` in post-assembly trimmed detector coordinates. 887 Must have a valid ``Detector`` attached that matches the detector 888 associated with sensorTransmission. 889 opticsTransmission : `lsst.afw.image.TransmissionCurve` 890 A ``TransmissionCurve`` that represents the throughput of the optics, 891 to be evaluated in focal-plane coordinates. 892 filterTransmission : `lsst.afw.image.TransmissionCurve` 893 A ``TransmissionCurve`` that represents the throughput of the filter 894 itself, to be evaluated in focal-plane coordinates. 895 sensorTransmission : `lsst.afw.image.TransmissionCurve` 896 A ``TransmissionCurve`` that represents the throughput of the sensor 897 itself, to be evaluated in post-assembly trimmed detector coordinates. 898 atmosphereTransmission : `lsst.afw.image.TransmissionCurve` 899 A ``TransmissionCurve`` that represents the throughput of the 900 atmosphere, assumed to be spatially constant. 904 combined : `lsst.afw.image.TransmissionCurve` 905 The TransmissionCurve attached to the exposure. 909 All ``TransmissionCurve`` arguments are optional; if none are provided, the 910 attached ``TransmissionCurve`` will have unit transmission everywhere. 912 combined = afwImage.TransmissionCurve.makeIdentity()
913 if atmosphereTransmission
is not None:
914 combined *= atmosphereTransmission
915 if opticsTransmission
is not None:
916 combined *= opticsTransmission
917 if filterTransmission
is not None:
918 combined *= filterTransmission
919 detector = exposure.getDetector()
920 fpToPix = detector.getTransform(fromSys=camGeom.FOCAL_PLANE,
921 toSys=camGeom.PIXELS)
922 combined = combined.transformedBy(fpToPix)
923 if sensorTransmission
is not None:
924 combined *= sensorTransmission
925 exposure.getInfo().setTransmissionCurve(combined)
930 """!Update the WCS in exposure with a distortion model based on camera 935 exposure : `lsst.afw.image.Exposure` 936 Exposure to process. Must contain a Detector and WCS. The 937 exposure is modified. 938 camera : `lsst.afw.cameraGeom.Camera` 944 Raised if ``exposure`` is lacking a Detector or WCS, or if 948 Add a model for optical distortion based on geometry found in ``camera`` 949 and the ``exposure``'s detector. The raw input exposure is assumed 950 have a TAN WCS that has no compensation for optical distortion. 951 Two other possibilities are: 952 - The raw input exposure already has a model for optical distortion, 953 as is the case for raw DECam data. 954 In that case you should set config.doAddDistortionModel False. 955 - The raw input exposure has a model for distortion, but it has known 956 deficiencies severe enough to be worth fixing (e.g. because they 957 cause problems for fitting a better WCS). In that case you should 958 override this method with a version suitable for your raw data. 961 wcs = exposure.getWcs()
963 raise RuntimeError(
"exposure has no WCS")
965 raise RuntimeError(
"camera is None")
966 detector = exposure.getDetector()
968 raise RuntimeError(
"exposure has no Detector")
969 pixelToFocalPlane = detector.getTransform(camGeom.PIXELS, camGeom.FOCAL_PLANE)
970 focalPlaneToFieldAngle = camera.getTransformMap().getTransform(camGeom.FOCAL_PLANE,
972 distortedWcs = makeDistortedTanWcs(wcs, pixelToFocalPlane, focalPlaneToFieldAngle)
973 exposure.setWcs(distortedWcs)
977 """Scale an exposure by the amplifier gains. 981 exposure : `lsst.afw.image.Exposure` 982 Exposure to process. The image is modified. 983 normalizeGains : `Bool`, optional 984 If True, then amplifiers are scaled to force the median of 985 each amplifier to equal the median of those medians. 987 ccd = exposure.getDetector()
988 ccdImage = exposure.getMaskedImage()
992 sim = ccdImage.Factory(ccdImage, amp.getBBox())
996 medians.append(numpy.median(sim.getImage().getArray()))
999 median = numpy.median(numpy.array(medians))
1000 for index, amp
in enumerate(ccd):
1001 sim = ccdImage.Factory(ccdImage, amp.getBBox())
1002 if medians[index] != 0.0:
1003 sim *= median/medians[index]
1007 """Grow the saturation trails by an amount dependent on the width of the trail. 1011 mask : `lsst.afw.image.Mask` 1012 Mask which will have the saturated areas grown. 1016 for i
in range(1, 6):
1017 extraGrowDict[i] = 0
1018 for i
in range(6, 8):
1019 extraGrowDict[i] = 1
1020 for i
in range(8, 10):
1021 extraGrowDict[i] = 3
1024 if extraGrowMax <= 0:
1027 saturatedBit = mask.getPlaneBitMask(
"SAT")
1029 xmin, ymin = mask.getBBox().getMin()
1030 width = mask.getWidth()
1032 thresh = afwDetection.Threshold(saturatedBit, afwDetection.Threshold.BITMASK)
1033 fpList = afwDetection.FootprintSet(mask, thresh).getFootprints()
1036 for s
in fp.getSpans():
1037 x0, x1 = s.getX0(), s.getX1()
1039 extraGrow = extraGrowDict.get(x1 - x0 + 1, extraGrowMax)
1042 x0 -= xmin + extraGrow
1043 x1 -= xmin - extraGrow
1050 mask.array[y, x0:x1+1] |= saturatedBit
1054 """Set all BAD areas of the chip to the average of the rest of the exposure 1058 exposure : `lsst.afw.image.Exposure` 1059 Exposure to mask. The exposure mask is modified. 1060 badStatistic : `str`, optional 1061 Statistic to use to generate the replacement value from the 1062 image data. Allowed values are 'MEDIAN' or 'MEANCLIP'. 1066 badPixelCount : scalar 1067 Number of bad pixels masked. 1068 badPixelValue : scalar 1069 Value substituted for bad pixels. 1074 Raised if `badStatistic` is not an allowed value. 1076 if badStatistic ==
"MEDIAN":
1077 statistic = afwMath.MEDIAN
1078 elif badStatistic ==
"MEANCLIP":
1079 statistic = afwMath.MEANCLIP
1081 raise RuntimeError(
"Impossible method %s of bad region correction" % badStatistic)
1083 mi = exposure.getMaskedImage()
1085 BAD = mask.getPlaneBitMask(
"BAD")
1086 INTRP = mask.getPlaneBitMask(
"INTRP")
1088 sctrl = afwMath.StatisticsControl()
1089 sctrl.setAndMask(BAD)
1090 value = afwMath.makeStatistics(mi, statistic, sctrl).getValue()
1092 maskArray = mask.getArray()
1093 imageArray = mi.getImage().getArray()
1094 badPixels = numpy.logical_and((maskArray & BAD) > 0, (maskArray & INTRP) == 0)
1095 imageArray[:] = numpy.where(badPixels, value, imageArray)
1097 return badPixels.sum(), value
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)
def getDefectListFromMask(maskedImage, maskName)
def interpolateFromMask(maskedImage, fwhm, growSaturatedFootprints=1, maskNameList=['SAT'], fallbackValue=None)
def interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=None)
def defectListFromFootprintList(fpList)
def transposeMaskedImage(maskedImage)
def trimToMatchCalibBBox(rawMaskedImage, calibMaskedImage)
def attachTransmissionCurve(exposure, opticsTransmission=None, filterTransmission=None, sensorTransmission=None, atmosphereTransmission=None)
def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False, trimToFit=False)
def makeThresholdMask(maskedImage, threshold, growFootprints=1, maskName='SAT')
def widenSaturationTrails(mask)
def applyGains(exposure, normalizeGains=False)
def darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False, trimToFit=False)
def biasCorrection(maskedImage, biasMaskedImage, trimToFit=False)
def gainContext(exp, image, apply)
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)
def maskPixelsFromDefectList(maskedImage, defectList, maskName='BAD')