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
19namespace Mantid::DataHandling {
20
21const size_t SaveFITS::g_maxLenHdr = 80;
22
23// TODO: add headers for ToF, time bin, counts, triggers, etc.
24const std::string SaveFITS::g_FITSHdrEnd = "END";
25const std::string SaveFITS::g_FITSHdrFirst = "SIMPLE = T / file does conform to FITS standard";
26
27// To build something like:
28// "BITPIX = 16 / number of bits per data pixel";
29const std::string SaveFITS::g_bitDepthPre = "BITPIX = ";
30const std::string SaveFITS::g_bitDepthPost = " / number of bits per data pixel";
31
32const std::string SaveFITS::g_FITSHdrAxes = "NAXIS = 2 / number of data axes";
33const std::string SaveFITS::g_FITSHdrExtensions =
34 "EXTEND = T / FITS dataset may contain extensions";
35const std::string SaveFITS::g_FITSHdrRefComment1 = "COMMENT FITS (Flexible Image "
36 "Transport System) format is defined in "
37 "'Astronomy";
38const std::string SaveFITS::g_FITSHdrRefComment2 = "COMMENT and Astrophysics', volume 376, page 359; bibcode: "
39 "2001A&A...376..359H";
40
41// extend this if we ever want to support 64 bits pixels
42const size_t SaveFITS::g_maxBitDepth = 32;
43// this has to have int type for the validator and getProperty
44const std::array<int, 3> SaveFITS::g_bitDepths = {{8, 16, static_cast<int>(g_maxBitDepth)}};
45const size_t SaveFITS::g_maxBytesPP = g_maxBitDepth / 8;
46
49
50// Register the algorithm into the AlgorithmFactory
51DECLARE_ALGORITHM(SaveFITS)
52
53//----------------------------------------------------------------------------------------------
54
55
56const std::string SaveFITS::name() const { return "SaveFITS"; }
57
59int SaveFITS::version() const { return 1; }
60
62const std::string SaveFITS::category() const { return "DataHandling\\Imaging"; }
63
65const std::string SaveFITS::summary() const {
66 return "Saves image data from a workspace in FITS (Flexible Image Transport "
67 "System) format";
68}
69
70namespace {
71const std::string PROP_INPUT_WS = "InputWorkspace";
72const std::string PROP_FILENAME = "Filename";
73const std::string PROP_BIT_DEPTH = "BitDepth";
74} // namespace
75
76//----------------------------------------------------------------------------------------------
80 declareProperty(std::make_unique<API::WorkspaceProperty<>>(PROP_INPUT_WS, "", Kernel::Direction::Input,
81 std::make_shared<API::WorkspaceUnitValidator>("Label")),
82 "Workspace holding an image (with one spectrum per pixel row).");
83
84 declareProperty(std::make_unique<API::FileProperty>(PROP_FILENAME, "", API::FileProperty::Save,
85 std::vector<std::string>(1, ".fits")),
86 "Name of the output file where the image is saved.");
87
88 declareProperty(PROP_BIT_DEPTH, 16, std::make_shared<Kernel::ListValidator<int>>(g_bitDepths),
89 "The bit depth or number of bits per pixel to use for the "
90 "output image(s). Only 16 bits is supported at the "
91 "moment.",
93}
94
95std::map<std::string, std::string> SaveFITS::validateInputs() {
96 std::map<std::string, std::string> result;
97
99 if (wks) {
100 if (0 == wks->blocksize()) {
101 result[PROP_INPUT_WS] = "The input workspace must have at least one "
102 "column (the X axis is empty)";
103 }
104 if (0 == wks->getNumberHistograms()) {
105 result[PROP_INPUT_WS] = "The input workspace must have at least one row "
106 "(the Y axis is empty)";
107 }
108 }
109
110 return result;
111}
112
113//----------------------------------------------------------------------------------------------
117 API::MatrixWorkspace_sptr ws = getProperty(PROP_INPUT_WS);
118 const auto filename = getPropertyValue(PROP_FILENAME);
119
120 saveFITSImage(ws, filename);
121 g_log.information() << "Image of size " + std::to_string(ws->blocksize()) + " columns by " +
122 std::to_string(ws->getNumberHistograms()) + " rows saved in '" + filename + "'\n";
123}
124
131void SaveFITS::saveFITSImage(const API::MatrixWorkspace_sptr &img, const std::string &filename) {
132 std::ofstream outfile(filename, std::ofstream::binary);
133
134 writeFITSHeaderBlock(img, outfile);
135 writeFITSImageMatrix(img, outfile);
136}
137
138void SaveFITS::writeFITSHeaderBlock(const API::MatrixWorkspace_sptr &img, std::ofstream &file) {
139 // minimal sequence of standard headers
141 int depth = getProperty(PROP_BIT_DEPTH);
142 const std::string bitDepthHdr = makeBitDepthHeader(depth);
143 writeFITSHeaderEntry(bitDepthHdr, file);
145 writeFITSHeaderAxesSizes(img, file);
150
151 const size_t entriesPerHDU = 36;
152 writePaddingFITSHeaders(entriesPerHDU - 9, file);
153}
154
155void SaveFITS::writeFITSImageMatrix(const API::MatrixWorkspace_sptr &img, std::ofstream &file) {
156 const size_t sizeX = img->blocksize();
157 const size_t sizeY = img->getNumberHistograms();
158
159 int bitDepth = getProperty(PROP_BIT_DEPTH);
160 const size_t bytespp = static_cast<size_t>(bitDepth) / 8;
161
162 for (size_t row = 0; row < sizeY; ++row) {
163 const auto &yData = img->y(row);
164 for (size_t col = 0; col < sizeX; ++col) {
165 int32_t pixelVal;
166 if (8 == bitDepth) {
167 pixelVal = static_cast<uint8_t>(yData[col]);
168 } else if (16 == bitDepth) {
169 pixelVal = static_cast<uint16_t>(yData[col]);
170 } else if (32 == bitDepth) {
171 pixelVal = static_cast<uint32_t>(yData[col]);
172 }
173
174 // change endianness: to sequence of bytes in big-endian
175 // this needs revisiting (similarly in LoadFITS)
176 // See https://github.com/mantidproject/mantid/pull/15964
177 std::array<uint8_t, g_maxBytesPP> bytesPixel;
178 auto *iter = reinterpret_cast<uint8_t *>(&pixelVal);
179 std::reverse_copy(iter, iter + bytespp, bytesPixel.data());
180
181 file.write(reinterpret_cast<const char *>(bytesPixel.data()), bytespp);
182 }
183 }
184}
185
186void SaveFITS::writeFITSHeaderEntry(const std::string &hdr, std::ofstream &file) {
187 static const std::vector<char> blanks(g_maxLenHdr, 32);
188
189 auto count = hdr.size();
190 if (count >= g_maxLenHdr)
192
193 file.write(hdr.c_str(), sizeof(char) * count);
194 file.write(blanks.data(), g_maxLenHdr - count);
195}
196
198 const std::string sizeX = std::to_string(img->blocksize());
199 const std::string sizeY = std::to_string(img->getNumberHistograms());
200
201 const size_t fieldWidth = 20;
202 std::stringstream axis1;
203 axis1 << "NAXIS1 = " << std::setw(fieldWidth) << sizeX << " / length of data axis 1";
204 writeFITSHeaderEntry(axis1.str(), file);
205
206 std::stringstream axis2;
207 axis2 << "NAXIS2 = " << std::setw(fieldWidth) << sizeY << " / length of data axis 2";
208 writeFITSHeaderEntry(axis2.str(), file);
209}
210
211std::string SaveFITS::makeBitDepthHeader(size_t depth) const {
212 std::stringstream hdr;
213 hdr << g_bitDepthPre << std::setw(2) << depth << g_bitDepthPost;
214 return hdr.str();
215}
216
226void SaveFITS::writePaddingFITSHeaders(size_t count, std::ofstream &file) {
227 static const std::vector<char> blanks(g_maxLenHdr, 32);
228
229 for (size_t i = 0; i < count; ++i) {
230 file.write(blanks.data(), g_maxLenHdr);
231 }
232}
233
234} // namespace Mantid::DataHandling
std::string name
Definition Run.cpp:60
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
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.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:422
@ Save
to specify a file to write to, the file may or may not exist
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:186
static const std::string g_FITSHdrExtensions
Definition SaveFITS.h:62
void init() override final
Initialize the algorithm's properties.
Definition SaveFITS.cpp:79
std::string makeBitDepthHeader(size_t depth) const
Definition SaveFITS.cpp:211
void writePaddingFITSHeaders(size_t count, std::ofstream &file)
Writes the padding required to fill every header block.
Definition SaveFITS.cpp:226
std::map< std::string, std::string > validateInputs() override
Perform validation of ALL the input properties of the algorithm.
Definition SaveFITS.cpp:95
void writeFITSHeaderBlock(const API::MatrixWorkspace_sptr &img, std::ofstream &file)
Definition SaveFITS.cpp:138
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:155
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:59
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:65
const std::string category() const override final
Algorithm's category for identification.
Definition SaveFITS.cpp:62
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:197
void exec() override final
Execute the algorithm.
Definition SaveFITS.cpp:116
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:131
ListValidator is a validator that requires the value of a property to be one of a defined list of pos...
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
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