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
_fits.cc
Go to the documentation of this file.
1 /*
2  * This file is part of afw.
3  *
4  * Developed for the LSST Data Management System.
5  * This product includes software developed by the LSST Project
6  * (https://www.lsst.org).
7  * See the COPYRIGHT file at the top-level directory of this distribution
8  * for details of code ownership.
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program. If not, see <https://www.gnu.org/licenses/>.
22  */
23 
24 #include <pybind11/pybind11.h>
25 #include <pybind11/stl.h>
26 
27 #include "lsst/utils/python.h"
28 
29 #include "ndarray/pybind11.h"
30 
34 #include "lsst/daf/base.h"
35 #include "lsst/afw/image/Image.h"
36 
37 #include "lsst/afw/fits.h"
38 
39 namespace py = pybind11;
40 
41 using namespace pybind11::literals;
42 namespace lsst {
43 namespace afw {
44 namespace fits {
45 namespace {
46 void declareImageCompression(lsst::utils::python::WrapperCollection &wrappers) {
47  auto options = wrappers.wrapType(
48  py::class_<ImageCompressionOptions>(wrappers.module, "ImageCompressionOptions"),
49  [](auto &mod, auto &cls) {
50  cls.def(py::init<ImageCompressionOptions::CompressionAlgorithm,
51  ImageCompressionOptions::Tiles, float>(),
52  "algorithm"_a, "tiles"_a, "quantizeLevel"_a = 0.0);
53  cls.def(py::init<ImageCompressionOptions::CompressionAlgorithm, int, float>(), "algorithm"_a,
54  "rows"_a = 1, "quantizeLevel"_a = 0.0);
55 
56  cls.def(py::init<lsst::afw::image::Image<unsigned char> const &>());
57  cls.def(py::init<lsst::afw::image::Image<unsigned short> const &>());
58  cls.def(py::init<lsst::afw::image::Image<short> const &>());
59  cls.def(py::init<lsst::afw::image::Image<int> const &>());
60  cls.def(py::init<lsst::afw::image::Image<unsigned int> const &>());
61  cls.def(py::init<lsst::afw::image::Image<float> const &>());
62  cls.def(py::init<lsst::afw::image::Image<double> const &>());
63  cls.def(py::init<lsst::afw::image::Image<std::uint64_t> const &>());
64 
65  cls.def(py::init<lsst::afw::image::Mask<unsigned char> const &>());
66  cls.def(py::init<lsst::afw::image::Mask<unsigned short> const &>());
67  cls.def(py::init<lsst::afw::image::Mask<short> const &>());
68  cls.def(py::init<lsst::afw::image::Mask<std::int32_t> const &>());
69 
70  cls.def_readonly("algorithm", &ImageCompressionOptions::algorithm);
71  cls.def_readonly("tiles", &ImageCompressionOptions::tiles);
72  cls.def_readonly("quantizeLevel", &ImageCompressionOptions::quantizeLevel);
73  });
74  wrappers.wrapType(
75  py::enum_<ImageCompressionOptions::CompressionAlgorithm>(options, "CompressionAlgorithm"),
76  [](auto &mod, auto &enm) {
77  enm.value("NONE", ImageCompressionOptions::CompressionAlgorithm::NONE);
78  enm.value("GZIP", ImageCompressionOptions::CompressionAlgorithm::GZIP);
79  enm.value("GZIP_SHUFFLE", ImageCompressionOptions::CompressionAlgorithm::GZIP_SHUFFLE);
80  enm.value("RICE", ImageCompressionOptions::CompressionAlgorithm::RICE);
81  enm.value("PLIO", ImageCompressionOptions::CompressionAlgorithm::PLIO);
82  enm.export_values();
83  });
84 }
85 
86 template <typename T>
87 void declareImageScalingOptionsTemplates(py::class_<ImageScalingOptions> &cls) {
88  cls.def("determine", &ImageScalingOptions::determine<T>);
89 }
90 
91 void declareImageScalingOptions(lsst::utils::python::WrapperCollection &wrappers) {
92  auto options = wrappers.wrapType(
93  py::class_<ImageScalingOptions>(wrappers.module, "ImageScalingOptions"),
94  [](auto &mod, auto &cls) {
95  cls.def(py::init<>());
96  cls.def(py::init<ImageScalingOptions::ScalingAlgorithm, int, std::vector<std::string> const &,
97  unsigned long, float, float, bool, double, double>(),
98  "algorithm"_a, "bitpix"_a, "maskPlanes"_a = std::vector<std::string>(), "seed"_a = 1,
99  "quantizeLevel"_a = 4.0, "quantizePad"_a = 5.0, "fuzz"_a = true, "bscale"_a = 1.0,
100  "bzero"_a = 0.0);
101 
102  cls.def_readonly("algorithm", &ImageScalingOptions::algorithm);
103  cls.def_readonly("bitpix", &ImageScalingOptions::bitpix);
104  cls.def_readonly("maskPlanes", &ImageScalingOptions::maskPlanes);
105  cls.def_readonly("seed", &ImageScalingOptions::seed);
106  cls.def_readonly("quantizeLevel", &ImageScalingOptions::quantizeLevel);
107  cls.def_readonly("quantizePad", &ImageScalingOptions::quantizePad);
108  cls.def_readonly("fuzz", &ImageScalingOptions::fuzz);
109  cls.def_readonly("bscale", &ImageScalingOptions::bscale);
110  cls.def_readonly("bzero", &ImageScalingOptions::bzero);
111 
112  declareImageScalingOptionsTemplates<float>(cls);
113  declareImageScalingOptionsTemplates<double>(cls);
114  });
115  wrappers.wrapType(py::enum_<ImageScalingOptions::ScalingAlgorithm>(options, "ScalingAlgorithm"),
116  [](auto &mod, auto &enm) {
117  enm.value("NONE", ImageScalingOptions::ScalingAlgorithm::NONE);
118  enm.value("RANGE", ImageScalingOptions::ScalingAlgorithm::RANGE);
119  enm.value("STDEV_POSITIVE", ImageScalingOptions::ScalingAlgorithm::STDEV_POSITIVE);
120  enm.value("STDEV_NEGATIVE", ImageScalingOptions::ScalingAlgorithm::STDEV_NEGATIVE);
121  enm.value("STDEV_BOTH", ImageScalingOptions::ScalingAlgorithm::STDEV_BOTH);
122  enm.value("MANUAL", ImageScalingOptions::ScalingAlgorithm::MANUAL);
123  enm.export_values();
124  });
125 }
126 
127 template <typename T>
128 void declareImageScaleTemplates(py::class_<ImageScale> &cls, std::string const &suffix) {
129  cls.def("toFits", &ImageScale::toFits<T>, "image"_a, "forceNonfiniteRemoval"_a = false, "fuzz"_a = true,
130  "tiles"_a = ndarray::Array<long, 1, 1>(), "seed"_a = 1);
131  cls.def("fromFits", &ImageScale::fromFits<T>);
132 }
133 
134 void declareImageScale(lsst::utils::python::WrapperCollection &wrappers) {
135  wrappers.wrapType(py::class_<ImageScale>(wrappers.module, "ImageScale"), [](auto &mod, auto &cls) {
136  cls.def(py::init<int, double, double>(), "bitpix"_a, "bscale"_a, "bzero"_a);
137  cls.def_readonly("bitpix", &ImageScale::bitpix);
138  cls.def_readonly("bscale", &ImageScale::bscale);
139  cls.def_readonly("bzero", &ImageScale::bzero);
140  cls.def_readonly("blank", &ImageScale::blank);
141 
142  declareImageScaleTemplates<float>(cls, "F");
143  declareImageScaleTemplates<double>(cls, "D");
144  });
145 }
146 
147 void declareImageWriteOptions(lsst::utils::python::WrapperCollection &wrappers) {
148  wrappers.wrapType(py::class_<ImageWriteOptions>(wrappers.module, "ImageWriteOptions"),
149  [](auto &mod, auto &cls) {
150  cls.def(py::init<lsst::afw::image::Image<std::uint16_t>>());
151  cls.def(py::init<lsst::afw::image::Image<std::int32_t>>());
152  cls.def(py::init<lsst::afw::image::Image<std::uint64_t>>());
153  cls.def(py::init<lsst::afw::image::Image<float>>());
154  cls.def(py::init<lsst::afw::image::Image<double>>());
155 
156  cls.def(py::init<lsst::afw::image::Mask<lsst::afw::image::MaskPixel>>());
157 
158  cls.def(py::init<ImageCompressionOptions const &, ImageScalingOptions const &>(),
159  "compression"_a, "scaling"_a = ImageScalingOptions());
160  cls.def(py::init<ImageScalingOptions const &>());
161 
162  cls.def(py::init<lsst::daf::base::PropertySet const &>());
163 
164  cls.def_readonly("compression", &ImageWriteOptions::compression);
165  cls.def_readonly("scaling", &ImageWriteOptions::scaling);
166 
167  cls.def_static("validate", &ImageWriteOptions::validate);
168  });
169 }
170 
171 // Wrapping for lsst::afw::fits::Fits
172 //
173 // Not every feature is wrapped, only those that we guess might be useful.
174 // In particular, the header keyword read/write and table read/write are not wrapped.
175 void declareFits(lsst::utils::python::WrapperCollection &wrappers) {
176  wrappers.wrapType(py::class_<Fits>(wrappers.module, "Fits"), [](auto &mod, auto &cls) {
177  cls.def(py::init<std::string const &, std::string const &, int>(), "filename"_a, "mode"_a,
178  "behavior"_a = Fits::AUTO_CLOSE | Fits::AUTO_CHECK);
179  cls.def(py::init<MemFileManager &, std::string const &, int>(), "manager"_a, "mode"_a,
180  "behavior"_a = Fits::AUTO_CLOSE | Fits::AUTO_CHECK);
181 
182  cls.def("closeFile", &Fits::closeFile);
183  cls.def("getFileName", &Fits::getFileName);
184  cls.def("getHdu", &Fits::getHdu);
185  cls.def("setHdu", &Fits::setHdu, "hdu"_a, "relative"_a = false);
186  cls.def("countHdus", &Fits::countHdus);
187 
188  cls.def("writeMetadata", &Fits::writeMetadata);
189  cls.def(
190  "readMetadata", [](Fits &self, bool strip = false) { return readMetadata(self, strip); },
191  "strip"_a = false);
192  cls.def("createEmpty", &Fits::createEmpty);
193 
194  cls.def("readImageI", [](Fits &self) {
195  ndarray::Vector<int, 2> const offset; // initialized to zero by default
196  ndarray::Vector<ndarray::Size, 2> shape = self.getImageShape<2>();
197  ndarray::Array<int, 2, 2> result = ndarray::allocate(shape[0], shape[1]);
198  self.readImage(result, offset);
199  return result;
200  });
201 
202  cls.def("gotoFirstHdu", [](Fits &self) { self.setHdu(DEFAULT_HDU); });
203 
204  cls.def("setImageCompression", &Fits::setImageCompression);
205  cls.def("getImageCompression", &Fits::getImageCompression);
206  cls.def("checkCompressedImagePhu", &Fits::checkCompressedImagePhu);
207 
208  cls.def_readonly("status", &Fits::status);
209  });
210 }
211 
212 void declareFitsModule(lsst::utils::python::WrapperCollection &wrappers) {
213  wrappers.wrap([](auto &mod) {
214  py::class_<MemFileManager> clsMemFileManager(mod, "MemFileManager");
215 
216  clsMemFileManager.def(py::init<>());
217  clsMemFileManager.def(py::init<size_t>());
218 
219  /* TODO: We should really revisit persistence and pickling as this is quite ugly.
220  * But it is what Swig did (sort of, it used the cdata.i extension), so I reckon this
221  * is cleaner because it does not expose casting to the Python side. */
222  clsMemFileManager.def("getLength", &MemFileManager::getLength);
223  clsMemFileManager.def("getData", [](MemFileManager &m) {
224  return py::bytes(static_cast<char *>(m.getData()), m.getLength());
225  });
226  clsMemFileManager.def("setData", [](MemFileManager &m, py::bytes const &d, size_t size) {
227  memcpy(m.getData(), PyBytes_AsString(d.ptr()), size);
228  });
229  clsMemFileManager.def(
230  "readMetadata",
231  [](MemFileManager &self, int hdu = DEFAULT_HDU, bool strip = false) {
232  return readMetadata(self, hdu, strip);
233  },
234  "hdu"_a = DEFAULT_HDU, "strip"_a = false);
235  mod.attr("DEFAULT_HDU") = DEFAULT_HDU;
236  mod.def("combineMetadata", combineMetadata, "first"_a, "second"_a);
237  mod.def("makeLimitedFitsHeader", &makeLimitedFitsHeader, "metadata"_a,
238  "excludeNames"_a = std::set<std::string>());
239  mod.def(
240  "readMetadata",
241  [](std::string const &filename, int hdu = DEFAULT_HDU, bool strip = false) {
242  return readMetadata(filename, hdu, strip);
243  },
244  "fileName"_a, "hdu"_a = DEFAULT_HDU, "strip"_a = false);
245  mod.def("setAllowImageCompression", &setAllowImageCompression, "allow"_a);
246  mod.def("getAllowImageCompression", &getAllowImageCompression);
247 
248  mod.def("compressionAlgorithmFromString", &compressionAlgorithmFromString);
249  mod.def("compressionAlgorithmToString", &compressionAlgorithmToString);
250  mod.def("scalingAlgorithmFromString", &scalingAlgorithmFromString);
251  mod.def("scalingAlgorithmToString", &scalingAlgorithmToString);
252  });
253 }
254 } // namespace
255 PYBIND11_MODULE(_fits, mod) {
256  lsst::utils::python::WrapperCollection wrappers(mod, "lsst.afw.fits");
257  wrappers.addSignatureDependency("lsst.pex.exceptions");
258  wrappers.addSignatureDependency("lsst.daf.base");
259  // FIXME: after afw.image pybind wrappers are converted
260  //wrappers.addSignatureDependency("lsst.afw.image");
261  wrappers.wrapException<FitsError, lsst::pex::exceptions::IoError>("FitsError", "IoError");
262  declareImageCompression(wrappers);
263  declareImageScalingOptions(wrappers);
264  declareImageScale(wrappers);
265  declareImageWriteOptions(wrappers);
266  declareFits(wrappers);
267  declareFitsModule(wrappers);
268  wrappers.finish();
269 }
270 } // namespace fits
271 } // namespace afw
272 } // namespace lsst
py::object result
Definition: _schema.cc:429
Fits * fits
Definition: FitsWriter.cc:90
int m
Definition: SpanSet.cc:48
table::Key< table::Array< std::uint8_t > > bytes
Definition: python.h:135
An exception thrown when problems are found when reading or writing FITS files.
Definition: fits.h:36
Reports errors in external input/output operations.
Definition: Runtime.h:160
bool strip
Definition: fits.cc:911
T memcpy(T... args)
const int DEFAULT_HDU
Specify that the default HDU should be read.
Definition: fitsDefaults.h:18
std::shared_ptr< daf::base::PropertyList > combineMetadata(std::shared_ptr< const daf::base::PropertyList > first, std::shared_ptr< const daf::base::PropertyList > second)
Combine two sets of metadata in a FITS-appropriate fashion.
Definition: fits.cc:1628
std::string compressionAlgorithmToString(ImageCompressionOptions::CompressionAlgorithm algorithm)
Provide string version of compression algorithm.
std::shared_ptr< daf::base::PropertyList > readMetadata(std::string const &fileName, int hdu=DEFAULT_HDU, bool strip=false)
Read FITS header.
Definition: fits.cc:1657
ImageScalingOptions::ScalingAlgorithm scalingAlgorithmFromString(std::string const &name)
Interpret scaling algorithm expressed in string.
PYBIND11_MODULE(_fits, mod)
Definition: _fits.cc:255
void setAllowImageCompression(bool allow)
Definition: fits.cc:1546
bool getAllowImageCompression()
Definition: fits.cc:1548
std::string scalingAlgorithmToString(ImageScalingOptions::ScalingAlgorithm algorithm)
Provide string version of compression algorithm.
std::string makeLimitedFitsHeader(lsst::daf::base::PropertySet const &metadata, std::set< std::string > const &excludeNames={})
Format a PropertySet into an FITS header string in a simplistic fashion.
Definition: fits.cc:457
ImageCompressionOptions::CompressionAlgorithm compressionAlgorithmFromString(std::string const &name)
Interpret compression algorithm expressed in string.
A base class for image defects.