22__all__ = [
"OverscanCorrectionTaskConfig",
"OverscanCorrectionTask"]
28import lsst.pipe.base
as pipeBase
31from .isr
import fitOverscanImage
32from .isrFunctions
import makeThresholdMask
36 """Overscan correction options.
38 fitType = pexConfig.ChoiceField(
40 doc="The method for fitting the overscan bias level.",
43 "POLY":
"Fit ordinary polynomial to the longest axis of the overscan region",
44 "CHEB":
"Fit Chebyshev polynomial to the longest axis of the overscan region",
45 "LEG":
"Fit Legendre polynomial to the longest axis of the overscan region",
46 "NATURAL_SPLINE":
"Fit natural spline to the longest axis of the overscan region",
47 "CUBIC_SPLINE":
"Fit cubic spline to the longest axis of the overscan region",
48 "AKIMA_SPLINE":
"Fit Akima spline to the longest axis of the overscan region",
49 "MEAN":
"Correct using the mean of the overscan region",
50 "MEANCLIP":
"Correct using a clipped mean of the overscan region",
51 "MEDIAN":
"Correct using the median of the overscan region",
52 "MEDIAN_PER_ROW":
"Correct using the median per row of the overscan region",
55 order = pexConfig.Field(
57 doc=(
"Order of polynomial to fit if overscan fit type is a polynomial, "
58 "or number of spline knots if overscan fit type is a spline."),
61 numSigmaClip = pexConfig.Field(
63 doc=
"Rejection threshold (sigma) for collapsing overscan before fit",
66 maskPlanes = pexConfig.ListField(
68 doc=
"Mask planes to reject when measuring overscan",
69 default=[
'BAD',
'SAT'],
71 overscanIsInt = pexConfig.Field(
73 doc=
"Treat overscan as an integer image for purposes of fitType=MEDIAN"
74 " and fitType=MEDIAN_PER_ROW.",
78 doParallelOverscan = pexConfig.Field(
80 doc=
"Correct using parallel overscan after serial overscan correction?",
83 parallelOverscanMaskThreshold = pexConfig.Field(
85 doc=
"Threshold above which pixels in the parallel overscan are masked as bleeds.",
88 parallelOverscanMaskGrowSize = pexConfig.Field(
90 doc=
"Masks created from saturated bleeds should be grown by this many "
91 "pixels during construction of the parallel overscan mask. "
92 "This value determined from the ITL chip in the LATISS camera",
96 leadingColumnsToSkip = pexConfig.Field(
98 doc=
"Number of leading columns to skip in serial overscan correction.",
101 trailingColumnsToSkip = pexConfig.Field(
103 doc=
"Number of trailing columns to skip in serial overscan correction.",
106 leadingRowsToSkip = pexConfig.Field(
108 doc=
"Number of leading rows to skip in parallel overscan correction.",
111 trailingRowsToSkip = pexConfig.Field(
113 doc=
"Number of trailing rows to skip in parallel overscan correction.",
117 maxDeviation = pexConfig.Field(
119 doc=
"Maximum deviation from median (in ADU) to mask in overscan correction.",
120 default=1000.0, check=
lambda x: x > 0,
125 """Correction task for overscan.
127 This class contains a number of utilities that are easier to
128 understand
and use when they are
not embedded
in nested
if/
else
134 Statistics control object.
136 ConfigClass = OverscanCorrectionTaskConfig
137 _DefaultName = "overscan"
147 self.
statControl.setNumSigmaClip(self.config.numSigmaClip)
148 self.
statControl.setAndMask(afwImage.Mask.getPlaneBitMask(self.config.maskPlanes))
150 def run(self, exposure, amp, isTransposed=False):
151 """Measure and remove an overscan from an amplifier image.
156 Image data that will have the overscan corrections applied.
158 Amplifier to use for debugging purposes.
159 isTransposed : `bool`, optional
160 Is the image transposed, such that serial
and parallel
161 overscan regions are reversed? Default
is False.
165 overscanResults : `lsst.pipe.base.Struct`
166 Result struct
with components:
169 Value
or fit subtracted
from the amplifier image data
172 Value
or fit subtracted
from the serial overscan image
175 Image of the serial overscan region
with the serial
176 overscan correction applied
178 estimate the amplifier read noise empirically.
179 ``parallelOverscanFit``
180 Value
or fit subtracted
from the parallel overscan
182 ``parallelOverscanImage``
183 Image of the parallel overscan region
with the
184 parallel overscan correction applied
190 Raised
if an invalid overscan type
is set.
193 serialOverscanBBox = amp.getRawSerialOverscanBBox()
194 imageBBox = amp.getRawDataBBox()
196 if self.config.doParallelOverscan:
199 parallelOverscanBBox = amp.getRawParallelOverscanBBox()
200 imageBBox = imageBBox.expandedTo(parallelOverscanBBox)
203 imageBBox.getMinY()),
205 imageBBox.getHeight()))
207 imageBBox, serialOverscanBBox, isTransposed=isTransposed)
208 overscanMean = serialResults.overscanMean
209 overscanMedian = serialResults.overscanMedian
210 overscanSigma = serialResults.overscanSigma
211 residualMean = serialResults.overscanMeanResidual
212 residualMedian = serialResults.overscanMedianResidual
213 residualSigma = serialResults.overscanSigmaResidual
216 parallelResults =
None
217 if self.config.doParallelOverscan:
220 parallelOverscanBBox = amp.getRawParallelOverscanBBox()
221 imageBBox = amp.getRawDataBBox()
223 maskIm = exposure.getMaskedImage()
224 maskIm = maskIm.Factory(maskIm, parallelOverscanBBox)
237 imageBBox, parallelOverscanBBox,
238 isTransposed=
not isTransposed)
239 overscanMean = (overscanMean, parallelResults.overscanMean)
240 overscanMedian = (overscanMedian, parallelResults.overscanMedian)
241 overscanSigma = (overscanSigma, parallelResults.overscanSigma)
242 residualMean = (residualMean, parallelResults.overscanMeanResidual)
243 residualMedian = (residualMedian, parallelResults.overscanMedianResidual)
244 residualSigma = (residualSigma, parallelResults.overscanSigmaResidual)
246 parallelOverscanFit = parallelResults.overscanOverscanModel
if parallelResults
else None
247 parallelOverscanImage = parallelResults.overscanImage
if parallelResults
else None
249 return pipeBase.Struct(imageFit=serialResults.ampOverscanModel,
250 overscanFit=serialResults.overscanOverscanModel,
251 overscanImage=serialResults.overscanImage,
253 parallelOverscanFit=parallelOverscanFit,
254 parallelOverscanImage=parallelOverscanImage,
255 overscanMean=overscanMean,
256 overscanMedian=overscanMedian,
257 overscanSigma=overscanSigma,
258 residualMean=residualMean,
259 residualMedian=residualMedian,
260 residualSigma=residualSigma)
263 """Trim the exposure, fit the overscan, subtract the fit, and
264 calculate statistics.
269 Exposure containing the data.
271 The amplifier that is to be corrected.
273 Bounding box of the image data that will have the overscan
274 subtracted. If parallel overscan will be performed, that
275 area
is added to the image bounding box during serial
278 Bounding box
for the overscan data.
280 If true, then the data will be transposed before fitting
285 results : `lsst.pipe.base.Struct`
287 Overscan model broadcast to the full image size.
289 ``overscanOverscanModel``
290 Overscan model broadcast to the full overscan image
293 Overscan image
with the overscan fit subtracted.
296 Overscan model. (`float`
or `np.array`)
298 Mean value of the overscan fit. (`float`)
300 Median value of the overscan fit. (`float`)
302 Standard deviation of the overscan fit. (`float`)
303 ``overscanMeanResidual``
304 Mean value of the overscan region after overscan
305 subtraction. (`float`)
306 ``overscanMedianResidual``
307 Median value of the overscan region after overscan
308 subtraction. (`float`)
309 ``overscanSigmaResidual``
310 Standard deviation of the overscan region after
311 overscan subtraction. (`float`)
313 overscanBox = self.trimOverscan(exposure, amp, overscanBBox,
314 self.config.leadingColumnsToSkip,
315 self.config.trailingColumnsToSkip,
316 transpose=isTransposed)
317 overscanImage = exposure[overscanBox].getMaskedImage()
318 overscanArray = overscanImage.image.array
321 maskVal = overscanImage.mask.getPlaneBitMask(self.config.maskPlanes)
322 overscanMask = ~((overscanImage.mask.array & maskVal) == 0)
324 median = np.ma.median(np.ma.masked_where(overscanMask, overscanArray))
325 bad = np.where(np.abs(overscanArray - median) > self.config.maxDeviation)
326 overscanImage.mask.array[bad] = overscanImage.mask.getPlaneBitMask(
"SAT")
330 overscanResults = self.
fitOverscan(overscanImage, isTransposed=isTransposed)
333 ampImage = exposure[imageBBox]
335 ampImage.image.array,
336 transpose=isTransposed)
337 ampImage.image.array -= ampOverscanModel
341 overscanImage = exposure[overscanBBox]
344 overscanImage.image.array)
345 self.
debugView(overscanImage, overscanResults.overscanValue, amp, isTransposed=isTransposed)
346 overscanImage.image.array -= overscanOverscanModel
349 stats = afwMath.makeStatistics(overscanImage.getMaskedImage(),
350 afwMath.MEAN | afwMath.MEDIAN | afwMath.STDEVCLIP, self.
statControl)
351 residualMean = stats.getValue(afwMath.MEAN)
352 residualMedian = stats.getValue(afwMath.MEDIAN)
353 residualSigma = stats.getValue(afwMath.STDEVCLIP)
355 return pipeBase.Struct(ampOverscanModel=ampOverscanModel,
356 overscanOverscanModel=overscanOverscanModel,
357 overscanImage=overscanImage,
358 overscanValue=overscanResults.overscanValue,
360 overscanMean=overscanResults.overscanMean,
361 overscanMedian=overscanResults.overscanMedian,
362 overscanSigma=overscanResults.overscanSigma,
363 overscanMeanResidual=residualMean,
364 overscanMedianResidual=residualMedian,
365 overscanSigmaResidual=residualSigma
369 """Broadcast 0 or 1 dimension fit to appropriate shape.
373 overscanValue : `numpy.ndarray`, (Nrows, ) or scalar
374 Overscan fit to broadcast.
375 imageArray : `numpy.ndarray`, (Nrows, Ncols)
376 Image array that we want to match.
377 transpose : `bool`, optional
378 Switch order to broadcast along the other axis.
382 overscanModel : `numpy.ndarray`, (Nrows, Ncols)
or scalar
383 Expanded overscan fit.
388 Raised
if no axis has the appropriate dimension.
390 if isinstance(overscanValue, np.ndarray):
391 overscanModel = np.zeros_like(imageArray)
393 if transpose
is False:
394 if imageArray.shape[0] == overscanValue.shape[0]:
395 overscanModel[:, :] = overscanValue[:, np.newaxis]
396 elif imageArray.shape[1] == overscanValue.shape[0]:
397 overscanModel[:, :] = overscanValue[np.newaxis, :]
398 elif imageArray.shape[0] == overscanValue.shape[1]:
399 overscanModel[:, :] = overscanValue[np.newaxis, :]
401 raise RuntimeError(f
"Could not broadcast {overscanValue.shape} to "
402 f
"match {imageArray.shape}")
404 if imageArray.shape[1] == overscanValue.shape[0]:
405 overscanModel[:, :] = overscanValue[np.newaxis, :]
406 elif imageArray.shape[0] == overscanValue.shape[0]:
407 overscanModel[:, :] = overscanValue[:, np.newaxis]
408 elif imageArray.shape[1] == overscanValue.shape[1]:
409 overscanModel[:, :] = overscanValue[:, np.newaxis]
411 raise RuntimeError(f
"Could not broadcast {overscanValue.shape} to "
412 f
"match {imageArray.shape}")
414 overscanModel = overscanValue
418 def trimOverscan(self, exposure, amp, bbox, skipLeading, skipTrailing, transpose=False):
419 """Trim overscan region to remove edges.
424 Exposure containing data.
426 Amplifier containing geometry information.
428 Bounding box of the overscan region.
430 Number of leading (towards data region) rows/columns to skip.
432 Number of trailing (away from data region) rows/columns to skip.
433 transpose : `bool`, optional
434 Operate on the transposed array.
438 overscanArray : `numpy.array`, (N, M)
440 overscanMask : `numpy.array`, (N, M)
443 dx0, dy0, dx1, dy1 = (0, 0, 0, 0)
444 dataBBox = amp.getRawDataBBox()
446 if dataBBox.getBeginY() < bbox.getBeginY():
453 if dataBBox.getBeginX() < bbox.getBeginX():
462 bbox.getHeight() - dy0 + dy1))
466 if self.config.fitType
in (
'MEAN',
'MEANCLIP',
'MEDIAN'):
469 overscanValue = overscanResult.overscanValue
470 overscanMean = overscanValue
471 overscanMedian = overscanValue
473 elif self.config.fitType
in (
'MEDIAN_PER_ROW',
'POLY',
'CHEB',
'LEG',
474 'NATURAL_SPLINE',
'CUBIC_SPLINE',
'AKIMA_SPLINE'):
477 overscanValue = overscanResult.overscanValue
479 stats = afwMath.makeStatistics(overscanResult.overscanValue,
480 afwMath.MEAN | afwMath.MEDIAN | afwMath.STDEVCLIP,
482 overscanMean = stats.getValue(afwMath.MEAN)
483 overscanMedian = stats.getValue(afwMath.MEDIAN)
484 overscanSigma = stats.getValue(afwMath.STDEVCLIP)
486 raise ValueError(
'%s : %s an invalid overscan type' %
487 (
"overscanCorrection", self.config.fitType))
489 return pipeBase.Struct(overscanValue=overscanValue,
490 overscanMean=overscanMean,
491 overscanMedian=overscanMedian,
492 overscanSigma=overscanSigma,
497 """Return an integer version of the input image.
502 Image to convert to integers.
507 The integer converted image.
512 Raised
if the input image could
not be converted.
514 if hasattr(image,
"image"):
516 imageI = image.image.convertI()
517 outI = afwImage.MaskedImageI(imageI, image.mask, image.variance)
518 elif hasattr(image,
"convertI"):
520 outI = image.convertI()
521 elif hasattr(image,
"astype"):
523 outI = image.astype(int)
525 raise RuntimeError(
"Could not convert this to integers: %s %s %s",
526 image, type(image), dir(image))
530 """Mask the union of high values on all amplifiers in the parallel
533 This operates on the image in-place.
538 An untrimmed raw exposure.
540 The detetor to use
for amplifier geometry.
545 dataView = afwImage.MaskedImageF(exposure.getMaskedImage(),
546 amp.getRawParallelOverscanBBox(),
549 maskedImage=dataView,
550 threshold=self.config.parallelOverscanMaskThreshold,
551 growFootprints=self.config.parallelOverscanMaskGrowSize,
554 if parallelMask
is None:
555 parallelMask = dataView.mask.array
557 parallelMask |= dataView.mask.array
559 dataView = afwImage.MaskedImageF(exposure.getMaskedImage(),
560 amp.getRawParallelOverscanBBox(),
562 dataView.mask.array |= parallelMask
566 """Measure a constant overscan value.
571 Image data to measure the overscan
from.
575 results : `lsst.pipe.base.Struct`
576 Overscan result
with entries:
577 - ``overscanValue``: Overscan value to subtract (`float`)
578 - ``isTransposed``: Orientation of the overscan (`bool`)
580 fitType = afwMath.stringToStatisticsProperty(self.config.fitType)
581 overscanValue = afwMath.makeStatistics(image, fitType, self.statControl).getValue()
583 return pipeBase.Struct(overscanValue=overscanValue,
588 """Extract the numpy array from the input image.
593 Image data to pull array
from.
595 calcImage : `numpy.ndarray`
596 Image data array
for numpy operating.
598 if hasattr(image,
"getImage"):
599 calcImage = image.getImage().getArray()
600 calcImage = np.ma.masked_where(image.getMask().getArray() & self.
statControl.getAndMask(),
603 calcImage = image.getArray()
607 """Mask outliers in a row of overscan data
from a robust sigma
612 imageArray : `numpy.ndarray`
613 Image to filter along numpy axis=1.
617 maskedArray : `numpy.ma.masked_array`
618 Masked image marking outliers.
620 lq, median, uq = np.percentile(imageArray, [25.0, 50.0, 75.0], axis=1)
622 axisStdev = 0.74*(uq - lq)
627 axisStdev = np.where(axisStdev > 2.0 * np.median(axisStdev),
628 np.median(axisStdev), axisStdev)
631 diff = np.abs(imageArray - axisMedians[:, np.newaxis])
632 masked = np.ma.masked_where(diff > self.
statControl.getNumSigmaClip()
633 * axisStdev[:, np.newaxis], imageArray)
638 """Fill masked/NaN pixels in the overscan.
642 overscanVector : `np.array` or `np.ma.masked_array`
643 Overscan vector to fill.
647 overscanVector : `np.ma.masked_array`
652 Each maskSlice
is a section of overscan
with contiguous masks.
653 Ideally this adds 5 pixels
from the left
and right of that
654 mask slice,
and takes the median of those values to fill the
655 slice. If this isn
't possible, the median of all non-masked
656 values is used. The mask
is removed
for the pixels filled.
658 workingCopy = overscanVector
659 if not isinstance(overscanVector, np.ma.MaskedArray):
660 workingCopy = np.ma.masked_array(overscanVector,
661 mask=~np.isfinite(overscanVector))
663 defaultValue = np.median(workingCopy.data[~workingCopy.mask])
664 for maskSlice
in np.ma.clump_masked(workingCopy):
666 if maskSlice.start > 5:
667 neighborhood.extend(workingCopy[maskSlice.start - 5:maskSlice.start].data)
668 if maskSlice.stop < workingCopy.size - 5:
669 neighborhood.extend(workingCopy[maskSlice.stop:maskSlice.stop+5].data)
670 if len(neighborhood) > 0:
671 workingCopy.data[maskSlice] = np.nanmedian(neighborhood)
672 workingCopy.mask[maskSlice] =
False
674 workingCopy.data[maskSlice] = defaultValue
675 workingCopy.mask[maskSlice] =
False
679 """Collapse overscan array (and mask) to a 1-D vector of values.
683 maskedArray : `numpy.ma.masked_array`
684 Masked array of input overscan data.
685 fillMasked : `bool`, optional
686 If true, fill any pixels that are masked with a median of
691 collapsed : `numpy.ma.masked_array`
692 Single dimensional overscan data, combined
with the mean.
695 collapsed = np.mean(maskedArray, axis=1)
696 if collapsed.mask.sum() > 0
and fillMasked:
702 """Collapse overscan array (and mask) to a 1-D vector of using the
703 correct integer median of row-values.
707 maskedArray : `numpy.ma.masked_array`
708 Masked array of input overscan data.
712 collapsed : `numpy.ma.masked_array`
713 Single dimensional overscan data, combined with the afwMath median.
718 fitType = afwMath.stringToStatisticsProperty('MEDIAN')
719 for row
in integerMI:
720 newRow = row.compressed()
722 rowMedian = afwMath.makeStatistics(newRow, fitType, self.
statControl).getValue()
725 collapsed.append(rowMedian)
727 return np.array(collapsed)
730 """Wrapper function to match spline fit API to polynomial fit API.
734 indices : `numpy.ndarray`
735 Locations to evaluate the spline.
736 collapsed : `numpy.ndarray`
737 Collapsed overscan values corresponding to the spline
740 Number of bins to use in constructing the spline.
745 Interpolation object
for later evaluation.
747 if not np.ma.is_masked(collapsed):
748 collapsed.mask = np.array(len(collapsed)*[np.ma.nomask])
750 numPerBin, binEdges = np.histogram(indices, bins=numBins,
751 weights=1 - collapsed.mask.astype(int))
752 with np.errstate(invalid=
"ignore"):
753 values = np.histogram(indices, bins=numBins,
754 weights=collapsed.data*~collapsed.mask)[0]/numPerBin
755 binCenters = np.histogram(indices, bins=numBins,
756 weights=indices*~collapsed.mask)[0]/numPerBin
758 if len(binCenters[numPerBin > 0]) < 5:
759 self.log.warn(
"Cannot do spline fitting for overscan: %s valid points.",
760 len(binCenters[numPerBin > 0]))
764 if len(values[numPerBin > 0]) != 0:
765 return float(values[numPerBin > 0][0])
769 interp = afwMath.makeInterpolate(binCenters.astype(float)[numPerBin > 0],
770 values.astype(float)[numPerBin > 0],
771 afwMath.stringToInterpStyle(self.config.fitType))
776 """Wrapper function to match spline evaluation API to polynomial fit
781 indices : `numpy.ndarray`
782 Locations to evaluate the spline.
783 interp : `lsst.afw.math.interpolate`
784 Interpolation object to use.
788 values : `numpy.ndarray`
789 Evaluated spline values at each index.
792 return interp.interpolate(indices.astype(float))
796 """Create mask if edges are extrapolated.
800 collapsed : `numpy.ma.masked_array`
801 Masked array to check the edges of.
805 maskArray : `numpy.ndarray`
806 Boolean numpy array of pixels to mask.
808 maskArray = np.full_like(collapsed, False, dtype=bool)
809 if np.ma.is_masked(collapsed):
811 for low
in range(num):
812 if not collapsed.mask[low]:
815 maskArray[:low] =
True
816 for high
in range(1, num):
817 if not collapsed.mask[-high]:
820 maskArray[-high:] =
True
824 """Calculate the 1-d vector overscan from the input overscan image.
829 Image containing the overscan data.
830 isTransposed : `bool`
831 If true, the image has been transposed.
835 results : `lsst.pipe.base.Struct`
836 Overscan result with entries:
839 Overscan value to subtract (`float`)
841 List of rows that should be masked
as ``SUSPECT`` when the
842 overscan solution
is applied. (`list` [ `bool` ])
844 Indicates
if the overscan data was transposed during
845 calcuation, noting along which axis the overscan should be
852 calcImage = np.transpose(calcImage)
855 if self.config.fitType ==
'MEDIAN_PER_ROW':
856 mi = afwImage.MaskedImageI(image.getBBox())
857 masked = masked.astype(int)
859 masked = masked.transpose()
861 mi.image.array[:, :] = masked.data[:, :]
862 if bool(masked.mask.shape):
863 mi.mask.array[:, :] = masked.mask[:, :]
865 overscanVector = fitOverscanImage(mi, self.config.maskPlanes, isTransposed)
872 indices = 2.0*np.arange(num)/float(num) - 1.0
876 'POLY': (poly.polynomial.polyfit, poly.polynomial.polyval),
877 'CHEB': (poly.chebyshev.chebfit, poly.chebyshev.chebval),
878 'LEG': (poly.legendre.legfit, poly.legendre.legval),
882 }[self.config.fitType]
886 coeffs = fitter(indices, collapsed, self.config.order)
888 if isinstance(coeffs, float):
889 self.log.warn(
"Using fallback value %f due to fitter failure. Amplifier will be masked.",
891 overscanVector = np.full_like(indices, coeffs)
892 maskArray = np.full_like(collapsed,
True, dtype=bool)
895 overscanVector = evaler(indices, coeffs)
898 return pipeBase.Struct(overscanValue=np.array(overscanVector),
900 isTransposed=isTransposed)
902 def debugView(self, image, model, amp=None, isTransposed=True):
903 """Debug display for the final overscan solution.
908 Input image the overscan solution was determined from.
909 model : `numpy.ndarray`
or `float`
910 Overscan model determined
for the image.
912 Amplifier to extract diagnostic information.
913 isTransposed : `bool`, optional
914 Does the data need to be transposed before display?
925 calcImage = np.transpose(calcImage)
930 indices = 2.0 * np.arange(num)/float(num) - 1.0
931 indices = np.arange(num)
933 if np.ma.is_masked(collapsed):
934 collapsedMask = collapsed.mask
936 collapsedMask = np.array(num*[np.ma.nomask])
938 import matplotlib.pyplot
as plot
939 figure = plot.figure(1)
941 axes = figure.add_axes((0.1, 0.1, 0.8, 0.8))
942 axes.plot(indices[~collapsedMask], collapsed[~collapsedMask],
'k+')
943 if collapsedMask.sum() > 0:
944 axes.plot(indices[collapsedMask], collapsed.data[collapsedMask],
'b+')
945 if isinstance(model, np.ndarray):
948 plotModel = np.zeros_like(indices)
951 axes.plot(indices, plotModel,
'r-')
952 plot.xlabel(
"position along overscan region")
953 plot.ylabel(
"pixel value/fit value")
955 plot.title(f
"{amp.getName()} DataX: "
956 f
"[{amp.getRawDataBBox().getBeginX()}:{amp.getRawBBox().getEndX()}]"
957 f
"OscanX: [{amp.getRawHorizontalOverscanBBox().getBeginX()}:"
958 f
"{amp.getRawHorizontalOverscanBBox().getEndX()}] {self.config.fitType}")
960 plot.title(
"No amp supplied.")
962 prompt =
"Press Enter or c to continue [chp]..."
964 ans = input(prompt).lower()
965 if ans
in (
"",
" ",
"c",):
974 print(
"[h]elp [c]ontinue [p]db e[x]itDebug")
def collapseArrayMedian(self, maskedArray)
def maskExtrapolated(collapsed)
def fitOverscan(self, overscanImage, isTransposed=False)
def measureVectorOverscan(self, image, isTransposed=False)
def maskParallelOverscan(self, exposure, detector)
def __init__(self, statControl=None, **kwargs)
def collapseArray(self, maskedArray, fillMasked=True)
def maskOutliers(self, imageArray)
def getImageArray(self, image)
def integerConvert(image)
def splineFit(self, indices, collapsed, numBins)
def broadcastFitToImage(self, overscanValue, imageArray, transpose=False)
def correctOverscan(self, exposure, amp, imageBBox, overscanBBox, isTransposed=True)
def trimOverscan(self, exposure, amp, bbox, skipLeading, skipTrailing, transpose=False)
def debugView(self, image, model, amp=None, isTransposed=True)
def run(self, exposure, amp, isTransposed=False)
def fillMaskedPixels(self, overscanVector)
def splineEval(indices, interp)
def measureConstantOverscan(self, image)