Visual Servoing Platform  version 3.6.1 under development (2024-05-18)
vpImageIoLibpng.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  * Libpng backend for PNG image I/O operations.
32  */
33 
39 #include "vpImageIoBackend.h"
40 #include <visp3/core/vpImageConvert.h>
41 
42 #if defined(VISP_HAVE_PNG)
43 #include <png.h>
44 #endif
45 
46 //--------------------------------------------------------------------------
47 // PNG
48 //--------------------------------------------------------------------------
49 
50 #if defined(VISP_HAVE_PNG)
51 
59 void writePNGLibpng(const vpImage<unsigned char> &I, const std::string &filename)
60 {
61  FILE *file;
62 
63  // Test the filename
64  if (filename.empty()) {
65  throw(vpImageException(vpImageException::ioError, "Cannot create PNG file: filename empty"));
66  }
67 
68  file = fopen(filename.c_str(), "wb");
69 
70  if (file == nullptr) {
71  throw(vpImageException(vpImageException::ioError, "Cannot create PNG file \"%s\"", filename.c_str()));
72  }
73 
74  /* create a png info struct */
75  png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
76  if (!png_ptr) {
77  fclose(file);
78  vpERROR_TRACE("Error during png_create_write_struct()\n");
79  throw(vpImageException(vpImageException::ioError, "PNG write error"));
80  }
81 
82  png_infop info_ptr = png_create_info_struct(png_ptr);
83  if (!info_ptr) {
84  fclose(file);
85  png_destroy_write_struct(&png_ptr, nullptr);
86  vpERROR_TRACE("Error during png_create_info_struct()\n");
87  throw(vpImageException(vpImageException::ioError, "PNG write error"));
88  }
89 
90  /* initialize the setjmp for returning properly after a libpng error occurred
91  */
92  if (setjmp(png_jmpbuf(png_ptr))) {
93  fclose(file);
94  png_destroy_write_struct(&png_ptr, &info_ptr);
95  vpERROR_TRACE("Error during init_io\n");
96  throw(vpImageException(vpImageException::ioError, "PNG write error"));
97  }
98 
99  /* setup libpng for using standard C fwrite() function with our FILE pointer
100  */
101  png_init_io(png_ptr, file);
102 
103  unsigned int width = I.getWidth();
104  unsigned int height = I.getHeight();
105  int bit_depth = 8;
106  int color_type = PNG_COLOR_TYPE_GRAY;
107  /* set some useful information from header */
108 
109  if (setjmp(png_jmpbuf(png_ptr))) {
110  fclose(file);
111  png_destroy_write_struct(&png_ptr, &info_ptr);
112  vpERROR_TRACE("Error during write header\n");
113  throw(vpImageException(vpImageException::ioError, "PNG write error"));
114  }
115 
116  png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
117  PNG_FILTER_TYPE_BASE);
118 
119  png_write_info(png_ptr, info_ptr);
120 
121  png_bytep *row_ptrs = new png_bytep[height];
122  for (unsigned int i = 0; i < height; ++i)
123  row_ptrs[i] = new png_byte[width];
124 
125  unsigned char *input = (unsigned char *)I.bitmap;
126 
127  for (unsigned int i = 0; i < height; ++i) {
128  png_byte *row = row_ptrs[i];
129  for (unsigned int j = 0; j < width; ++j) {
130  row[j] = *(input);
131  input++;
132  }
133  }
134 
135  png_write_image(png_ptr, row_ptrs);
136 
137  png_write_end(png_ptr, nullptr);
138 
139  for (unsigned int j = 0; j < height; ++j)
140  delete[] row_ptrs[j];
141 
142  delete[] row_ptrs;
143 
144  png_destroy_write_struct(&png_ptr, &info_ptr);
145 
146  fclose(file);
147 }
148 
156 void writePNGLibpng(const vpImage<vpRGBa> &I, const std::string &filename)
157 {
158  FILE *file;
159 
160  // Test the filename
161  if (filename.empty()) {
162  throw(vpImageException(vpImageException::ioError, "Cannot create PNG file: filename empty"));
163  }
164 
165  file = fopen(filename.c_str(), "wb");
166 
167  if (file == nullptr) {
168  throw(vpImageException(vpImageException::ioError, "Cannot create PNG file \"%s\"", filename.c_str()));
169  }
170 
171  /* create a png info struct */
172  png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
173  if (!png_ptr) {
174  fclose(file);
175  vpERROR_TRACE("Error during png_create_write_struct()\n");
176  throw(vpImageException(vpImageException::ioError, "PNG write error"));
177  }
178 
179  png_infop info_ptr = png_create_info_struct(png_ptr);
180  if (!info_ptr) {
181  fclose(file);
182  png_destroy_write_struct(&png_ptr, nullptr);
183  vpERROR_TRACE("Error during png_create_info_struct()\n");
184  throw(vpImageException(vpImageException::ioError, "PNG write error"));
185  }
186 
187  /* initialize the setjmp for returning properly after a libpng error occurred
188  */
189  if (setjmp(png_jmpbuf(png_ptr))) {
190  fclose(file);
191  png_destroy_write_struct(&png_ptr, &info_ptr);
192  vpERROR_TRACE("Error during init_io\n");
193  throw(vpImageException(vpImageException::ioError, "PNG write error"));
194  }
195 
196  /* setup libpng for using standard C fwrite() function with our FILE pointer
197  */
198  png_init_io(png_ptr, file);
199 
200  unsigned int width = I.getWidth();
201  unsigned int height = I.getHeight();
202  int bit_depth = 8;
203  int color_type = PNG_COLOR_TYPE_RGB;
204  /* set some useful information from header */
205 
206  if (setjmp(png_jmpbuf(png_ptr))) {
207  fclose(file);
208  png_destroy_write_struct(&png_ptr, &info_ptr);
209  vpERROR_TRACE("Error during write header\n");
210  throw(vpImageException(vpImageException::ioError, "PNG write error"));
211  }
212 
213  png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
214  PNG_FILTER_TYPE_BASE);
215 
216  png_write_info(png_ptr, info_ptr);
217 
218  png_bytep *row_ptrs = new png_bytep[height];
219  for (unsigned int i = 0; i < height; ++i)
220  row_ptrs[i] = new png_byte[3 * width];
221 
222  unsigned char *input = (unsigned char *)I.bitmap;
223 
224  for (unsigned int i = 0; i < height; ++i) {
225  png_byte *row = row_ptrs[i];
226  for (unsigned int j = 0; j < width; ++j) {
227  row[3 * j] = *(input);
228  input++;
229  row[3 * j + 1] = *(input);
230  input++;
231  row[3 * j + 2] = *(input);
232  input++;
233  input++;
234  }
235  }
236 
237  png_write_image(png_ptr, row_ptrs);
238 
239  png_write_end(png_ptr, nullptr);
240 
241  for (unsigned int j = 0; j < height; ++j)
242  delete[] row_ptrs[j];
243 
244  delete[] row_ptrs;
245 
246  png_destroy_write_struct(&png_ptr, &info_ptr);
247 
248  fclose(file);
249 }
250 
266 void readPNGLibpng(vpImage<unsigned char> &I, const std::string &filename)
267 {
268  FILE *file;
269  png_byte magic[8];
270  // Test the filename
271  if (filename.empty()) {
272  throw(vpImageException(vpImageException::ioError, "Cannot read PNG image: filename empty"));
273  }
274 
275  file = fopen(filename.c_str(), "rb");
276 
277  if (file == nullptr) {
278  throw(vpImageException(vpImageException::ioError, "Cannot read file \"%s\"", filename.c_str()));
279  }
280 
281  /* read magic number */
282  if (fread(magic, 1, sizeof(magic), file) != sizeof(magic)) {
283  fclose(file);
284  throw(vpImageException(vpImageException::ioError, "Cannot read magic number in file \"%s\"", filename.c_str()));
285  }
286 
287  /* check for valid magic number */
288  if (png_sig_cmp(magic, 0, sizeof(magic))) {
289  fclose(file);
290  throw(vpImageException(vpImageException::ioError, "Cannot read PNG file: \"%s\" is not a valid PNG image",
291  filename.c_str()));
292  }
293 
294  /* create a png read struct */
295  // printf("version %s\n", PNG_LIBPNG_VER_STRING);
296  png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
297  if (png_ptr == nullptr) {
298  fprintf(stderr, "error: can't create a png read structure!\n");
299  fclose(file);
300  throw(vpImageException(vpImageException::ioError, "error reading png file"));
301  }
302 
303  /* create a png info struct */
304  png_infop info_ptr = png_create_info_struct(png_ptr);
305  if (info_ptr == nullptr) {
306  fprintf(stderr, "error: can't create a png info structure!\n");
307  fclose(file);
308  png_destroy_read_struct(&png_ptr, nullptr, nullptr);
309  throw(vpImageException(vpImageException::ioError, "error reading png file"));
310  }
311 
312  /* initialize the setjmp for returning properly after a libpng error occurred
313  */
314  if (setjmp(png_jmpbuf(png_ptr))) {
315  fclose(file);
316  png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
317  vpERROR_TRACE("Error during init io\n");
318  throw(vpImageException(vpImageException::ioError, "PNG read error"));
319  }
320 
321  /* setup libpng for using standard C fread() function with our FILE pointer
322  */
323  png_init_io(png_ptr, file);
324 
325  /* tell libpng that we have already read the magic number */
326  png_set_sig_bytes(png_ptr, sizeof(magic));
327 
328  /* read png info */
329  png_read_info(png_ptr, info_ptr);
330 
331  unsigned int width = png_get_image_width(png_ptr, info_ptr);
332  unsigned int height = png_get_image_height(png_ptr, info_ptr);
333 
334  unsigned int bit_depth, channels, color_type;
335  /* get some useful information from header */
336  bit_depth = png_get_bit_depth(png_ptr, info_ptr);
337  channels = png_get_channels(png_ptr, info_ptr);
338  color_type = png_get_color_type(png_ptr, info_ptr);
339 
340  /* convert index color images to RGB images */
341  if (color_type == PNG_COLOR_TYPE_PALETTE)
342  png_set_palette_to_rgb(png_ptr);
343 
344  /* convert 1-2-4 bits grayscale images to 8 bits grayscale. */
345  if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
346  png_set_expand(png_ptr);
347 
348  // if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
349  // png_set_tRNS_to_alpha (png_ptr);
350 
351  if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
352  png_set_strip_alpha(png_ptr);
353 
354  if (bit_depth == 16)
355  png_set_strip_16(png_ptr);
356  else if (bit_depth < 8)
357  png_set_packing(png_ptr);
358 
359  /* update info structure to apply transformations */
360  png_read_update_info(png_ptr, info_ptr);
361 
362  channels = png_get_channels(png_ptr, info_ptr);
363 
364  if ((width != I.getWidth()) || (height != I.getHeight()))
365  I.resize(height, width);
366 
367  png_bytep *rowPtrs = new png_bytep[height];
368 
369  unsigned int stride = png_get_rowbytes(png_ptr, info_ptr);
370  unsigned char *data = new unsigned char[stride * height];
371 
372  for (unsigned int i = 0; i < height; ++i)
373  rowPtrs[i] = (png_bytep)data + (i * stride);
374 
375  png_read_image(png_ptr, rowPtrs);
376 
377  vpImage<vpRGBa> Ic(height, width);
378  unsigned char *output;
379 
380  switch (channels) {
381  case 1:
382  output = (unsigned char *)I.bitmap;
383  for (unsigned int i = 0; i < width * height; ++i) {
384  *(output++) = data[i];
385  }
386  break;
387 
388  case 2:
389  output = (unsigned char *)I.bitmap;
390  for (unsigned int i = 0; i < width * height; ++i) {
391  *(output++) = data[i * 2];
392  }
393  break;
394 
395  case 3:
396  output = (unsigned char *)Ic.bitmap;
397  for (unsigned int i = 0; i < width * height; ++i) {
398  *(output++) = data[i * 3];
399  *(output++) = data[i * 3 + 1];
400  *(output++) = data[i * 3 + 2];
401  *(output++) = vpRGBa::alpha_default;
402  }
404  break;
405 
406  case 4:
407  output = (unsigned char *)Ic.bitmap;
408  for (unsigned int i = 0; i < width * height; ++i) {
409  *(output++) = data[i * 4];
410  *(output++) = data[i * 4 + 1];
411  *(output++) = data[i * 4 + 2];
412  *(output++) = data[i * 4 + 3];
413  }
415  break;
416  }
417 
418  delete[](png_bytep) rowPtrs;
419  delete[] data;
420  png_read_end(png_ptr, nullptr);
421  png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
422  fclose(file);
423 }
424 
443 void readPNGLibpng(vpImage<vpRGBa> &I, const std::string &filename)
444 {
445  FILE *file;
446  png_byte magic[8];
447 
448  // Test the filename
449  if (filename.empty()) {
450  throw(vpImageException(vpImageException::ioError, "Cannot read PNG image: filename empty"));
451  }
452 
453  file = fopen(filename.c_str(), "rb");
454 
455  if (file == nullptr) {
456  throw(vpImageException(vpImageException::ioError, "Cannot read file \"%s\"", filename.c_str()));
457  }
458 
459  /* read magic number */
460  if (fread(magic, 1, sizeof(magic), file) != sizeof(magic)) {
461  fclose(file);
462  throw(vpImageException(vpImageException::ioError, "Cannot read magic number in file \"%s\"", filename.c_str()));
463  }
464 
465  /* check for valid magic number */
466  if (png_sig_cmp(magic, 0, sizeof(magic))) {
467  fclose(file);
468  throw(vpImageException(vpImageException::ioError, "Cannot read PNG file: \"%s\" is not a valid PNG image",
469  filename.c_str()));
470  }
471 
472  /* create a png read struct */
473  png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
474  if (!png_ptr) {
475  fclose(file);
476  vpERROR_TRACE("Error during png_create_read_struct()\n");
477  throw(vpImageException(vpImageException::ioError, "PNG read error"));
478  }
479 
480  /* create a png info struct */
481  png_infop info_ptr = png_create_info_struct(png_ptr);
482  if (!info_ptr) {
483  fclose(file);
484  png_destroy_read_struct(&png_ptr, nullptr, nullptr);
485  vpERROR_TRACE("Error during png_create_info_struct()\n");
486  throw(vpImageException(vpImageException::ioError, "PNG read error"));
487  }
488 
489  /* initialize the setjmp for returning properly after a libpng error occurred
490  */
491  if (setjmp(png_jmpbuf(png_ptr))) {
492  fclose(file);
493  png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
494  vpERROR_TRACE("Error during init io\n");
495  throw(vpImageException(vpImageException::ioError, "PNG read error"));
496  }
497 
498  /* setup libpng for using standard C fread() function with our FILE pointer
499  */
500  png_init_io(png_ptr, file);
501 
502  /* tell libpng that we have already read the magic number */
503  png_set_sig_bytes(png_ptr, sizeof(magic));
504 
505  /* read png info */
506  png_read_info(png_ptr, info_ptr);
507 
508  unsigned int width = png_get_image_width(png_ptr, info_ptr);
509  unsigned int height = png_get_image_height(png_ptr, info_ptr);
510 
511  unsigned int bit_depth, channels, color_type;
512  /* get some useful information from header */
513  bit_depth = png_get_bit_depth(png_ptr, info_ptr);
514  channels = png_get_channels(png_ptr, info_ptr);
515  color_type = png_get_color_type(png_ptr, info_ptr);
516 
517  /* convert index color images to RGB images */
518  if (color_type == PNG_COLOR_TYPE_PALETTE)
519  png_set_palette_to_rgb(png_ptr);
520 
521  /* convert 1-2-4 bits grayscale images to 8 bits grayscale. */
522  if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
523  png_set_expand(png_ptr);
524 
525  // if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
526  // png_set_tRNS_to_alpha (png_ptr);
527 
528  if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
529  png_set_strip_alpha(png_ptr);
530 
531  if (bit_depth == 16)
532  png_set_strip_16(png_ptr);
533  else if (bit_depth < 8)
534  png_set_packing(png_ptr);
535 
536  /* update info structure to apply transformations */
537  png_read_update_info(png_ptr, info_ptr);
538 
539  channels = png_get_channels(png_ptr, info_ptr);
540 
541  if ((width != I.getWidth()) || (height != I.getHeight()))
542  I.resize(height, width);
543 
544  png_bytep *rowPtrs = new png_bytep[height];
545 
546  unsigned int stride = png_get_rowbytes(png_ptr, info_ptr);
547  unsigned char *data = new unsigned char[stride * height];
548 
549  for (unsigned int i = 0; i < height; ++i)
550  rowPtrs[i] = (png_bytep)data + (i * stride);
551 
552  png_read_image(png_ptr, rowPtrs);
553 
554  vpImage<unsigned char> Ig(height, width);
555  unsigned char *output;
556 
557  switch (channels) {
558  case 1:
559  output = (unsigned char *)Ig.bitmap;
560  for (unsigned int i = 0; i < width * height; ++i) {
561  *(output++) = data[i];
562  }
564  break;
565 
566  case 2:
567  output = (unsigned char *)Ig.bitmap;
568  for (unsigned int i = 0; i < width * height; ++i) {
569  *(output++) = data[i * 2];
570  }
572  break;
573 
574  case 3:
575  output = (unsigned char *)I.bitmap;
576  for (unsigned int i = 0; i < width * height; ++i) {
577  *(output++) = data[i * 3];
578  *(output++) = data[i * 3 + 1];
579  *(output++) = data[i * 3 + 2];
580  *(output++) = vpRGBa::alpha_default;
581  }
582  break;
583 
584  case 4:
585  output = (unsigned char *)I.bitmap;
586  for (unsigned int i = 0; i < width * height; ++i) {
587  *(output++) = data[i * 4];
588  *(output++) = data[i * 4 + 1];
589  *(output++) = data[i * 4 + 2];
590  *(output++) = data[i * 4 + 3];
591  }
592  break;
593  }
594 
595  delete[](png_bytep) rowPtrs;
596  delete[] data;
597  png_read_end(png_ptr, nullptr);
598  png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
599  fclose(file);
600 }
601 #endif
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
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
Type * bitmap
points toward the bitmap
Definition: vpImage.h:139
unsigned int getHeight() const
Definition: vpImage.h:184
@ alpha_default
Definition: vpRGBa.h:63
#define vpERROR_TRACE
Definition: vpDebug.h:384