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

72 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-17 03:44 -0700

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(self) -> MaskedImageF: 

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

94 variance planes. 

95 """ 

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

97 

98 

99class OwnedImagePlanes(ImagePlanes): 

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

101 afw objects. 

102 """ 

103 

104 def __init__( 

105 self, 

106 *, 

107 image: ImageLike, 

108 mask: Mask, 

109 variance: ImageLike, 

110 mask_fractions: ImageLike | None = None, 

111 noise_realizations: Sequence[ImageLike] = (), 

112 ): 

113 self._image = image 

114 self._mask = mask 

115 self._variance = variance 

116 self._mask_fractions = mask_fractions 

117 self._noise_realizations = tuple(noise_realizations) 

118 

119 @classmethod 

120 def from_masked_image( 

121 cls, 

122 masked_image: MaskedImage, 

123 mask_fractions: ImageLike | None = None, 

124 noise_realizations: Sequence[ImageLike] = (), 

125 ) -> Self: 

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

127 

128 Parameters 

129 ---------- 

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

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

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

133 planes of the constructed object. 

134 mask_fractions : `ImageLike`, optional 

135 The mask fractions image. 

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

137 The noise realizations. 

138 

139 Returns 

140 ------- 

141 self : `OwnedImagePlanes` 

142 An instance of OwnedImagePlanes. 

143 """ 

144 return cls( 

145 image=masked_image.image, 

146 mask=masked_image.mask, 

147 variance=masked_image.variance, 

148 mask_fractions=mask_fractions, 

149 noise_realizations=noise_realizations, 

150 ) 

151 

152 @property 

153 def bbox(self) -> Box2I: 

154 # Docstring inherited. 

155 return self._image.getBBox() 

156 

157 @property 

158 def image(self) -> ImageLike: 

159 # Docstring inherited. 

160 return self._image 

161 

162 @property 

163 def mask(self) -> Mask: 

164 # Docstring inherited. 

165 return self._mask 

166 

167 @property 

168 def variance(self) -> ImageLike: 

169 # Docstring inherited. 

170 return self._variance 

171 

172 @property 

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

174 # Docstring inherited. 

175 return self._mask_fractions 

176 

177 @property 

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

179 # Docstring inherited. 

180 return self._noise_realizations 

181 

182 

183class ViewImagePlanes(ImagePlanes): 

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

185 from another target `ImagePlanes` instance. 

186 

187 Parameters 

188 ---------- 

189 target : `ImagePlanes` 

190 Planes to construct views of. 

191 make_view : `Callable` 

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

193 bbox : `Box2I`, optional 

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

195 """ 

196 

197 def __init__( 

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

199 ): 

200 self._target = target 

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

202 self._make_view = make_view 

203 

204 @property 

205 def bbox(self) -> Box2I: 

206 # Docstring inherited. 

207 return self._bbox 

208 

209 @property 

210 def image(self) -> ImageLike: 

211 # Docstring inherited. 

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

213 

214 @property 

215 def mask(self) -> Mask: 

216 # Docstring inherited. 

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

218 

219 @property 

220 def variance(self) -> ImageLike: 

221 # Docstring inherited. 

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

223 

224 @property 

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

226 # Docstring inherited. 

227 if self._target.mask_fractions is not None: 

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

229 

230 return None 

231 

232 @property 

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

234 # Docstring inherited. 

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

236 # doesn't seem worthwhile. 

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