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