Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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 warnings 

26import lsst.daf.base as dafBase 

27import lsst.geom 

28import lsst.afw.image as afwImage 

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

30from lsst.utils import suppress_deprecations 

31from . import mathLib as afwMath 

32 

33 

34class BackgroundList: 

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

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

37 tuples. 

38 

39 Parameters 

40 ---------- 

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

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

43 In deference to the deprecated-but-not-yet-removed 

44 `~lsst.afw.math.Background.getImageF()` API, we also accept a single 

45 `lsst.afw.math.Background` and extract the ``interpStyle`` and 

46 ``undersampleStyle`` from the as-used values. 

47 """ 

48 

49 def __init__(self, *args): 

50 self._backgrounds = [] 

51 for a in args: 

52 self.append(a) 

53 

54 def __getitem__(self, *args): 

55 """Return an item 

56 

57 Parameters 

58 ---------- 

59 *args 

60 Any valid list index. 

61 """ 

62 # 

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

64 # 

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

66 bkgd, interpStyle, undersampleStyle, approxStyle, \ 

67 approxOrderX, approxOrderY, approxWeighting = val 

68 if interpStyle is None or undersampleStyle is None: 

69 interpStyle = bkgd.getAsUsedInterpStyle() 

70 undersampleStyle = bkgd.getAsUsedUndersampleStyle() 

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

72 approxStyle = actrl.getStyle() 

73 approxOrderX = actrl.getOrderX() 

74 approxOrderY = actrl.getOrderY() 

75 approxWeighting = actrl.getWeighting() 

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

77 approxStyle, approxOrderX, approxOrderY, approxWeighting) 

78 # 

79 # And return what they wanted 

80 # 

81 return self._backgrounds.__getitem__(*args) 

82 

83 def __len__(self, *args): 

84 return self._backgrounds.__len__(*args) 

85 

86 def append(self, val): 

87 try: 

88 bkgd, interpStyle, undersampleStyle, approxStyle, \ 

89 approxOrderX, approxOrderY, approxWeighting = val 

90 except TypeError: 

91 warnings.warn("Passing Background objects to BackgroundList is deprecated; " 

92 "use a (Background, Interpolation.Style, UndersampleStyle, " 

93 "ApproximateControl.Style, int, int, bool) tuple instead.", 

94 category=FutureWarning, stacklevel=2) 

95 bkgd = val 

96 interpStyle = None 

97 undersampleStyle = None 

98 approxStyle = None 

99 approxOrderX = None 

100 approxOrderY = None 

101 approxWeighting = None 

102 

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

104 approxOrderX, approxOrderY, approxWeighting) 

105 self._backgrounds.append(bgInfo) 

106 

107 def clone(self): 

108 """Return a shallow copy 

109 

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

111 but do share changes to contained background objects. 

112 """ 

113 return BackgroundList(*self) 

114 

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

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

117 

118 Parameters 

119 ----------- 

120 fileName : `str` 

121 FITS file to write 

122 flags : `int` 

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

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

125 """ 

126 

127 for i, bkgd in enumerate(self): 

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

129 approxWeighting) = bkgd 

130 

131 statsImage = bkgd.getStatsImage() 

132 

133 md = dafBase.PropertyList() 

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

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

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

137 md.set("APPROXORDERX", approxOrderX) 

138 md.set("APPROXORDERY", approxOrderY) 

139 md.set("APPROXWEIGHTING", approxWeighting) 

140 bbox = bkgd.getImageBBox() 

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

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

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

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

145 

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

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

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

149 

150 @staticmethod 

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

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

153 

154 Parameters 

155 ---------- 

156 fileName : `str` 

157 FITS file to read 

158 hdu : `int` 

159 First Header/Data Unit to attempt to read from 

160 flags : `int` 

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

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

163 

164 See Also 

165 -------- 

166 getImage() 

167 """ 

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

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

170 

171 self = BackgroundList() 

172 

173 f = Fits(fileName, 'r') 

174 nHdus = f.countHdus() 

175 f.closeFile() 

176 if nHdus % 3 != 0: 

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

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

179 

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

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

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

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

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

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

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

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

188 md = imageReader.readMetadata() 

189 

190 x0 = md["BKGD_X0"] 

191 y0 = md["BKGD_Y0"] 

192 width = md["BKGD_WIDTH"] 

193 height = md["BKGD_HEIGHT"] 

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

195 

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

197 undersampleStyle = afwMath.UndersampleStyle(md["UNDERSAMPLESTYLE"]) 

198 

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

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

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

202 # to saying approxOrderY = approxOrderX. 

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

204 approxStyle = afwMath.ApproximateControl.Style(approxStyle) 

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

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

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

208 

209 bkgd = afwMath.BackgroundMI(imageBBox, statsImage) 

210 bctrl = bkgd.getBackgroundControl() 

211 

212 # TODO: DM-22814: remove this after v20. 

213 # Still needed until then because other code might call the old-style getImageF. 

214 with suppress_deprecations(): 

215 bctrl.setInterpStyle(interpStyle) 

216 

217 bctrl.setUndersampleStyle(undersampleStyle) 

218 actrl = afwMath.ApproximateControl(approxStyle, approxOrderX, approxOrderY, approxWeighting) 

219 bctrl.setApproximateControl(actrl) 

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

221 approxOrderX, approxOrderY, approxWeighting) 

222 self.append(bgInfo) 

223 

224 return self 

225 

226 def getImage(self): 

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

228 (Background, interpStyle, undersampleStyle). 

229 """ 

230 

231 bkgdImage = None 

232 for (bkgd, interpStyle, undersampleStyle, approxStyle, 

233 approxOrderX, approxOrderY, approxWeighting) in self: 

234 if not bkgdImage: 

235 bkgdImage = bkgd.getImageF(interpStyle, undersampleStyle) 

236 else: 

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

238 

239 return bkgdImage 

240 

241 def __reduce__(self): 

242 return reduceToFits(self)