Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpImageIoTinyEXR.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 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
38
39
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
55
56void readEXRTiny(vpImage<float> &I, const std::string &filename)
57{
58 EXRVersion exr_version;
59
60 int ret = ParseEXRVersionFromFile(&exr_version, filename.c_str());
61 if (ret != 0) {
62 throw(vpImageException(vpImageException::ioError, "Error: Invalid EXR file %s", filename.c_str()));
63 }
64
65 if (exr_version.multipart) {
66 // must be multipart flag is false.
67 throw(vpImageException(vpImageException::ioError, "Error: Multipart EXR images are not supported."));
68 }
69
70 EXRHeader exr_header;
71 InitEXRHeader(&exr_header);
72
73 const char *err = nullptr; // or `nullptr` in C++11 or later.
74 ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, filename.c_str(), &err);
75 if (ret != 0) {
76 std::string err_msg(err);
77 FreeEXRErrorMessage(err); // free's buffer for an error message
78 throw(vpImageException(vpImageException::ioError, "Error: Unable to parse EXR header from %s : %s", filename.c_str(), err_msg.c_str()));
79 }
80
81 // Read HALF channel as FLOAT.
82 for (int i = 0; i < exr_header.num_channels; ++i) {
83 if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
84 exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
85 }
86 }
87
88 EXRImage exr_image;
89 InitEXRImage(&exr_image);
90
91 ret = LoadEXRImageFromFile(&exr_image, &exr_header, filename.c_str(), &err);
92
93 if (ret != 0) {
94 std::string err_msg(err);
95 FreeEXRHeader(&exr_header);
96 FreeEXRErrorMessage(err); // free's buffer for an error message
97 throw(vpImageException(vpImageException::ioError, "Error: Unable to load EXR image from %s : %s", filename.c_str(), err_msg.c_str()));
98 }
99
100 // `exr_image.images` will be filled when EXR is scanline format.
101 // `exr_image.tiled` will be filled when EXR is tiled format.
102 if (exr_image.images) {
103 I.resize(static_cast<unsigned int>(exr_image.height), static_cast<unsigned int>(exr_image.width));
104 memcpy(I.bitmap, exr_image.images[0], exr_image.height*exr_image.width*sizeof(float));
105 }
106 else if (exr_image.tiles) {
107 I.resize(static_cast<unsigned int>(exr_image.height), static_cast<unsigned int>(exr_image.width));
108 size_t data_width = static_cast<size_t>(exr_header.data_window.max_x - exr_header.data_window.min_x + 1);
109
110 for (int tile_idx = 0; tile_idx < exr_image.num_tiles; ++tile_idx) {
111 int sx = exr_image.tiles[tile_idx].offset_x * exr_header.tile_size_x;
112 int sy = exr_image.tiles[tile_idx].offset_y * exr_header.tile_size_y;
113 int ex = exr_image.tiles[tile_idx].offset_x * exr_header.tile_size_x + exr_image.tiles[tile_idx].width;
114 int ey = exr_image.tiles[tile_idx].offset_y * exr_header.tile_size_y + exr_image.tiles[tile_idx].height;
115
116 for (unsigned int y = 0; y < static_cast<unsigned int>(ey - sy); ++y) {
117 for (unsigned int x = 0; x < static_cast<unsigned int>(ex - sx); ++x) {
118 const float *src_image = reinterpret_cast<const float *>(exr_image.tiles[tile_idx].images[0]);
119 I.bitmap[(y + sy) * data_width + (x + sx)] = src_image[y * exr_header.tile_size_x + x];
120 }
121 }
122 }
123 }
124
125 FreeEXRImage(&exr_image);
126 FreeEXRHeader(&exr_header);
127}
128
129void readEXRTiny(vpImage<vpRGBf> &I, const std::string &filename)
130{
131 EXRVersion exr_version;
132
133 int ret = ParseEXRVersionFromFile(&exr_version, filename.c_str());
134 if (ret != 0) {
135 throw(vpImageException(vpImageException::ioError, "Error: Invalid EXR file %s", filename.c_str()));
136 }
137
138 if (exr_version.multipart) {
139 // must be multipart flag is false.
140 throw(vpImageException(vpImageException::ioError, "Error: Multipart EXR images are not supported."));
141 }
142
143 EXRHeader exr_header;
144 InitEXRHeader(&exr_header);
145
146 const char *err = nullptr; // or `nullptr` in C++11 or later.
147 ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, filename.c_str(), &err);
148 if (ret != 0) {
149 std::string err_msg(err);
150 FreeEXRErrorMessage(err); // free's buffer for an error message
151 throw(vpImageException(vpImageException::ioError, "Error: Unable to parse EXR header from %s : %s", filename.c_str(), err_msg.c_str()));
152 }
153
154 // Read HALF channel as FLOAT.
155 for (int i = 0; i < exr_header.num_channels; ++i) {
156 if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) {
157 exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
158 }
159 }
160
161 EXRImage exr_image;
162 InitEXRImage(&exr_image);
163
164 ret = LoadEXRImageFromFile(&exr_image, &exr_header, filename.c_str(), &err);
165
166 if (ret != 0) {
167 std::string err_msg(err);
168 FreeEXRHeader(&exr_header);
169 FreeEXRErrorMessage(err); // free's buffer for an error message
170 throw(vpImageException(vpImageException::ioError, "Error: Unable to load EXR image from %s : %s", filename.c_str(), err_msg.c_str()));
171 }
172
173 // `exr_image.images` will be filled when EXR is scanline format.
174 // `exr_image.tiled` will be filled when EXR is tiled format.
175 if (exr_image.images) {
176 I.resize(static_cast<unsigned int>(exr_image.height), static_cast<unsigned int>(exr_image.width));
177 for (int i = 0; i < exr_image.height; ++i) {
178 for (int j = 0; j < exr_image.width; ++j) {
179 I[i][j].R = reinterpret_cast<float **>(exr_image.images)[2][i * exr_image.width + j];
180 I[i][j].G = reinterpret_cast<float **>(exr_image.images)[1][i * exr_image.width + j];
181 I[i][j].B = reinterpret_cast<float **>(exr_image.images)[0][i * exr_image.width + j];
182 }
183 }
184 }
185 else if (exr_image.tiles) {
186 I.resize(exr_image.height, exr_image.width);
187 size_t data_width = static_cast<size_t>(exr_header.data_window.max_x - exr_header.data_window.min_x + 1);
188
189 for (int tile_idx = 0; tile_idx < exr_image.num_tiles; ++tile_idx) {
190 int sx = exr_image.tiles[tile_idx].offset_x * exr_header.tile_size_x;
191 int sy = exr_image.tiles[tile_idx].offset_y * exr_header.tile_size_y;
192 int ex = exr_image.tiles[tile_idx].offset_x * exr_header.tile_size_x + exr_image.tiles[tile_idx].width;
193 int ey = exr_image.tiles[tile_idx].offset_y * exr_header.tile_size_y + exr_image.tiles[tile_idx].height;
194
195 //for (size_t c = 0; c < static_cast<size_t>(exr_header.num_channels); ++c) {
196 // const float *src_image = reinterpret_cast<const float *>(exr_image.tiles[tile_idx].images[c]);
197 // for (size_t y = 0; y < static_cast<size_t>(ey - sy); ++y) {
198 // for (size_t x = 0; x < static_cast<size_t>(ex - sx); ++x) {
199 // reinterpret_cast<float *>(I.bitmap)[(y + sy) * data_width * 3 + (x + sx) * 3 + c] = src_image[y * exr_header.tile_size_x + x];
200 // }
201 // }
202 //}
203
204 for (unsigned int y = 0; y < static_cast<unsigned int>(ey - sy); ++y) {
205 for (unsigned int x = 0; x < static_cast<unsigned int>(ex - sx); ++x) {
206 for (unsigned int c = 0; c < 3; ++c) {
207 const float *src_image = reinterpret_cast<const float *>(exr_image.tiles[tile_idx].images[c]);
208 reinterpret_cast<float *>(I.bitmap)[(y + sy) * data_width * 3 + (x + sx) * 3 + c] = src_image[y * exr_header.tile_size_x + x];
209 }
210 }
211 }
212 }
213 }
214
215 FreeEXRImage(&exr_image);
216 FreeEXRHeader(&exr_header);
217}
218
219void writeEXRTiny(const vpImage<float> &I, const std::string &filename)
220{
221 EXRHeader header;
222 InitEXRHeader(&header);
223
224 EXRImage image;
225 InitEXRImage(&image);
226
227 image.num_channels = 1;
228
229 image.images = (unsigned char **)&I.bitmap;
230 image.width = I.getWidth();
231 image.height = I.getHeight();
232
233 header.num_channels = 1;
234 header.channels = (EXRChannelInfo *)malloc(sizeof(EXRChannelInfo) * header.num_channels);
235 // Must be (A)BGR order, since most of EXR viewers expect this channel order.
236 strncpy(header.channels[0].name, "Y", 255); header.channels[0].name[strlen("Y")] = '\0';
237
238 header.pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
239 header.requested_pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
240 header.compression_type = TINYEXR_COMPRESSIONTYPE_ZIP;
241 for (int i = 0; i < header.num_channels; ++i) {
242 header.pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image
243 header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of output image to be stored in .EXR
244 }
245
246 const char *err = nullptr; // or nullptr in C++11 or later.
247 int ret = SaveEXRImageToFile(&image, &header, filename.c_str(), &err);
248 if (ret != TINYEXR_SUCCESS) {
249 std::string err_msg(err);
250 FreeEXRErrorMessage(err); // free's buffer for an error message
251 free(header.channels);
252 free(header.requested_pixel_types);
253 free(header.pixel_types);
254 throw(vpImageException(vpImageException::ioError, "Error: Unable to save EXR image to %s : %s", filename.c_str(), err_msg.c_str()));
255 }
256
257 free(header.channels);
258 free(header.requested_pixel_types);
259 free(header.pixel_types);
260}
261
262void writeEXRTiny(const vpImage<vpRGBf> &I, const std::string &filename)
263{
264 EXRHeader header;
265 InitEXRHeader(&header);
266
267 EXRImage image;
268 InitEXRImage(&image);
269
270 image.num_channels = 3;
271
272 std::vector<float> images[3];
273 images[0].resize(I.getSize());
274 images[1].resize(I.getSize());
275 images[2].resize(I.getSize());
276
277 // Split RGBRGBRGB... into R, G and B layer
278 for (unsigned int i = 0; i < I.getSize(); ++i) {
279 images[0][i] = I.bitmap[i].R;
280 images[1][i] = I.bitmap[i].G;
281 images[2][i] = I.bitmap[i].B;
282 }
283
284 float *image_ptr[3];
285 image_ptr[0] = &(images[2].at(0)); // B
286 image_ptr[1] = &(images[1].at(0)); // G
287 image_ptr[2] = &(images[0].at(0)); // R
288
289 image.images = (unsigned char **)image_ptr;
290 image.width = I.getWidth();
291 image.height = I.getHeight();
292
293 header.num_channels = 3;
294 header.channels = (EXRChannelInfo *)malloc(sizeof(EXRChannelInfo) * header.num_channels);
295 // Must be (A)BGR order, since most of EXR viewers expect this channel order.
296 strncpy(header.channels[0].name, "B", 255); header.channels[0].name[strlen("B")] = '\0';
297 strncpy(header.channels[1].name, "G", 255); header.channels[1].name[strlen("G")] = '\0';
298 strncpy(header.channels[2].name, "R", 255); header.channels[2].name[strlen("R")] = '\0';
299
300 header.pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
301 header.requested_pixel_types = (int *)malloc(sizeof(int) * header.num_channels);
302 header.compression_type = TINYEXR_COMPRESSIONTYPE_ZIP;
303 for (int i = 0; i < header.num_channels; ++i) {
304 header.pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of input image
305 header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; // pixel type of output image to be stored in .EXR
306 }
307
308 const char *err = nullptr; // or nullptr in C++11 or later.
309 int ret = SaveEXRImageToFile(&image, &header, filename.c_str(), &err);
310 if (ret != TINYEXR_SUCCESS) {
311 std::string err_msg(err);
312 FreeEXRErrorMessage(err); // free's buffer for an error message
313 free(header.channels);
314 free(header.requested_pixel_types);
315 free(header.pixel_types);
316 throw(vpImageException(vpImageException::ioError, "Error: Unable to save EXR image to %s : %s", filename.c_str(), err_msg.c_str()));
317 }
318
319 free(header.channels);
320 free(header.requested_pixel_types);
321 free(header.pixel_types);
322}
323
324END_VISP_NAMESPACE
325
326#endif
Error that can be emitted by the vpImage class and its derivatives.
@ ioError
Image io error.
Definition of the vpImage class member functions.
Definition vpImage.h:131