Coverage for python / lsst / cell_coadds / _image_planes.py: 51%

70 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-15 00:10 +0000

1# This file is part of cell_coadds. 

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 

22from __future__ import annotations 

23 

24__all__ = ( 

25 "ImagePlanes", 

26 "OwnedImagePlanes", 

27 "ViewImagePlanes", 

28) 

29 

30from abc import ABC, abstractmethod 

31from collections.abc import Callable, Sequence 

32from typing import TYPE_CHECKING, Self 

33 

34from lsst.afw.image import MaskedImageF 

35 

36if TYPE_CHECKING: 36 ↛ 37line 36 didn't jump to line 37 because the condition on line 36 was never true

37 from lsst.afw.image import Mask, MaskedImage 

38 from lsst.geom import Box2I 

39 

40 from .typing_helpers import ImageLike 

41 

42 

43class ImagePlanes(ABC): 

44 """Struct interface for the image-like planes we coadd. 

45 

46 Notes 

47 ----- 

48 The extends the set of planes in `lsst.afw.image.MaskedImage` by adding 

49 noise realizations and "mask fraction" images. 

50 """ 

51 

52 @property 

53 @abstractmethod 

54 def bbox(self) -> Box2I: 

55 """The bounding box common to all image planes.""" 

56 raise NotImplementedError() 

57 

58 @property 

59 @abstractmethod 

60 def image(self) -> ImageLike: 

61 """The data image itself.""" 

62 raise NotImplementedError() 

63 

64 @property 

65 @abstractmethod 

66 def mask(self) -> Mask: 

67 """An integer bitmask.""" 

68 raise NotImplementedError() 

69 

70 @property 

71 @abstractmethod 

72 def variance(self) -> ImageLike: 

73 """Per-pixel variances for the image.""" 

74 raise NotImplementedError() 

75 

76 @property 

77 @abstractmethod 

78 def mask_fractions(self) -> ImageLike | None: 

79 """The (weighted) fraction of masked pixels that contribute to each 

80 pixel. 

81 """ 

82 raise NotImplementedError() 

83 

84 @property 

85 @abstractmethod 

86 def noise_realizations(self) -> Sequence[ImageLike]: 

87 """A sequence of noise realizations that were coadded with the same 

88 operations that were appled to the data image. 

89 """ 

90 raise NotImplementedError() 

91 

92 def asMaskedImage( 

93 self, 

94 *, 

95 noise_index: int | None = None, 

96 ) -> MaskedImageF: 

97 """Return an `lsst.afw.image.MaskedImage` view of the image, mask, and 

98 variance planes. 

99 

100 Parameters 

101 ---------- 

102 noise_index : `int` or `None`, optional 

103 If `None`, return the masked image formed from the 

104 main image, mask, and variance planes. If an integer index is 

105 provided, return the masked image formed from the specified noise 

106 realization, along with the mask and variance planes. 

107 

108 Returns 

109 ------- 

110 masked_image : `lsst.afw.image.MaskedImageF` 

111 The masked image formed from the specified planes. 

112 

113 Raises 

114 ------ 

115 ValueError 

116 Raised if ``noise_index`` is out of range for the available noise 

117 realizations. 

118 """ 

119 if noise_index is None: 

120 return MaskedImageF(self.image, self.mask, self.variance) 

121 elif 0 <= noise_index < len(self.noise_realizations): 

122 return MaskedImageF(self.noise_realizations[noise_index], self.mask, self.variance) 

123 else: 

124 raise ValueError( 

125 f"noise_index {noise_index} is out of range for " 

126 f"{len(self.noise_realizations)} noise realizations" 

127 ) 

128 

129 

130class OwnedImagePlanes(ImagePlanes): 

131 """An implementation of the `ImagePlanes` interface backed by actual 

132 afw objects. 

133 """ 

134 

135 def __init__( 

136 self, 

137 *, 

138 image: ImageLike, 

139 mask: Mask, 

140 variance: ImageLike, 

141 mask_fractions: ImageLike | None = None, 

142 noise_realizations: Sequence[ImageLike] = (), 

143 ): 

144 self._image = image 

145 self._mask = mask 

146 self._variance = variance 

147 self._mask_fractions = mask_fractions 

148 self._noise_realizations = tuple(noise_realizations) 

149 

150 @classmethod 

151 def from_masked_image( 

152 cls, 

153 masked_image: MaskedImage, 

154 mask_fractions: ImageLike | None = None, 

155 noise_realizations: Sequence[ImageLike] = (), 

156 ) -> Self: 

157 """Construct from an `lsst.afw.image.MaskedImage`. 

158 

159 Parameters 

160 ---------- 

161 masked_image : `~lsst.afw.image.MaskedImage` 

162 The image to construct from. The image, mask and variance planes 

163 of ``masked_image`` will be used as the image, mask and variance 

164 planes of the constructed object. 

165 mask_fractions : `ImageLike`, optional 

166 The mask fractions image. 

167 noise_realizations : `Sequence` [`ImageLike`], optional 

168 The noise realizations. 

169 

170 Returns 

171 ------- 

172 self : `OwnedImagePlanes` 

173 An instance of OwnedImagePlanes. 

174 """ 

175 return cls( 

176 image=masked_image.image, 

177 mask=masked_image.mask, 

178 variance=masked_image.variance, 

179 mask_fractions=mask_fractions, 

180 noise_realizations=noise_realizations, 

181 ) 

182 

183 @property 

184 def bbox(self) -> Box2I: 

185 # Docstring inherited. 

186 return self._image.getBBox() 

187 

188 @property 

189 def image(self) -> ImageLike: 

190 # Docstring inherited. 

191 return self._image 

192 

193 @property 

194 def mask(self) -> Mask: 

195 # Docstring inherited. 

196 return self._mask 

197 

198 @property 

199 def variance(self) -> ImageLike: 

200 # Docstring inherited. 

201 return self._variance 

202 

203 @property 

204 def mask_fractions(self) -> ImageLike | None: 

205 # Docstring inherited. 

206 return self._mask_fractions 

207 

208 @property 

209 def noise_realizations(self) -> Sequence[ImageLike]: 

210 # Docstring inherited. 

211 return self._noise_realizations 

212 

213 

214class ViewImagePlanes(ImagePlanes): 

215 """An implementation of the `ImagePlanes` interface that extracts views 

216 from another target `ImagePlanes` instance. 

217 

218 Parameters 

219 ---------- 

220 target : `ImagePlanes` 

221 Planes to construct views of. 

222 make_view : `Callable` 

223 Callable that takes an original image plane and returns a view into it. 

224 bbox : `Box2I`, optional 

225 Bounding box of the new image plane. Defaults to ``target.bbox``. 

226 """ 

227 

228 def __init__( 

229 self, target: ImagePlanes, make_view: Callable[[ImageLike], ImageLike], bbox: Box2I | None = None 

230 ): 

231 self._target = target 

232 self._bbox = bbox if bbox is not None else self._target.bbox 

233 self._make_view = make_view 

234 

235 @property 

236 def bbox(self) -> Box2I: 

237 # Docstring inherited. 

238 return self._bbox 

239 

240 @property 

241 def image(self) -> ImageLike: 

242 # Docstring inherited. 

243 return self._make_view(self._target.image) 

244 

245 @property 

246 def mask(self) -> Mask: 

247 # Docstring inherited. 

248 return self._make_view(self._target.mask) 

249 

250 @property 

251 def variance(self) -> ImageLike: 

252 # Docstring inherited. 

253 return self._make_view(self._target.variance) 

254 

255 @property 

256 def mask_fractions(self) -> ImageLike | None: 

257 # Docstring inherited. 

258 if self._target.mask_fractions is not None: 

259 return self._make_view(self._target.mask_fractions) 

260 

261 return None 

262 

263 @property 

264 def noise_realizations(self) -> Sequence[ImageLike]: 

265 # Docstring inherited. 

266 # We could make this even lazier with a custom Sequence class, but it 

267 # doesn't seem worthwhile. 

268 return tuple(self._make_view(r) for r in self._target.noise_realizations)