BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
OutputDataReadWriteTiff.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file Device/InputOutput/OutputDataReadWriteTiff.cpp
6 //! @brief Implements class OutputDataReadWriteTiff
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
15 #ifdef BORNAGAIN_TIFF_SUPPORT
16 
18 #include "Base/Utils/SysUtils.h"
19 #include <tiffio.hxx>
20 
21 OutputDataReadWriteTiff::OutputDataReadWriteTiff()
22  : m_tiff(nullptr)
23  , m_width(0)
24  , m_height(0)
25  , m_bitsPerSample(0)
26  , m_samplesPerPixel(0)
27  , m_sampleFormat(0)
28 {
29 }
30 
31 OutputDataReadWriteTiff::~OutputDataReadWriteTiff()
32 {
33  close();
34 }
35 
36 void OutputDataReadWriteTiff::read(std::istream& input_stream)
37 {
38  m_tiff = TIFFStreamOpen("MemTIFF", &input_stream);
39  if (!m_tiff)
40  throw std::runtime_error("OutputDataReadWriteTiff::read() -> Can't open the file.");
41 
42  read_header();
43  read_data();
44  close();
45 }
46 
47 OutputData<double>* OutputDataReadWriteTiff::readOutputData(std::istream& input_stream)
48 {
49  read(input_stream);
50  return m_data->clone();
51 }
52 
53 void OutputDataReadWriteTiff::writeOutputData(const OutputData<double>& data,
54  std::ostream& output_stream)
55 {
56  m_data.reset(data.clone());
57  if (m_data->rank() != 2)
58  throw std::runtime_error("OutputDataReadWriteTiff::write -> Error. "
59  "Only 2-dim arrays supported");
60  m_tiff = TIFFStreamOpen("MemTIFF", &output_stream);
61  m_width = m_data->axis(0).size();
62  m_height = m_data->axis(1).size(); // this does not exist for 1d data
63  write_header();
64  write_data();
65  close();
66 }
67 
68 void OutputDataReadWriteTiff::read_header()
69 {
70  ASSERT(m_tiff);
71  uint32 width(0);
72  uint32 height(0);
73  if (!TIFFGetField(m_tiff, TIFFTAG_IMAGEWIDTH, &width)
74  || !TIFFGetField(m_tiff, TIFFTAG_IMAGELENGTH, &height)) {
75  throw std::runtime_error("OutputDataReadWriteTiff::read_header() -> Error. "
76  "Can't read width/height.");
77  }
78 
79  m_width = (size_t)width;
80  m_height = (size_t)height;
81 
82  uint16 orientationTag(0);
83  TIFFGetField(m_tiff, TIFFTAG_ORIENTATION, &orientationTag);
84 
85  bool good = true;
86 
87  // BitsPerSample defaults to 1 according to the TIFF spec.
88  if (!TIFFGetField(m_tiff, TIFFTAG_BITSPERSAMPLE, &m_bitsPerSample))
89  m_bitsPerSample = 1;
90  if (8 != m_bitsPerSample && 16 != m_bitsPerSample && 32 != m_bitsPerSample)
91  good = false;
92 
93  // they may be e.g. grayscale with 2 samples per pixel
94  if (!TIFFGetField(m_tiff, TIFFTAG_SAMPLESPERPIXEL, &m_samplesPerPixel))
95  m_samplesPerPixel = 1;
96  if (m_samplesPerPixel != 1)
97  good = false;
98 
99  if (!TIFFGetField(m_tiff, TIFFTAG_SAMPLEFORMAT, &m_sampleFormat))
100  m_sampleFormat = 1;
101 
102  switch (m_sampleFormat) {
103  case 1: // unsigned int
104  case 2: // signed int
105  break;
106  case 3: // IEEE float
107  if (32 != m_bitsPerSample)
108  good = false;
109  break;
110  default:
111  good = false;
112  }
113 
114  if (!good) {
115  std::ostringstream message;
116  message << "OutputDataReadWriteTiff::read_header() -> Error. "
117  << "Can't read tiff image with following parameters:" << std::endl
118  << " TIFFTAG_BITSPERSAMPLE: " << m_bitsPerSample << std::endl
119  << " TIFFTAG_SAMPLESPERPIXEL: " << m_samplesPerPixel << std::endl
120  << " TIFFTAG_SAMPLEFORMAT: " << m_sampleFormat << std::endl;
121  throw std::runtime_error(message.str());
122  }
123 }
124 
125 void OutputDataReadWriteTiff::read_data()
126 {
127  ASSERT(m_tiff);
128 
129  ASSERT(0 == m_bitsPerSample % 8);
130  uint16 bytesPerSample = m_bitsPerSample / 8;
131  tmsize_t buf_size = TIFFScanlineSize(m_tiff);
132  tmsize_t expected_size = bytesPerSample * m_width;
133  if (buf_size != expected_size)
134  throw std::runtime_error(
135  "OutputDataReadWriteTiff::read_data() -> Error. Wrong scanline size.");
136 
137  tdata_t buf = _TIFFmalloc(buf_size);
138  if (!buf)
139  throw std::runtime_error(
140  "OutputDataReadWriteTiff::read_data() -> Error. Can't allocate buffer.");
141 
142  create_output_data();
143 
144  std::vector<int8> line_buf;
145  line_buf.resize(buf_size, 0);
146 
147  std::vector<unsigned> axes_indices(2);
148 
149  for (uint32 row = 0; row < (uint32)m_height; row++) {
150  if (TIFFReadScanline(m_tiff, buf, row) < 0)
151  throw std::runtime_error(
152  "OutputDataReadWriteTiff::read_data() -> Error. Error in scanline.");
153 
154  memcpy(&line_buf[0], buf, buf_size);
155 
156  for (unsigned col = 0; col < m_width; ++col) {
157  axes_indices[0] = col;
158  axes_indices[1] = static_cast<unsigned>(m_height) - 1 - row;
159  size_t global_index = m_data->toGlobalIndex(axes_indices);
160 
161  void* incoming = &line_buf[col * bytesPerSample];
162  double sample = 0;
163 
164  switch (m_sampleFormat) {
165  case 1: // unsigned int
166  switch (m_bitsPerSample) {
167  case 8:
168  sample = *reinterpret_cast<uint8*>(incoming);
169  break;
170  case 16:
171  sample = *reinterpret_cast<uint16*>(incoming);
172  break;
173  case 32:
174  sample = *reinterpret_cast<uint32*>(incoming);
175  break;
176  }
177  break;
178  case 2: // signed int
179  switch (m_bitsPerSample) {
180  case 8:
181  sample = *reinterpret_cast<int8*>(incoming);
182  break;
183  case 16:
184  sample = *reinterpret_cast<int16*>(incoming);
185  break;
186  case 32:
187  sample = *reinterpret_cast<int32*>(incoming);
188  break;
189  }
190  break;
191  case 3: // IEEE float
192  sample = double(*reinterpret_cast<float*>(incoming));
193  break;
194  default:
195  throw std::runtime_error("OutputDataReadWriteTiff: unexpected sample format");
196  }
197 
198  (*m_data)[global_index] = sample;
199  }
200  }
201  _TIFFfree(buf);
202 }
203 
204 void OutputDataReadWriteTiff::write_header()
205 {
206  ASSERT(m_tiff);
207  TIFFSetField(m_tiff, TIFFTAG_ARTIST, "BornAgain.IOFactory");
208  TIFFSetField(m_tiff, TIFFTAG_DATETIME, SysUtils::getCurrentDateAndTime().c_str());
209  TIFFSetField(m_tiff, TIFFTAG_IMAGEDESCRIPTION,
210  "Image converted from BornAgain intensity file.");
211  TIFFSetField(m_tiff, TIFFTAG_SOFTWARE, "BornAgain");
212 
213  uint32 width = static_cast<uint32>(m_width);
214  uint32 height = static_cast<uint32>(m_height);
215  TIFFSetField(m_tiff, TIFFTAG_IMAGEWIDTH, width);
216  TIFFSetField(m_tiff, TIFFTAG_IMAGELENGTH, height);
217 
218  // output format, hardcoded here
219  uint16 bitPerSample = 32, samplesPerPixel = 1;
220  TIFFSetField(m_tiff, TIFFTAG_BITSPERSAMPLE, bitPerSample);
221  TIFFSetField(m_tiff, TIFFTAG_SAMPLESPERPIXEL, samplesPerPixel);
222 
223  TIFFSetField(m_tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
224 }
225 
226 void OutputDataReadWriteTiff::write_data()
227 {
228  typedef int sample_t;
229  tmsize_t buf_size = sizeof(sample_t) * m_width;
230  tdata_t buf = _TIFFmalloc(buf_size);
231  if (!buf)
232  throw std::runtime_error(
233  "OutputDataReadWriteTiff::write_data() -> Error. Can't allocate buffer.");
234 
235  std::vector<sample_t> line_buf;
236  line_buf.resize(m_width, 0);
237  std::vector<unsigned> axes_indices(2);
238  for (unsigned row = 0; row < (uint32)m_height; row++) {
239  for (unsigned col = 0; col < line_buf.size(); ++col) {
240  axes_indices[0] = col;
241  axes_indices[1] = static_cast<unsigned>(m_height) - 1 - row;
242  size_t global_index = m_data->toGlobalIndex(axes_indices);
243  line_buf[col] = static_cast<sample_t>((*m_data)[global_index]);
244  }
245  memcpy(buf, &line_buf[0], buf_size);
246 
247  if (TIFFWriteScanline(m_tiff, buf, row) < 0)
248  throw std::runtime_error(
249  "OutputDataReadWriteTiff::write_data() -> Error. Error in TIFFWriteScanline.");
250  }
251  _TIFFfree(buf);
252  TIFFFlush(m_tiff);
253 }
254 
255 void OutputDataReadWriteTiff::close()
256 {
257  if (m_tiff) {
258  TIFFClose(m_tiff);
259  m_tiff = nullptr;
260  m_width = 0;
261  m_height = 0;
262  }
263 }
264 
265 void OutputDataReadWriteTiff::create_output_data()
266 {
267  ASSERT(m_tiff);
268  m_data.reset(new OutputData<double>);
269  m_data->addAxis("x", m_width, 0.0, double(m_width));
270  m_data->addAxis("y", m_height, 0.0, double(m_height));
271 }
272 
273 #endif // BORNAGAIN_TIFF_SUPPORT
#define ASSERT(condition)
Definition: Assert.h:31
Defines class OutputDataReadWriteTiff.
Defines various stuff in namespace Utils.
OutputData * clone() const
Definition: OutputData.h:259
std::string getCurrentDateAndTime()
Definition: SysUtils.cpp:22