Coverage for python/lsst/afw/image/exposure/_multiband.py: 22%

68 statements  

« prev     ^ index     » next       coverage.py v6.4, created at 2022-05-24 02:38 -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# (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__all__ = ["MultibandExposure"] 

23 

24import numpy as np 

25 

26from lsst.geom import Point2D 

27from . import Exposure, ExposureF 

28from ..image._multiband import MultibandImage, MultibandTripleBase 

29from ..image._multiband import tripleFromSingles, tripleFromArrays, makeTripleFromKwargs 

30from ..maskedImage import MaskedImage 

31 

32 

33class MultibandExposure(MultibandTripleBase): 

34 """MultibandExposure class 

35 

36 This class acts as a container for multiple `afw.Exposure` objects. 

37 All exposures must have the same bounding box, and the associated 

38 images must all have the same data type. 

39 

40 See `MultibandTripleBase` for parameter definitions. 

41 """ 

42 def __init__(self, filters, image, mask, variance, psfs=None): 

43 super().__init__(filters, image, mask, variance) 

44 if psfs is not None: 

45 for psf, exposure in zip(psfs, self.singles): 

46 exposure.setPsf(psf) 

47 

48 @staticmethod 

49 def fromExposures(filters, singles): 

50 """Construct a MultibandImage from a collection of single band images 

51 

52 see `tripleFromExposures` for a description of parameters 

53 """ 

54 psfs = [s.getPsf() for s in singles] 

55 return tripleFromSingles(MultibandExposure, filters, singles, psfs=psfs) 

56 

57 @staticmethod 

58 def fromArrays(filters, image, mask, variance, bbox=None): 

59 """Construct a MultibandExposure from a collection of arrays 

60 

61 see `tripleFromArrays` for a description of parameters 

62 """ 

63 return tripleFromArrays(MultibandExposure, filters, image, mask, variance, bbox) 

64 

65 @staticmethod 

66 def fromKwargs(filters, filterKwargs, singleType=ExposureF, **kwargs): 

67 """Build a MultibandImage from a set of keyword arguments 

68 

69 see `makeTripleFromKwargs` for a description of parameters 

70 """ 

71 return makeTripleFromKwargs(MultibandExposure, filters, filterKwargs, singleType, **kwargs) 

72 

73 def _buildSingles(self, image=None, mask=None, variance=None): 

74 """Make a new list of single band objects 

75 

76 Parameters 

77 ---------- 

78 image: `list` 

79 List of `Image` objects that represent the image in each band. 

80 mask: `list` 

81 List of `Mask` objects that represent the mask in each band. 

82 variance: `list` 

83 List of `Image` objects that represent the variance in each band. 

84 

85 Returns 

86 ------- 

87 singles: tuple 

88 Tuple of `MaskedImage` objects for each band, 

89 where the `image`, `mask`, and `variance` of each `single` 

90 point to the multiband objects. 

91 """ 

92 singles = [] 

93 if image is None: 

94 image = self.image 

95 if mask is None: 

96 mask = self.mask 

97 if variance is None: 

98 variance = self.variance 

99 

100 dtype = image.array.dtype 

101 for f in self.filters: 

102 maskedImage = MaskedImage(image=image[f], mask=mask[f], variance=variance[f], dtype=dtype) 

103 single = Exposure(maskedImage, dtype=dtype) 

104 singles.append(single) 

105 return tuple(singles) 

106 

107 @staticmethod 

108 def fromButler(butler, filters, filterKwargs, *args, **kwargs): 

109 """Load a multiband exposure from a butler 

110 

111 Because each band is stored in a separate exposure file, 

112 this method can be used to load all of the exposures for 

113 a given set of bands 

114 

115 Parameters 

116 ---------- 

117 butler: `Butler` 

118 Butler connection to use to load the single band 

119 calibrated images 

120 filters: `list` or `str` 

121 List of filter names for each band 

122 filterKwargs: `dict` 

123 Keyword arguments to pass to the Butler 

124 that are different for each filter. 

125 The keys are the names of the arguments and the values 

126 should also be dictionaries, with filter names as keys 

127 and the value of the argument for the given filter as values. 

128 args: `list` 

129 Arguments to the Butler. 

130 kwargs: `dict` 

131 Keyword arguments to pass to the Butler 

132 that are the same in all bands. 

133 

134 Returns 

135 ------- 

136 result: `MultibandExposure` 

137 The new `MultibandExposure` created by combining all of the 

138 single band exposures. 

139 """ 

140 # Load the Exposure in each band 

141 exposures = [] 

142 for f in filters: 

143 if filterKwargs is not None: 

144 for key, value in filterKwargs: 

145 kwargs[key] = value[f] 

146 exposures.append(butler.get(*args, filter=f, **kwargs)) 

147 return MultibandExposure.fromExposures(filters, exposures) 

148 

149 def computePsfKernelImage(self, position=None): 

150 """Get a multiband PSF image 

151 

152 The PSF Kernel Image is computed for each band 

153 and combined into a (filter, y, x) array and stored 

154 as `self._psfImage`. 

155 The result is not cached, so if the same PSF is expected 

156 to be used multiple times it is a good idea to store the 

157 result in another variable. 

158 

159 Parameters 

160 ---------- 

161 position: `Point2D` or `tuple` 

162 Coordinates to evaluate the PSF. If `position` is `None` 

163 then `Psf.getAveragePosition()` is used. 

164 

165 Returns 

166 ------- 

167 self._psfImage: array 

168 The multiband PSF image. 

169 """ 

170 psfs = [] 

171 # Make the coordinates into a Point2D (if necessary) 

172 if not isinstance(position, Point2D) and position is not None: 

173 position = Point2D(position[0], position[1]) 

174 for single in self.singles: 

175 if position is None: 

176 psfs.append(single.getPsf().computeKernelImage().array) 

177 else: 

178 psfs.append(single.getPsf().computeKernelImage(position).array) 

179 psfs = np.array(psfs) 

180 psfImage = MultibandImage(self.filters, array=psfs) 

181 return psfImage 

182 

183 def computePsfImage(self, position=None): 

184 """Get a multiband PSF image 

185 

186 The PSF Kernel Image is computed for each band 

187 and combined into a (filter, y, x) array and stored 

188 as `self._psfImage`. 

189 The result is not cached, so if the same PSF is expected 

190 to be used multiple times it is a good idea to store the 

191 result in another variable. 

192 

193 Parameters 

194 ---------- 

195 position: `Point2D` or `tuple` 

196 Coordinates to evaluate the PSF. If `position` is `None` 

197 then `Psf.getAveragePosition()` is used. 

198 

199 Returns 

200 ------- 

201 self._psfImage: array 

202 The multiband PSF image. 

203 """ 

204 psfs = [] 

205 # Make the coordinates into a Point2D (if necessary) 

206 if not isinstance(position, Point2D) and position is not None: 

207 position = Point2D(position[0], position[1]) 

208 for single in self.singles: 

209 if position is None: 

210 psfs.append(single.getPsf().computeImage().array) 

211 else: 

212 psfs.append(single.getPsf().computeImage(position).array) 

213 psfs = np.array(psfs) 

214 psfImage = MultibandImage(self.filters, array=psfs) 

215 return psfImage