Coverage for python/lsst/cell_coadds/_exploded_coadd.py: 47%
64 statements
« prev ^ index » next coverage.py v7.4.3, created at 2024-03-06 10:50 +0000
« prev ^ index » next coverage.py v7.4.3, created at 2024-03-06 10:50 +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__ = ("ExplodedCoadd",)
26from collections.abc import Iterator, Set
27from functools import partial
28from typing import TYPE_CHECKING
30from lsst.afw.image import ImageF
32from ._image_planes import ImagePlanes, ViewImagePlanes
33from ._stitched_image_planes import StitchedImagePlanes
34from ._uniform_grid import UniformGrid
35from .typing_helpers import ImageLike
37if TYPE_CHECKING: 37 ↛ 38line 37 didn't jump to line 38, because the condition on line 37 was never true
38 from lsst.geom import Box2I, Point2I
40 from ._multiple_cell_coadd import MultipleCellCoadd
43class ExplodedCoadd(StitchedImagePlanes):
44 """A lazy-evaluation coadd that stitches together the outer regions of the
45 cells in a `MultipleCellCoadd` (including multiple values for most pixels).
47 Parameters
48 ----------
49 cell_coadd : `MultipleCellCoadd`
50 Cell-based coadd to stitch together.
51 pad_psfs_with : `float` or `None`, optional
52 A floating-point value to pad PSF images with so each PSF-image cell
53 has the same dimensions as the image (outer) cell it corresponds to.
54 If `None`, PSF images will not be padded and the full PSF image will
55 generally be smaller than the exploded image it corresponds to.
57 Notes
58 -----
59 An `ExplodedCoadd` cannot be serialized in FITS format directly. Instead,
60 the recommended way is to serialize the `MultipleCellCoadd` instance that
61 was used to construct the object and reconstruct the `ExplodedCoadd` by
62 calling the `explode` method on it.
63 """
65 def __init__(self, cell_coadd: MultipleCellCoadd, *, pad_psfs_with: float | None = None):
66 super().__init__()
67 self._grid = UniformGrid(cell_coadd.outer_cell_size, cell_coadd.grid.shape)
68 if pad_psfs_with is None:
69 self._psf_grid = UniformGrid(cell_coadd.psf_image_size, cell_coadd.grid.shape)
70 elif (cell_coadd.psf_image_size.x > cell_coadd.outer_cell_size.x) or (
71 cell_coadd.psf_image_size.y > cell_coadd.outer_cell_size.y
72 ):
73 raise ValueError(
74 f"PSF image dimensions {cell_coadd.psf_image_size} are larger than "
75 f"outer cell dimensions {cell_coadd.outer_cell_size}; cannot pad."
76 )
77 else:
78 self._psf_grid = self._grid
79 self._cell_coadd = cell_coadd
80 self._pad_psfs_with = pad_psfs_with
81 self._psf_image: ImageF | None = None
83 @property
84 def bbox(self) -> Box2I:
85 # Docstring inherited.
86 return self._grid.bbox
88 @property
89 def grid(self) -> UniformGrid:
90 """Object that defines the piecewise grid (of outer cell regions) that
91 this object stitches together.
93 This grid always starts at `(0, 0)`; because there is no way to align
94 this "exploded" grid with the inner cell grid over more than one cell,
95 no attempt is made to align it with the inner cell grid's overall
96 offset.
97 """
98 return self._grid
100 @property
101 def psf_grid(self) -> UniformGrid:
102 """Object that describes the grid on which PSF model images are
103 stitched together.
104 """
105 return self._psf_grid
107 @property
108 def n_noise_realizations(self) -> int:
109 # Docstring inherited.
110 return self._cell_coadd.n_noise_realizations
112 @property
113 def mask_fraction_names(self) -> Set[str]:
114 # Docstring inherited.
115 return self._cell_coadd.mask_fraction_names
117 @staticmethod
118 def _make_view_with_xy0(original: ImageLike, xy0: Point2I) -> ImageLike:
119 result = original[:, :] # copy bbox, share pixel data.
120 result.setXY0(xy0)
121 return result
123 def _iter_cell_planes(self) -> Iterator[ImagePlanes]:
124 # Docstring inherited.
125 for cell in self._cell_coadd.cells.values():
126 new_bbox = self._grid.bbox_of(cell.identifiers.cell.index)
127 make_view = partial(self._make_view_with_xy0, xy0=new_bbox.getMin())
129 yield ViewImagePlanes(cell.outer, make_view, bbox=new_bbox)
131 @property
132 def psf_image(self) -> ImageF:
133 """A stitched-together image of the PSF models for each cell."""
134 if self._psf_image is None:
135 stitched_psf_image = ImageF(self.psf_grid.bbox)
136 if self._pad_psfs_with is not None:
137 stitched_psf_image.set(self._pad_psfs_with)
138 for cell in self._cell_coadd.cells.values():
139 target_subimage = stitched_psf_image[self.psf_grid.bbox_of(cell.identifiers.cell)]
140 target_dimensions = target_subimage.getDimensions()
141 source_dimensions = cell.psf_image.getDimensions()
142 offset = (target_dimensions - source_dimensions) // 2
143 # Use numpy views instead of Image methods because the images
144 # are not in the same coordinate system to begin with, so xy0
145 # doesn't help us.
146 target_subimage.array[
147 offset.y : offset.y + source_dimensions.y, offset.x : offset.x + source_dimensions.x
148 ] = cell.psf_image.array
149 self._psf_image = stitched_psf_image
150 return self._psf_image