LSST Applications g0da5cf3356+25b44625d0,g17e5ecfddb+50a5ac4092,g1c76d35bf8+585f0f68a2,g295839609d+8ef6456700,g2e2c1a68ba+cc1f6f037e,g38293774b4+62d12e78cb,g3b44f30a73+2891c76795,g48ccf36440+885b902d19,g4b2f1765b6+0c565e8f25,g5320a0a9f6+bd4bf1dc76,g56364267ca+403c24672b,g56b687f8c9+585f0f68a2,g5c4744a4d9+78cd207961,g5ffd174ac0+bd4bf1dc76,g6075d09f38+3075de592a,g667d525e37+cacede5508,g6f3e93b5a3+da81c812ee,g71f27ac40c+cacede5508,g7212e027e3+eb621d73aa,g774830318a+18d2b9fa6c,g7985c39107+62d12e78cb,g79ca90bc5c+fa2cc03294,g881bdbfe6c+cacede5508,g91fc1fa0cf+82a115f028,g961520b1fb+2534687f64,g96f01af41f+f2060f23b6,g9ca82378b8+cacede5508,g9d27549199+78cd207961,gb065e2a02a+ad48cbcda4,gb1df4690d6+585f0f68a2,gb35d6563ee+62d12e78cb,gbc3249ced9+bd4bf1dc76,gbec6a3398f+bd4bf1dc76,gd01420fc67+bd4bf1dc76,gd59336e7c4+c7bb92e648,gf46e8334de+81c9a61069,gfed783d017+bd4bf1dc76,v25.0.1.rc3
LSST Data Management Base Package
Loading...
Searching...
No Matches
_multiband.py
Go to the documentation of this file.
1# This file is part of afw.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://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 <http://www.gnu.org/licenses/>.
21
22__all__ = ["MultibandExposure", "computePsfImage", "IncompleteDataError"]
23
24import numpy as np
25
26from lsst.geom import Point2D, Point2I, Box2I
27from lsst.pex.exceptions import InvalidParameterError
28from . import Exposure, ExposureF
29from ..utils import projectImage
30from ..image._multiband import MultibandTripleBase, MultibandPixel
31from ..image._multiband import tripleFromSingles, tripleFromArrays, makeTripleFromKwargs
32from ..maskedImage import MaskedImage
33
34
35class IncompleteDataError(Exception):
36 """The PSF could not be computed due to incomplete data
37 """
38 pass
39
40
41def computePsfImage(psfModels, position, bands, useKernelImage=True):
42 """Get a multiband PSF image
43
44 The PSF Kernel Image is computed for each band
45 and combined into a (filter, y, x) array.
46
47 Parameters
48 ----------
49 psfList : `list` of `lsst.afw.detection.Psf`
50 The list of PSFs in each band.
51 position : `Point2D` or `tuple`
52 Coordinates to evaluate the PSF.
53 bands: `list` or `str`
54 List of names for each band
55 Returns
56 -------
57 psfs: `np.ndarray`
58 The multiband PSF image.
59 """
60 psfs = []
61 # Make the coordinates into a Point2D (if necessary)
62 if not isinstance(position, Point2D):
63 position = Point2D(position[0], position[1])
64
65 for bidx, psfModel in enumerate(psfModels):
66 try:
67 if useKernelImage:
68 psf = psfModel.computeKernelImage(position)
69 else:
70 psf = psfModel.computeImage(position)
71 psfs.append(psf)
72 except InvalidParameterError:
73 # This band failed to compute the PSF due to incomplete data
74 # at that location. This is unlikely to be a problem for Rubin,
75 # however the edges of some HSC COSMOS fields contain incomplete
76 # data in some bands, so we track this error to distinguish it
77 # from unknown errors.
78 msg = "Failed to compute PSF at {} in band {}"
79 raise IncompleteDataError(msg.format(position, bands[bidx])) from None
80
81 left = np.min([psf.getBBox().getMinX() for psf in psfs])
82 bottom = np.min([psf.getBBox().getMinY() for psf in psfs])
83 right = np.max([psf.getBBox().getMaxX() for psf in psfs])
84 top = np.max([psf.getBBox().getMaxY() for psf in psfs])
85 bbox = Box2I(Point2I(left, bottom), Point2I(right, top))
86 psfs = np.array([projectImage(psf, bbox).array for psf in psfs])
87 return psfs
88
89
91 """MultibandExposure class
92
93 This class acts as a container for multiple `afw.Exposure` objects.
94 All exposures must have the same bounding box, and the associated
95 images must all have the same data type.
96
97 See `MultibandTripleBase` for parameter definitions.
98 """
99 def __init__(self, filters, image, mask, variance, psfs=None):
100 super().__init__(filters, image, mask, variance)
101 if psfs is not None:
102 for psf, exposure in zip(psfs, self.singles):
103 exposure.setPsf(psf)
104
105 @staticmethod
106 def fromExposures(filters, singles):
107 """Construct a MultibandImage from a collection of single band images
108
109 see `tripleFromExposures` for a description of parameters
110 """
111 psfs = [s.getPsf() for s in singles]
112 return tripleFromSingles(MultibandExposure, filters, singles, psfs=psfs)
113
114 @staticmethod
115 def fromArrays(filters, image, mask, variance, bbox=None):
116 """Construct a MultibandExposure from a collection of arrays
117
118 see `tripleFromArrays` for a description of parameters
119 """
120 return tripleFromArrays(MultibandExposure, filters, image, mask, variance, bbox)
121
122 @staticmethod
123 def fromKwargs(filters, filterKwargs, singleType=ExposureF, **kwargs):
124 """Build a MultibandImage from a set of keyword arguments
125
126 see `makeTripleFromKwargs` for a description of parameters
127 """
128 return makeTripleFromKwargs(MultibandExposure, filters, filterKwargs, singleType, **kwargs)
129
130 def _buildSingles(self, image=None, mask=None, variance=None):
131 """Make a new list of single band objects
132
133 Parameters
134 ----------
135 image: `list`
136 List of `Image` objects that represent the image in each band.
137 mask: `list`
138 List of `Mask` objects that represent the mask in each band.
139 variance: `list`
140 List of `Image` objects that represent the variance in each band.
141
142 Returns
143 -------
144 singles: tuple
145 Tuple of `MaskedImage` objects for each band,
146 where the `image`, `mask`, and `variance` of each `single`
147 point to the multiband objects.
148 """
149 singles = []
150 if image is None:
151 image = self.image
152 if mask is None:
153 mask = self.mask
154 if variance is None:
155 variance = self.variance
156
157 dtype = image.array.dtype
158 for f in self.filters:
159 maskedImage = MaskedImage(image=image[f], mask=mask[f], variance=variance[f], dtype=dtype)
160 single = Exposure(maskedImage, dtype=dtype)
161 singles.append(single)
162 return tuple(singles)
163
164 @staticmethod
165 def fromButler(butler, bands, *args, **kwargs):
166 """Load a multiband exposure from a butler
167
168 Because each band is stored in a separate exposure file,
169 this method can be used to load all of the exposures for
170 a given set of bands
171
172 Parameters
173 ----------
174 butler: `lsst.daf.butler.Butler`
175 Butler connection to use to load the single band
176 calibrated images
177 bands: `list` or `str`
178 List of names for each band
179 args: `list`
180 Arguments to the Butler.
181 kwargs: `dict`
182 Keyword arguments to pass to the Butler
183 that are the same in all bands.
184
185 Returns
186 -------
187 result: `MultibandExposure`
188 The new `MultibandExposure` created by combining all of the
189 single band exposures.
190 """
191 # Load the Exposure in each band
192 exposures = []
193 for band in bands:
194 exposures.append(butler.get(*args, band=band, **kwargs))
195 return MultibandExposure.fromExposures(bands, exposures)
196
197 def computePsfKernelImage(self, position):
198 """Get a multiband PSF image
199
200 The PSF Kernel Image is computed for each band
201 and combined into a (filter, y, x) array and stored
202 as `self._psfImage`.
203 The result is not cached, so if the same PSF is expected
204 to be used multiple times it is a good idea to store the
205 result in another variable.
206
207 Parameters
208 ----------
209 position: `Point2D` or `tuple`
210 Coordinates to evaluate the PSF.
211
212 Returns
213 -------
214 self._psfImage: array
215 The multiband PSF image.
216 """
217 return computePsfImage(
218 psfModels=self.getPsfs(),
219 position=position,
220 bands=self.filters,
221 useKernelImage=True,
222 )
223
224 def computePsfImage(self, position=None):
225 """Get a multiband PSF image
226
227 The PSF Kernel Image is computed for each band
228 and combined into a (filter, y, x) array and stored
229 as `self._psfImage`.
230 The result is not cached, so if the same PSF is expected
231 to be used multiple times it is a good idea to store the
232 result in another variable.
233
234 Parameters
235 ----------
236 position: `Point2D` or `tuple`
237 Coordinates to evaluate the PSF. If `position` is `None`
238 then `Psf.getAveragePosition()` is used.
239
240 Returns
241 -------
242 self._psfImage: array
243 The multiband PSF image.
244 """
245 return computePsfImage(
246 psfModels=self.getPsfs(),
247 position=position,
248 bands=self.filters,
249 useKernelImage=True,
250 )
251
252 def getPsfs(self):
253 """Extract the PSF model in each band
254
255 Returns
256 -------
257 psfs : `list` of `lsst.afw.detection.Psf`
258 The PSF in each band
259 """
260 return [s.getPsf() for s in self]
261
262 def _slice(self, filters, filterIndex, indices):
263 """Slice the current object and return the result
264
265 See `Multiband._slice` for a list of the parameters.
266 This overwrites the base method to attach the PSF to
267 each individual exposure.
268 """
269 image = self.image._slice(filters, filterIndex, indices)
270 if self.mask is not None:
271 mask = self._mask._slice(filters, filterIndex, indices)
272 else:
273 mask = None
274 if self.variance is not None:
275 variance = self._variance._slice(filters, filterIndex, indices)
276 else:
277 variance = None
278
279 # If only a single pixel is selected, return the tuple of MultibandPixels
280 if isinstance(image, MultibandPixel):
281 if mask is not None:
282 assert isinstance(mask, MultibandPixel)
283 if variance is not None:
284 assert isinstance(variance, MultibandPixel)
285 return (image, mask, variance)
286
287 result = MultibandExposure(
288 filters=filters,
289 image=image,
290 mask=mask,
291 variance=variance,
292 psfs=self.getPsfs(),
293 )
294
295 assert all([r.getBBox() == result._bbox for r in [result._mask, result._variance]])
296 return result
table::Key< int > a
A polymorphic base class for representing an image's Point Spread Function.
Definition: Psf.h:76
def fromButler(butler, bands, *args, **kwargs)
Definition: _multiband.py:165
def __init__(self, filters, image, mask, variance, psfs=None)
Definition: _multiband.py:99
def fromArrays(filters, image, mask, variance, bbox=None)
Definition: _multiband.py:115
def fromKwargs(filters, filterKwargs, singleType=ExposureF, **kwargs)
Definition: _multiband.py:123
An integer coordinate rectangle.
Definition: Box.h:55
def computePsfImage(psfModels, position, bands, useKernelImage=True)
Definition: _multiband.py:41