Coverage for python/lsst/afw/math/backgroundList.py : 15%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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# (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/>.
22__all__ = ["BackgroundList"]
24import os
25import lsst.daf.base as dafBase
26import lsst.geom
27import lsst.afw.image as afwImage
28from lsst.afw.fits import MemFileManager, reduceToFits, Fits
29from .interpolate import Interpolate
30from .approximate import ApproximateControl
31from .background import BackgroundMI
32from .background import UndersampleStyle
35class BackgroundList:
36 """A list-like class to contain a list of (`lsst.afw.math.Background`,
37 `lsst.afw.math.Interpolate.Style`, `~lsst.afw.math.UndersampleStyle`)
38 tuples.
40 Parameters
41 ----------
42 *args : `tuple` or `~lsst.afw.math.Background`
43 A sequence of arguments, each of which becomes an element of the list.
44 We also accept a single `lsst.afw.math.Background` and extract the
45 ``interpStyle`` and ``undersampleStyle`` from the as-used values.
46 """
48 def __init__(self, *args):
49 self._backgrounds = []
50 for a in args:
51 self.append(a)
53 def __getitem__(self, *args):
54 """Return an item
56 Parameters
57 ----------
58 *args
59 Any valid list index.
60 """
61 #
62 # Set any previously-unknown Styles (they are set by bkgd.getImage())
63 #
64 for i, val in enumerate(self._backgrounds):
65 bkgd, interpStyle, undersampleStyle, approxStyle, \
66 approxOrderX, approxOrderY, approxWeighting = val
67 if interpStyle is None or undersampleStyle is None:
68 interpStyle = bkgd.getAsUsedInterpStyle()
69 undersampleStyle = bkgd.getAsUsedUndersampleStyle()
70 actrl = bkgd.getBackgroundControl().getApproximateControl()
71 approxStyle = actrl.getStyle()
72 approxOrderX = actrl.getOrderX()
73 approxOrderY = actrl.getOrderY()
74 approxWeighting = actrl.getWeighting()
75 self._backgrounds[i] = (bkgd, interpStyle, undersampleStyle,
76 approxStyle, approxOrderX, approxOrderY, approxWeighting)
77 #
78 # And return what they wanted
79 #
80 return self._backgrounds.__getitem__(*args)
82 def __len__(self, *args):
83 return self._backgrounds.__len__(*args)
85 def append(self, val):
86 try:
87 bkgd, interpStyle, undersampleStyle, approxStyle, \
88 approxOrderX, approxOrderY, approxWeighting = val
89 except TypeError:
90 bkgd = val
91 interpStyle = None
92 undersampleStyle = None
93 approxStyle = None
94 approxOrderX = None
95 approxOrderY = None
96 approxWeighting = None
98 bgInfo = (bkgd, interpStyle, undersampleStyle, approxStyle,
99 approxOrderX, approxOrderY, approxWeighting)
100 self._backgrounds.append(bgInfo)
102 def clone(self):
103 """Return a shallow copy
105 Shallow copies do not share backgrounds that are appended after copying,
106 but do share changes to contained background objects.
107 """
108 return BackgroundList(*self)
110 def writeFits(self, fileName, flags=0):
111 """Save our list of Backgrounds to a file.
113 Parameters
114 -----------
115 fileName : `str`
116 FITS file to write
117 flags : `int`
118 Flags to control details of writing; currently unused, but present
119 for consistency with `lsst.afw.table.BaseCatalog.writeFits`.
120 """
122 for i, bkgd in enumerate(self):
123 (bkgd, interpStyle, undersampleStyle, approxStyle, approxOrderX, approxOrderY,
124 approxWeighting) = bkgd
126 statsImage = bkgd.getStatsImage()
128 md = dafBase.PropertyList()
129 md.set("INTERPSTYLE", int(interpStyle))
130 md.set("UNDERSAMPLESTYLE", int(undersampleStyle))
131 md.set("APPROXSTYLE", int(approxStyle))
132 md.set("APPROXORDERX", approxOrderX)
133 md.set("APPROXORDERY", approxOrderY)
134 md.set("APPROXWEIGHTING", approxWeighting)
135 bbox = bkgd.getImageBBox()
136 md.set("BKGD_X0", bbox.getMinX())
137 md.set("BKGD_Y0", bbox.getMinY())
138 md.set("BKGD_WIDTH", bbox.getWidth())
139 md.set("BKGD_HEIGHT", bbox.getHeight())
141 statsImage.getImage().writeFits(fileName, md, "w" if i == 0 else "a")
142 statsImage.getMask().writeFits(fileName, md, "a")
143 statsImage.getVariance().writeFits(fileName, md, "a")
145 @staticmethod
146 def readFits(fileName, hdu=0, flags=0):
147 """Read our list of Backgrounds from a file.
149 Parameters
150 ----------
151 fileName : `str`
152 FITS file to read
153 hdu : `int`
154 First Header/Data Unit to attempt to read from
155 flags : `int`
156 Flags to control details of reading; currently unused, but present
157 for consistency with `lsst.afw.table.BaseCatalog.readFits`.
159 See Also
160 --------
161 getImage
162 """
163 if not isinstance(fileName, MemFileManager) and not os.path.exists(fileName):
164 raise RuntimeError(f"File not found: {fileName}")
166 self = BackgroundList()
168 f = Fits(fileName, 'r')
169 nHdus = f.countHdus()
170 f.closeFile()
171 if nHdus % 3 != 0:
172 raise RuntimeError(f"BackgroundList FITS file {fileName} has {nHdus} HDUs;"
173 f"expected a multiple of 3 (compression is not supported).")
175 for hdu in range(0, nHdus, 3):
176 # It seems like we ought to be able to just use
177 # MaskedImageFitsReader here, but it warns about EXTTYPE and still
178 # doesn't work quite naturally when starting from a nonzero HDU.
179 imageReader = afwImage.ImageFitsReader(fileName, hdu=hdu)
180 maskReader = afwImage.MaskFitsReader(fileName, hdu=hdu + 1)
181 varianceReader = afwImage.ImageFitsReader(fileName, hdu=hdu + 2)
182 statsImage = afwImage.MaskedImageF(imageReader.read(), maskReader.read(), varianceReader.read())
183 md = imageReader.readMetadata()
185 x0 = md["BKGD_X0"]
186 y0 = md["BKGD_Y0"]
187 width = md["BKGD_WIDTH"]
188 height = md["BKGD_HEIGHT"]
189 imageBBox = lsst.geom.BoxI(lsst.geom.PointI(x0, y0), lsst.geom.ExtentI(width, height))
191 interpStyle = Interpolate.Style(md["INTERPSTYLE"])
192 undersampleStyle = UndersampleStyle(md["UNDERSAMPLESTYLE"])
194 # Older outputs won't have APPROX* settings. Provide alternative defaults.
195 # Note: Currently X- and Y-orders must be equal due to a limitation in
196 # math::Chebyshev1Function2. Setting approxOrderY = -1 is equivalent
197 # to saying approxOrderY = approxOrderX.
198 approxStyle = md.get("APPROXSTYLE", ApproximateControl.UNKNOWN)
199 approxStyle = ApproximateControl.Style(approxStyle)
200 approxOrderX = md.get("APPROXORDERX", 1)
201 approxOrderY = md.get("APPROXORDERY", -1)
202 approxWeighting = md.get("APPROXWEIGHTING", True)
204 bkgd = BackgroundMI(imageBBox, statsImage)
205 bctrl = bkgd.getBackgroundControl()
206 bctrl.setInterpStyle(interpStyle)
207 bctrl.setUndersampleStyle(undersampleStyle)
208 actrl = ApproximateControl(approxStyle, approxOrderX, approxOrderY, approxWeighting)
209 bctrl.setApproximateControl(actrl)
210 bgInfo = (bkgd, interpStyle, undersampleStyle, approxStyle,
211 approxOrderX, approxOrderY, approxWeighting)
212 self.append(bgInfo)
214 return self
216 def getImage(self):
217 """Compute and return a full-resolution image from our list of
218 (Background, interpStyle, undersampleStyle).
219 """
221 bkgdImage = None
222 for (bkgd, interpStyle, undersampleStyle, approxStyle,
223 approxOrderX, approxOrderY, approxWeighting) in self:
224 if not bkgdImage:
225 if approxStyle != ApproximateControl.UNKNOWN:
226 bkgdImage = bkgd.getImageF()
227 else:
228 bkgdImage = bkgd.getImageF(interpStyle, undersampleStyle)
229 else:
230 if approxStyle != ApproximateControl.UNKNOWN:
231 bkgdImage += bkgd.getImageF()
232 else:
233 bkgdImage += bkgd.getImageF(interpStyle, undersampleStyle)
235 return bkgdImage
237 def __reduce__(self):
238 return reduceToFits(self)