SourceXtractorPlusPlus 0.19
SourceXtractor++, the next generation SExtractor
Loading...
Searching...
No Matches
FitsImageSource.cpp
Go to the documentation of this file.
1
18/*
19 * FitsImageSource.cpp
20 *
21 * Created on: Mar 21, 2018
22 * Author: mschefer
23 */
24
27#include "SEUtils/VariantCast.h"
30#include <boost/algorithm/string/case_conv.hpp>
31#include <boost/algorithm/string/trim.hpp>
32#include <boost/filesystem/operations.hpp>
33#include <boost/filesystem/path.hpp>
34#include <boost/regex.hpp>
35#include <fstream>
36#include <iomanip>
37#include <numeric>
38#include <string>
39
40namespace SourceXtractor {
41
43
44namespace {
45
46ImageTile::ImageType convertImageType(int bitpix) {
47 ImageTile::ImageType image_type;
48
49 switch (bitpix) {
50 case FLOAT_IMG:
51 image_type = ImageTile::FloatImage;
52 break;
53 case DOUBLE_IMG:
54 image_type = ImageTile::DoubleImage;
55 break;
56 case LONG_IMG:
57 image_type = ImageTile::IntImage;
58 break;
59 case ULONG_IMG:
60 image_type = ImageTile::UIntImage;
61 break;
62 case LONGLONG_IMG:
63 image_type = ImageTile::LongLongImage;
64 break;
65 default:
66 throw Elements::Exception() << "Unsupported FITS image type: " << bitpix;
67 }
68
69 return image_type;
70}
71
72}
73
74FitsImageSource::FitsImageSource(const std::string& filename, int hdu_number,
75 ImageTile::ImageType image_type,
77 : m_filename(filename)
78 , m_file_manager(std::move(manager))
79 , m_handler(m_file_manager->getFileHandler(filename))
80 , m_hdu_number(hdu_number) {
81 int status = 0;
82 int bitpix, naxis;
83 long naxes[3] = {1, 1, 1};
84
85 auto acc = m_handler->getAccessor<FitsFile>();
86 auto fptr = acc->m_fd.getFitsFilePtr();
87
88 if (m_hdu_number <= 0) {
89 if (fits_get_hdu_num(fptr, &m_hdu_number) < 0) {
90 throw Elements::Exception() << "Can't get the active HDU from the FITS file: " << filename;
91 }
92 }
93 else {
95 }
96
97 fits_get_img_param(fptr, 2, &bitpix, &naxis, naxes, &status);
98 if (status != 0 || (naxis != 2 && naxis != 3)) {
100 << "Can't find 2D image or data cube in FITS file: " << filename << "[" << m_hdu_number << "]";
101 }
102
103 m_width = naxes[0];
104 m_height = naxes[1];
105 m_depth = naxis >= 3 ? naxes[2] : 1;
106
107 if (image_type < 0) {
108 m_image_type = convertImageType(bitpix);
109 }
110 else {
111 m_image_type = image_type;
112 }
113}
114
115FitsImageSource::FitsImageSource(const std::string& filename, int width, int height, ImageTile::ImageType image_type,
116 const std::shared_ptr<CoordinateSystem> coord_system, bool append, bool empty_primary,
118 : m_filename(filename)
119 , m_file_manager(std::move(manager))
120 , m_handler(m_file_manager->getFileHandler(filename))
121 , m_width(width)
122 , m_height(height)
123 , m_image_type(image_type) {
124
125 int status = 0;
126 fitsfile* fptr = nullptr;
127
128 if (!append) {
129 // delete file if it exists already
130 boost::filesystem::remove(m_filename);
131 }
132
133 {
134 auto acc = m_handler->getAccessor<FitsFile>(FileHandler::kWrite);
135 fptr = acc->m_fd.getFitsFilePtr();
136
137 assert(fptr != nullptr);
138
139 if (empty_primary && acc->m_fd.getImageHdus().empty()) {
140 fits_create_img(fptr, FLOAT_IMG, 0, nullptr, &status);
141 if (status != 0) {
142 throw Elements::Exception() << "Can't create empty hdu: " << filename << " status: " << status;
143 }
144 }
145
146 long naxes[2] = {width, height};
147 fits_create_img(fptr, getImageType(), 2, naxes, &status);
148
149 if (fits_get_hdu_num(fptr, &m_hdu_number) < 0) {
150 throw Elements::Exception() << "Can't get the active HDU from the FITS file: " << filename << " status: " << status;
151 }
152
153 int hdutype = 0;
154 fits_movabs_hdu(fptr, m_hdu_number, &hdutype, &status);
155
156 if (coord_system) {
157 auto headers = coord_system->getFitsHeaders();
158 for (const auto& h : headers) {
159 std::ostringstream padded_key, serializer;
160 padded_key << std::setw(8) << std::left << h.first;
161
162 serializer << padded_key.str() << "= " << std::left << std::setw(70) << h.second;
163 auto str = serializer.str();
164
165 fits_update_card(fptr, padded_key.str().c_str(), str.c_str(), &status);
166 if (status != 0) {
167 char err_txt[31];
168 fits_get_errstatus(status, err_txt);
169 throw Elements::Exception() << "Couldn't write the WCS headers (" << err_txt << "): " << str << " status: " << status;
170 }
171 }
172 }
173
174 std::vector<char> buffer(width * ImageTile::getTypeSize(image_type));
175 for (int i = 0; i < height; i++) {
176 long first_pixel[2] = {1, i + 1};
177 fits_write_pix(fptr, getDataType(), first_pixel, width, &buffer[0], &status);
178 }
179
180 if (status != 0) {
181 throw Elements::Exception() << "Couldn't allocate space for new FITS file: " << filename << " status: " << status;
182 }
183
184 acc->m_fd.refresh(); // make sure changes to the file structure are taken into account
185
186 }
187
188 // work around for canonical name bug:
189 // our file's canonical name might be wrong if it didn't exist, so we need to make sure we get the correct handler
190 // after we created the file
191
192 m_handler = nullptr;
193 m_handler = m_file_manager->getFileHandler(filename);
194}
195
196std::shared_ptr<ImageTile> FitsImageSource::getImageTile(int x, int y, int width, int height) const {
197 auto acc = m_handler->getAccessor<FitsFile>();
198 auto fptr = acc->m_fd.getFitsFilePtr();
199 switchHdu(fptr, m_hdu_number);
200
201 auto tile = ImageTile::create(m_image_type, x, y, width, height,
202 std::const_pointer_cast<ImageSource>(shared_from_this()));
203
204 long first_pixel[3] = {x + 1, y + 1, m_current_layer+1};
205 long last_pixel[3] = {x + width, y + height, m_current_layer+1};
206 long increment[3] = {1, 1, 1};
207 int status = 0;
208
209 fits_read_subset(fptr, getDataType(), first_pixel, last_pixel, increment,
210 nullptr, tile->getDataPtr(), nullptr, &status);
211 if (status != 0) {
212 throw Elements::Exception() << "Error reading image tile from FITS file.";
213 }
214
215 return tile;
216}
217
219 auto acc = m_handler->getAccessor<FitsFile>(FileHandler::kWrite);
220 auto fptr = acc->m_fd.getFitsFilePtr();
221 switchHdu(fptr, m_hdu_number);
222
223 int x = tile.getPosX();
224 int y = tile.getPosY();
225 int width = tile.getWidth();
226 int height = tile.getHeight();
227
228 long first_pixel[2] = {x + 1, y + 1};
229 long last_pixel[2] = {x + width, y + height};
230 int status = 0;
231
232 fits_write_subset(fptr, getDataType(), first_pixel, last_pixel, tile.getDataPtr(), &status);
233 if (status != 0) {
234 throw Elements::Exception() << "Error saving image tile to FITS file.";
235 }
236 fits_flush_buffer(fptr, 0, &status);
237}
238
239void FitsImageSource::switchHdu(fitsfile *fptr, int hdu_number) const {
240 int status = 0;
241 int hdu_type = 0;
242
243 fits_movabs_hdu(fptr, hdu_number, &hdu_type, &status);
244
245 if (status != 0) {
246 throw Elements::Exception() << "Could not switch to HDU # " << hdu_number << " in file "
247 << m_filename;
248 }
249 if (hdu_type != IMAGE_HDU) {
250 throw Elements::Exception() << "Trying to access non-image HDU in file " << m_filename;
251 }
252}
253
255 if (layer < 0 && layer >= m_depth) {
256 throw Elements::Exception() << "Trying to access an inexistent data cube layer (" << layer << ") in " << m_filename;
257 }
258 m_current_layer = layer;
259}
260
262 number_of_records = 0;
263 std::string records;
264
265 auto& headers = getMetadata();
266 for (const auto& record : headers) {
267 const auto& key = record.first;
268
269 std::string record_string(key);
270 if (record_string.size() > 8) {
271 throw Elements::Exception() << "FITS keyword longer than 8 characters";
272 }
273 else if (record_string.size() < 8) {
274 record_string += std::string(8 - record_string.size(), ' ');
275 }
276
277 if (headers.at(key).m_value.type() == typeid(std::string)) {
278 record_string += "= '" + VariantCast<std::string>(headers.at(key).m_value) + "'";
279 }
280 else {
281 record_string += "= " + VariantCast<std::string>(headers.at(key).m_value);
282 }
283
284 if (record_string.size() > 80) {
285 throw Elements::Exception() << "FITS record longer than 80 characters";
286 }
287
288
289 if (record_string.size() < 80) {
290 record_string += std::string(80 - record_string.size(), ' ');
291 }
292
293 records += record_string;
294 number_of_records++;
295 }
296
297 std::string record_string("END");
298 record_string += std::string(80 - record_string.size(), ' ');
299 records += record_string;
300
301 auto buffer = make_unique<std::vector<char>>(records.begin(), records.end());
302 buffer->emplace_back(0);
303 return buffer;
304}
305
307 auto acc = m_handler->getAccessor<FitsFile>();
308 return acc->m_fd.getHDUHeaders(m_hdu_number);
309}
310
312 auto acc = m_handler->getAccessor<FitsFile>();
313 auto fptr = acc->m_fd.getFitsFilePtr();
314 switchHdu(fptr, m_hdu_number);
315
316 std::ostringstream padded_key, serializer;
317 padded_key << std::setw(8) << std::left << key;
318
319 serializer << padded_key.str() << "= " << std::left << std::setw(70)
320 << VariantCast<std::string>(value.m_value);
321 auto str = serializer.str();
322
323 int status = 0;
324 fits_update_card(fptr, padded_key.str().c_str(), str.c_str(), &status);
325
326 if (status != 0) {
327 char err_txt[31];
328 fits_get_errstatus(status, err_txt);
329 throw Elements::Exception() << "Couldn't write the metadata (" << err_txt << "): " << str;
330 }
331
332 // update the metadata
333 acc->m_fd.getHDUHeaders(m_hdu_number)[key] = value;
334}
335
337 switch (m_image_type) {
338 default:
340 return TFLOAT;
342 return TDOUBLE;
344 return TINT;
346 return TUINT;
348 return TLONGLONG;
349 }
350}
351
353 switch (m_image_type) {
354 default:
356 return FLOAT_IMG;
358 return DOUBLE_IMG;
360 return LONG_IMG;
362 return ULONG_IMG;
364 return LONGLONG_IMG;
365 }
366}
367
368//FIXME add missing types
369
370}
std::shared_ptr< DependentParameter< std::shared_ptr< EngineParameter > > > x
std::shared_ptr< DependentParameter< std::shared_ptr< EngineParameter > > > y
T begin(T... args)
represents access to a whole FITS file and handles loading and caching FITS headers
Definition: FitsFile.h:43
std::map< std::string, MetadataEntry > & getHDUHeaders(int hdu)
Definition: FitsFile.cpp:110
fitsfile * getFitsFilePtr()
Definition: FitsFile.cpp:102
std::shared_ptr< FileManager > m_file_manager
const std::map< std::string, MetadataEntry > & getMetadata() const override
std::shared_ptr< ImageTile > getImageTile(int x, int y, int width, int height) const override
ImageTile::ImageType m_image_type
void saveTile(ImageTile &tile) override
FitsImageSource(const std::string &filename, int hdu_number=0, ImageTile::ImageType image_type=ImageTile::AutoType, std::shared_ptr< FileManager > manager=FileManager::getDefault())
std::unique_ptr< std::vector< char > > getFitsHeaders(int &number_of_records) const
void setMetadata(const std::string &key, const MetadataEntry &value) override
std::shared_ptr< FileHandler > m_handler
void switchHdu(fitsfile *fptr, int hdu_number) const
static size_t getTypeSize(ImageType image_type)
Definition: ImageTile.h:117
virtual void * getDataPtr()=0
static std::shared_ptr< ImageTile > create(ImageType image_type, int x, int y, int width, int height, std::shared_ptr< ImageSource > source=nullptr)
Definition: ImageTile.cpp:24
T end(T... args)
T left(T... args)
T move(T... args)
std::unique_ptr< T > make_unique(Args &&... args)
STL namespace.
T setw(T... args)
T size(T... args)
T str(T... args)