LSST Applications g180d380827+0f66a164bb,g2079a07aa2+86d27d4dc4,g2305ad1205+7d304bc7a0,g29320951ab+500695df56,g2bbee38e9b+0e5473021a,g337abbeb29+0e5473021a,g33d1c0ed96+0e5473021a,g3a166c0a6a+0e5473021a,g3ddfee87b4+e42ea45bea,g48712c4677+36a86eeaa5,g487adcacf7+2dd8f347ac,g50ff169b8f+96c6868917,g52b1c1532d+585e252eca,g591dd9f2cf+c70619cc9d,g5a732f18d5+53520f316c,g5ea96fc03c+341ea1ce94,g64a986408d+f7cd9c7162,g858d7b2824+f7cd9c7162,g8a8a8dda67+585e252eca,g99cad8db69+469ab8c039,g9ddcbc5298+9a081db1e4,ga1e77700b3+15fc3df1f7,gb0e22166c9+60f28cb32d,gba4ed39666+c2a2e4ac27,gbb8dafda3b+c92fc63c7e,gbd866b1f37+f7cd9c7162,gc120e1dc64+02c66aa596,gc28159a63d+0e5473021a,gc3e9b769f7+b0068a2d9f,gcf0d15dbbd+e42ea45bea,gdaeeff99f8+f9a426f77a,ge6526c86ff+84383d05b3,ge79ae78c31+0e5473021a,gee10cc3b42+585e252eca,gff1a9f87cc+f7cd9c7162,w.2024.17
LSST Data Management Base Package
Loading...
Searching...
No Matches
_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"
36
37#include "lsst/afw/fits.h"
38
39namespace py = pybind11;
40
41using namespace pybind11::literals;
42namespace lsst {
43namespace afw {
44namespace fits {
45namespace {
46void 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) {
82 enm.export_values();
83 });
84}
85
86template <typename T>
87void declareImageScalingOptionsTemplates(py::class_<ImageScalingOptions> &cls) {
88 cls.def(
89 "determine",
90 // It seems like py::overload cast should work here, and I don't
91 // understand why it doesn't.
92 [](
93 ImageScalingOptions const & self,
96 ) {
97 return self.determine(image, mask);
98 },
99 "image"_a,
100 "mask"_a=nullptr
101 );
102}
103
104void declareImageScalingOptions(lsst::utils::python::WrapperCollection &wrappers) {
105 auto options = wrappers.wrapType(
106 py::class_<ImageScalingOptions>(wrappers.module, "ImageScalingOptions"),
107 [](auto &mod, auto &cls) {
108 cls.def(py::init<>());
109 cls.def(py::init<ImageScalingOptions::ScalingAlgorithm, int, std::vector<std::string> const &,
110 unsigned long, float, float, bool, double, double>(),
111 "algorithm"_a, "bitpix"_a, "maskPlanes"_a = std::vector<std::string>(), "seed"_a = 1,
112 "quantizeLevel"_a = 4.0, "quantizePad"_a = 5.0, "fuzz"_a = true, "bscale"_a = 1.0,
113 "bzero"_a = 0.0);
114
115 cls.def_readonly("algorithm", &ImageScalingOptions::algorithm);
116 cls.def_readonly("bitpix", &ImageScalingOptions::bitpix);
117 cls.def_readonly("maskPlanes", &ImageScalingOptions::maskPlanes);
118 cls.def_readonly("seed", &ImageScalingOptions::seed);
119 cls.def_readonly("quantizeLevel", &ImageScalingOptions::quantizeLevel);
120 cls.def_readonly("quantizePad", &ImageScalingOptions::quantizePad);
121 cls.def_readonly("fuzz", &ImageScalingOptions::fuzz);
122 cls.def_readonly("bscale", &ImageScalingOptions::bscale);
123 cls.def_readonly("bzero", &ImageScalingOptions::bzero);
124
125 declareImageScalingOptionsTemplates<float>(cls);
126 declareImageScalingOptionsTemplates<double>(cls);
127 });
128 wrappers.wrapType(py::enum_<ImageScalingOptions::ScalingAlgorithm>(options, "ScalingAlgorithm"),
129 [](auto &mod, auto &enm) {
136 enm.export_values();
137 });
138}
139
140template <typename T>
141void declareImageScaleTemplates(py::class_<ImageScale> &cls, std::string const &suffix) {
142 cls.def("toFits", &ImageScale::toFits<T>, "image"_a, "forceNonfiniteRemoval"_a = false, "fuzz"_a = true,
143 "tiles"_a = ndarray::Array<long, 1, 1>(), "seed"_a = 1);
144 cls.def("fromFits", &ImageScale::fromFits<T>);
145}
146
147void declareImageScale(lsst::utils::python::WrapperCollection &wrappers) {
148 wrappers.wrapType(py::class_<ImageScale>(wrappers.module, "ImageScale"), [](auto &mod, auto &cls) {
149 cls.def(py::init<int, double, double>(), "bitpix"_a, "bscale"_a, "bzero"_a);
150 cls.def_readonly("bitpix", &ImageScale::bitpix);
151 cls.def_readonly("bscale", &ImageScale::bscale);
152 cls.def_readonly("bzero", &ImageScale::bzero);
153 cls.def_readonly("blank", &ImageScale::blank);
154
155 declareImageScaleTemplates<float>(cls, "F");
156 declareImageScaleTemplates<double>(cls, "D");
157 });
158}
159
160void declareImageWriteOptions(lsst::utils::python::WrapperCollection &wrappers) {
161 wrappers.wrapType(py::class_<ImageWriteOptions>(wrappers.module, "ImageWriteOptions"),
162 [](auto &mod, auto &cls) {
163 cls.def(py::init<lsst::afw::image::Image<std::uint16_t>>());
164 cls.def(py::init<lsst::afw::image::Image<std::int32_t>>());
165 cls.def(py::init<lsst::afw::image::Image<std::uint64_t>>());
166 cls.def(py::init<lsst::afw::image::Image<float>>());
167 cls.def(py::init<lsst::afw::image::Image<double>>());
168
169 cls.def(py::init<lsst::afw::image::Mask<lsst::afw::image::MaskPixel>>());
170
171 cls.def(py::init<ImageCompressionOptions const &, ImageScalingOptions const &>(),
172 "compression"_a, "scaling"_a = ImageScalingOptions());
173 cls.def(py::init<ImageScalingOptions const &>());
174
175 cls.def(py::init<lsst::daf::base::PropertySet const &>());
176
177 cls.def_readonly("compression", &ImageWriteOptions::compression);
178 cls.def_readonly("scaling", &ImageWriteOptions::scaling);
179
180 cls.def_static("validate", &ImageWriteOptions::validate);
181 });
182}
183
184// Wrapping for lsst::afw::fits::Fits
185//
186// Not every feature is wrapped, only those that we guess might be useful.
187// In particular, the header keyword read/write and table read/write are not wrapped.
188void declareFits(lsst::utils::python::WrapperCollection &wrappers) {
189 wrappers.wrapType(py::class_<Fits>(wrappers.module, "Fits"), [](auto &mod, auto &cls) {
190 cls.def(py::init<std::string const &, std::string const &, int>(), "filename"_a, "mode"_a,
191 "behavior"_a = Fits::AUTO_CLOSE | Fits::AUTO_CHECK);
192 cls.def(py::init<MemFileManager &, std::string const &, int>(), "manager"_a, "mode"_a,
193 "behavior"_a = Fits::AUTO_CLOSE | Fits::AUTO_CHECK);
194
195 cls.def("closeFile", &Fits::closeFile);
196 cls.def("getFileName", &Fits::getFileName);
197 cls.def("getHdu", &Fits::getHdu);
198 cls.def("setHdu", py::overload_cast<int, bool>(&Fits::setHdu), "hdu"_a, "relative"_a = false);
199 cls.def(
200 "setHdu", [](Fits &self, std::string const &name) { self.setHdu(name); }, "name"_a);
201 cls.def("countHdus", &Fits::countHdus);
202
203 cls.def("writeMetadata", &Fits::writeMetadata);
204 cls.def(
205 "readMetadata", [](Fits &self, bool strip = false) { return readMetadata(self, strip); },
206 "strip"_a = false);
207 cls.def("createEmpty", &Fits::createEmpty);
208
209 cls.def("readImageI", [](Fits &self) {
210 ndarray::Vector<int, 2> const offset; // initialized to zero by default
211 ndarray::Vector<ndarray::Size, 2> shape = self.getImageShape<2>();
212 ndarray::Array<int, 2, 2> result = ndarray::allocate(shape[0], shape[1]);
213 self.readImage(result, offset);
214 return result;
215 });
216
217 cls.def("gotoFirstHdu", [](Fits &self) { self.setHdu(DEFAULT_HDU); });
218
219 cls.def("setImageCompression", &Fits::setImageCompression);
220 cls.def("getImageCompression", &Fits::getImageCompression);
221 cls.def("checkCompressedImagePhu", &Fits::checkCompressedImagePhu);
222
223 cls.def_readonly("status", &Fits::status);
224 });
225}
226
227void declareFitsModule(lsst::utils::python::WrapperCollection &wrappers) {
228 wrappers.wrap([](auto &mod) {
229 py::class_<MemFileManager> clsMemFileManager(mod, "MemFileManager");
230
231 clsMemFileManager.def(py::init<>());
232 clsMemFileManager.def(py::init<size_t>());
233
234 /* TODO: We should really revisit persistence and pickling as this is quite ugly.
235 * But it is what Swig did (sort of, it used the cdata.i extension), so I reckon this
236 * is cleaner because it does not expose casting to the Python side. */
237 clsMemFileManager.def("getLength", &MemFileManager::getLength);
238 clsMemFileManager.def("getData", [](MemFileManager &m) {
239 return py::bytes(static_cast<char *>(m.getData()), m.getLength());
240 });
241 clsMemFileManager.def("setData", [](MemFileManager &m, py::bytes const &d, size_t size) {
242 memcpy(m.getData(), PyBytes_AsString(d.ptr()), size);
243 });
244 clsMemFileManager.def(
245 "readMetadata",
246 [](MemFileManager &self, int hdu = DEFAULT_HDU, bool strip = false) {
247 return readMetadata(self, hdu, strip);
248 },
249 "hdu"_a = DEFAULT_HDU, "strip"_a = false);
250 mod.attr("DEFAULT_HDU") = DEFAULT_HDU;
251 mod.def(
252 "combineMetadata",
253 py::overload_cast<daf::base::PropertyList const&, daf::base::PropertyList const &>(
255 ),
256 "first"_a, "second"_a
257 );
258 mod.def("makeLimitedFitsHeader", &makeLimitedFitsHeader, "metadata"_a,
259 "excludeNames"_a = std::set<std::string>());
260 mod.def(
261 "readMetadata",
262 [](std::string const &filename, int hdu = DEFAULT_HDU, bool strip = false) {
263 return readMetadata(filename, hdu, strip);
264 },
265 "fileName"_a, "hdu"_a = DEFAULT_HDU, "strip"_a = false);
266
267 mod.def(
268 "readMetadata",
269 [](std::string const &filename, std::string const &hduname, bool strip = false) {
270 return readMetadata(filename, hduname, HduType::ANY, 0, strip);
271 },
272 "fileName"_a, "hduName"_a, "strip"_a = false);
273
274 mod.def("setAllowImageCompression", &setAllowImageCompression, "allow"_a);
275 mod.def("getAllowImageCompression", &getAllowImageCompression);
276
277 mod.def("compressionAlgorithmFromString", &compressionAlgorithmFromString);
278 mod.def("compressionAlgorithmToString", &compressionAlgorithmToString);
279 mod.def("scalingAlgorithmFromString", &scalingAlgorithmFromString);
280 mod.def("scalingAlgorithmToString", &scalingAlgorithmToString);
281 });
282}
283} // namespace
284PYBIND11_MODULE(_fits, mod) {
285 lsst::utils::python::WrapperCollection wrappers(mod, "lsst.afw.fits");
286 wrappers.addInheritanceDependency("lsst.pex.exceptions");
287 wrappers.addSignatureDependency("lsst.daf.base");
288 // FIXME: after afw.image pybind wrappers are converted
289 //wrappers.addSignatureDependency("lsst.afw.image");
290 auto cls = wrappers.wrapException<FitsError, lsst::pex::exceptions::IoError>("FitsError", "IoError");
291 cls.def(py::init<std::string const &>());
292 declareImageCompression(wrappers);
293 declareImageScalingOptions(wrappers);
294 declareImageScale(wrappers);
295 declareImageWriteOptions(wrappers);
296 declareFits(wrappers);
297 declareFitsModule(wrappers);
298 wrappers.finish();
299}
300} // namespace fits
301} // namespace afw
302} // namespace lsst
py::object result
Definition _schema.cc:429
afw::table::Key< afw::table::Array< MaskPixelT > > mask
int m
Definition SpanSet.cc:48
An exception thrown when problems are found when reading or writing FITS files.
Definition fits.h:36
ImageCompressionOptions getImageCompression()
Return the current image compression settings.
Definition fits.cc:1515
void createEmpty()
Create an empty image HDU with NAXIS=0 at the end of the file.
Definition fits.cc:1259
bool checkCompressedImagePhu()
Go to the first image header in the FITS file.
Definition fits.cc:1767
int countHdus()
Return the number of HDUs in the file.
Definition fits.cc:553
void setImageCompression(ImageCompressionOptions const &options)
Set compression options for writing FITS images.
Definition fits.cc:1538
void writeMetadata(daf::base::PropertySet const &metadata)
Read a FITS header into a PropertySet or PropertyList.
Definition fits.cc:1114
@ STDEV_NEGATIVE
Scale based on the standard deviation, dynamic range negative.
@ STDEV_POSITIVE
Scale based on the standard deviation. dynamic range positive.
@ STDEV_BOTH
Scale based on the standard deviation, dynamic range positive+negative.
@ RANGE
Scale to preserve dynamic range.
std::size_t getLength() const
Return the buffer length.
Definition fits.h:198
The base class for all image classed (Image, Mask, MaskedImage, ...)
Definition ImageBase.h:102
Represent a 2-dimensional array of bitmask pixels.
Definition Mask.h:77
Reports errors in external input/output operations.
Definition Runtime.h:160
PYBIND11_MODULE(detect_pybind11, mod)
bool strip
Definition fits.cc:930
T memcpy(T... args)
std::shared_ptr< daf::base::PropertyList > combineMetadata(daf::base::PropertyList const &first, daf::base::PropertyList const &second)
Combine two sets of metadata in a FITS-appropriate fashion.
Definition fits.cc:1648
const int DEFAULT_HDU
Specify that the default HDU should be read.
std::shared_ptr< daf::base::PropertyList > readMetadata(std::string const &fileName, int hdu=DEFAULT_HDU, bool strip=false)
Read FITS header.
Definition fits.cc:1689
ImageScalingOptions::ScalingAlgorithm scalingAlgorithmFromString(std::string const &name)
Interpret scaling algorithm expressed in string.
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:468
ImageCompressionOptions::CompressionAlgorithm compressionAlgorithmFromString(std::string const &name)
Interpret compression algorithm expressed in string.
@ GZIP_SHUFFLE
GZIP compression with shuffle (most-significant byte first)