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