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 obs_base. 

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/>. 

21 

22"""Support for assembling and disassembling afw Exposures.""" 

23 

24# Need to enable PSFs to be instantiated 

25import lsst.afw.detection # noqa: F401 

26from lsst.afw.image import makeExposure, makeMaskedImage 

27 

28from lsst.daf.butler import CompositeAssembler 

29 

30 

31class ExposureAssembler(CompositeAssembler): 

32 

33 EXPOSURE_COMPONENTS = set(("image", "variance", "mask", "wcs", "psf")) 

34 EXPOSURE_INFO_COMPONENTS = set(("apCorrMap", "coaddInputs", "photoCalib", "metadata", 

35 "filter", "transmissionCurve", "visitInfo")) 

36 

37 def _groupRequestedComponents(self): 

38 """Group requested components into top level and ExposureInfo. 

39 

40 Returns 

41 ------- 

42 expComps : `dict` 

43 Components associated with the top level Exposure. 

44 expInfoComps : `dict` 

45 Components associated with the ExposureInfo 

46 

47 Raises 

48 ------ 

49 ValueError 

50 There are components defined in the storage class that are not 

51 expected by this assembler. 

52 """ 

53 requested = set(self.storageClass.components.keys()) 

54 

55 # Check that we are requesting something that we support 

56 unknown = requested - (self.EXPOSURE_COMPONENTS | self.EXPOSURE_INFO_COMPONENTS) 

57 if unknown: 

58 raise ValueError("Asking for unrecognized component: {}".format(unknown)) 

59 

60 expItems = requested & self.EXPOSURE_COMPONENTS 

61 expInfoItems = requested & self.EXPOSURE_INFO_COMPONENTS 

62 return expItems, expInfoItems 

63 

64 def getComponent(self, composite, componentName): 

65 """Get a component from an Exposure 

66 

67 Parameters 

68 ---------- 

69 composite : `~lsst.afw.image.Exposure` 

70 `Exposure` to access component. 

71 componentName : `str` 

72 Name of component to retrieve. 

73 

74 Returns 

75 ------- 

76 component : `object` 

77 The component. Can be None. 

78 

79 Raises 

80 ------ 

81 AttributeError 

82 The component can not be found. 

83 """ 

84 if componentName in self.EXPOSURE_COMPONENTS: 

85 return super().getComponent(composite, componentName) 

86 elif componentName in self.EXPOSURE_INFO_COMPONENTS: 

87 if hasattr(composite, "getInfo"): 

88 # it is possible for this method to be called with 

89 # an ExposureInfo composite so trap for that and only get 

90 # the ExposureInfo if the method is supported 

91 composite = composite.getInfo() 

92 return super().getComponent(composite, componentName) 

93 else: 

94 raise AttributeError("Do not know how to retrieve component {} from {}".format(componentName, 

95 type(composite))) 

96 

97 def getValidComponents(self, composite): 

98 """Extract all non-None components from a composite. 

99 

100 Parameters 

101 ---------- 

102 composite : `object` 

103 Composite from which to extract components. 

104 

105 Returns 

106 ------- 

107 comps : `dict` 

108 Non-None components extracted from the composite, indexed by the 

109 component name as derived from the `self.storageClass`. 

110 """ 

111 # For Exposure we call the generic version twice: once for top level 

112 # components, and again for ExposureInfo. 

113 expItems, expInfoItems = self._groupRequestedComponents() 

114 

115 components = super().getValidComponents(composite) 

116 infoComps = super().getValidComponents(composite.getInfo()) 

117 components.update(infoComps) 

118 return components 

119 

120 def disassemble(self, composite): 

121 """Disassemble an afw Exposure. 

122 

123 This implementation attempts to extract components from the parent 

124 by looking for attributes of the same name or getter methods derived 

125 from the component name. 

126 

127 Parameters 

128 ---------- 

129 composite : `~lsst.afw.image.Exposure` 

130 `Exposure` composite object consisting of components to be 

131 extracted. 

132 

133 Returns 

134 ------- 

135 components : `dict` 

136 `dict` with keys matching the components defined in 

137 `self.storageClass` and values being `DatasetComponent` instances 

138 describing the component. 

139 

140 Raises 

141 ------ 

142 ValueError 

143 A requested component can not be found in the parent using generic 

144 lookups. 

145 TypeError 

146 The parent object does not match the supplied `self.storageClass`. 

147 """ 

148 if not self.storageClass.validateInstance(composite): 

149 raise TypeError("Unexpected type mismatch between parent and StorageClass" 

150 " ({} != {})".format(type(composite), self.storageClass.pytype)) 

151 

152 # Only look for components that are defined by the StorageClass 

153 components = {} 

154 expItems, expInfoItems = self._groupRequestedComponents() 

155 

156 fromExposure = super().disassemble(composite, subset=expItems) 

157 components.update(fromExposure) 

158 

159 fromExposureInfo = super().disassemble(composite, 

160 subset=expInfoItems, override=composite.getInfo()) 

161 components.update(fromExposureInfo) 

162 

163 return components 

164 

165 def assemble(self, components): 

166 """Construct an Exposure from components. 

167 

168 Parameters 

169 ---------- 

170 components : `dict` 

171 All the components from which to construct the Exposure. 

172 Some can be missing. 

173 

174 Returns 

175 ------- 

176 exposure : `~lsst.afw.image.Exposure` 

177 Assembled exposure. 

178 

179 Raises 

180 ------ 

181 ValueError 

182 Some supplied components are not recognized. 

183 """ 

184 components = components.copy() 

185 maskedImageComponents = {} 

186 hasMaskedImage = False 

187 for component in ("image", "variance", "mask"): 

188 value = None 

189 if component in components: 

190 hasMaskedImage = True 

191 value = components.pop(component) 

192 maskedImageComponents[component] = value 

193 

194 wcs = None 

195 if "wcs" in components: 

196 wcs = components.pop("wcs") 

197 

198 pytype = self.storageClass.pytype 

199 if hasMaskedImage: 

200 maskedImage = makeMaskedImage(**maskedImageComponents) 

201 exposure = makeExposure(maskedImage, wcs=wcs) 

202 

203 if not isinstance(exposure, pytype): 

204 raise RuntimeError("Unexpected type created in assembly;" 

205 " was {} expected {}".format(type(exposure), pytype)) 

206 

207 else: 

208 exposure = pytype() 

209 if wcs is not None: 

210 exposure.setWcs(wcs) 

211 

212 # Set other components 

213 exposure.setPsf(components.pop("psf", None)) 

214 exposure.setPhotoCalib(components.pop("photoCalib", None)) 

215 

216 info = exposure.getInfo() 

217 if "visitInfo" in components: 

218 info.setVisitInfo(components.pop("visitInfo")) 

219 info.setApCorrMap(components.pop("apCorrMap", None)) 

220 info.setCoaddInputs(components.pop("coaddInputs", None)) 

221 info.setMetadata(components.pop("metadata", None)) 

222 info.setFilter(components.pop("filter", None)) 

223 

224 # If we have some components left over that is a problem 

225 if components: 

226 raise ValueError("The following components were not understood:" 

227 " {}".format(list(components.keys()))) 

228 

229 return exposure 

230 

231 def handleParameters(self, inMemoryDataset, parameters=None): 

232 """Modify the in-memory dataset using the supplied parameters, 

233 returning a possibly new object. 

234 

235 Parameters 

236 ---------- 

237 inMemoryDataset : `object` 

238 Object to modify based on the parameters. 

239 parameters : `dict`, optional 

240 Parameters to apply. Values are specific to the parameter. 

241 Supported parameters are defined in the associated 

242 `StorageClass`. If no relevant parameters are specified the 

243 inMemoryDataset will be return unchanged. 

244 

245 Returns 

246 ------- 

247 inMemoryDataset : `object` 

248 Updated form of supplied in-memory dataset, after parameters 

249 have been used. 

250 """ 

251 # Understood by *this* subset command 

252 understood = ("bbox", "origin") 

253 use = self.storageClass.filterParameters(parameters, subset=understood) 

254 if use: 

255 inMemoryDataset = inMemoryDataset.subset(**use) 

256 

257 return inMemoryDataset