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