Coverage for python/lsst/afw/math/_backgroundList.py: 14%

111 statements  

« prev     ^ index     » next       coverage.py v7.5.0, created at 2024-04-25 00:01 -0700

1# This file is part of afw. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (https://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 <https://www.gnu.org/licenses/>. 

21 

22__all__ = ["BackgroundList"] 

23 

24import os 

25import lsst.daf.base as dafBase 

26import lsst.geom 

27import lsst.afw.image as afwImage 

28from lsst.afw.fits import MemFileManager, reduceToFits, Fits 

29from ._math import Interpolate, ApproximateControl, BackgroundMI, UndersampleStyle 

30 

31 

32class BackgroundList: 

33 """A list-like class to contain a list of (`lsst.afw.math.Background`, 

34 `lsst.afw.math.Interpolate.Style`, `~lsst.afw.math.UndersampleStyle`) 

35 tuples. 

36 

37 Parameters 

38 ---------- 

39 *args : `tuple` or `~lsst.afw.math.Background` 

40 A sequence of arguments, each of which becomes an element of the list. 

41 We also accept a single `lsst.afw.math.Background` and extract the 

42 ``interpStyle`` and ``undersampleStyle`` from the as-used values. 

43 """ 

44 

45 def __init__(self, *args): 

46 self._backgrounds = [] 

47 for a in args: 

48 self.append(a) 

49 

50 def __getitem__(self, *args): 

51 """Return an item 

52 

53 Parameters 

54 ---------- 

55 *args 

56 Any valid list index. 

57 """ 

58 # 

59 # Set any previously-unknown Styles (they are set by bkgd.getImage()) 

60 # 

61 for i, val in enumerate(self._backgrounds): 

62 bkgd, interpStyle, undersampleStyle, approxStyle, \ 

63 approxOrderX, approxOrderY, approxWeighting = val 

64 if interpStyle is None or undersampleStyle is None: 

65 interpStyle = bkgd.getAsUsedInterpStyle() 

66 undersampleStyle = bkgd.getAsUsedUndersampleStyle() 

67 actrl = bkgd.getBackgroundControl().getApproximateControl() 

68 approxStyle = actrl.getStyle() 

69 approxOrderX = actrl.getOrderX() 

70 approxOrderY = actrl.getOrderY() 

71 approxWeighting = actrl.getWeighting() 

72 self._backgrounds[i] = (bkgd, interpStyle, undersampleStyle, 

73 approxStyle, approxOrderX, approxOrderY, approxWeighting) 

74 # 

75 # And return what they wanted 

76 # 

77 return self._backgrounds.__getitem__(*args) 

78 

79 def __len__(self, *args): 

80 return self._backgrounds.__len__(*args) 

81 

82 def append(self, val): 

83 try: 

84 bkgd, interpStyle, undersampleStyle, approxStyle, \ 

85 approxOrderX, approxOrderY, approxWeighting = val 

86 except TypeError: 

87 bkgd = val 

88 interpStyle = None 

89 undersampleStyle = None 

90 approxStyle = None 

91 approxOrderX = None 

92 approxOrderY = None 

93 approxWeighting = None 

94 

95 bgInfo = (bkgd, interpStyle, undersampleStyle, approxStyle, 

96 approxOrderX, approxOrderY, approxWeighting) 

97 self._backgrounds.append(bgInfo) 

98 

99 def clone(self): 

100 """Return a shallow copy 

101 

102 Shallow copies do not share backgrounds that are appended after copying, 

103 but do share changes to contained background objects. 

104 """ 

105 return BackgroundList(*self) 

106 

107 def writeFits(self, fileName, flags=0): 

108 """Save our list of Backgrounds to a file. 

109 

110 Parameters 

111 ----------- 

112 fileName : `str` 

113 FITS file to write 

114 flags : `int` 

115 Flags to control details of writing; currently unused, but present 

116 for consistency with `lsst.afw.table.BaseCatalog.writeFits`. 

117 """ 

118 

119 for i, bkgd in enumerate(self): 

120 (bkgd, interpStyle, undersampleStyle, approxStyle, approxOrderX, approxOrderY, 

121 approxWeighting) = bkgd 

122 

123 statsImage = bkgd.getStatsImage() 

124 

125 md = dafBase.PropertyList() 

126 md.set("INTERPSTYLE", int(interpStyle)) 

127 md.set("UNDERSAMPLESTYLE", int(undersampleStyle)) 

128 md.set("APPROXSTYLE", int(approxStyle)) 

129 md.set("APPROXORDERX", approxOrderX) 

130 md.set("APPROXORDERY", approxOrderY) 

131 md.set("APPROXWEIGHTING", approxWeighting) 

132 bbox = bkgd.getImageBBox() 

133 md.set("BKGD_X0", bbox.getMinX()) 

134 md.set("BKGD_Y0", bbox.getMinY()) 

135 md.set("BKGD_WIDTH", bbox.getWidth()) 

136 md.set("BKGD_HEIGHT", bbox.getHeight()) 

137 

138 statsImage.getImage().writeFits(fileName, md, "w" if i == 0 else "a") 

139 statsImage.getMask().writeFits(fileName, md, "a") 

140 statsImage.getVariance().writeFits(fileName, md, "a") 

141 

142 @staticmethod 

143 def readFits(fileName, hdu=0, flags=0): 

144 """Read our list of Backgrounds from a file. 

145 

146 Parameters 

147 ---------- 

148 fileName : `str` 

149 FITS file to read 

150 hdu : `int` 

151 First Header/Data Unit to attempt to read from 

152 flags : `int` 

153 Flags to control details of reading; currently unused, but present 

154 for consistency with `lsst.afw.table.BaseCatalog.readFits`. 

155 

156 See Also 

157 -------- 

158 getImage 

159 """ 

160 if not isinstance(fileName, MemFileManager) and not os.path.exists(fileName): 

161 raise RuntimeError(f"File not found: {fileName}") 

162 

163 self = BackgroundList() 

164 

165 f = Fits(fileName, 'r') 

166 nHdus = f.countHdus() 

167 f.closeFile() 

168 if nHdus % 3 != 0: 

169 raise RuntimeError(f"BackgroundList FITS file {fileName} has {nHdus} HDUs;" 

170 f"expected a multiple of 3 (compression is not supported).") 

171 

172 for hdu in range(0, nHdus, 3): 

173 # It seems like we ought to be able to just use 

174 # MaskedImageFitsReader here, but it warns about EXTTYPE and still 

175 # doesn't work quite naturally when starting from a nonzero HDU. 

176 imageReader = afwImage.ImageFitsReader(fileName, hdu=hdu) 

177 maskReader = afwImage.MaskFitsReader(fileName, hdu=hdu + 1) 

178 varianceReader = afwImage.ImageFitsReader(fileName, hdu=hdu + 2) 

179 statsImage = afwImage.MaskedImageF(imageReader.read(), maskReader.read(), varianceReader.read()) 

180 md = imageReader.readMetadata() 

181 

182 x0 = md["BKGD_X0"] 

183 y0 = md["BKGD_Y0"] 

184 width = md["BKGD_WIDTH"] 

185 height = md["BKGD_HEIGHT"] 

186 imageBBox = lsst.geom.BoxI(lsst.geom.PointI(x0, y0), lsst.geom.ExtentI(width, height)) 

187 

188 interpStyle = Interpolate.Style(md["INTERPSTYLE"]) 

189 undersampleStyle = UndersampleStyle(md["UNDERSAMPLESTYLE"]) 

190 

191 # Older outputs won't have APPROX* settings. Provide alternative defaults. 

192 # Note: Currently X- and Y-orders must be equal due to a limitation in 

193 # math::Chebyshev1Function2. Setting approxOrderY = -1 is equivalent 

194 # to saying approxOrderY = approxOrderX. 

195 approxStyle = md.get("APPROXSTYLE", ApproximateControl.UNKNOWN) 

196 approxStyle = ApproximateControl.Style(approxStyle) 

197 approxOrderX = md.get("APPROXORDERX", 1) 

198 approxOrderY = md.get("APPROXORDERY", -1) 

199 approxWeighting = md.get("APPROXWEIGHTING", True) 

200 

201 bkgd = BackgroundMI(imageBBox, statsImage) 

202 bctrl = bkgd.getBackgroundControl() 

203 bctrl.setInterpStyle(interpStyle) 

204 bctrl.setUndersampleStyle(undersampleStyle) 

205 actrl = ApproximateControl(approxStyle, approxOrderX, approxOrderY, approxWeighting) 

206 bctrl.setApproximateControl(actrl) 

207 bgInfo = (bkgd, interpStyle, undersampleStyle, approxStyle, 

208 approxOrderX, approxOrderY, approxWeighting) 

209 self.append(bgInfo) 

210 

211 return self 

212 

213 def getImage(self): 

214 """Compute and return a full-resolution image from our list of 

215 (Background, interpStyle, undersampleStyle). 

216 """ 

217 

218 bkgdImage = None 

219 for (bkgd, interpStyle, undersampleStyle, approxStyle, 

220 approxOrderX, approxOrderY, approxWeighting) in self: 

221 if not bkgdImage: 

222 if approxStyle != ApproximateControl.UNKNOWN: 

223 bkgdImage = bkgd.getImageF() 

224 else: 

225 bkgdImage = bkgd.getImageF(interpStyle, undersampleStyle) 

226 else: 

227 if approxStyle != ApproximateControl.UNKNOWN: 

228 bkgdImage += bkgd.getImageF() 

229 else: 

230 bkgdImage += bkgd.getImageF(interpStyle, undersampleStyle) 

231 

232 return bkgdImage 

233 

234 def __reduce__(self): 

235 return reduceToFits(self)