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 lsst.daf.base as dafBase 

26import lsst.geom 

27import lsst.afw.image as afwImage 

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

29from .interpolate import Interpolate 

30from .approximate import ApproximateControl 

31from .background import BackgroundMI 

32from .background import UndersampleStyle 

33 

34 

35class BackgroundList: 

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

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

38 tuples. 

39 

40 Parameters 

41 ---------- 

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

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

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

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

46 """ 

47 

48 def __init__(self, *args): 

49 self._backgrounds = [] 

50 for a in args: 

51 self.append(a) 

52 

53 def __getitem__(self, *args): 

54 """Return an item 

55 

56 Parameters 

57 ---------- 

58 *args 

59 Any valid list index. 

60 """ 

61 # 

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

63 # 

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

65 bkgd, interpStyle, undersampleStyle, approxStyle, \ 

66 approxOrderX, approxOrderY, approxWeighting = val 

67 if interpStyle is None or undersampleStyle is None: 

68 interpStyle = bkgd.getAsUsedInterpStyle() 

69 undersampleStyle = bkgd.getAsUsedUndersampleStyle() 

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

71 approxStyle = actrl.getStyle() 

72 approxOrderX = actrl.getOrderX() 

73 approxOrderY = actrl.getOrderY() 

74 approxWeighting = actrl.getWeighting() 

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

76 approxStyle, approxOrderX, approxOrderY, approxWeighting) 

77 # 

78 # And return what they wanted 

79 # 

80 return self._backgrounds.__getitem__(*args) 

81 

82 def __len__(self, *args): 

83 return self._backgrounds.__len__(*args) 

84 

85 def append(self, val): 

86 try: 

87 bkgd, interpStyle, undersampleStyle, approxStyle, \ 

88 approxOrderX, approxOrderY, approxWeighting = val 

89 except TypeError: 

90 bkgd = val 

91 interpStyle = None 

92 undersampleStyle = None 

93 approxStyle = None 

94 approxOrderX = None 

95 approxOrderY = None 

96 approxWeighting = None 

97 

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

99 approxOrderX, approxOrderY, approxWeighting) 

100 self._backgrounds.append(bgInfo) 

101 

102 def clone(self): 

103 """Return a shallow copy 

104 

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

106 but do share changes to contained background objects. 

107 """ 

108 return BackgroundList(*self) 

109 

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

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

112 

113 Parameters 

114 ----------- 

115 fileName : `str` 

116 FITS file to write 

117 flags : `int` 

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

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

120 """ 

121 

122 for i, bkgd in enumerate(self): 

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

124 approxWeighting) = bkgd 

125 

126 statsImage = bkgd.getStatsImage() 

127 

128 md = dafBase.PropertyList() 

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

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

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

132 md.set("APPROXORDERX", approxOrderX) 

133 md.set("APPROXORDERY", approxOrderY) 

134 md.set("APPROXWEIGHTING", approxWeighting) 

135 bbox = bkgd.getImageBBox() 

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

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

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

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

140 

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

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

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

144 

145 @staticmethod 

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

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

148 

149 Parameters 

150 ---------- 

151 fileName : `str` 

152 FITS file to read 

153 hdu : `int` 

154 First Header/Data Unit to attempt to read from 

155 flags : `int` 

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

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

158 

159 See Also 

160 -------- 

161 getImage 

162 """ 

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

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

165 

166 self = BackgroundList() 

167 

168 f = Fits(fileName, 'r') 

169 nHdus = f.countHdus() 

170 f.closeFile() 

171 if nHdus % 3 != 0: 

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

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

174 

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

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

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

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

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

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

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

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

183 md = imageReader.readMetadata() 

184 

185 x0 = md["BKGD_X0"] 

186 y0 = md["BKGD_Y0"] 

187 width = md["BKGD_WIDTH"] 

188 height = md["BKGD_HEIGHT"] 

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

190 

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

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

193 

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

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

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

197 # to saying approxOrderY = approxOrderX. 

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

199 approxStyle = ApproximateControl.Style(approxStyle) 

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

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

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

203 

204 bkgd = BackgroundMI(imageBBox, statsImage) 

205 bctrl = bkgd.getBackgroundControl() 

206 bctrl.setInterpStyle(interpStyle) 

207 bctrl.setUndersampleStyle(undersampleStyle) 

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

209 bctrl.setApproximateControl(actrl) 

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

211 approxOrderX, approxOrderY, approxWeighting) 

212 self.append(bgInfo) 

213 

214 return self 

215 

216 def getImage(self): 

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

218 (Background, interpStyle, undersampleStyle). 

219 """ 

220 

221 bkgdImage = None 

222 for (bkgd, interpStyle, undersampleStyle, approxStyle, 

223 approxOrderX, approxOrderY, approxWeighting) in self: 

224 if not bkgdImage: 

225 if approxStyle != ApproximateControl.UNKNOWN: 

226 bkgdImage = bkgd.getImageF() 

227 else: 

228 bkgdImage = bkgd.getImageF(interpStyle, undersampleStyle) 

229 else: 

230 if approxStyle != ApproximateControl.UNKNOWN: 

231 bkgdImage += bkgd.getImageF() 

232 else: 

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

234 

235 return bkgdImage 

236 

237 def __reduce__(self): 

238 return reduceToFits(self)