Wildmeshing Toolkit
load_image_exr.cpp
Go to the documentation of this file.
1 #include "load_image_exr.hpp"
2 
3 #define TINYEXR_USE_STB_ZLIB 1
4 #define TINYEXR_USE_MINIZ 0
5 #define TINYEXR_IMPLEMENTATION
6 #include <tinyexr.h>
7 #include <cassert>
8 #include <wmtk/utils/Logger.hpp>
9 using namespace wmtk;
10 auto load_image_exr_red_channel(const std::filesystem::path& path)
11  -> std::tuple<size_t, size_t, std::vector<float>>
12 {
13  using namespace wmtk;
14  wmtk::logger().debug("[load_image_exr_red_channel] start \"{}\"", path.string());
15  assert(std::filesystem::exists(path));
16  const std::string filename_ = path.string();
17  const char* filename = filename_.c_str();
18 
19  const auto exr_version = [&filename, &path]() -> EXRVersion { // parse version
20  EXRVersion exr_version_;
21 
22  const auto ret = ParseEXRVersionFromFile(&exr_version_, filename);
23  if (ret != TINYEXR_SUCCESS) {
24  wmtk::logger().error("failed LoadImageEXR \"{}\" \"version error\"", path.string());
25  throw std::runtime_error("LoadImageEXRError");
26  }
27 
28  if (exr_version_.multipart || exr_version_.non_image) {
29  wmtk::logger().error(
30  "failed LoadImageEXR \"{}\" \"multipart or non image\"",
31  path.string());
32  throw std::runtime_error("LoadImageEXRError");
33  }
34 
35  return exr_version_;
36  }();
37 
38  auto exr_header_data =
39  [&filename, &path, &exr_version]() -> std::tuple<EXRHeader, int> { // parse header
40  EXRHeader exr_header_;
41  InitEXRHeader(&exr_header_);
42 
43  [[maybe_unused]] const char* err = nullptr;
44  const auto ret = ParseEXRHeaderFromFile(&exr_header_, &exr_version, filename, &err);
45  if (ret != TINYEXR_SUCCESS) {
46  wmtk::logger().error("failed LoadImageEXR \"{}\" \"header error\"", path.string());
47  FreeEXRHeader(&exr_header_);
48  throw std::runtime_error("LoadImageEXRError");
49  }
50 
51  // sanity check, only support all channels are the same type
52  for (int i = 0; i < exr_header_.num_channels; i++) {
53  if (exr_header_.pixel_types[i] != exr_header_.pixel_types[0] ||
54  exr_header_.requested_pixel_types[i] != exr_header_.pixel_types[i]) {
55  wmtk::logger().error(
56  "failed LoadImageEXR \"{}\" \"inconsistent pixel_types\"",
57  path.string());
58  FreeEXRHeader(&exr_header_);
59  throw std::runtime_error("LoadImageEXRError");
60  }
61  }
62 
63  // read HALF channel as FLOAT.
64  for (int i = 0; i < exr_header_.num_channels; i++) {
65  if (exr_header_.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
66  exr_header_.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
67  }
68  }
69 
70  // only FLOAT are supported
71  if (exr_header_.requested_pixel_types[0] != TINYEXR_PIXELTYPE_FLOAT) {
72  wmtk::logger().error(
73  "failed LoadImageEXR \"{}\" \"only float exr are supported\"",
74  path.string());
75  FreeEXRHeader(&exr_header_);
76  throw std::runtime_error("LoadImageEXRError");
77  }
78 
79  // only non tiled image are supported
80  if (exr_header_.tiled) {
81  wmtk::logger().error(
82  "failed LoadImageEXR \"{}\" \"only non tiled exr are supported\"",
83  path.string());
84  FreeEXRHeader(&exr_header_);
85  throw std::runtime_error("LoadImageEXRError");
86  }
87 
88 
89  int index_red_ = -1;
90  for (int i = 0; i < exr_header_.num_channels; i++) {
91  if (strcmp(exr_header_.channels[i].name, "R") == 0) index_red_ = i;
92  }
93  if (index_red_ < 0) {
94  wmtk::logger().warn("Could not find R channel. Looking for Y channel instead.");
95  for (int i = 0; i < exr_header_.num_channels; i++) {
96  if (strcmp(exr_header_.channels[i].name, "Y") == 0) index_red_ = i;
97  }
98  }
99 
100  if (index_red_ < 0) {
101  std::vector<std::string> channels;
102  for (int i = 0; i < exr_header_.num_channels; i++) {
103  channels.push_back(exr_header_.channels[i].name);
104  }
105  wmtk::logger().error(
106  "failed LoadImageEXR \"{}\" can't find all expected channels: [{}]",
107  path.string(),
108  fmt::join(channels, ","));
109  FreeEXRHeader(&exr_header_);
110  throw std::runtime_error("LoadImageEXRError");
111  }
112 
113  return {exr_header_, index_red_};
114  }();
115  auto& exr_header = std::get<0>(exr_header_data);
116  const auto& index_data = std::get<1>(exr_header_data);
117 
118  auto exr_image = [&filename, &path, &exr_header]() -> EXRImage {
119  EXRImage exr_image_;
120  InitEXRImage(&exr_image_);
121 
122  [[maybe_unused]] const char* err = nullptr;
123  const auto ret = LoadEXRImageFromFile(&exr_image_, &exr_header, filename, &err);
124  if (ret != TINYEXR_SUCCESS) {
125  wmtk::logger().error(
126  "failed LoadImageEXR \"{}\" \"failed to load image data\"",
127  path.string());
128  FreeEXRHeader(&exr_header);
129  FreeEXRImage(&exr_image_);
130  throw std::runtime_error("LoadImageEXRError");
131  }
132 
133  return exr_image_;
134  }();
135 
136  wmtk::logger().debug(
137  "[load_image_exr_red_channel] num_channels {} tiled {}",
138  exr_header.num_channels,
139  exr_header.tiled);
140  wmtk::logger().debug("[load_image_exr_red_channel] index_data {}", index_data);
141  assert(index_data >= 0);
142  assert(!exr_header.tiled);
143 
144  std::vector<float> data_r;
145  data_r.reserve(static_cast<size_t>(exr_image.width) * static_cast<size_t>(exr_image.height));
146 
147  const auto images = reinterpret_cast<float**>(exr_image.images);
148  for (int i = 0; i < exr_image.width * exr_image.height; i++)
149  data_r.emplace_back(images[index_data][i]);
150 
151  FreeEXRHeader(&exr_header);
152  FreeEXRImage(&exr_image);
153 
154  wmtk::logger().debug("[load_image_exr_red_channel] done \"{}\"", path.string());
155 
156  return {
157  static_cast<size_t>(exr_image.width),
158  static_cast<size_t>(exr_image.height),
159  std::move(data_r),
160  };
161 }
162 
163 auto load_image_exr_split_3channels(const std::filesystem::path& path) -> std::
164  tuple<size_t, size_t, int, int, int, std::vector<float>, std::vector<float>, std::vector<float>>
165 {
166  using namespace wmtk;
167  wmtk::logger().debug("[load_image_exr_red_channel] start \"{}\"", path.string());
168  assert(std::filesystem::exists(path));
169  const std::string filename_ = path.string();
170  const char* filename = filename_.c_str();
171 
172  const auto exr_version = [&filename, &path]() -> EXRVersion { // parse version
173  EXRVersion exr_version_;
174 
175  const auto ret = ParseEXRVersionFromFile(&exr_version_, filename);
176  if (ret != TINYEXR_SUCCESS) {
177  wmtk::logger().error("failed LoadImageEXR \"{}\" \"version error\"", path.string());
178  throw std::runtime_error("LoadImageEXRError");
179  }
180 
181  if (exr_version_.multipart || exr_version_.non_image) {
182  wmtk::logger().error(
183  "failed LoadImageEXR \"{}\" \"multipart or non image\"",
184  path.string());
185  throw std::runtime_error("LoadImageEXRError");
186  }
187 
188  return exr_version_;
189  }();
190 
191  auto exr_header_data =
192  [&filename, &path, &exr_version]() -> std::tuple<EXRHeader, int, int, int> { // parse header
193  EXRHeader exr_header_;
194  InitEXRHeader(&exr_header_);
195 
196  [[maybe_unused]] const char* err = nullptr;
197  const auto ret = ParseEXRHeaderFromFile(&exr_header_, &exr_version, filename, &err);
198  if (ret != TINYEXR_SUCCESS) {
199  wmtk::logger().error("failed LoadImageEXR \"{}\" \"header error\"", path.string());
200  FreeEXRHeader(&exr_header_);
201  throw std::runtime_error("LoadImageEXRError");
202  }
203 
204  // sanity check, only support all channels are the same type
205  for (int i = 0; i < exr_header_.num_channels; i++) {
206  if (exr_header_.pixel_types[i] != exr_header_.pixel_types[0] ||
207  exr_header_.requested_pixel_types[i] != exr_header_.pixel_types[i]) {
208  wmtk::logger().error(
209  "failed LoadImageEXR \"{}\" \"inconsistent pixel_types\"",
210  path.string());
211  FreeEXRHeader(&exr_header_);
212  throw std::runtime_error("LoadImageEXRError");
213  }
214  }
215 
216  // read HALF channel as FLOAT.
217  for (int i = 0; i < exr_header_.num_channels; i++) {
218  if (exr_header_.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
219  exr_header_.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
220  }
221  }
222 
223  // only FLOAT are supported
224  if (exr_header_.requested_pixel_types[0] != TINYEXR_PIXELTYPE_FLOAT) {
225  wmtk::logger().error(
226  "failed LoadImageEXR \"{}\" \"only float exr are supported\"",
227  path.string());
228  FreeEXRHeader(&exr_header_);
229  throw std::runtime_error("LoadImageEXRError");
230  }
231 
232  // only non tiled image are supported
233  if (exr_header_.tiled) {
234  wmtk::logger().error(
235  "failed LoadImageEXR \"{}\" \"only non tiled exr are supported\"",
236  path.string());
237  FreeEXRHeader(&exr_header_);
238  throw std::runtime_error("LoadImageEXRError");
239  }
240 
241 
242  int index_red_ = -1;
243  int index_green_ = -1;
244  int index_blue_ = -1;
245  if (exr_header_.num_channels == 1) {
246  wmtk::logger().warn("Treat grayscale image as RGB: {}", path.string());
247  index_red_ = 0;
248  index_green_ = 0;
249  index_blue_ = 0;
250  } else {
251  for (int i = 0; i < exr_header_.num_channels; i++) {
252  if (strcmp(exr_header_.channels[i].name, "R") == 0) index_red_ = i;
253  if (strcmp(exr_header_.channels[i].name, "G") == 0) index_green_ = i;
254  if (strcmp(exr_header_.channels[i].name, "B") == 0) index_blue_ = i;
255  }
256  }
257 
258  if (index_red_ < 0) {
259  std::vector<std::string> channels;
260  for (int i = 0; i < exr_header_.num_channels; i++) {
261  channels.push_back(exr_header_.channels[i].name);
262  }
263  wmtk::logger().error(
264  "failed LoadImageEXR \"{}\" can't find all 3 expected channels: [{}]",
265  path.string(),
266  fmt::join(channels, ","));
267  FreeEXRHeader(&exr_header_);
268  throw std::runtime_error("LoadImageEXRError");
269  }
270 
271  return {exr_header_, index_red_, index_green_, index_blue_};
272  }();
273  auto& exr_header = std::get<0>(exr_header_data);
274  const auto& index_red = std::get<1>(exr_header_data);
275  const auto& index_green = std::get<2>(exr_header_data);
276  const auto& index_blue = std::get<3>(exr_header_data);
277 
278  auto exr_image = [&filename, &path, &exr_header]() -> EXRImage {
279  EXRImage exr_image_;
280  InitEXRImage(&exr_image_);
281 
282  [[maybe_unused]] const char* err = nullptr;
283  const auto ret = LoadEXRImageFromFile(&exr_image_, &exr_header, filename, &err);
284  if (ret != TINYEXR_SUCCESS) {
285  wmtk::logger().error(
286  "failed LoadImageEXR \"{}\" \"failed to load image data\"",
287  path.string());
288  FreeEXRHeader(&exr_header);
289  FreeEXRImage(&exr_image_);
290  throw std::runtime_error("LoadImageEXRError");
291  }
292 
293  return exr_image_;
294  }();
295 
296  wmtk::logger().debug(
297  "[load_image_exr_3channels] num_channels {} tiled {}",
298  exr_header.num_channels,
299  exr_header.tiled);
300  wmtk::logger().debug("[load_image_exr_3channels] index_red {}", index_red);
301  wmtk::logger().debug("[load_image_exr_3channels] index_green {}", index_green);
302  wmtk::logger().debug("[load_image_exr_3channels] index_blue {}", index_blue);
303  assert(index_red >= 0);
304  assert(index_green >= 0);
305  assert(index_blue >= 0);
306  assert(!exr_header.tiled);
307 
308  std::vector<float> data_r;
309  std::vector<float> data_g;
310  std::vector<float> data_b;
311  data_r.reserve(static_cast<size_t>(exr_image.width) * static_cast<size_t>(exr_image.height));
312  data_g.reserve(static_cast<size_t>(exr_image.width) * static_cast<size_t>(exr_image.height));
313  data_b.reserve(static_cast<size_t>(exr_image.width) * static_cast<size_t>(exr_image.height));
314 
315  const auto images = reinterpret_cast<float**>(exr_image.images);
316  for (int i = 0; i < exr_image.width * exr_image.height; i++) {
317  data_r.emplace_back(images[index_red][i]);
318  data_g.emplace_back(images[index_green][i]);
319  data_b.emplace_back(images[index_blue][i]);
320  }
321  FreeEXRHeader(&exr_header);
322  FreeEXRImage(&exr_image);
323 
324  wmtk::logger().debug("[load_image_exr_3channels] done \"{}\"", path.string());
325 
326  return {
327  static_cast<size_t>(exr_image.width),
328  static_cast<size_t>(exr_image.height),
329  std::move(index_red),
330  std::move(index_green),
331  std::move(index_blue),
332  std::move(data_r),
333  std::move(data_g),
334  std::move(data_b)};
335 }
auto load_image_exr_split_3channels(const std::filesystem::path &path) -> std::tuple< size_t, size_t, int, int, int, std::vector< float >, std::vector< float >, std::vector< float >>
auto load_image_exr_red_channel(const std::filesystem::path &path) -> std::tuple< size_t, size_t, std::vector< float >>
Definition: Accessor.hpp:6
spdlog::logger & logger()
Retrieves the current logger.
Definition: Logger.cpp:58