LSST Applications  21.0.0-172-gfb10e10a+18fedfabac,22.0.0+297cba6710,22.0.0+80564b0ff1,22.0.0+8d77f4f51a,22.0.0+a28f4c53b1,22.0.0+dcf3732eb2,22.0.1-1-g7d6de66+2a20fdde0d,22.0.1-1-g8e32f31+297cba6710,22.0.1-1-geca5380+7fa3b7d9b6,22.0.1-12-g44dc1dc+2a20fdde0d,22.0.1-15-g6a90155+515f58c32b,22.0.1-16-g9282f48+790f5f2caa,22.0.1-2-g92698f7+dcf3732eb2,22.0.1-2-ga9b0f51+7fa3b7d9b6,22.0.1-2-gd1925c9+bf4f0e694f,22.0.1-24-g1ad7a390+a9625a72a8,22.0.1-25-g5bf6245+3ad8ecd50b,22.0.1-25-gb120d7b+8b5510f75f,22.0.1-27-g97737f7+2a20fdde0d,22.0.1-32-gf62ce7b1+aa4237961e,22.0.1-4-g0b3f228+2a20fdde0d,22.0.1-4-g243d05b+871c1b8305,22.0.1-4-g3a563be+32dcf1063f,22.0.1-4-g44f2e3d+9e4ab0f4fa,22.0.1-42-gca6935d93+ba5e5ca3eb,22.0.1-5-g15c806e+85460ae5f3,22.0.1-5-g58711c4+611d128589,22.0.1-5-g75bb458+99c117b92f,22.0.1-6-g1c63a23+7fa3b7d9b6,22.0.1-6-g50866e6+84ff5a128b,22.0.1-6-g8d3140d+720564cf76,22.0.1-6-gd805d02+cc5644f571,22.0.1-8-ge5750ce+85460ae5f3,master-g6e05de7fdc+babf819c66,master-g99da0e417a+8d77f4f51a,w.2021.48
LSST Data Management Base Package
ExposureFitsReader.cc
Go to the documentation of this file.
1 /*
2  * Developed for the LSST Data Management System.
3  * This product includes software developed by the LSST Project
4  * (https://www.lsst.org).
5  * See the COPYRIGHT file at the top-level directory of this distribution
6  * for details of code ownership.
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <map>
23 #include <optional>
24 #include <regex>
25 #include <set>
26 
27 #include "lsst/log/Log.h"
28 
31 #include "lsst/afw/geom/SkyWcs.h"
34 #include "lsst/afw/detection/Psf.h"
37 
38 namespace lsst {
39 namespace afw {
40 namespace image {
41 
42 namespace {
43 
44 LOG_LOGGER _log = LOG_GET("lsst.afw.image.fits.ExposureFitsReader");
45 
46 template <typename T, std::size_t N>
47 bool _contains(std::array<T, N> const& array, T const& value) {
48  for (T const& element : array) {
49  if (element == value) {
50  return true;
51  }
52  }
53  return false;
54 }
55 
56 // Map from compatibility "afw name" to correct filter label
57 std::map<std::string, FilterLabel> const _AFW_NAMES = {
58  std::make_pair("r2", FilterLabel::fromBandPhysical("r", "HSC-R2")),
59  std::make_pair("i2", FilterLabel::fromBandPhysical("i", "HSC-I2")),
60  std::make_pair("SOLID", FilterLabel::fromPhysical("solid plate 0.0 0.0")),
61 };
62 
68 bool _isBand(std::string const& name) {
69  static std::set<std::string> const BANDS = {"u", "g", "r", "i", "z", "y", "SH", "PH", "VR", "white"};
70  // Standard band
71  if (BANDS.count(name) > 0) {
72  return true;
73  }
74  // Looks like a narrow-band band
75  if (std::regex_match(name, std::regex("N\\d+"))) {
76  return true;
77  }
78  // Looks like an intermediate-band band; exclude "I2"
79  if (std::regex_match(name, std::regex("I\\d{2,}"))) {
80  return true;
81  }
82  return false;
83 }
84 
85 } // namespace
86 
96  static Filter const DEFAULT;
97  // Avoid turning dummy filters into real FilterLabels.
98  if (name == DEFAULT.getName()) {
99  return nullptr;
100  }
101 
102  // FilterLabel::from* returns a statically allocated object, so only way
103  // to get it into shared_ptr is to copy it.
104  if (_isBand(name)) {
105  return std::make_shared<FilterLabel>(FilterLabel::fromBand(name));
106  } else {
107  return std::make_shared<FilterLabel>(FilterLabel::fromPhysical(name));
108  }
109 }
110 
121 // TODO: compatibility code to be removed in DM-27177
123  static Filter const DEFAULT;
124  // Avoid turning dummy filters into real FilterLabels.
125  // Default filter has id=UNKNOWN, but others do too.
126  if (filter.getId() == DEFAULT.getId() && filter.getName() == DEFAULT.getName()) {
127  return nullptr;
128  }
129 
130  // Filter has no self-consistency guarantees whatsoever, and most methods
131  // are unsafe. Program extremely defensively.
132  if (filter.getId() == Filter::UNKNOWN) {
133  // Not a registered filter; guarantees on canonical name do not apply
134  return makeFilterLabelDirect(filter.getName());
135  }
136 
137  // obs.base.FilterDefinition ensures the canonical name is the first defined
138  // of afwname, band name, physical filter name.
139  std::string canonical;
140  try {
141  canonical = filter.getCanonicalName();
142  } catch (pex::exceptions::NotFoundError const&) {
143  // Not a registered filter; guarantees on canonical name do not apply
144  return makeFilterLabelDirect(filter.getName());
145  }
146 
147  if (_AFW_NAMES.count(canonical) > 0) {
148  return std::make_shared<FilterLabel>(_AFW_NAMES.at(canonical));
149  } else if (_isBand(canonical)) {
150  // physical filter is one of the aliases, but can't tell which one
151  // (FilterDefinition helpfully sorts them). Safer to leave it blank.
152  std::vector<std::string> aliases;
153  try {
154  aliases = filter.getAliases();
155  } catch (pex::exceptions::NotFoundError const&) {
156  // No aliases; leave the vector empty
157  }
158  if (aliases.size() == 1) {
159  return std::make_shared<FilterLabel>(FilterLabel::fromBandPhysical(canonical, aliases.front()));
160  } else {
161  return std::make_shared<FilterLabel>(FilterLabel::fromBand(canonical));
162  }
163  } else {
164  return std::make_shared<FilterLabel>(FilterLabel::fromPhysical(canonical));
165  }
166 }
167 
179  if (_AFW_NAMES.count(name) > 0) {
180  return std::make_shared<FilterLabel>(_AFW_NAMES.at(name));
181  }
182  // else name is either a band, a physical filter, or a deprecated alias
183 
184  try {
185  // To ease the transition to FilterLabel, use Filter to get all the names
186  // TODO: after DM-27177, leave only the catch block
187  Filter filter(name, false);
189  // Make use of the extra information that `name` is a preferred name
190  // If name is not a band, it is likely the physical filter
191  if (converted && !converted->hasPhysicalLabel() && converted->hasBandLabel() &&
192  name != converted->getBandLabel()) {
193  return std::make_shared<FilterLabel>(
194  FilterLabel::fromBandPhysical(converted->getBandLabel(), name));
195  } else {
196  return converted;
197  }
198  } catch (pex::exceptions::NotFoundError const&) {
199  // Unknown filter, no extra info to be gained
200  return makeFilterLabelDirect(name);
201  }
202 }
203 
212  // Filters still have standard aliases, so can use almost any name to define them.
213  // Prefer afw_name or band because that's what most code assumes is Filter.getName().
214  for (auto const& keyValue : _AFW_NAMES) {
215  std::string const& afwName = keyValue.first;
216  FilterLabel const& afwFilter = keyValue.second;
217  if (label == afwFilter) {
218  return Filter(afwName);
219  }
220  }
221 
222  if (label.hasBandLabel()) {
223  return Filter(label.getBandLabel(), true);
224  } else {
225  // FilterLabel guarantees at least one of band or physical is defined.
226  return Filter(label.getPhysicalLabel(), true);
227  }
228 }
229 
231 public:
235  if (primaryMetadata->exists(versionName)) {
236  version = primaryMetadata->getAsInt(versionName);
237  primaryMetadata->remove(versionName);
238  } else {
239  version = 0; // unversioned files are implicitly version 0
240  }
243  str(boost::format("Cannot read Exposure FITS version > %i") %
245  }
246 
247  // Try to read WCS from image metadata, and if found, strip the keywords used
248  try {
249  wcs = afw::geom::makeSkyWcs(*imageMetadata, true);
250  } catch (lsst::pex::exceptions::TypeError const&) {
251  LOGLS_DEBUG(_log, "No WCS found in FITS metadata");
252  }
253  if (wcs && any(xy0.ne(lsst::geom::Point2I(0, 0)))) {
254  wcs = wcs->copyAtShiftedPixelOrigin(lsst::geom::Extent2D(xy0));
255  }
256 
257  // Strip LTV1, LTV2 from imageMetadata, because we don't use it internally
258  imageMetadata->remove("LTV1");
259  imageMetadata->remove("LTV2");
260 
261  if (!imageMetadata->exists("INHERIT")) {
262  // New-style exposures put everything but the Wcs in the primary HDU, use
263  // INHERIT keyword in the others. For backwards compatibility, if we don't
264  // find the INHERIT keyword, we ignore the primary HDU metadata and expect
265  // everything to be in the image HDU metadata. Note that we can't merge them,
266  // because they're probably duplicates.
267  metadata = imageMetadata;
268  } else {
269  metadata = primaryMetadata;
270  }
271 
272  // Earlier versions persisted Filter as header keyword, version 2 persists FilterLabel as a Storable
273  if (version < 2) {
274  std::string key = "FILTER";
275  if (metadata->exists(key)) {
276  // Original Filter code depended on Boost for string trimming.
277  // DIY to avoid making this module depend on Boost.
278  std::string name = metadata->getAsString(key);
279  size_t end = name.find_last_not_of(' ');
280  filterLabel = makeFilterLabel(name.substr(0, end + 1));
281  }
282  }
283 
284  // EXPID keyword used in all versions, but was VisitInfo's responsibility before VisitInfo v3.
285  if (metadata->exists("EXPID")) {
286  exposureId = metadata->getAsInt64("EXPID");
287  }
288 
289  visitInfo = std::make_shared<VisitInfo>(*metadata);
291 
292  // This keyword is no longer handled by VisitInfo version >= 3.
293  metadata->remove("EXPID");
294 
295  // Version 0 persisted Calib FLUXMAG0 in the metadata, >=1 persisted PhotoCalib as a binary table.
296  if (version == 0) {
298  }
299 
300  // Strip MJD-OBS and DATE-OBS from metadata; those may be read by
301  // either SkyWcs or VisitInfo or both, so neither can strip them.
302  metadata->remove("MJD-OBS");
303  metadata->remove("DATE-OBS");
304 
305  // Strip DETSER, DETNAME; these are added when writing an Exposure
306  // with a Detector
307  metadata->remove("DETNAME");
308  metadata->remove("DETSER");
309  }
310 
311  int version;
312  std::optional<table::RecordId> exposureId;
318 };
319 
321 public:
322  enum Component {
323  PSF = 0,
332  };
333 
335  auto popInt = [&metadata](std::string const& name) {
336  // The default of zero will cause archive.get to return a
337  // null/empty pointer, just as if a null/empty pointer was
338  // originally written to the archive.
339  int r = 0;
340  if (metadata.exists(name)) {
341  r = metadata.get<int>(name);
342  // We remove metadata entries to maintaing our practice
343  // of stripped metadata entries that have been used to
344  // construct more structured components.
345  metadata.remove(name);
346  }
347  return r;
348  };
349  _hdu = popInt("AR_HDU");
350  if (_hdu == 0) {
351  _state = ArchiveState::MISSING;
352  } else {
353  --_hdu; // Switch from FITS 1-indexed convention to LSST 0-indexed convention.
354  _state = ArchiveState::PRESENT;
355  }
356  // Read in traditional components using old-style IDs, for backwards compatibility
357  _ids[PSF] = popInt("PSF_ID");
358  _ids[WCS] = popInt("SKYWCS_ID");
359  _ids[COADD_INPUTS] = popInt("COADD_INPUTS_ID");
360  _ids[AP_CORR_MAP] = popInt("AP_CORR_MAP_ID");
361  _ids[VALID_POLYGON] = popInt("VALID_POLYGON_ID");
362  _ids[TRANSMISSION_CURVE] = popInt("TRANSMISSION_CURVE_ID");
363  _ids[DETECTOR] = popInt("DETECTOR_ID");
364  _ids[PHOTOCALIB] = popInt("PHOTOCALIB_ID");
365 
366  // "Extra" components use a different keyword convention to avoid collisions with non-persistence IDs
367  std::vector<std::string> toStrip;
368  for (std::string const& headerKey : metadata) {
369  static std::string const PREFIX = "ARCHIVE_ID_";
370  if (headerKey.substr(0, PREFIX.size()) == PREFIX) {
371  std::string componentName = headerKey.substr(PREFIX.size());
372  int archiveId = metadata.get<int>(headerKey);
373  _genericIds.emplace(componentName, archiveId);
374  if (!_contains(_ids, archiveId)) {
375  _extraIds.emplace(componentName);
376  }
377  toStrip.push_back(headerKey);
378  toStrip.push_back(componentName + "_ID"); // strip corresponding old-style ID, if it exists
379  }
380  }
381  for (std::string const& key : toStrip) {
382  metadata.remove(key);
383  }
384  }
385 
395  template <typename T>
397  if (!_ensureLoaded(fitsFile)) {
398  return nullptr;
399  }
400  return _archive.get<T>(_ids[c]);
401  }
402 
419  // This method takes a string instead of a strongly typed Key because
420  // readExtraComponents() gets its keys from the FITS metadata.
421  // Using a Key would make the calling code more complicated.
422  template <typename T>
424  if (!_ensureLoaded(fitsFile)) {
425  return nullptr;
426  }
427 
428  if (_genericIds.count(c) > 0) {
429  int archiveId = _genericIds.at(c);
430  return _archive.get<T>(archiveId);
431  } else {
432  return nullptr;
433  }
434  }
435 
446  afw::fits::Fits* fitsFile) {
448 
449  if (!_ensureLoaded(fitsFile)) {
450  return result;
451  }
452 
453  // Not safe to call getAll if a component cannot be unpersisted
454  // Instead, look for the archives registered in the metadata
455  for (std::string const& componentName : _extraIds) {
456  try {
457  result.emplace(componentName, readComponent<table::io::Persistable>(fitsFile, componentName));
458  } catch (pex::exceptions::NotFoundError const& err) {
459  LOGLS_WARN(_log,
460  "Could not read component " << componentName << "; skipping: " << err.what());
461  }
462  }
463  return result;
464  }
465 
466 private:
467  bool _ensureLoaded(afw::fits::Fits* fitsFile) {
468  if (_state == ArchiveState::MISSING) {
469  return false;
470  }
471  if (_state == ArchiveState::PRESENT) {
472  afw::fits::HduMoveGuard guard(*fitsFile, _hdu);
473  _archive = table::io::InputArchive::readFits(*fitsFile);
474  _state = ArchiveState::LOADED;
475  }
476  assert(_state == ArchiveState::LOADED); // constructor body should guarantee it's not UNKNOWN
477  return true;
478  }
479 
480  enum class ArchiveState { UNKNOWN, MISSING, PRESENT, LOADED };
481 
482  int _hdu = 0;
483  ArchiveState _state = ArchiveState::UNKNOWN;
484  table::io::InputArchive _archive;
486  std::map<std::string, int> _genericIds;
487  std::set<std::string> _extraIds; // _genericIds not included in _ids
488 };
489 
490 ExposureFitsReader::ExposureFitsReader(std::string const& fileName) : _maskedImageReader(fileName) {}
491 
492 ExposureFitsReader::ExposureFitsReader(fits::MemFileManager& manager) : _maskedImageReader(manager) {}
493 
494 ExposureFitsReader::ExposureFitsReader(fits::Fits* fitsFile) : _maskedImageReader(fitsFile) {}
495 
496 ExposureFitsReader::~ExposureFitsReader() noexcept = default;
497 
499  return _maskedImageReader.readBBox(origin);
500 }
501 
503  return _maskedImageReader.readXY0(bbox, origin);
504 }
505 
507  _ensureReaders();
508  return _metadataReader->version;
509 }
510 
511 std::string ExposureFitsReader::readImageDType() const { return _maskedImageReader.readImageDType(); }
512 
513 std::string ExposureFitsReader::readMaskDType() const { return _maskedImageReader.readMaskDType(); }
514 
516 
518  _ensureReaders();
519  return _metadataReader->metadata;
520 }
521 
522 std::optional<table::RecordId> ExposureFitsReader::readExposureId() {
523  _ensureReaders();
524  return _metadataReader->exposureId;
525 }
526 
528  _ensureReaders();
529  auto r = _archiveReader->readComponent<afw::geom::SkyWcs>(_getFitsFile(), ArchiveReader::WCS);
530  if (!r) {
531  r = _metadataReader->wcs;
532  }
533  return r;
534 }
535 
538  if (label) {
539  return makeFilter(*label);
540  } else {
541  // Old exposures always had a Filter, even if only the default
542  return Filter();
543  }
544 }
545 
547  _ensureReaders();
548  if (_metadataReader->version < 2) {
549  return _metadataReader->filterLabel;
550  } else {
551  return _archiveReader->readComponent<FilterLabel>(_getFitsFile(), ExposureInfo::KEY_FILTER.getId());
552  }
553 }
554 
556  _ensureReaders();
557  if (_metadataReader->version == 0) {
558  return _metadataReader->photoCalib;
559  } else {
560  return _archiveReader->readComponent<image::PhotoCalib>(_getFitsFile(), ArchiveReader::PHOTOCALIB);
561  }
562 }
563 
565  _ensureReaders();
566  return _archiveReader->readComponent<detection::Psf>(_getFitsFile(), ArchiveReader::PSF);
567 }
568 
570  _ensureReaders();
571  return _archiveReader->readComponent<afw::geom::polygon::Polygon>(_getFitsFile(),
573 }
574 
576  _ensureReaders();
577  return _archiveReader->readComponent<ApCorrMap>(_getFitsFile(), ArchiveReader::AP_CORR_MAP);
578 }
579 
581  _ensureReaders();
582  return _archiveReader->readComponent<CoaddInputs>(_getFitsFile(), ArchiveReader::COADD_INPUTS);
583 }
584 
586  _ensureReaders();
587  return _metadataReader->visitInfo;
588 }
589 
591  _ensureReaders();
592  return _archiveReader->readComponent<TransmissionCurve>(_getFitsFile(),
594 }
595 
597  _ensureReaders();
598  return _archiveReader->readComponent<cameraGeom::Detector>(_getFitsFile(), ArchiveReader::DETECTOR);
599 }
600 
602  _ensureReaders();
603  return _archiveReader->readComponent<typehandling::Storable>(_getFitsFile(), componentName);
604 }
605 
607  _ensureReaders();
608  return _archiveReader->readExtraComponents(_getFitsFile());
609 }
610 
612  auto result = std::make_shared<ExposureInfo>();
613  result->setMetadata(readMetadata());
614  result->setPhotoCalib(readPhotoCalib());
615  result->setVisitInfo(readVisitInfo());
616  // Override ID set in visitInfo, if necessary
617  std::optional<table::RecordId> exposureId = readExposureId();
618  if (exposureId) {
619  result->setId(*exposureId);
620  }
621  // When reading an ExposureInfo (as opposed to reading individual
622  // components), we warn and try to proceed when a component is present
623  // but can't be read due its serialization factory not being set up
624  // (that's what throws the NotFoundErrors caught below).
625  try {
626  result->setPsf(readPsf());
627  } catch (pex::exceptions::NotFoundError& err) {
628  LOGLS_WARN(_log, "Could not read PSF; setting to null: " << err.what());
629  }
630  try {
631  result->setCoaddInputs(readCoaddInputs());
632  } catch (pex::exceptions::NotFoundError& err) {
633  LOGLS_WARN(_log, "Could not read CoaddInputs; setting to null: " << err.what());
634  }
635  try {
636  result->setApCorrMap(readApCorrMap());
637  } catch (pex::exceptions::NotFoundError& err) {
638  LOGLS_WARN(_log, "Could not read ApCorrMap; setting to null: " << err.what());
639  }
640  try {
641  result->setValidPolygon(readValidPolygon());
642  } catch (pex::exceptions::NotFoundError& err) {
643  LOGLS_WARN(_log, "Could not read ValidPolygon; setting to null: " << err.what());
644  }
645  try {
646  result->setTransmissionCurve(readTransmissionCurve());
647  } catch (pex::exceptions::NotFoundError& err) {
648  LOGLS_WARN(_log, "Could not read TransmissionCurve; setting to null: " << err.what());
649  }
650  try {
651  result->setDetector(readDetector());
652  } catch (pex::exceptions::NotFoundError& err) {
653  LOGLS_WARN(_log, "Could not read Detector; setting to null: " << err.what());
654  }
655  // In the case of WCS, we fall back to the metadata WCS if the one from
656  // the archive can't be read.
657  _ensureReaders();
658  result->setWcs(_metadataReader->wcs);
659  try {
660  auto wcs = _archiveReader->readComponent<afw::geom::SkyWcs>(_getFitsFile(), ArchiveReader::WCS);
661  if (!wcs) {
662  LOGLS_DEBUG(_log, "No WCS found in binary table");
663  } else {
664  result->setWcs(wcs);
665  }
666  } catch (pex::exceptions::NotFoundError& err) {
667  auto msg = str(boost::format("Could not read WCS extension; setting to null: %s") % err.what());
668  if (result->hasWcs()) {
669  msg += " ; using WCS from FITS header";
670  }
671  LOGLS_WARN(_log, msg);
672  }
673  for (const auto& keyValue : readExtraComponents()) {
675  std::string key = keyValue.first;
676  StorablePtr object = std::dynamic_pointer_cast<StorablePtr::element_type>(keyValue.second);
677 
678  if (object.use_count() > 0) { // Failed cast guarantees empty pointer, but not a null one
679  result->setComponent(typehandling::makeKey<StorablePtr>(key), object);
680  } else {
681  LOGLS_WARN(_log, "Data corruption: generic component " << key << " is not a Storable; skipping.");
682  }
683  }
684  // Convert old-style Filter to new-style FilterLabel
685  // In newer versions this is handled by readExtraComponents()
686  if (_metadataReader->version < 2 && !result->hasFilterLabel()) {
687  result->setFilterLabel(readFilterLabel());
688  }
689  return result;
690 } // namespace image
691 
692 template <typename ImagePixelT>
694  bool allowUnsafe) {
695  return _maskedImageReader.readImage<ImagePixelT>(bbox, origin, allowUnsafe);
696 }
697 
698 template <typename ImagePixelT>
699 ndarray::Array<ImagePixelT, 2, 2> ExposureFitsReader::readImageArray(lsst::geom::Box2I const& bbox,
700  ImageOrigin origin, bool allowUnsafe) {
701  return _maskedImageReader.readImageArray<ImagePixelT>(bbox, origin, allowUnsafe);
702 }
703 
704 template <typename MaskPixelT>
706  bool conformMasks, bool allowUnsafe) {
707  return _maskedImageReader.readMask<MaskPixelT>(bbox, origin, conformMasks, allowUnsafe);
708 }
709 
710 template <typename MaskPixelT>
711 ndarray::Array<MaskPixelT, 2, 2> ExposureFitsReader::readMaskArray(lsst::geom::Box2I const& bbox,
712  ImageOrigin origin, bool allowUnsafe) {
713  return _maskedImageReader.readMaskArray<MaskPixelT>(bbox, origin, allowUnsafe);
714 }
715 
716 template <typename VariancePixelT>
718  bool allowUnsafe) {
719  return _maskedImageReader.readVariance<VariancePixelT>(bbox, origin, allowUnsafe);
720 }
721 
722 template <typename VariancePixelT>
723 ndarray::Array<VariancePixelT, 2, 2> ExposureFitsReader::readVarianceArray(lsst::geom::Box2I const& bbox,
724  ImageOrigin origin,
725  bool allowUnsafe) {
726  return _maskedImageReader.readVarianceArray<VariancePixelT>(bbox, origin, allowUnsafe);
727 }
728 
729 template <typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
731  lsst::geom::Box2I const& bbox, ImageOrigin origin, bool conformMasks, bool allowUnsafe) {
732  return _maskedImageReader.read<ImagePixelT, MaskPixelT, VariancePixelT>(bbox, origin, conformMasks,
733  /* needAllHdus= */ false,
734  allowUnsafe);
735 }
736 
737 template <typename ImagePixelT, typename MaskPixelT, typename VariancePixelT>
739  ImageOrigin origin,
740  bool conformMasks,
741  bool allowUnsafe) {
742  auto mi =
743  readMaskedImage<ImagePixelT, MaskPixelT, VariancePixelT>(bbox, origin, conformMasks, allowUnsafe);
745 }
746 
747 void ExposureFitsReader::_ensureReaders() {
748  if (!_metadataReader) {
749  auto metadataReader = std::make_unique<MetadataReader>(_maskedImageReader.readPrimaryMetadata(),
750  _maskedImageReader.readImageMetadata(),
751  _maskedImageReader.readXY0());
752  _archiveReader = std::make_unique<ArchiveReader>(*metadataReader->metadata);
753  _metadataReader = std::move(metadataReader); // deferred for exception safety
754  }
755  assert(_archiveReader); // should always be initialized with _metadataReader.
756 }
757 
758 #define INSTANTIATE(ImagePixelT) \
759  template Exposure<ImagePixelT, MaskPixel, VariancePixel> ExposureFitsReader::read( \
760  lsst::geom::Box2I const&, ImageOrigin, bool, bool); \
761  template Image<ImagePixelT> ExposureFitsReader::readImage(lsst::geom::Box2I const&, ImageOrigin, bool); \
762  template ndarray::Array<ImagePixelT, 2, 2> ExposureFitsReader::readImageArray(lsst::geom::Box2I const&, \
763  ImageOrigin, bool); \
764  template MaskedImage<ImagePixelT, MaskPixel, VariancePixel> ExposureFitsReader::readMaskedImage( \
765  lsst::geom::Box2I const&, ImageOrigin, bool, bool)
766 
768 INSTANTIATE(int);
769 INSTANTIATE(float);
770 INSTANTIATE(double);
772 
773 template Mask<MaskPixel> ExposureFitsReader::readMask(lsst::geom::Box2I const&, ImageOrigin, bool, bool);
774 template ndarray::Array<MaskPixel, 2, 2> ExposureFitsReader::readMaskArray(lsst::geom::Box2I const&,
775  ImageOrigin, bool);
776 
777 template Image<VariancePixel> ExposureFitsReader::readVariance(lsst::geom::Box2I const&, ImageOrigin, bool);
778 template ndarray::Array<VariancePixel, 2, 2> ExposureFitsReader::readVarianceArray(lsst::geom::Box2I const&,
779  ImageOrigin, bool);
780 
781 } // namespace image
782 } // namespace afw
783 } // namespace lsst
py::object result
Definition: _schema.cc:429
table::Key< std::string > name
Definition: Amplifier.cc:116
AmpInfoBoxKey bbox
Definition: Amplifier.cc:117
double element[2]
Definition: BaseTable.cc:90
int end
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition: Exception.h:48
#define INSTANTIATE(ImagePixelT)
LSST DM logging module built on log4cxx.
#define LOGLS_WARN(logger, message)
Log a warn-level message using an iostream-based interface.
Definition: Log.h:659
#define LOG_GET(logger)
Returns a Log object associated with logger.
Definition: Log.h:75
#define LOG_LOGGER
Definition: Log.h:714
#define LOGLS_DEBUG(logger, message)
Log a debug-level message using an iostream-based interface.
Definition: Log.h:619
Implementation of the Photometric Calibration class.
table::Key< table::Array< std::uint8_t > > wcs
Definition: SkyWcs.cc:66
table::Key< table::RecordId > exposureId
Definition: VisitInfo.cc:199
T at(T... args)
A representation of a detector in a mosaic camera.
Definition: Detector.h:185
A polymorphic base class for representing an image's Point Spread Function.
Definition: Psf.h:76
A simple struct that combines the two arguments that must be passed to most cfitsio routines and cont...
Definition: fits.h:297
RAII scoped guard for moving the HDU in a Fits object.
Definition: fits.h:724
Lifetime-management for memory that goes into FITS memory files.
Definition: fits.h:121
A 2-dimensional celestial WCS that transform pixels to ICRS RA/Dec, using the LSST standard for pixel...
Definition: SkyWcs.h:117
Cartesian polygons.
Definition: Polygon.h:59
A thin wrapper around std::map to allow aperture corrections to be attached to Exposures.
Definition: ApCorrMap.h:45
A simple Persistable struct containing ExposureCatalogs that record the inputs to a coadd.
Definition: CoaddInputs.h:49
std::shared_ptr< T > readComponent(afw::fits::Fits *fitsFile, Component c)
Read a known component, if available.
std::shared_ptr< T > readComponent(afw::fits::Fits *fitsFile, std::string c)
Read an arbitrary component, if available.
std::map< std::string, std::shared_ptr< table::io::Persistable > > readExtraComponents(afw::fits::Fits *fitsFile)
Read the components that are stored using arbitrary-component support.
std::shared_ptr< daf::base::PropertyList > metadata
MetadataReader(std::shared_ptr< daf::base::PropertyList > primaryMetadata, std::shared_ptr< daf::base::PropertyList > imageMetadata, lsst::geom::Point2I const &xy0)
A FITS reader class for Exposures and their components.
std::shared_ptr< TransmissionCurve > readTransmissionCurve()
Read the Exposure's transmission curve.
std::string readMaskDType() const
Read a string describing the pixel type of the on-disk image plane.
Exposure< ImagePixelT, MaskPixelT, VariancePixelT > read(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT, bool conformMasks=false, bool allowUnsafe=false)
Read the full Exposure.
std::shared_ptr< typehandling::Storable > readComponent(std::string const &componentName)
Read an arbitrary non-standard component by name.
std::shared_ptr< afw::geom::SkyWcs > readWcs()
Read the Exposure's world coordinate system.
std::shared_ptr< FilterLabel > readFilterLabel()
Read the Exposure's filter information.
MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > readMaskedImage(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT, bool conformMasks=false, bool allowUnsafe=false)
Read the MaskedImage.
std::map< std::string, std::shared_ptr< table::io::Persistable > > readExtraComponents()
Read the Exposure's non-standard components.
lsst::geom::Point2I readXY0(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT)
Read the image origin from the on-disk image or a subimage thereof.
ndarray::Array< VariancePixelT, 2, 2 > readVarianceArray(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT, bool allowUnsafe=false)
Read the variance plane.
int readSerializationVersion()
Read the serialization version number from the header.
ExposureFitsReader(std::string const &fileName)
Construct a FITS reader object.
std::shared_ptr< detection::Psf > readPsf()
Read the Exposure's point-spread function.
std::shared_ptr< afw::geom::polygon::Polygon > readValidPolygon()
Read the polygon describing the region of validity for the Exposure.
Filter readFilter()
Read the Exposure's filter.
Image< ImagePixelT > readImage(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT, bool allowUnsafe=false)
Read the image plane.
std::string readVarianceDType() const
Read a string describing the pixel type of the on-disk image plane.
ndarray::Array< ImagePixelT, 2, 2 > readImageArray(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT, bool allowUnsafe=false)
Read the image plane.
lsst::geom::Box2I readBBox(ImageOrigin origin=PARENT)
Read the bounding box of the on-disk image.
Image< VariancePixelT > readVariance(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT, bool allowUnsafe=false)
Read the variance plane.
std::shared_ptr< daf::base::PropertyList > readMetadata()
Read the flexible metadata associated with the Exposure.
std::shared_ptr< PhotoCalib > readPhotoCalib()
Read the Exposure's photometric calibration.
std::optional< table::RecordId > readExposureId()
Read the Exposure's exposure ID, if it exists.
std::shared_ptr< ApCorrMap > readApCorrMap()
Read the Exposure's aperture correction map.
ndarray::Array< MaskPixelT, 2, 2 > readMaskArray(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT, bool allowUnsafe=false)
Read the mask plane.
std::string readImageDType() const
Read a string describing the pixel type of the on-disk image plane.
std::shared_ptr< CoaddInputs > readCoaddInputs()
Read the Exposure's coadd input catalogs.
std::shared_ptr< cameraGeom::Detector > readDetector()
Read the Exposure's detector.
std::shared_ptr< ExposureInfo > readExposureInfo()
Read the ExposureInfo containing all non-image components.
std::shared_ptr< VisitInfo > readVisitInfo()
Read the Exposure's visit metadata.
Mask< MaskPixelT > readMask(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT, bool conformMasks=false, bool allowUnsafe=false)
Read the mask plane.
A class to contain the data, WCS, and other information needed to describe an image of the sky.
Definition: Exposure.h:72
static int getFitsSerializationVersion()
Get the version of FITS serialization that this ExposureInfo understands.
static std::string const & getFitsSerializationVersionName()
Get the version of FITS serialization version info name.
static typehandling::Key< std::string, std::shared_ptr< FilterLabel const > > const KEY_FILTER
Standard key for looking up filter information.
Definition: ExposureInfo.h:110
A group of labels for a filter in an exposure or coadd.
Definition: FilterLabel.h:58
std::string getBandLabel() const
Return the band label.
Definition: FilterLabel.cc:87
bool hasBandLabel() const noexcept
Return whether the filter label names a band.
Definition: FilterLabel.cc:85
static FilterLabel fromBand(std::string const &band)
Construct a FilterLabel from specific inputs.
Definition: FilterLabel.cc:72
std::string getPhysicalLabel() const
Return the physical filter label.
Definition: FilterLabel.cc:98
static FilterLabel fromBandPhysical(std::string const &band, std::string const &physical)
Construct a FilterLabel from specific inputs.
Definition: FilterLabel.cc:68
static FilterLabel fromPhysical(std::string const &physical)
Construct a FilterLabel from specific inputs.
Definition: FilterLabel.cc:74
A class to represent a 2-dimensional array of pixels.
Definition: Image.h:51
Represent a 2-dimensional array of bitmask pixels.
Definition: Mask.h:77
std::string readMaskDType() const
Read a string describing the pixel type of the on-disk image plane.
std::shared_ptr< daf::base::PropertyList > readImageMetadata()
Read the FITS header of one of the HDUs.
std::shared_ptr< daf::base::PropertyList > readPrimaryMetadata()
Read the FITS header of one of the HDUs.
std::string readVarianceDType() const
Read a string describing the pixel type of the on-disk image plane.
ndarray::Array< MaskPixelT, 2, 2 > readMaskArray(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT, bool allowUnsafe=false)
Read the mask plane.
std::string readImageDType() const
Read a string describing the pixel type of the on-disk image plane.
ndarray::Array< ImagePixelT, 2, 2 > readImageArray(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT, bool allowUnsafe=false)
Read the image plane.
ndarray::Array< VariancePixelT, 2, 2 > readVarianceArray(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT, bool allowUnsafe=false)
Read the variance plane.
Mask< MaskPixelT > readMask(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT, bool conformMasks=false, bool allowUnsafe=false)
Read the mask plane.
MaskedImage< ImagePixelT, MaskPixelT, VariancePixelT > read(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT, bool conformMasks=false, bool needAllHdus=false, bool allowUnsafe=false)
Read the full MaskedImage.
Image< ImagePixelT > readImage(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT, bool allowUnsafe=false)
Read the image plane.
Image< VariancePixelT > readVariance(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT, bool allowUnsafe=false)
Read the variance plane.
lsst::geom::Point2I readXY0(lsst::geom::Box2I const &bbox=lsst::geom::Box2I(), ImageOrigin origin=PARENT)
Read the image origin from the on-disk image or a subimage thereof.
A class to manipulate images, masks, and variance as a single object.
Definition: MaskedImage.h:73
The photometric calibration of an exposure.
Definition: PhotoCalib.h:114
A spatially-varying transmission curve as a function of wavelength.
static InputArchive readFits(fits::Fits &fitsfile)
Read an object from an already open FITS object.
std::shared_ptr< Persistable > get(int id) const
Load the Persistable with the given ID and return it.
Interface supporting iteration over heterogenous containers.
Definition: Storable.h:58
Class for storing ordered metadata with comments.
Definition: PropertyList.h:68
T get(std::string const &name) const
Get the last value for a property name (possibly hierarchical).
Definition: PropertyList.cc:62
virtual void remove(std::string const &name)
Remove all values for a property name (possibly hierarchical).
bool exists(std::string const &name) const
Determine if a name (possibly hierarchical) exists.
An integer coordinate rectangle.
Definition: Box.h:55
CoordinateExpr< N > ne(Point< T, N > const &other) const noexcept
Definition: Point.cc:83
virtual char const * what(void) const noexcept
Return a character string summarizing this exception.
Definition: Exception.cc:99
Reports attempts to access elements using an invalid key.
Definition: Runtime.h:151
Reports errors from accepting an object of an unexpected or inappropriate type.
Definition: Runtime.h:167
T count(T... args)
T emplace(T... args)
T front(T... args)
T make_pair(T... args)
T move(T... args)
std::shared_ptr< SkyWcs > makeSkyWcs(daf::base::PropertySet &metadata, bool strip=false)
Construct a SkyWcs from FITS keywords.
Definition: SkyWcs.cc:521
int stripVisitInfoKeywords(daf::base::PropertySet &metadata)
Remove VisitInfo-related keywords from the metadata.
Definition: VisitInfo.cc:346
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
Filter makeFilter(FilterLabel const &label)
Convert a FilterLabel back to an old-style Filter.
std::shared_ptr< PhotoCalib > makePhotoCalibFromMetadata(daf::base::PropertySet &metadata, bool strip=false)
Construct a PhotoCalib from FITS FLUXMAG0/FLUXMAG0ERR keywords.
Definition: PhotoCalib.cc:595
std::shared_ptr< FilterLabel > makeFilterLabel(Filter const &filter)
Convert an old-style Filter to a FilterLabel.
std::shared_ptr< FilterLabel > makeFilterLabelDirect(std::string const &name)
Convert an old-style filter name to a FilterLabel without external information.
bool any(CoordinateExpr< N > const &expr) noexcept
Return true if any elements are true.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:174
A base class for image defects.
T push_back(T... args)
T regex_match(T... args)
T size(T... args)
T substr(T... args)