Coverage for python / lsst / cell_coadds / _image_planes.py: 51%
70 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-22 09:18 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-22 09:18 +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/>.
22from __future__ import annotations
24__all__ = (
25 "ImagePlanes",
26 "OwnedImagePlanes",
27 "ViewImagePlanes",
28)
30from abc import ABC, abstractmethod
31from collections.abc import Callable, Sequence
32from typing import TYPE_CHECKING, Self
34from lsst.afw.image import MaskedImageF
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
40 from .typing_helpers import ImageLike
43class ImagePlanes(ABC):
44 """Struct interface for the image-like planes we coadd.
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 """
52 @property
53 @abstractmethod
54 def bbox(self) -> Box2I:
55 """The bounding box common to all image planes."""
56 raise NotImplementedError()
58 @property
59 @abstractmethod
60 def image(self) -> ImageLike:
61 """The data image itself."""
62 raise NotImplementedError()
64 @property
65 @abstractmethod
66 def mask(self) -> Mask:
67 """An integer bitmask."""
68 raise NotImplementedError()
70 @property
71 @abstractmethod
72 def variance(self) -> ImageLike:
73 """Per-pixel variances for the image."""
74 raise NotImplementedError()
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()
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()
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.
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.
108 Returns
109 -------
110 masked_image : `lsst.afw.image.MaskedImageF`
111 The masked image formed from the specified planes.
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 )
130class OwnedImagePlanes(ImagePlanes):
131 """An implementation of the `ImagePlanes` interface backed by actual
132 afw objects.
133 """
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)
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`.
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.
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 )
183 @property
184 def bbox(self) -> Box2I:
185 # Docstring inherited.
186 return self._image.getBBox()
188 @property
189 def image(self) -> ImageLike:
190 # Docstring inherited.
191 return self._image
193 @property
194 def mask(self) -> Mask:
195 # Docstring inherited.
196 return self._mask
198 @property
199 def variance(self) -> ImageLike:
200 # Docstring inherited.
201 return self._variance
203 @property
204 def mask_fractions(self) -> ImageLike | None:
205 # Docstring inherited.
206 return self._mask_fractions
208 @property
209 def noise_realizations(self) -> Sequence[ImageLike]:
210 # Docstring inherited.
211 return self._noise_realizations
214class ViewImagePlanes(ImagePlanes):
215 """An implementation of the `ImagePlanes` interface that extracts views
216 from another target `ImagePlanes` instance.
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 """
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
235 @property
236 def bbox(self) -> Box2I:
237 # Docstring inherited.
238 return self._bbox
240 @property
241 def image(self) -> ImageLike:
242 # Docstring inherited.
243 return self._make_view(self._target.image)
245 @property
246 def mask(self) -> Mask:
247 # Docstring inherited.
248 return self._make_view(self._target.mask)
250 @property
251 def variance(self) -> ImageLike:
252 # Docstring inherited.
253 return self._make_view(self._target.variance)
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)
261 return None
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)