Coverage for python/lsst/cp/verify/verifyDefects.py: 23%

42 statements  

« prev     ^ index     » next       coverage.py v7.2.3, created at 2023-04-22 03:27 -0700

1# This file is part of cp_verify. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <http://www.gnu.org/licenses/>. 

21import numpy as np 

22import scipy.stats 

23 

24from .verifyStats import CpVerifyStatsConfig, CpVerifyStatsTask, CpVerifyStatsConnections 

25 

26 

27__all__ = ['CpVerifyDefectsConfig', 'CpVerifyDefectsTask'] 

28 

29 

30class CpVerifyDefectsConfig(CpVerifyStatsConfig, 

31 pipelineConnections=CpVerifyStatsConnections): 

32 """Inherits from base CpVerifyStatsConfig. 

33 """ 

34 

35 def setDefaults(self): 

36 super().setDefaults() 

37 self.maskNameList = ['BAD'] # noqa F821 

38 

39 self.imageStatKeywords = {'DEFECT_PIXELS': 'NMASKED', # noqa F821 

40 'OUTLIERS': 'NCLIPPED', 

41 'MEDIAN': 'MEDIAN', 

42 'STDEV': 'STDEVCLIP', 

43 'MIN': 'MIN', 

44 'MAX': 'MAX', } 

45 self.unmaskedImageStatKeywords = {'UNMASKED_MIN': 'MIN', # noqa F821 

46 'UNMASKED_MAX': 'MAX', 

47 'UNMASKED_STDEV': 'STDEVCLIP', 

48 'UNMASKED_OUTLIERS': 'NCLIPPED', } 

49 

50 

51class CpVerifyDefectsTask(CpVerifyStatsTask): 

52 """Defects verification sub-class, implementing the verify method. 

53 

54 This also applies additional image processing statistics. 

55 """ 

56 ConfigClass = CpVerifyDefectsConfig 

57 _DefaultName = 'cpVerifyDefects' 

58 

59 def imageStatistics(self, exposure, statControl): 

60 """Measure additional defect statistics. 

61 

62 This calls the parent class method first, then adds additional 

63 measurements. 

64 

65 Parameters 

66 ---------- 

67 exposure : `lsst.afw.image.Exposure` 

68 Exposure containing the ISR processed data to measure. 

69 statControl : `lsst.afw.math.StatControl` 

70 Statistics control object with parameters defined by 

71 the config. 

72 

73 Returns 

74 ------- 

75 outputStatistics : `dict` [`str`, `dict` [`str`, scalar]] 

76 A dictionary indexed by the amplifier name, containing 

77 dictionaries of the statistics measured and their values. 

78 """ 

79 outputStatistics = super().imageStatistics(exposure, statControl) 

80 

81 # Is this a useful test? It saves having to do chi^2 fits, 

82 # which are going to be biased by the bulk of points. 

83 for amp in exposure.getDetector(): 

84 ampName = amp.getName() 

85 ampExp = exposure.Factory(exposure, amp.getBBox()) 

86 

87 normImage = ampExp.getImage() 

88 normArray = normImage.getArray() 

89 

90 normArray -= outputStatistics[ampName]['MEDIAN'] 

91 normArray /= outputStatistics[ampName]['STDEV'] 

92 

93 probability = scipy.stats.norm.pdf(normArray) 

94 outliers = np.where(probability < 1.0 / probability.size, 1.0, 0.0) 

95 outputStatistics[ampName]['STAT_OUTLIERS'] = int(np.sum(outliers)) 

96 

97 return outputStatistics 

98 

99 def verify(self, exposure, statisticsDict): 

100 """Verify that the measured statistics meet the verification criteria. 

101 

102 Parameters 

103 ---------- 

104 exposure : `lsst.afw.image.Exposure` 

105 The exposure the statistics are from. 

106 statisticsDictionary : `dict` [`str`, `dict` [`str`, scalar]], 

107 Dictionary of measured statistics. The inner dictionary 

108 should have keys that are statistic names (`str`) with 

109 values that are some sort of scalar (`int` or `float` are 

110 the mostly likely types). 

111 

112 Returns 

113 ------- 

114 outputStatistics : `dict` [`str`, `dict` [`str`, `bool`]] 

115 A dictionary indexed by the amplifier name, containing 

116 dictionaries of the verification criteria. 

117 success : `bool` 

118 A boolean indicating if all tests have passed. 

119 """ 

120 ampStats = statisticsDict['AMP'] 

121 verifyStats = {} 

122 success = True 

123 for ampName, stats in ampStats.items(): 

124 verify = {} 

125 

126 # These are not defined in DMTN-101 yet. 

127 verify['OUTLIERS'] = bool(stats['UNMASKED_OUTLIERS'] >= stats['OUTLIERS']) 

128 verify['STDEV'] = bool(stats['UNMASKED_STDEV'] >= stats['STDEV']) 

129 verify['MIN'] = bool(stats['UNMASKED_MIN'] <= stats['MIN']) 

130 verify['MAX'] = bool(stats['UNMASKED_MAX'] >= stats['MAX']) 

131 

132 # This test is bad, and should be made not bad. 

133 verify['PROB_TEST'] = bool(stats['STAT_OUTLIERS'] == stats['DEFECT_PIXELS']) 

134 

135 verify['SUCCESS'] = bool(np.all(list(verify.values()))) 

136 if verify['SUCCESS'] is False: 

137 success = False 

138 

139 verifyStats[ampName] = verify 

140 

141 return {'AMP': verifyStats}, bool(success)