1 from __future__
import division, print_function, absolute_import
2 from builtins
import input
3 from builtins
import range
38 """Make a double Gaussian PSF 40 @param[in] fwhm FWHM of double Gaussian smoothing kernel 41 @return measAlg.DoubleGaussianPsf 43 ksize = 4*int(fwhm) + 1
44 return measAlg.DoubleGaussianPsf(ksize, ksize, fwhm/(2*math.sqrt(2*math.log(2))))
47 """Make a transposed copy of a masked image 49 @param[in] maskedImage afw.image.MaskedImage to process 50 @return transposed masked image 52 transposed = maskedImage.Factory(afwGeom.Extent2I(maskedImage.getHeight(), maskedImage.getWidth()))
53 transposed.getImage().getArray()[:] = maskedImage.getImage().getArray().T
54 transposed.getMask().getArray()[:] = maskedImage.getMask().getArray().T
55 transposed.getVariance().getArray()[:] = maskedImage.getVariance().getArray().T
60 """Interpolate over defects specified in a defect list 62 @param[in,out] maskedImage masked image to process 63 @param[in] defectList defect list 64 @param[in] fwhm FWHM of double Gaussian smoothing kernel 65 @param[in] fallbackValue fallback value if an interpolated value cannot be determined; 66 if None then use clipped mean image value 69 if fallbackValue
is None:
70 fallbackValue = afwMath.makeStatistics(maskedImage.getImage(), afwMath.MEANCLIP).getValue()
71 if 'INTRP' not in maskedImage.getMask().getMaskPlaneDict():
72 maskedImage.getMask.addMaskPlane(
'INTRP')
73 measAlg.interpolateOverDefects(maskedImage, psf, defectList, fallbackValue,
True)
77 """Compute a defect list from a footprint list, optionally growing the footprints 79 @param[in] fpList footprint list 83 for bbox
in afwDetection.footprintToBBoxList(fp):
84 defect = measAlg.Defect(bbox)
85 defectList.append(defect)
90 """Make a transposed copy of a defect list 92 @param[in] defectList a list of defects (afw.meas.algorithms.Defect) 93 @return a defect list with transposed defects 96 for defect
in defectList:
97 bbox = defect.getBBox()
98 nbbox = afwGeom.Box2I(afwGeom.Point2I(bbox.getMinY(), bbox.getMinX()),
99 afwGeom.Extent2I(bbox.getDimensions()[1], bbox.getDimensions()[0]))
100 retDefectList.append(measAlg.Defect(nbbox))
105 """Set mask plane based on a defect list 107 @param[in,out] maskedImage afw.image.MaskedImage to process; mask plane is updated 108 @param[in] defectList a list of defects (afw.meas.algorithms.Defect) 109 @param[in] maskName mask plane name 112 mask = maskedImage.getMask()
113 bitmask = mask.getPlaneBitMask(maskName)
114 for defect
in defectList:
115 bbox = defect.getBBox()
116 afwGeom.SpanSet(bbox).clippedTo(mask.getBBox()).setMask(mask, bitmask)
120 """Compute a defect list from a specified mask plane 122 @param[in] maskedImage masked image to process 123 @param[in] maskName mask plane name, or list of names 125 mask = maskedImage.getMask()
126 thresh = afwDetection.Threshold(mask.getPlaneBitMask(maskName), afwDetection.Threshold.BITMASK)
127 fpList = afwDetection.FootprintSet(mask, thresh).getFootprints()
132 """Mask pixels based on threshold detection 134 @param[in,out] maskedImage afw.image.MaskedImage to process; the mask is altered 135 @param[in] threshold detection threshold 136 @param[in] growFootprints amount by which to grow footprints of detected regions 137 @param[in] maskName mask plane name 138 @return a list of defects (meas.algrithms.Defect) of regions set in the mask. 141 thresh = afwDetection.Threshold(threshold)
142 fs = afwDetection.FootprintSet(maskedImage, thresh)
144 if growFootprints > 0:
145 fs = afwDetection.FootprintSet(fs, growFootprints)
147 fpList = fs.getFootprints()
149 mask = maskedImage.getMask()
150 bitmask = mask.getPlaneBitMask(maskName)
151 afwDetection.setMaskFromFootprintList(mask, fpList, bitmask)
157 """Interpolate over defects identified by a particular mask plane 159 @param[in,out] maskedImage afw.image.MaskedImage to process 160 @param[in] fwhm FWHM of double Gaussian smoothing kernel 161 @param[in] growFootprints amount by which to grow footprints of detected regions 162 @param[in] maskName mask plane name 163 @param[in] fallbackValue value of last resort for interpolation 165 mask = maskedImage.getMask()
166 thresh = afwDetection.Threshold(mask.getPlaneBitMask(maskName), afwDetection.Threshold.BITMASK)
167 fpSet = afwDetection.FootprintSet(mask, thresh)
168 if growFootprints > 0:
169 fpSet = afwDetection.FootprintSet(fpSet, rGrow=growFootprints, isotropic=
False)
172 fpSet.setMask(mask, maskName)
177 def saturationCorrection(maskedImage, saturation, fwhm, growFootprints=1, interpolate=True, maskName='SAT',
179 """Mark saturated pixels and optionally interpolate over them 181 @param[in,out] maskedImage afw.image.MaskedImage to process 182 @param[in] saturation saturation level (used as a detection threshold) 183 @param[in] fwhm FWHM of double Gaussian smoothing kernel 184 @param[in] growFootprints amount by which to grow footprints of detected regions 185 @param[in] interpolate interpolate over saturated pixels? 186 @param[in] maskName mask plane name 187 @param[in] fallbackValue value of last resort for interpolation 190 maskedImage=maskedImage,
191 threshold=saturation,
192 growFootprints=growFootprints,
200 """Apply bias correction in place 202 @param[in,out] maskedImage masked image to correct 203 @param[in] biasMaskedImage bias, as a masked image 205 if maskedImage.getBBox(afwImage.LOCAL) != biasMaskedImage.getBBox(afwImage.LOCAL):
206 raise RuntimeError(
"maskedImage bbox %s != biasMaskedImage bbox %s" %
207 (maskedImage.getBBox(afwImage.LOCAL), biasMaskedImage.getBBox(afwImage.LOCAL)))
208 maskedImage -= biasMaskedImage
211 def darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False):
212 """Apply dark correction in place 214 maskedImage -= dark * expScaling / darkScaling 216 @param[in,out] maskedImage afw.image.MaskedImage to correct 217 @param[in] darkMaskedImage dark afw.image.MaskedImage 218 @param[in] expScale exposure scale 219 @param[in] darkScale dark scale 220 @param[in] invert if True, remove the dark from an already-corrected image 222 if maskedImage.getBBox(afwImage.LOCAL) != darkMaskedImage.getBBox(afwImage.LOCAL):
223 raise RuntimeError(
"maskedImage bbox %s != darkMaskedImage bbox %s" %
224 (maskedImage.getBBox(afwImage.LOCAL), darkMaskedImage.getBBox(afwImage.LOCAL)))
226 scale = expScale / darkScale
228 maskedImage.scaledMinus(scale, darkMaskedImage)
230 maskedImage.scaledPlus(scale, darkMaskedImage)
234 """Set the variance plane based on the image plane 236 @param[in,out] maskedImage afw.image.MaskedImage; image plane is read and variance plane is written 237 @param[in] gain amplifier gain (e-/ADU) 238 @param[in] readNoise amplifier read noise (ADU/pixel) 240 var = maskedImage.getVariance()
241 var[:] = maskedImage.getImage()
246 def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False):
247 """Apply flat correction in place 249 @param[in,out] maskedImage afw.image.MaskedImage to correct 250 @param[in] flatMaskedImage flat field afw.image.MaskedImage 251 @param[in] scalingType how to compute flat scale; one of 'MEAN', 'MEDIAN' or 'USER' 252 @param[in] userScale scale to use if scalingType is 'USER', else ignored 253 @param[in] invert if True, unflatten an already-flattened image instead. 255 if maskedImage.getBBox(afwImage.LOCAL) != flatMaskedImage.getBBox(afwImage.LOCAL):
256 raise RuntimeError(
"maskedImage bbox %s != flatMaskedImage bbox %s" %
257 (maskedImage.getBBox(afwImage.LOCAL), flatMaskedImage.getBBox(afwImage.LOCAL)))
262 if scalingType ==
'MEAN':
263 flatScale = afwMath.makeStatistics(flatMaskedImage.getImage(), afwMath.MEAN).getValue(afwMath.MEAN)
264 elif scalingType ==
'MEDIAN':
265 flatScale = afwMath.makeStatistics(flatMaskedImage.getImage(),
266 afwMath.MEDIAN).getValue(afwMath.MEDIAN)
267 elif scalingType ==
'USER':
268 flatScale = userScale
270 raise pexExcept.Exception(
'%s : %s not implemented' % (
"flatCorrection", scalingType))
273 maskedImage.scaledDivides(1.0/flatScale, flatMaskedImage)
275 maskedImage.scaledMultiplies(1.0/flatScale, flatMaskedImage)
279 """Apply illumination correction in place 281 @param[in,out] maskedImage afw.image.MaskedImage to correct 282 @param[in] illumMaskedImage illumination correction masked image 283 @param[in] illumScale scale value for illumination correction 285 if maskedImage.getBBox(afwImage.LOCAL) != illumMaskedImage.getBBox(afwImage.LOCAL):
286 raise RuntimeError(
"maskedImage bbox %s != illumMaskedImage bbox %s" %
287 (maskedImage.getBBox(afwImage.LOCAL), illumMaskedImage.getBBox(afwImage.LOCAL)))
289 maskedImage.scaledDivides(1./illumScale, illumMaskedImage)
292 def overscanCorrection(ampMaskedImage, overscanImage, fitType='MEDIAN', order=1, collapseRej=3.0,
294 """Apply overscan correction in place 296 @param[in,out] ampMaskedImage masked image to correct 297 @param[in] overscanImage overscan data as an afw.image.Image or afw.image.MaskedImage. 298 If a masked image is passed in the mask plane will be used 299 to constrain the fit of the bias level. 300 @param[in] fitType type of fit for overscan correction; one of: 303 - 'POLY' (ordinary polynomial) 304 - 'CHEB' (Chebyshev polynomial) 305 - 'LEG' (Legendre polynomial) 306 - 'NATURAL_SPLINE', 'CUBIC_SPLINE', 'AKIMA_SPLINE' (splines) 307 @param[in] order polynomial order or spline knots (ignored unless fitType 308 indicates a polynomial or spline) 309 @param[in] collapseRej Rejection threshold (sigma) for collapsing dimension of overscan 310 @param[in] statControl Statistics control object 312 ampImage = ampMaskedImage.getImage()
313 if statControl
is None:
314 statControl = afwMath.StatisticsControl()
315 if fitType ==
'MEAN':
316 offImage = afwMath.makeStatistics(overscanImage, afwMath.MEAN, statControl).getValue(afwMath.MEAN)
317 elif fitType ==
'MEDIAN':
318 offImage = afwMath.makeStatistics(overscanImage, afwMath.MEDIAN, statControl).getValue(afwMath.MEDIAN)
319 elif fitType
in (
'POLY',
'CHEB',
'LEG',
'NATURAL_SPLINE',
'CUBIC_SPLINE',
'AKIMA_SPLINE'):
320 if hasattr(overscanImage,
"getImage"):
321 biasArray = overscanImage.getImage().getArray()
322 biasArray = numpy.ma.masked_where(overscanImage.getMask().getArray() & statControl.getAndMask(),
325 biasArray = overscanImage.getArray()
327 shortInd = numpy.argmin(biasArray.shape)
330 biasArray = numpy.transpose(biasArray)
333 percentiles = numpy.percentile(biasArray, [25.0, 50.0, 75.0], axis=1)
334 medianBiasArr = percentiles[1]
335 stdevBiasArr = 0.74*(percentiles[2] - percentiles[0])
336 diff = numpy.abs(biasArray - medianBiasArr[:, numpy.newaxis])
337 biasMaskedArr = numpy.ma.masked_where(diff > collapseRej*stdevBiasArr[:, numpy.newaxis], biasArray)
338 collapsed = numpy.mean(biasMaskedArr, axis=1)
339 if collapsed.mask.sum() > 0:
340 collapsed.data[collapsed.mask] = numpy.mean(biasArray.data[collapsed.mask], axis=1)
341 del biasArray, percentiles, stdevBiasArr, diff, biasMaskedArr
344 collapsed = numpy.transpose(collapsed)
347 indices = 2.0*numpy.arange(num)/float(num) - 1.0
349 if fitType
in (
'POLY',
'CHEB',
'LEG'):
351 poly = numpy.polynomial
352 fitter, evaler = {
"POLY": (poly.polynomial.polyfit, poly.polynomial.polyval),
353 "CHEB": (poly.chebyshev.chebfit, poly.chebyshev.chebval),
354 "LEG": (poly.legendre.legfit, poly.legendre.legval),
357 coeffs = fitter(indices, collapsed, order)
358 fitBiasArr = evaler(indices, coeffs)
359 elif 'SPLINE' in fitType:
368 collapsedMask = collapsed.mask
370 if collapsedMask == numpy.ma.nomask:
371 collapsedMask = numpy.array(len(collapsed)*[numpy.ma.nomask])
375 numPerBin, binEdges = numpy.histogram(indices, bins=numBins,
376 weights=1-collapsedMask.astype(int))
379 values = numpy.histogram(indices, bins=numBins,
380 weights=collapsed.data*~collapsedMask)[0]/numPerBin
381 binCenters = numpy.histogram(indices, bins=numBins,
382 weights=indices*~collapsedMask)[0]/numPerBin
383 interp = afwMath.makeInterpolate(binCenters.astype(float)[numPerBin > 0],
384 values.astype(float)[numPerBin > 0],
385 afwMath.stringToInterpStyle(fitType))
386 fitBiasArr = numpy.array([interp.interpolate(i)
for i
in indices])
390 import matplotlib.pyplot
as plot
391 figure = plot.figure(1)
393 axes = figure.add_axes((0.1, 0.1, 0.8, 0.8))
394 axes.plot(indices[~collapsedMask], collapsed[~collapsedMask],
'k+')
395 if collapsedMask.sum() > 0:
396 axes.plot(indices[collapsedMask], collapsed.data[collapsedMask],
'b+')
397 axes.plot(indices, fitBiasArr,
'r-')
399 prompt =
"Press Enter or c to continue [chp]... " 401 ans = input(prompt).lower()
402 if ans
in (
"",
"c",):
408 print(
"h[elp] c[ontinue] p[db]")
411 offImage = ampImage.Factory(ampImage.getDimensions())
412 offArray = offImage.getArray()
414 offArray[:, :] = fitBiasArr[:, numpy.newaxis]
416 offArray[:, :] = fitBiasArr[numpy.newaxis, :]
424 mask = ampMaskedImage.getMask()
425 maskArray = mask.getArray()
if shortInd == 1
else mask.getArray().transpose()
426 suspect = mask.getPlaneBitMask(
"SUSPECT")
428 if collapsed.mask == numpy.ma.nomask:
432 for low
in range(num):
433 if not collapsed.mask[low]:
436 maskArray[:low, :] |= suspect
437 for high
in range(1, num):
438 if not collapsed.mask[-high]:
441 maskArray[-high:, :] |= suspect
444 raise pexExcept.Exception(
'%s : %s an invalid overscan type' % \
445 (
"overscanCorrection", fitType))
def darkCorrection(maskedImage, darkMaskedImage, expScale, darkScale, invert=False)
def illuminationCorrection(maskedImage, illumMaskedImage, illumScale)
def saturationCorrection(maskedImage, saturation, fwhm, growFootprints=1, interpolate=True, maskName='SAT', fallbackValue=None)
def transposeDefectList(defectList)
def getDefectListFromMask(maskedImage, maskName)
def interpolateDefectList(maskedImage, defectList, fwhm, fallbackValue=None)
def defectListFromFootprintList(fpList)
def transposeMaskedImage(maskedImage)
def biasCorrection(maskedImage, biasMaskedImage)
def interpolateFromMask(maskedImage, fwhm, growFootprints=1, maskName='SAT', fallbackValue=None)
def makeThresholdMask(maskedImage, threshold, growFootprints=1, maskName='SAT')
def overscanCorrection(ampMaskedImage, overscanImage, fitType='MEDIAN', order=1, collapseRej=3.0, statControl=None)
def flatCorrection(maskedImage, flatMaskedImage, scalingType, userScale=1.0, invert=False)
def updateVariance(maskedImage, gain, readNoise)
def maskPixelsFromDefectList(maskedImage, defectList, maskName='BAD')