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