Mantid
Loading...
Searching...
No Matches
SaveFITS.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8
13
14#include <fstream>
15#include <iomanip>
16
17#include <boost/pointer_cast.hpp>
18
19#include <Poco/File.h>
20#include <Poco/Path.h>
21
22namespace Mantid::DataHandling {
23
24const size_t SaveFITS::g_maxLenHdr = 80;
25
26// TODO: add headers for ToF, time bin, counts, triggers, etc.
27const std::string SaveFITS::g_FITSHdrEnd = "END";
28const std::string SaveFITS::g_FITSHdrFirst = "SIMPLE = T / file does conform to FITS standard";
29
30// To build something like:
31// "BITPIX = 16 / number of bits per data pixel";
32const std::string SaveFITS::g_bitDepthPre = "BITPIX = ";
33const std::string SaveFITS::g_bitDepthPost = " / number of bits per data pixel";
34
35const std::string SaveFITS::g_FITSHdrAxes = "NAXIS = 2 / number of data axes";
36const std::string SaveFITS::g_FITSHdrExtensions =
37 "EXTEND = T / FITS dataset may contain extensions";
38const std::string SaveFITS::g_FITSHdrRefComment1 = "COMMENT FITS (Flexible Image "
39 "Transport System) format is defined in "
40 "'Astronomy";
41const std::string SaveFITS::g_FITSHdrRefComment2 = "COMMENT and Astrophysics', volume 376, page 359; bibcode: "
42 "2001A&A...376..359H";
43
44// extend this if we ever want to support 64 bits pixels
45const size_t SaveFITS::g_maxBitDepth = 32;
46// this has to have int type for the validator and getProperty
47const std::array<int, 3> SaveFITS::g_bitDepths = {{8, 16, static_cast<int>(g_maxBitDepth)}};
48const size_t SaveFITS::g_maxBytesPP = g_maxBitDepth / 8;
49
52
53// Register the algorithm into the AlgorithmFactory
54DECLARE_ALGORITHM(SaveFITS)
55
56//----------------------------------------------------------------------------------------------
57
58
59const std::string SaveFITS::name() const { return "SaveFITS"; }
60
62int SaveFITS::version() const { return 1; }
63
65const std::string SaveFITS::category() const { return "DataHandling\\Imaging"; }
66
68const std::string SaveFITS::summary() const {
69 return "Saves image data from a workspace in FITS (Flexible Image Transport "
70 "System) format";
71}
72
73namespace {
74const std::string PROP_INPUT_WS = "InputWorkspace";
75const std::string PROP_FILENAME = "Filename";
76const std::string PROP_BIT_DEPTH = "BitDepth";
77} // namespace
78
79//----------------------------------------------------------------------------------------------
83 declareProperty(std::make_unique<API::WorkspaceProperty<>>(PROP_INPUT_WS, "", Kernel::Direction::Input,
84 std::make_shared<API::WorkspaceUnitValidator>("Label")),
85 "Workspace holding an image (with one spectrum per pixel row).");
86
87 declareProperty(std::make_unique<API::FileProperty>(PROP_FILENAME, "", API::FileProperty::Save,
88 std::vector<std::string>(1, ".fits")),
89 "Name of the output file where the image is saved.");
90
91 declareProperty(PROP_BIT_DEPTH, 16, std::make_shared<Kernel::ListValidator<int>>(g_bitDepths),
92 "The bit depth or number of bits per pixel to use for the "
93 "output image(s). Only 16 bits is supported at the "
94 "moment.",
96}
97
98std::map<std::string, std::string> SaveFITS::validateInputs() {
99 std::map<std::string, std::string> result;
100
101 API::MatrixWorkspace_const_sptr wks = getProperty(PROP_INPUT_WS);
102 if (wks) {
103 if (0 == wks->blocksize()) {
104 result[PROP_INPUT_WS] = "The input workspace must have at least one "
105 "column (the X axis is empty)";
106 }
107 if (0 == wks->getNumberHistograms()) {
108 result[PROP_INPUT_WS] = "The input workspace must have at least one row "
109 "(the Y axis is empty)";
110 }
111 }
112
113 return result;
114}
115
116//----------------------------------------------------------------------------------------------
120 API::MatrixWorkspace_sptr ws = getProperty(PROP_INPUT_WS);
121 const auto filename = getPropertyValue(PROP_FILENAME);
122
123 saveFITSImage(ws, filename);
124 g_log.information() << "Image of size " + std::to_string(ws->blocksize()) + " columns by " +
125 std::to_string(ws->getNumberHistograms()) + " rows saved in '" + filename + "'\n";
126}
127
134void SaveFITS::saveFITSImage(const API::MatrixWorkspace_sptr &img, const std::string &filename) {
135 std::ofstream outfile(filename, std::ofstream::binary);
136
137 writeFITSHeaderBlock(img, outfile);
138 writeFITSImageMatrix(img, outfile);
139}
140
141void SaveFITS::writeFITSHeaderBlock(const API::MatrixWorkspace_sptr &img, std::ofstream &file) {
142 // minimal sequence of standard headers
144 int depth = getProperty(PROP_BIT_DEPTH);
145 const std::string bitDepthHdr = makeBitDepthHeader(depth);
146 writeFITSHeaderEntry(bitDepthHdr, file);
148 writeFITSHeaderAxesSizes(img, file);
153
154 const size_t entriesPerHDU = 36;
155 writePaddingFITSHeaders(entriesPerHDU - 9, file);
156}
157
158void SaveFITS::writeFITSImageMatrix(const API::MatrixWorkspace_sptr &img, std::ofstream &file) {
159 const size_t sizeX = img->blocksize();
160 const size_t sizeY = img->getNumberHistograms();
161
162 int bitDepth = getProperty(PROP_BIT_DEPTH);
163 const size_t bytespp = static_cast<size_t>(bitDepth) / 8;
164
165 for (size_t row = 0; row < sizeY; ++row) {
166 const auto &yData = img->y(row);
167 for (size_t col = 0; col < sizeX; ++col) {
168 int32_t pixelVal;
169 if (8 == bitDepth) {
170 pixelVal = static_cast<uint8_t>(yData[col]);
171 } else if (16 == bitDepth) {
172 pixelVal = static_cast<uint16_t>(yData[col]);
173 } else if (32 == bitDepth) {
174 pixelVal = static_cast<uint32_t>(yData[col]);
175 }
176
177 // change endianness: to sequence of bytes in big-endian
178 // this needs revisiting (similarly in LoadFITS)
179 // See https://github.com/mantidproject/mantid/pull/15964
180 std::array<uint8_t, g_maxBytesPP> bytesPixel;
181 auto *iter = reinterpret_cast<uint8_t *>(&pixelVal);
182 std::reverse_copy(iter, iter + bytespp, bytesPixel.data());
183
184 file.write(reinterpret_cast<const char *>(bytesPixel.data()), bytespp);
185 }
186 }
187}
188
189void SaveFITS::writeFITSHeaderEntry(const std::string &hdr, std::ofstream &file) {
190 static const std::vector<char> blanks(g_maxLenHdr, 32);
191
192 auto count = hdr.size();
193 if (count >= g_maxLenHdr)
195
196 file.write(hdr.c_str(), sizeof(char) * count);
197 file.write(blanks.data(), g_maxLenHdr - count);
198}
199
201 const std::string sizeX = std::to_string(img->blocksize());
202 const std::string sizeY = std::to_string(img->getNumberHistograms());
203
204 const size_t fieldWidth = 20;
205 std::stringstream axis1;
206 axis1 << "NAXIS1 = " << std::setw(fieldWidth) << sizeX << " / length of data axis 1";
207 writeFITSHeaderEntry(axis1.str(), file);
208
209 std::stringstream axis2;
210 axis2 << "NAXIS2 = " << std::setw(fieldWidth) << sizeY << " / length of data axis 2";
211 writeFITSHeaderEntry(axis2.str(), file);
212}
213
214std::string SaveFITS::makeBitDepthHeader(size_t depth) const {
215 std::stringstream hdr;
216 hdr << g_bitDepthPre << std::setw(2) << depth << g_bitDepthPost;
217 return hdr.str();
218}
219
229void SaveFITS::writePaddingFITSHeaders(size_t count, std::ofstream &file) {
230 static const std::vector<char> blanks(g_maxLenHdr, 32);
231
232 for (size_t i = 0; i < count; ++i) {
233 file.write(blanks.data(), g_maxLenHdr);
234 }
235}
236
237} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
int count
counter
Definition: Matrix.cpp:37
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Definition: Algorithm.cpp:1913
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
Definition: Algorithm.cpp:2026
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
Kernel::Logger & g_log
Definition: Algorithm.h:451
@ Save
to specify a file to write to, the file may or may not exist
Definition: FileProperty.h:49
A property class for workspaces.
SaveFITS : Save images in FITS formats.
Definition: SaveFITS.h:19
static const std::string g_bitDepthPost
Definition: SaveFITS.h:60
static const size_t g_maxBytesPP
Definition: SaveFITS.h:53
void writeFITSHeaderEntry(const std::string &hdr, std::ofstream &file)
Definition: SaveFITS.cpp:189
static const std::string g_FITSHdrExtensions
Definition: SaveFITS.h:62
void init() override final
Initialize the algorithm's properties.
Definition: SaveFITS.cpp:82
std::string makeBitDepthHeader(size_t depth) const
Definition: SaveFITS.cpp:214
void writePaddingFITSHeaders(size_t count, std::ofstream &file)
Writes the padding required to fill every header block.
Definition: SaveFITS.cpp:229
std::map< std::string, std::string > validateInputs() override
Perform validation of ALL the input properties of the algorithm.
Definition: SaveFITS.cpp:98
void writeFITSHeaderBlock(const API::MatrixWorkspace_sptr &img, std::ofstream &file)
Definition: SaveFITS.cpp:141
static const std::array< int, 3 > g_bitDepths
Definition: SaveFITS.h:52
void writeFITSImageMatrix(const API::MatrixWorkspace_sptr &img, std::ofstream &file)
Definition: SaveFITS.cpp:158
static const size_t g_maxLenHdr
Definition: SaveFITS.h:55
static const std::string g_FITSHdrEnd
Definition: SaveFITS.h:57
static const std::string g_FITSHdrRefComment2
Definition: SaveFITS.h:64
int version() const override final
Algorithm's version for identification.
Definition: SaveFITS.cpp:62
static const std::string g_FITSHdrFirst
Definition: SaveFITS.h:58
static const size_t g_maxBitDepth
Definition: SaveFITS.h:51
const std::string summary() const override final
Algorithm's summary for use in the GUI and help.
Definition: SaveFITS.cpp:68
const std::string category() const override final
Algorithm's category for identification.
Definition: SaveFITS.cpp:65
static const std::string g_bitDepthPre
Definition: SaveFITS.h:59
static const std::string g_FITSHdrRefComment1
Definition: SaveFITS.h:63
void writeFITSHeaderAxesSizes(const API::MatrixWorkspace_sptr &img, std::ofstream &file)
Definition: SaveFITS.cpp:200
void exec() override final
Execute the algorithm.
Definition: SaveFITS.cpp:119
static const std::string g_FITSHdrAxes
Definition: SaveFITS.h:61
void saveFITSImage(const API::MatrixWorkspace_sptr &img, const std::string &filename)
Save an image workspace into a file.
Definition: SaveFITS.cpp:134
ListValidator is a validator that requires the value of a property to be one of a defined list of pos...
Definition: ListValidator.h:29
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
STL namespace.
std::string to_string(const wide_integer< Bits, Signed > &n)
Describes the direction (within an algorithm) of a Property.
Definition: Property.h:50
@ Input
An input workspace.
Definition: Property.h:53