LSST Applications  21.0.0+75b29a8a7f,21.0.0+e70536a077,21.0.0-1-ga51b5d4+62c747d40b,21.0.0-10-gbfb87ad6+3307648ee3,21.0.0-15-gedb9d5423+47cba9fc36,21.0.0-2-g103fe59+fdf0863a2a,21.0.0-2-g1367e85+d38a93257c,21.0.0-2-g45278ab+e70536a077,21.0.0-2-g5242d73+d38a93257c,21.0.0-2-g7f82c8f+e682ffb718,21.0.0-2-g8dde007+d179fbfa6a,21.0.0-2-g8f08a60+9402881886,21.0.0-2-ga326454+e682ffb718,21.0.0-2-ga63a54e+08647d4b1b,21.0.0-2-gde069b7+26c92b3210,21.0.0-2-gecfae73+0445ed2f95,21.0.0-2-gfc62afb+d38a93257c,21.0.0-27-gbbd0d29+ae871e0f33,21.0.0-28-g5fc5e037+feb0e9397b,21.0.0-3-g21c7a62+f4b9c0ff5c,21.0.0-3-g357aad2+57b0bddf0b,21.0.0-3-g4be5c26+d38a93257c,21.0.0-3-g65f322c+3f454acf5d,21.0.0-3-g7d9da8d+75b29a8a7f,21.0.0-3-gaa929c8+9e4ef6332c,21.0.0-3-ge02ed75+4b120a55c4,21.0.0-4-g3300ddd+e70536a077,21.0.0-4-g591bb35+4b120a55c4,21.0.0-4-gc004bbf+4911b9cd27,21.0.0-4-gccdca77+f94adcd104,21.0.0-4-ge8fba5a+2b3a696ff9,21.0.0-5-gb155db7+2c5429117a,21.0.0-5-gdf36809+637e4641ee,21.0.0-6-g00874e7+c9fd7f7160,21.0.0-6-g4e60332+4b120a55c4,21.0.0-7-gc8ca178+40eb9cf840,21.0.0-8-gfbe0b4b+9e4ef6332c,21.0.0-9-g2fd488a+d83b7cd606,w.2021.05
LSST Data Management Base Package
exposureAssembler.py
Go to the documentation of this file.
1 # This file is part of obs_base.
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 """Support for assembling and disassembling afw Exposures."""
23 
24 # Need to enable PSFs to be instantiated
25 import lsst.afw.detection # noqa: F401
26 from lsst.afw.image import makeExposure, makeMaskedImage
27 
28 from lsst.daf.butler import StorageClassDelegate
29 
30 
31 class ExposureAssembler(StorageClassDelegate):
32 
33  EXPOSURE_COMPONENTS = set(("image", "variance", "mask", "wcs", "psf"))
34  EXPOSURE_INFO_COMPONENTS = set(("apCorrMap", "coaddInputs", "photoCalib", "metadata",
35  "filterLabel", "transmissionCurve", "visitInfo",
36  "detector", "validPolygon", "summaryStats"))
37  EXPOSURE_READ_COMPONENTS = {"bbox", "dimensions", "xy0", "filter"}
38 
39  COMPONENT_MAP = {"bbox": "BBox", "xy0": "XY0"}
40  """Map component name to actual getter name."""
41 
42  def _groupRequestedComponents(self):
43  """Group requested components into top level and ExposureInfo.
44 
45  Returns
46  -------
47  expComps : `set` [`str`]
48  Components associated with the top level Exposure.
49  expInfoComps : `set` [`str`]
50  Components associated with the ExposureInfo
51 
52  Raises
53  ------
54  ValueError
55  There are components defined in the storage class that are not
56  expected by this assembler.
57  """
58  requested = set(self.storageClass.components.keys())
59 
60  # Check that we are requesting something that we support
61  unknown = requested - (self.EXPOSURE_COMPONENTSEXPOSURE_COMPONENTS | self.EXPOSURE_INFO_COMPONENTSEXPOSURE_INFO_COMPONENTS)
62  if unknown:
63  raise ValueError("Asking for unrecognized component: {}".format(unknown))
64 
65  expItems = requested & self.EXPOSURE_COMPONENTSEXPOSURE_COMPONENTS
66  expInfoItems = requested & self.EXPOSURE_INFO_COMPONENTSEXPOSURE_INFO_COMPONENTS
67  return expItems, expInfoItems
68 
69  def getComponent(self, composite, componentName):
70  """Get a component from an Exposure
71 
72  Parameters
73  ----------
74  composite : `~lsst.afw.image.Exposure`
75  `Exposure` to access component.
76  componentName : `str`
77  Name of component to retrieve.
78 
79  Returns
80  -------
81  component : `object`
82  The component. Can be None.
83 
84  Raises
85  ------
86  AttributeError
87  The component can not be found.
88  """
89  if componentName in self.EXPOSURE_COMPONENTSEXPOSURE_COMPONENTS or componentName in self.EXPOSURE_READ_COMPONENTSEXPOSURE_READ_COMPONENTS:
90  # Use getter translation if relevant or the name itself
91  return super().getComponent(composite, self.COMPONENT_MAPCOMPONENT_MAP.get(componentName, componentName))
92  elif componentName in self.EXPOSURE_INFO_COMPONENTSEXPOSURE_INFO_COMPONENTS:
93  if hasattr(composite, "getInfo"):
94  # it is possible for this method to be called with
95  # an ExposureInfo composite so trap for that and only get
96  # the ExposureInfo if the method is supported
97  composite = composite.getInfo()
98  return super().getComponent(composite, self.COMPONENT_MAPCOMPONENT_MAP.get(componentName, componentName))
99  else:
100  raise AttributeError("Do not know how to retrieve component {} from {}".format(componentName,
101  type(composite)))
102 
103  def getValidComponents(self, composite):
104  """Extract all non-None components from a composite.
105 
106  Parameters
107  ----------
108  composite : `object`
109  Composite from which to extract components.
110 
111  Returns
112  -------
113  comps : `dict`
114  Non-None components extracted from the composite, indexed by the
115  component name as derived from the `self.storageClass`.
116  """
117  # For Exposure we call the generic version twice: once for top level
118  # components, and again for ExposureInfo.
119  expItems, expInfoItems = self._groupRequestedComponents_groupRequestedComponents()
120 
121  components = super().getValidComponents(composite)
122  infoComps = super().getValidComponents(composite.getInfo())
123  components.update(infoComps)
124  return components
125 
126  def disassemble(self, composite):
127  """Disassemble an afw Exposure.
128 
129  This implementation attempts to extract components from the parent
130  by looking for attributes of the same name or getter methods derived
131  from the component name.
132 
133  Parameters
134  ----------
135  composite : `~lsst.afw.image.Exposure`
136  `Exposure` composite object consisting of components to be
137  extracted.
138 
139  Returns
140  -------
141  components : `dict`
142  `dict` with keys matching the components defined in
143  `self.storageClass` and values being `DatasetComponent` instances
144  describing the component.
145 
146  Raises
147  ------
148  ValueError
149  A requested component can not be found in the parent using generic
150  lookups.
151  TypeError
152  The parent object does not match the supplied `self.storageClass`.
153  """
154  if not self.storageClass.validateInstance(composite):
155  raise TypeError("Unexpected type mismatch between parent and StorageClass"
156  " ({} != {})".format(type(composite), self.storageClass.pytype))
157 
158  # Only look for components that are defined by the StorageClass
159  components = {}
160  expItems, expInfoItems = self._groupRequestedComponents_groupRequestedComponents()
161 
162  fromExposure = super().disassemble(composite, subset=expItems)
163  components.update(fromExposure)
164 
165  fromExposureInfo = super().disassemble(composite,
166  subset=expInfoItems, override=composite.getInfo())
167  components.update(fromExposureInfo)
168 
169  return components
170 
171  def assemble(self, components):
172  """Construct an Exposure from components.
173 
174  Parameters
175  ----------
176  components : `dict`
177  All the components from which to construct the Exposure.
178  Some can be missing.
179 
180  Returns
181  -------
182  exposure : `~lsst.afw.image.Exposure`
183  Assembled exposure.
184 
185  Raises
186  ------
187  ValueError
188  Some supplied components are not recognized.
189  """
190  components = components.copy()
191  maskedImageComponents = {}
192  hasMaskedImage = False
193  for component in ("image", "variance", "mask"):
194  value = None
195  if component in components:
196  hasMaskedImage = True
197  value = components.pop(component)
198  maskedImageComponents[component] = value
199 
200  wcs = None
201  if "wcs" in components:
202  wcs = components.pop("wcs")
203 
204  pytype = self.storageClass.pytype
205  if hasMaskedImage:
206  maskedImage = makeMaskedImage(**maskedImageComponents)
207  exposure = makeExposure(maskedImage, wcs=wcs)
208 
209  if not isinstance(exposure, pytype):
210  raise RuntimeError("Unexpected type created in assembly;"
211  " was {} expected {}".format(type(exposure), pytype))
212 
213  else:
214  exposure = pytype()
215  if wcs is not None:
216  exposure.setWcs(wcs)
217 
218  # Set other components
219  exposure.setPsf(components.pop("psf", None))
220  exposure.setPhotoCalib(components.pop("photoCalib", None))
221 
222  info = exposure.getInfo()
223  if "visitInfo" in components:
224  info.setVisitInfo(components.pop("visitInfo"))
225  info.setApCorrMap(components.pop("apCorrMap", None))
226  info.setCoaddInputs(components.pop("coaddInputs", None))
227  info.setMetadata(components.pop("metadata", None))
228  info.setValidPolygon(components.pop("validPolygon", None))
229  info.setDetector(components.pop("detector", None))
230  info.setTransmissionCurve(components.pop("transmissionCurve", None))
231  info.setSummaryStats(components.pop("summaryStats", None))
232 
233  # TODO: switch back to "filter" as primary component in DM-27177
234  info.setFilterLabel(components.pop("filterLabel", None))
235 
236  # If we have some components left over that is a problem
237  if components:
238  raise ValueError("The following components were not understood:"
239  " {}".format(list(components.keys())))
240 
241  return exposure
242 
243  def handleParameters(self, inMemoryDataset, parameters=None):
244  """Modify the in-memory dataset using the supplied parameters,
245  returning a possibly new object.
246 
247  Parameters
248  ----------
249  inMemoryDataset : `object`
250  Object to modify based on the parameters.
251  parameters : `dict`, optional
252  Parameters to apply. Values are specific to the parameter.
253  Supported parameters are defined in the associated
254  `StorageClass`. If no relevant parameters are specified the
255  inMemoryDataset will be return unchanged.
256 
257  Returns
258  -------
259  inMemoryDataset : `object`
260  Updated form of supplied in-memory dataset, after parameters
261  have been used.
262  """
263  # Understood by *this* subset command
264  understood = ("bbox", "origin")
265  use = self.storageClass.filterParameters(parameters, subset=understood)
266  if use:
267  inMemoryDataset = inMemoryDataset.subset(**use)
268 
269  return inMemoryDataset
270 
271  @classmethod
272  def selectResponsibleComponent(cls, readComponent: str, fromComponents) -> str:
273  imageComponents = ["mask", "image", "variance"]
274  forwarderMap = {
275  "bbox": imageComponents,
276  "dimensions": imageComponents,
277  "xy0": imageComponents,
278  "filter": ["filterLabel"],
279  }
280  forwarder = forwarderMap.get(readComponent)
281  if forwarder is not None:
282  for c in forwarder:
283  if c in fromComponents:
284  return c
285  raise ValueError(f"Can not calculate read component {readComponent} from {fromComponents}")
str selectResponsibleComponent(cls, str readComponent, fromComponents)
def handleParameters(self, inMemoryDataset, parameters=None)
def getComponent(self, composite, componentName)
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
std::shared_ptr< Exposure< ImagePixelT, MaskPixelT, VariancePixelT > > makeExposure(MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > &mimage, std::shared_ptr< geom::SkyWcs const > wcs=std::shared_ptr< geom::SkyWcs const >())
A function to return an Exposure of the correct type (cf.
Definition: Exposure.h:462
MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > * makeMaskedImage(typename std::shared_ptr< Image< ImagePixelT >> image, typename std::shared_ptr< Mask< MaskPixelT >> mask=Mask< MaskPixelT >(), typename std::shared_ptr< Image< VariancePixelT >> variance=Image< VariancePixelT >())
A function to return a MaskedImage of the correct type (cf.
Definition: MaskedImage.h:1268
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:174
table::Key< int > type
Definition: Detector.cc:163
daf::base::PropertyList * list
Definition: fits.cc:913
daf::base::PropertySet * set
Definition: fits.cc:912