Skip to content

Commit c71e9ff

Browse files
Merge pull request #54 from lsst/tickets/DM-53132
DM-53132: Add APIs to stitch noise images
2 parents 5d25b8b + 9e42460 commit c71e9ff

3 files changed

Lines changed: 76 additions & 11 deletions

File tree

python/lsst/cell_coadds/_image_planes.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,42 @@ def noise_realizations(self) -> Sequence[ImageLike]:
8989
"""
9090
raise NotImplementedError()
9191

92-
def asMaskedImage(self) -> MaskedImageF:
92+
def asMaskedImage(
93+
self,
94+
*,
95+
noise_index: int | None = None,
96+
) -> MaskedImageF:
9397
"""Return an `lsst.afw.image.MaskedImage` view of the image, mask, and
9498
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.
95118
"""
96-
return MaskedImageF(self.image, self.mask, self.variance)
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+
)
97128

98129

99130
class OwnedImagePlanes(ImagePlanes):

python/lsst/cell_coadds/_stitched_coadd.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,25 @@ def set_cell_edges(self, *, edge_width: int = 1, edge_mask_name: str = "CELL_EDG
166166
for cell in self._cell_coadd.cells.values():
167167
cell._set_cell_edges(edge_width=edge_width, edge_mask_name=edge_mask_name)
168168

169-
def asExposure(self) -> ExposureF:
170-
"""Return an `lsst.afw.image.Exposure` view of this piecewise image."""
171-
result = ExposureF(self.asMaskedImage())
169+
def asExposure(
170+
self,
171+
*,
172+
noise_index: int | None = None,
173+
) -> ExposureF:
174+
"""Return an `lsst.afw.image.Exposure` view of this piecewise image.
175+
176+
Parameters
177+
----------
178+
noise_index : `int` or `None`, optional
179+
If specified, return an exposure containing the specified noise
180+
realization instead of the data image.
181+
182+
Returns
183+
-------
184+
exposure : `lsst.afw.image.ExposureF`
185+
The stitched exposure.
186+
"""
187+
result = ExposureF(self.asMaskedImage(noise_index=noise_index))
172188
# Exposure components derived from "common" components are all simple.
173189
result.setWcs(self._cell_coadd.wcs)
174190
result.setFilter(FilterLabel(band=self.band))

tests/test_coadds.py

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -561,9 +561,29 @@ def test_asExposure(self):
561561
geom.Extent2I(self.inner_size_x, self.inner_size_y),
562562
)
563563
index = Index2D(x=x, y=y)
564-
self.assertImagesEqual(exposure.image[bbox], self.exposures[index].image[bbox])
565-
self.assertImagesEqual(exposure.variance[bbox], self.exposures[index].variance[bbox])
566-
self.assertImagesEqual(exposure.mask[bbox], self.exposures[index].mask[bbox])
564+
self.assertMaskedImagesEqual(exposure[bbox], self.exposures[index][bbox])
565+
566+
self.assertMaskedImagesEqual(self.stitched_coadd.asExposure(noise_index=None), exposure)
567+
for noise_index in range(self.n_noise_realizations):
568+
noise_exposure = self.stitched_coadd.asExposure(noise_index=noise_index)
569+
570+
self.assertImagesEqual(noise_exposure.variance, exposure.variance)
571+
self.assertImagesEqual(noise_exposure.mask, exposure.mask)
572+
573+
for y in range(self.ny):
574+
for x in range(self.nx):
575+
bbox = geom.Box2I(
576+
geom.Point2I(self.x0 + x * self.inner_size_x, self.y0 + y * self.inner_size_y),
577+
geom.Extent2I(self.inner_size_x, self.inner_size_y),
578+
)
579+
index = Index2D(x=x, y=y)
580+
self.assertImagesEqual(
581+
noise_exposure.image[bbox],
582+
self.multiple_cell_coadd.cells[index].outer.noise_realizations[noise_index][bbox],
583+
)
584+
585+
with self.assertRaises(ValueError):
586+
self.stitched_coadd.asExposure(noise_index=self.n_noise_realizations)
567587

568588
def test_aperture_correction(self):
569589
"""Test the aperture correction values are what we expect."""
@@ -613,9 +633,7 @@ def test_fits(self):
613633
read_exposure = ExposureF.readFits(filename) # Test the readFits method.
614634

615635
# Test that the image planes are identical.
616-
self.assertImagesEqual(read_exposure.image, write_exposure.image)
617-
self.assertImagesEqual(read_exposure.variance, write_exposure.variance)
618-
self.assertImagesEqual(read_exposure.mask, write_exposure.mask)
636+
self.assertMaskedImagesEqual(read_exposure, write_exposure)
619637

620638
# Test the PSF images in the StitchedPsf.
621639
for index in write_exposure.psf.images.indices():

0 commit comments

Comments
 (0)