Visual Servoing Platform  version 3.6.1 under development (2024-05-18)
vpImageIoTinyEXR.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2023 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See https://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  *
30  * Description:
31  * TinyEXR backend for EXR image I/O operations.
32  */
33 
40 #include <visp3/core/vpConfig.h>
41 
42 #if defined(VISP_HAVE_STBIMAGE) && defined(VISP_HAVE_TINYEXR)
43 
44 #include "vpImageIoBackend.h"
45 
46 #define TINYEXR_USE_MINIZ 0
47 #define TINYEXR_USE_STB_ZLIB 1
48 #include <stb_image.h>
49 #include <stb_image_write.h>
50 
51 #define TINYEXR_IMPLEMENTATION
52 #include <tinyexr.h>
53 
54 void readEXRTiny(vpImage<float> &I, const std::string &filename)
55 {
56  EXRVersion exr_version;
57 
58  int ret = ParseEXRVersionFromFile(&exr_version, filename.c_str());
59  if (ret != 0) {
60  throw(vpImageException(vpImageException::ioError, "Error: Invalid EXR file %s", filename.c_str()));
61  }
62 
63  if (exr_version.multipart) {
64  // must be multipart flag is false.
65  throw(vpImageException(vpImageException::ioError, "Error: Multipart EXR images are not supported."));
66  }
67 
68  EXRHeader exr_header;
69  InitEXRHeader(&exr_header);
70 
71  const char *err = nullptr; // or `nullptr` in C++11 or later.
72  ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, filename.c_str(), &err);
73  if (ret != 0) {
74  std::string err_msg(err);
75  FreeEXRErrorMessage(err); // free's buffer for an error message
76  throw(vpImageException(vpImageException::ioError, "Error: Unable to parse EXR header from %s : %s", filename.c_str(), err_msg.c_str()));
77  }
78 
79  // Read HALF channel as FLOAT.
80  for (int i = 0; i < exr_header.num_channels; ++i) {
81  if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
82  exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
83  }
84  }
85 
86  EXRImage exr_image;
87  InitEXRImage(&exr_image);
88 
89  ret = LoadEXRImageFromFile(&exr_image, &exr_header, filename.c_str(), &err);
90 
91  if (ret != 0) {
92  std::string err_msg(err);
93  FreeEXRHeader(&exr_header);
94  FreeEXRErrorMessage(err); // free's buffer for an error message
95  throw(vpImageException(vpImageException::ioError, "Error: Unable to load EXR image from %s : %s", filename.c_str(), err_msg.c_str()));
96  }
97 
98  // `exr_image.images` will be filled when EXR is scanline format.
99  // `exr_image.tiled` will be filled when EXR is tiled format.
100  if (exr_image.images) {
101  I.resize(exr_image.height, exr_image.width);
102  memcpy(I.bitmap, exr_image.images[0], exr_image.height*exr_image.width*sizeof(float));
103  }
104  else if (exr_image.tiles) {
105  I.resize(exr_image.height, exr_image.width);
106  size_t data_width = static_cast<size_t>(exr_header.data_window.max_x - exr_header.data_window.min_x + 1);
107 
108  for (int tile_idx = 0; tile_idx < exr_image.num_tiles; ++tile_idx) {
109  int sx = exr_image.tiles[tile_idx].offset_x * exr_header.tile_size_x;
110  int sy = exr_image.tiles[tile_idx].offset_y * exr_header.tile_size_y;
111  int ex = exr_image.tiles[tile_idx].offset_x * exr_header.tile_size_x + exr_image.tiles[tile_idx].width;
112  int ey = exr_image.tiles[tile_idx].offset_y * exr_header.tile_size_y + exr_image.tiles[tile_idx].height;
113 
114  for (unsigned int y = 0; y < static_cast<unsigned int>(ey - sy); ++y) {
115  for (unsigned int x = 0; x < static_cast<unsigned int>(ex - sx); ++x) {
116  const float *src_image = reinterpret_cast<const float *>(exr_image.tiles[tile_idx].images[0]);
117  I.bitmap[(y + sy) * data_width + (x + sx)] = src_image[y * exr_header.tile_size_x + x];
118  }
119  }
120  }
121  }
122 
123  FreeEXRImage(&exr_image);
124  FreeEXRHeader(&exr_header);
125 }
126 
127 void readEXRTiny(vpImage<vpRGBf> &I, const std::string &filename)
128 {
129  EXRVersion exr_version;
130 
131  int ret = ParseEXRVersionFromFile(&exr_version, filename.c_str());
132  if (ret != 0) {
133  throw(vpImageException(vpImageException::ioError, "Error: Invalid EXR file %s", filename.c_str()));
134  }
135 
136  if (exr_version.multipart) {
137  // must be multipart flag is false.
138  throw(vpImageException(vpImageException::ioError, "Error: Multipart EXR images are not supported."));
139  }
140 
141  EXRHeader exr_header;
142  InitEXRHeader(&exr_header);
143 
144  const char *err = nullptr; // or `nullptr` in C++11 or later.
145  ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, filename.c_str(), &err);
146  if (ret != 0) {
147  std::string err_msg(err);
148  FreeEXRErrorMessage(err); // free's buffer for an error message
149  throw(vpImageException(vpImageException::ioError, "Error: Unable to parse EXR header from %s : %s", filename.c_str(), err_msg.c_str()));
150  }
151 
152  // Read HALF channel as FLOAT.
153  for (int i = 0; i < exr_header.num_channels; ++i) {
154  if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
155  exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
156  }
157  }
158 
159  EXRImage exr_image;
160  InitEXRImage(&exr_image);
161 
162  ret = LoadEXRImageFromFile(&exr_image, &exr_header, filename.c_str(), &err);
163 
164  if (ret != 0) {
165  std::string err_msg(err);
166  FreeEXRHeader(&exr_header);
167  FreeEXRErrorMessage(err); // free's buffer for an error message
168  throw(vpImageException(vpImageException::ioError, "Error: Unable to load EXR image from %s : %s", filename.c_str(), err_msg.c_str()));
169  }
170 
171  // `exr_image.images` will be filled when EXR is scanline format.
172  // `exr_image.tiled` will be filled when EXR is tiled format.
173  if (exr_image.images) {
174  I.resize(exr_image.height, exr_image.width);
175  for (int i = 0; i < exr_image.height; ++i) {
176  for (int j = 0; j < exr_image.width; ++j) {
177  I[i][j].R = reinterpret_cast<float **>(exr_image.images)[2][i * exr_image.width + j];
178  I[i][j].G = reinterpret_cast<float **>(exr_image.images)[1][i * exr_image.width + j];
179  I[i][j].B = reinterpret_cast<float **>(exr_image.images)[0][i * exr_image.width + j];
180  }
181  }
182  }
183  else if (exr_image.tiles) {
184  I.resize(exr_image.height, exr_image.width);
185  size_t data_width = static_cast<size_t>(exr_header.data_window.max_x - exr_header.data_window.min_x + 1);
186 
187  for (int tile_idx = 0; tile_idx < exr_image.num_tiles; ++tile_idx) {
188  int sx = exr_image.tiles[tile_idx].offset_x * exr_header.tile_size_x;
189  int sy = exr_image.tiles[tile_idx].offset_y * exr_header.tile_size_y;
190  int ex = exr_image.tiles[tile_idx].offset_x * exr_header.tile_size_x + exr_image.tiles[tile_idx].width;
191  int ey = exr_image.tiles[tile_idx].offset_y * exr_header.tile_size_y + exr_image.tiles[tile_idx].height;
192 
193  //for (size_t c = 0; c < static_cast<size_t>(exr_header.num_channels); ++c) {
194  // const float *src_image = reinterpret_cast<const float *>(exr_image.tiles[tile_idx].images[c]);
195  // for (size_t y = 0; y < static_cast<size_t>(ey - sy); ++y) {
196  // for (size_t x = 0; x < static_cast<size_t>(ex - sx); ++x) {
197  // reinterpret_cast<float *>(I.bitmap)[(y + sy) * data_width * 3 + (x + sx) * 3 + c] = src_image[y * exr_header.tile_size_x + x];
198  // }
199  // }
200  //}
201 
202  for (unsigned int y = 0; y < static_cast<unsigned int>(ey - sy); ++y) {
203  for (unsigned int x = 0; x < static_cast<unsigned int>(ex - sx); ++x) {
204  for (unsigned int c = 0; c < 3; ++c) {
205  const float *src_image = reinterpret_cast<const float *>(exr_image.tiles[tile_idx].images[c]);
206  reinterpret_cast<float *>(I.bitmap)[(y + sy) * data_width * 3 + (x + sx) * 3 + c] = src_image[y * exr_header.tile_size_x + x];
207  }
208  }
209  }
210  }
211  }
212 
213  FreeEXRImage(&exr_image);
214  FreeEXRHeader(&exr_header);
215 }
216 
217 void writeEXRTiny(const vpImage<float> &I, const std::string &filename)
218 {
219  EXRHeader header;
220  InitEXRHeader(&header);
221 
222  EXRImage image;
223  InitEXRImage(&image);
224 
225  image.num_channels = 1;
226 
227  image.images = (unsigned char **)&I.bitmap;
228  image.width = I.getWidth();
229  image.height = I.getHeight();
230 
231  header.num_channels = 1;
232  header.channels = (EXRChannelInfo *)malloc(sizeof(EXRChannelInfo) * header.num_channels);
233  // Must be (A)BGR order, since most of EXR viewers expect this channel order.
234  strncpy(header.channels[0].name, "Y", 255); header.channels[0].name[strlen("Y")] = '\0';
235 
236  header.pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
237  header.requested_pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
238  header.compression_type = TINYEXR_COMPRESSIONTYPE_ZIP;
239  for (int i = 0; i < header.num_channels; ++i) {
240  header.pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image
241  header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of output image to be stored in .EXR
242  }
243 
244  const char *err = nullptr; // or nullptr in C++11 or later.
245  int ret = SaveEXRImageToFile(&image, &header, filename.c_str(), &err);
246  if (ret != TINYEXR_SUCCESS) {
247  std::string err_msg(err);
248  FreeEXRErrorMessage(err); // free's buffer for an error message
249  free(header.channels);
250  free(header.requested_pixel_types);
251  free(header.pixel_types);
252  throw(vpImageException(vpImageException::ioError, "Error: Unable to save EXR image to %s : %s", filename.c_str(), err_msg.c_str()));
253  }
254 
255  free(header.channels);
256  free(header.requested_pixel_types);
257  free(header.pixel_types);
258 }
259 
260 void writeEXRTiny(const vpImage<vpRGBf> &I, const std::string &filename)
261 {
262  EXRHeader header;
263  InitEXRHeader(&header);
264 
265  EXRImage image;
266  InitEXRImage(&image);
267 
268  image.num_channels = 3;
269 
270  std::vector<float> images[3];
271  images[0].resize(I.getSize());
272  images[1].resize(I.getSize());
273  images[2].resize(I.getSize());
274 
275  // Split RGBRGBRGB... into R, G and B layer
276  for (unsigned int i = 0; i < I.getSize(); ++i) {
277  images[0][i] = I.bitmap[i].R;
278  images[1][i] = I.bitmap[i].G;
279  images[2][i] = I.bitmap[i].B;
280  }
281 
282  float *image_ptr[3];
283  image_ptr[0] = &(images[2].at(0)); // B
284  image_ptr[1] = &(images[1].at(0)); // G
285  image_ptr[2] = &(images[0].at(0)); // R
286 
287  image.images = (unsigned char **)image_ptr;
288  image.width = I.getWidth();
289  image.height = I.getHeight();
290 
291  header.num_channels = 3;
292  header.channels = (EXRChannelInfo *)malloc(sizeof(EXRChannelInfo) * header.num_channels);
293  // Must be (A)BGR order, since most of EXR viewers expect this channel order.
294  strncpy(header.channels[0].name, "B", 255); header.channels[0].name[strlen("B")] = '\0';
295  strncpy(header.channels[1].name, "G", 255); header.channels[1].name[strlen("G")] = '\0';
296  strncpy(header.channels[2].name, "R", 255); header.channels[2].name[strlen("R")] = '\0';
297 
298  header.pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
299  header.requested_pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
300  header.compression_type = TINYEXR_COMPRESSIONTYPE_ZIP;
301  for (int i = 0; i < header.num_channels; ++i) {
302  header.pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image
303  header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of output image to be stored in .EXR
304  }
305 
306  const char *err = nullptr; // or nullptr in C++11 or later.
307  int ret = SaveEXRImageToFile(&image, &header, filename.c_str(), &err);
308  if (ret != TINYEXR_SUCCESS) {
309  std::string err_msg(err);
310  FreeEXRErrorMessage(err); // free's buffer for an error message
311  free(header.channels);
312  free(header.requested_pixel_types);
313  free(header.pixel_types);
314  throw(vpImageException(vpImageException::ioError, "Error: Unable to save EXR image to %s : %s", filename.c_str(), err_msg.c_str()));
315  }
316 
317  free(header.channels);
318  free(header.requested_pixel_types);
319  free(header.pixel_types);
320 }
321 
322 #endif
Error that can be emitted by the vpImage class and its derivatives.
@ ioError
Image io error.
unsigned int getWidth() const
Definition: vpImage.h:245
void resize(unsigned int h, unsigned int w)
resize the image : Image initialization
Definition: vpImage.h:798
unsigned int getSize() const
Definition: vpImage.h:224
Type * bitmap
points toward the bitmap
Definition: vpImage.h:139
unsigned int getHeight() const
Definition: vpImage.h:184