23 __all__ = [
'FindDefectsTask',
24 'FindDefectsTaskConfig', ]
30 import lsst.pex.config
as pexConfig
41 from .utils
import NonexistentDatasetTaskDataIdContainer, SingleVisitListTaskRunner, countMaskedPixels, \
46 """Config class for defect finding""" 48 isrForFlats = pexConfig.ConfigurableField(
50 doc=
"Task to perform instrumental signature removal",
52 isrForDarks = pexConfig.ConfigurableField(
54 doc=
"Task to perform instrumental signature removal",
56 isrMandatoryStepsFlats = pexConfig.ListField(
58 doc=(
"isr operations that must be performed for valid results when using flats." 59 " Raises if any of these are False"),
60 default=[
'doAssembleCcd',
'doFringe']
62 isrMandatoryStepsDarks = pexConfig.ListField(
64 doc=(
"isr operations that must be performed for valid results when using darks. " 65 "Raises if any of these are False"),
66 default=[
'doAssembleCcd',
'doFringe']
68 isrForbiddenStepsFlats = pexConfig.ListField(
70 doc=(
"isr operations that must NOT be performed for valid results when using flats." 71 " Raises if any of these are True"),
72 default=[
'doAddDistortionModel',
'doBrighterFatter',
'doUseOpticsTransmission',
73 'doUseFilterTransmission',
'doUseSensorTransmission',
'doUseAtmosphereTransmission']
75 isrForbiddenStepsDarks = pexConfig.ListField(
77 doc=(
"isr operations that must NOT be performed for valid results when using darks." 78 " Raises if any of these are True"),
79 default=[
'doAddDistortionModel',
'doBrighterFatter',
'doUseOpticsTransmission',
80 'doUseFilterTransmission',
'doUseSensorTransmission',
'doUseAtmosphereTransmission']
82 isrDesirableSteps = pexConfig.ListField(
84 doc=(
"isr operations that it is advisable to perform, but are not mission-critical." 85 " WARNs are logged for any of these found to be False."),
88 ccdKey = pexConfig.Field(
90 doc=
"The key by which to pull a detector from a dataId, e.g. 'ccd' or 'detector'",
93 imageTypeKey = pexConfig.Field(
95 doc=
"The key for the butler to use by which to check whether images are darks or flats",
98 mode = pexConfig.ChoiceField(
99 doc=(
"Use single master calibs (flat and dark) for finding defects, or a list of raw visits?" 100 " If MASTER, a single visit number should be supplied, for which the corresponding master flat" 101 " and dark will be used. If VISITS, the list of visits will be used, treating the flats and " 102 " darks as appropriate, depending on their image types, as determined by their imageType from" 103 " config.imageTypeKey"),
107 "VISITS":
"Calculate defects from a list of raw visits",
108 "MASTER":
"Use the corresponding master calibs from the specified visit to measure defects",
111 nSigmaBright = pexConfig.Field(
113 doc=(
"Number of sigma above mean for bright pixel detection. The default value was found to be",
114 " appropriate for some LSST sensors in DM-17490."),
117 nSigmaDark = pexConfig.Field(
119 doc=(
"Number of sigma below mean for dark pixel detection. The default value was found to be",
120 " appropriate for some LSST sensors in DM-17490."),
123 nPixBorderUpDown = pexConfig.Field(
125 doc=
"Number of pixels to exclude from top & bottom of image when looking for defects.",
128 nPixBorderLeftRight = pexConfig.Field(
130 doc=
"Number of pixels to exclude from left & right of image when looking for defects.",
133 badOnAndOffPixelColumnThreshold = pexConfig.Field(
135 doc=(
"If BPC is the set of all the bad pixels in a given column (not necessarily consecutive) ",
136 "and the size of BPC is at least 'badOnAndOffPixelColumnThreshold', all the pixels between the ",
137 "pixels that satisfy minY (BPC) and maxY (BPC) will be marked as bad, with 'Y' being the long ",
138 "axis of the amplifier (and 'X' the other axis, which for a column is a constant for all ",
139 "pixels in the set BPC). If there are more than 'goodPixelColumnGapThreshold' consecutive ",
140 "non-bad pixels in BPC, an exception to the above is made and those consecutive ",
141 "'goodPixelColumnGapThreshold' are not marked as bad."),
144 goodPixelColumnGapThreshold = pexConfig.Field(
146 doc=(
"Size, in pixels, of usable consecutive pixels in a column with on and off bad pixels (see ",
147 "'badOnAndOffPixelColumnThreshold')."),
150 edgesAsDefects = pexConfig.Field(
152 doc=(
"Mark all edge pixels, as defined by nPixBorder[UpDown, LeftRight], as defects." 153 " Normal treatment is to simply exclude this region from the defect finding, such that no" 154 " defect will be located there."),
157 assertSameRun = pexConfig.Field(
159 doc=(
"Ensure that all visits are from the same run? Raises if this is not the case, or" 160 "if the run key isn't found."),
163 combinationMode = pexConfig.ChoiceField(
164 doc=
"Which types of defects to identify",
168 "AND":
"Logical AND the pixels found in each visit to form set ",
169 "OR":
"Logical OR the pixels found in each visit to form set ",
170 "FRACTION":
"Use pixels found in more than config.combinationFraction of visits ",
173 combinationFraction = pexConfig.RangeField(
175 doc=(
"The fraction (0..1) of visits in which a pixel was found to be defective across" 176 " the visit list in order to be marked as a defect. Note, upper bound is exclusive, so use" 177 " mode AND to require pixel to appear in all images."),
182 makePlots = pexConfig.Field(
184 doc=(
"Plot histograms for each visit for each amp (one plot per detector) and the final" 185 " defects overlaid on the sensor."),
188 writeAs = pexConfig.ChoiceField(
189 doc=
"Write the output file as ASCII or FITS table",
193 "ASCII":
"Write the output as an ASCII file",
194 "FITS":
"Write the output as an FITS table",
195 "BOTH":
"Write the output as both a FITS table and an ASCII file",
201 """Task for finding defects in sensors. 203 The task has two modes of operation, defect finding in raws and in 204 master calibrations, which work as follows. 206 Master calib defect finding 207 ---------------------------- 209 A single visit number is supplied, for which the corresponding flat & dark 210 will be used. This is because, at present at least, there is no way to pass 211 a calibration exposure ID from the command line to a command line task. 213 The task retrieves the corresponding dark and flat exposures for the 214 supplied visit. If a flat is available the task will (be able to) look 215 for both bright and dark defects. If only a dark is found then only bright 216 defects will be sought. 218 All pixels above/below the specified nSigma which lie with the specified 219 borders for flats/darks are identified as defects. 221 Raw visit defect finding 222 ------------------------ 224 A list of exposure IDs are supplied for defect finding. The task will 225 detect bright pixels in the dark frames, if supplied, and bright & dark 226 pixels in the flats, if supplied, i.e. if you only supply darks you will 227 only be given bright defects. This is done automatically from the imageType 228 of the exposure, so the input exposure list can be a mix. 230 As with the master calib detection, all pixels above/below the specified 231 nSigma which lie with the specified borders for flats/darks are identified 232 as defects. Then, a post-processing step is done to merge these detections, 233 with pixels appearing in a fraction [0..1] of the images are kept as defects 234 and those appearing below that occurrence-threshold are discarded. 237 RunnerClass = SingleVisitListTaskRunner
238 ConfigClass = FindDefectsTaskConfig
239 _DefaultName =
"findDefects" 242 pipeBase.CmdLineTask.__init__(self, *args, **kwargs)
243 self.makeSubtask(
"isrForFlats")
244 self.makeSubtask(
"isrForDarks")
247 self.config.isrForbiddenStepsFlats, self.config.isrDesirableSteps)
249 self.config.isrForbiddenStepsDarks, self.config.isrDesirableSteps)
250 self.config.validate()
254 def _makeArgumentParser(cls):
255 """Augment argument parser for the FindDefectsTask.""" 257 parser.add_argument(
"--visitList", dest=
"visitList", nargs=
"*",
258 help=(
"List of visits to use. Same for each detector." 259 " Uses the normal 0..10:3^234 syntax"))
260 parser.add_id_argument(
"--id", datasetType=
"newDefects",
261 ContainerClass=NonexistentDatasetTaskDataIdContainer,
262 help=
"The ccds to use, e.g. --id ccd=0..100")
267 """Run the defect finding task. 269 Find the defects, as described in the main task docstring, from a 270 dataRef and a list of visit(s). 274 dataRef : `lsst.daf.persistence.ButlerDataRef` 275 dataRef for the detector for the visits to be fit. 276 visitList : `list` [`int`] 277 List of visits to be processed. If config.mode == 'VISITS' then the 278 list of visits is used. If config.mode == 'MASTER' then the length 279 of visitList must be one, and the corresponding master calibrations 284 result : `lsst.pipe.base.Struct` 285 Result struct with Components: 287 - ``defects`` : `lsst.meas.algorithms.Defect` 288 The defects found by the task. 289 - ``exitStatus`` : `int` 293 detNum = dataRef.dataId[self.config.ccdKey]
294 self.log.info(
"Calculating defects using %s visits for detector %s" % (visitList, detNum))
296 defectLists = {
'dark': [],
'flat': []}
298 if self.config.mode ==
'MASTER':
299 if len(visitList) > 1:
300 raise RuntimeError(f
"Must only specify one visit when using mode MASTER, got {visitList}")
301 dataRef.dataId[
'visit'] = visitList[0]
303 for datasetType
in defectLists.keys():
304 exp = dataRef.get(datasetType)
307 msg =
"Found %s defects containing %s pixels in master %s" 308 self.log.info(msg, len(defects), self.
_nPixFromDefects(defects), datasetType)
309 defectLists[datasetType].append(defects)
310 if self.config.makePlots:
312 defects, datasetType)
314 elif self.config.mode ==
'VISITS':
315 butler = dataRef.getButler()
317 if self.config.assertSameRun:
320 raise RuntimeError(f
"Got data from runs {runs} with assertSameRun==True")
322 for visit
in visitList:
323 imageType = butler.queryMetadata(
'raw', self.config.imageTypeKey, dataId={
'visit': visit})[0]
324 imageType = imageType.lower()
325 dataRef.dataId[
'visit'] = visit
326 if imageType ==
'flat':
327 exp = self.isrForFlats.
runDataRef(dataRef).exposure
329 defectLists[
'flat'].append(defects)
331 elif imageType ==
'dark':
332 exp = self.isrForDarks.
runDataRef(dataRef).exposure
334 defectLists[
'dark'].append(defects)
337 raise RuntimeError(f
"Failed on imageType {imageType}. Only flats and darks supported")
339 msg =
"Found %s defects containing %s pixels in visit %s" 342 if self.config.makePlots:
345 msg =
"Combining %s defect sets from darks for detector %s" 346 self.log.info(msg, len(defectLists[
'dark']), detNum)
348 self.config.combinationMode)
349 msg =
"Combining %s defect sets from flats for detector %s" 350 self.log.info(msg, len(defectLists[
'flat']), detNum)
352 self.config.combinationMode)
354 msg =
"Combining bright and dark defect sets for detector %s" 355 self.log.info(msg, detNum)
356 brightDarkPostMerge = [mergedDefectsFromDarks, mergedDefectsFromFlats]
361 self.log.info(
"Finished finding defects in detector %s" % detNum)
362 return pipeBase.Struct(defects=allDefects, exitStatus=0)
364 def _getNsigmaForPlot(self, imageType):
365 assert imageType
in [
'flat',
'dark']
366 nSig = self.config.nSigmaBright
if imageType ==
'flat' else self.config.nSigmaDark
370 def _nPixFromDefects(defect):
371 """Count the number of pixels in a defect object.""" 374 nPix += d.getBBox().getArea()
377 def _writeData(self, dataRef, defects):
378 """Write the data out to the defect file. 382 dataRef : `lsst.daf.persistence.ButlerDataRef` 383 dataRef for the detector for defects to be written. 384 defects : `lsst.meas.algorithms.Defect` 385 The defects to be written. 387 filename = dataRef.getUri(write=
True)
388 dirname = os.path.dirname(filename)
389 if not os.path.exists(dirname):
392 msg =
"Writing defects to %s in format: %s" 393 self.log.info(msg, os.path.splitext(filename)[0], self.config.writeAs)
395 if self.config.writeAs
in [
'FITS',
'BOTH']:
396 defects.writeFits(filename)
397 if self.config.writeAs
in [
'ASCII',
'BOTH']:
398 wroteTo = defects.writeText(filename)
399 assert(os.path.splitext(wroteTo)[0] == os.path.splitext(filename)[0])
403 def _getRunListFromVisits(butler, visitList):
404 """Return the set of runs for the visits in visitList.""" 406 for visit
in visitList:
407 runs.add(butler.queryMetadata(
'raw',
'run', dataId={
'visit': visit})[0])
410 def _postProcessDefectSets(self, defectList, imageDimensions, mode):
411 """Combine a list of defects to make a single defect object. 413 AND, OR or use percentage of visits in which defects appear 418 defectList : `list` [`lsst.meas.algorithms.Defect`] 419 The lList of defects to merge. 420 imageDimensions : `tuple` [`int`] 421 The size of the image. 423 The combination mode to use, either 'AND', 'OR' or 'FRACTION' 427 defects : `lsst.meas.algorithms.Defect` 428 The defect set resulting from the merge. 435 if len(defectList) == 1:
438 sumImage = afwImage.MaskedImageF(imageDimensions)
439 for defects
in defectList:
440 for defect
in defects:
441 sumImage.image[defect.getBBox()] += 1
442 sumImage /= len(defectList)
444 nDetected = len(np.where(sumImage.image.array > 0)[0])
445 self.log.info(
"Pre-merge %s pixels with non-zero detections" % nDetected)
448 indices = np.where(sumImage.image.array > 0)
452 elif mode ==
'FRACTION':
453 threshold = self.config.combinationFraction
455 raise RuntimeError(f
"Got unsupported combinationMode {mode}")
456 indices = np.where(sumImage.image.array >= threshold)
458 BADBIT = sumImage.mask.getPlaneBitMask(
'BAD')
459 sumImage.mask.array[indices] |= BADBIT
461 self.log.info(
"Post-merge %s pixels marked as defects" % len(indices[0]))
463 if self.config.edgesAsDefects:
464 self.log.info(
"Masking edge pixels as defects in addition to previously identified defects")
467 defects = measAlg.Defects.fromMask(sumImage,
'BAD')
471 def _getNumGoodPixels(maskedIm, badMaskString="NO_DATA"):
472 """Return the number of non-bad pixels in the image.""" 473 nPixels = maskedIm.mask.array.size
475 return nPixels - nBad
478 """Find hot and cold pixels in an image. 480 Using config-defined thresholds on a per-amp basis, mask pixels 481 that are nSigma above threshold in dark frames (hot pixels), 482 or nSigma away from the clipped mean in flats (hot & cold pixels). 486 exp : `lsst.afw.image.exposure.Exposure` 487 The exposure in which to find defects. 489 The image type, either 'dark' or 'flat'. 491 If true, update exp with hot and cold pixels. 493 cold: DETECTED_NEGATIVE 497 defects : `lsst.meas.algorithms.Defect` 498 The defects found in the image. 500 assert imageType
in [
'flat',
'dark']
503 maskedIm = exp.maskedImage
508 polarities = {
'dark': [
True],
'flat': [
True,
False]}[imageType]
512 for amp
in exp.getDetector():
513 ampImg = maskedIm[amp.getBBox()].clone()
516 if self.config.nPixBorderLeftRight:
517 if ampImg.getX0() == 0:
518 ampImg = ampImg[self.config.nPixBorderLeftRight:, :, afwImage.LOCAL]
520 ampImg = ampImg[:-self.config.nPixBorderLeftRight, :, afwImage.LOCAL]
521 if self.config.nPixBorderUpDown:
522 if ampImg.getY0() == 0:
523 ampImg = ampImg[:, self.config.nPixBorderUpDown:, afwImage.LOCAL]
525 ampImg = ampImg[:, :-self.config.nPixBorderUpDown, afwImage.LOCAL]
530 ampImg -= afwMath.makeStatistics(ampImg, afwMath.MEANCLIP, ).getValue()
533 for polarity
in polarities:
534 nSig = self.config.nSigmaBright
if polarity
else self.config.nSigmaDark
535 threshold = afwDetection.createThreshold(nSig,
'stdev', polarity=polarity)
537 footprintSet = afwDetection.FootprintSet(ampImg, threshold)
539 footprintSet.setMask(maskedIm.mask, (
"DETECTED" if polarity
else "DETECTED_NEGATIVE"))
541 if mergedSet
is None:
542 mergedSet = footprintSet
544 mergedSet.merge(footprintSet)
546 footprintList += mergedSet.getFootprints()
548 defects = measAlg.Defects.fromFootprintList(footprintList)
554 """Mask blocks in a column if there are on-and-off bad pixels 556 If there's a column with on and off bad pixels, mask all the pixels in between, 557 except if there is a large enough gap of consecutive good pixels between two 558 bad pixels in the column. 562 defects: `lsst.meas.algorithms.Defect` 563 The defects found in the image so far 567 defects: `lsst.meas.algorithms.Defect` 568 If the number of bad pixels in a column is not larger or equal than 569 self.config.badPixelColumnThreshold, the iput list is returned. Otherwise, 570 the defects list returned will include boxes that mask blocks of on-and-of 575 for defect
in defects:
576 bbox = defect.getBBox()
577 x0, y0 = bbox.getMinX(), bbox.getMinY()
578 deltaX0, deltaY0 = bbox.getDimensions()
579 for j
in np.arange(y0, y0+deltaY0):
580 for i
in np.arange(x0, x0 + deltaX0):
581 coordinates.append((i, j))
584 for coordinatePair
in coordinates:
585 x.append(coordinatePair[0])
586 y.append(coordinatePair[1])
591 unique, counts = np.unique(x, return_counts=
True)
593 for (a, b)
in zip(unique, counts):
594 if b >= self.config.badOnAndOffPixelColumnThreshold:
596 if len(multipleX) != 0:
601 def _markBlocksInBadColumn(self, x, y, multipleX, defects):
602 """Mask blocks in a column if number of on-and-off bad pixels is above threshold. 604 This function is called if the number of on-and-off bad pixels in a column 605 is larger or equal than self.config.badOnAndOffPixelColumnThreshold. 610 Lower left x coordinate of defect box. x coordinate is along the short axis if amp. 613 Lower left y coordinate of defect box. x coordinate is along the long axis if amp. 616 List of x coordinates in amp. with multiple bad pixels (i.e., columns with defects). 618 defects: `lsst.meas.algorithms.Defect` 619 The defcts found in the image so far 623 defects: `lsst.meas.algorithms.Defect` 624 The defects list returned that will include boxes that mask blocks 627 goodPixelColumnGapThreshold = self.config.goodPixelColumnGapThreshold
629 index = np.where(x == x0)
631 minY, maxY = np.min(multipleY), np.max(multipleY)
634 diffIndex = np.where(np.diff(multipleY) >= goodPixelColumnGapThreshold)[0]
635 if len(diffIndex) != 0:
637 for gapIndex
in diffIndex:
638 limits.append(multipleY[gapIndex])
639 limits.append(multipleY[gapIndex+1])
641 assert len(limits)%2 == 0,
'limits is even by design, but check anyways' 642 for i
in np.arange(0, len(limits)-1, 2):
652 def _setEdgeBits(self, exposureOrMaskedImage, maskplaneToSet='EDGE'):
653 """Set edge bits on an exposure or maskedImage. 658 Raised if parameter ``exposureOrMaskedImage`` is an invalid type. 660 if isinstance(exposureOrMaskedImage, afwImage.Exposure):
661 mi = exposureOrMaskedImage.maskedImage
662 elif isinstance(exposureOrMaskedImage, afwImage.MaskedImage):
663 mi = exposureOrMaskedImage
665 t = type(exposureOrMaskedImage)
666 raise TypeError(f
"Function supports exposure or maskedImage but not {t}")
668 MASKBIT = mi.mask.getPlaneBitMask(maskplaneToSet)
669 if self.config.nPixBorderLeftRight:
670 mi.mask[: self.config.nPixBorderLeftRight, :, afwImage.LOCAL] |= MASKBIT
671 mi.mask[-self.config.nPixBorderLeftRight:, :, afwImage.LOCAL] |= MASKBIT
672 if self.config.nPixBorderUpDown:
673 mi.mask[:, : self.config.nPixBorderUpDown, afwImage.LOCAL] |= MASKBIT
674 mi.mask[:, -self.config.nPixBorderUpDown:, afwImage.LOCAL] |= MASKBIT
676 def _plot(self, dataRef, exp, visit, nSig, defects, imageType):
677 """Plot the defects and pixel histograms. 681 dataRef : `lsst.daf.persistence.ButlerDataRef` 682 dataRef for the detector. 683 exp : `lsst.afw.image.exposure.Exposure` 684 The exposure in which the defects were found. 688 The number of sigma used for detection 689 defects : `lsst.meas.algorithms.Defect` 692 The type of image, either 'dark' or 'flat'. 694 Currently only for LSST sensors. Plots are written to the path 695 given by the butler for the ``cpPipePlotRoot`` dataset type. 697 import matplotlib.pyplot
as plt
698 from matplotlib.backends.backend_pdf
import PdfPages
700 afwDisplay.setDefaultBackend(
"matplotlib")
701 plt.interactive(
False)
703 dirname = dataRef.getUri(datasetType=
'cpPipePlotRoot', write=
True)
704 if not os.path.exists(dirname):
707 detNum = exp.getDetector().getId()
708 nAmps = len(exp.getDetector())
710 if self.config.mode ==
"MASTER":
711 filename = f
"defectPlot_det{detNum}_master-{imageType}_for-exp{visit}.pdf" 712 elif self.config.mode ==
"VISITS":
713 filename = f
"defectPlot_det{detNum}_{imageType}_exp{visit}.pdf" 715 filenameFull = os.path.join(dirname, filename)
717 with warnings.catch_warnings():
718 msg =
"Matplotlib is currently using agg, which is a non-GUI backend, so cannot show the figure." 719 warnings.filterwarnings(
"ignore", message=msg)
720 with PdfPages(filenameFull)
as pdfPages:
727 self.log.info(
"Wrote plot(s) to %s" % filenameFull)
729 def _plotDefects(self, exp, visit, defects, imageType):
730 """Plot the defects found by the task. 734 exp : `lsst.afw.image.exposure.Exposure` 735 The exposure in which the defects were found. 738 defects : `lsst.meas.algorithms.Defect` 741 The type of image, either 'dark' or 'flat'. 743 expCopy = exp.clone()
745 maskedIm = expCopy.maskedImage
747 defects.maskPixels(expCopy.maskedImage,
"BAD")
748 detector = expCopy.getDetector()
750 disp = afwDisplay.Display(0, reopenPlot=
True, dpi=200)
752 if imageType ==
"flat":
754 ampIm = maskedIm.image[amp.getBBox()]
755 ampIm -= afwMath.makeStatistics(ampIm, afwMath.MEANCLIP).getValue() + 1
757 mpDict = maskedIm.mask.getMaskPlaneDict()
758 for plane
in mpDict.keys():
761 disp.setMaskPlaneColor(plane, afwDisplay.IGNORE)
763 disp.scale(
'asinh',
'zscale')
764 disp.setMaskTransparency(80)
765 disp.setMaskPlaneColor(
"BAD", afwDisplay.RED)
767 disp.setImageColormap(
'gray')
768 title = (f
"Detector: {detector.getName()[-3:]} {detector.getSerial()}" 769 f
", Type: {imageType}, visit: {visit}")
770 disp.mtv(maskedIm, title=title)
772 cameraGeom.utils.overlayCcdBoxes(detector, isTrimmed=
True, display=disp)
774 def _plotAmpHistogram(self, dataRef, exp, visit, nSigmaUsed):
776 Make a histogram of the distribution of pixel values for each amp. 778 The main image data histogram is plotted in blue. Edge pixels, 779 if masked, are in red. Note that masked edge pixels do not contribute 780 to the underflow and overflow numbers. 782 Note that this currently only supports the 16-amp LSST detectors. 786 dataRef : `lsst.daf.persistence.ButlerDataRef` 787 dataRef for the detector. 788 exp : `lsst.afw.image.exposure.Exposure` 789 The exposure in which the defects were found. 793 The number of sigma used for detection 795 import matplotlib.pyplot
as plt
797 detector = exp.getDetector()
799 if len(detector) != 16:
800 raise RuntimeError(
"Plotting currently only supported for 16 amp detectors")
801 fig, ax = plt.subplots(nrows=4, ncols=4, sharex=
'col', sharey=
'row', figsize=(13, 10))
803 expTime = exp.getInfo().getVisitInfo().getExposureTime()
805 for (amp, a)
in zip(reversed(detector), ax.flatten()):
806 mi = exp.maskedImage[amp.getBBox()]
809 mi.image.array /= expTime
810 stats = afwMath.makeStatistics(mi, afwMath.MEANCLIP | afwMath.STDEVCLIP)
811 mean, sigma = stats.getValue(afwMath.MEANCLIP), stats.getValue(afwMath.STDEVCLIP)
814 EDGEBIT = exp.maskedImage.mask.getPlaneBitMask(
"EDGE")
815 imgData = mi.image.array[(mi.mask.array & EDGEBIT) == 0].flatten()
816 edgeData = mi.image.array[(mi.mask.array & EDGEBIT) != 0].flatten()
818 thrUpper = mean + nSigmaUsed*sigma
819 thrLower = mean - nSigmaUsed*sigma
821 nRight = len(imgData[imgData > thrUpper])
822 nLeft = len(imgData[imgData < thrLower])
824 nsig = nSigmaUsed + 1.2
825 leftEdge = mean - nsig * nSigmaUsed*sigma
826 rightEdge = mean + nsig * nSigmaUsed*sigma
827 nbins = np.linspace(leftEdge, rightEdge, 1000)
828 ey, bin_borders, patches = a.hist(edgeData, histtype=
'step', bins=nbins, lw=1, edgecolor=
'red')
829 y, bin_borders, patches = a.hist(imgData, histtype=
'step', bins=nbins, lw=3, edgecolor=
'blue')
832 nOverflow = len(imgData[imgData > rightEdge])
833 nUnderflow = len(imgData[imgData < leftEdge])
836 a.axvline(thrUpper, c=
'k')
837 a.axvline(thrLower, c=
'k')
838 msg = f
"{amp.getName()}\nmean:{mean: .2f}\n$\\sigma$:{sigma: .2f}" 839 a.text(0.65, 0.6, msg, transform=a.transAxes, fontsize=11)
840 msg = f
"nLeft:{nLeft}\nnRight:{nRight}\nnOverflow:{nOverflow}\nnUnderflow:{nUnderflow}" 841 a.text(0.03, 0.6, msg, transform=a.transAxes, fontsize=11.5)
844 a.set_ylim([1., 1.7*np.max(y)])
845 lPlot, rPlot = a.get_xlim()
846 a.set_xlim(np.array([lPlot, rPlot]))
848 a.set_xlabel(
"ADU/s")
def _getNsigmaForPlot(self, imageType)
def runDataRef(self, dataRef, visitList)
def _getRunListFromVisits(butler, visitList)
def countMaskedPixels(maskedIm, maskPlane)
def _plotDefects(self, exp, visit, defects, imageType)
def validateIsrConfig(isrTask, mandatory=None, forbidden=None, desirable=None, undesirable=None, checkTrim=True, logName=None)
def _markBlocksInBadColumn(self, x, y, multipleX, defects)
def findHotAndColdPixels(self, exp, imageType, setMask=False)
def _nPixFromDefects(defect)
def _postProcessDefectSets(self, defectList, imageDimensions, mode)
def _setEdgeBits(self, exposureOrMaskedImage, maskplaneToSet='EDGE')
def _writeData(self, dataRef, defects)
def maskBlocksIfIntermitentBadPixelsInColumn(self, defects)
def __init__(self, args, kwargs)
def _plot(self, dataRef, exp, visit, nSig, defects, imageType)
def _getNumGoodPixels(maskedIm, badMaskString="NO_DATA")
def _plotAmpHistogram(self, dataRef, exp, visit, nSigmaUsed)