1 // This file is part of OpenMVG, an Open Multiple View Geometry C++ library.
2 
3 // Copyright (c) 2012, 2013 Pierre MOULON.
4 
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #include "openMVG/image/image_io.hpp"
10 
11 #include <cmath>
12 #include <string>
13 
14 extern "C" {
15   #include "png.h"
16   #include "tiffio.h"
17   #include "jpeglib.h"
18 }
19 
20 #include "openMVG/system/logger.hpp"
21 
22 namespace openMVG {
23 namespace image {
24 
25 template class Image<unsigned char>;
26 template class Image<float>;
27 template class Image<double>;
28 template class Image<RGBColor>;
29 template class Image<RGBAColor>;
30 
CmpFormatExt(const char * a,const char * b)31 inline bool CmpFormatExt(const char *a, const char *b) {
32   const size_t len_a = strlen(a);
33   const size_t len_b = strlen(b);
34   if (len_a != len_b) return false;
35   for (size_t i = 0; i < len_a; ++i)
36     if (tolower(a[i]) != tolower(b[i]))
37       return false;
38   return true;
39 }
40 
GetFormat(const char * c)41 Format GetFormat(const char *c) {
42   const char *p = strrchr (c, '.');
43 
44   if (!p)
45     return Unknown;
46 
47   if (CmpFormatExt(p, ".png")) return Png;
48   if (CmpFormatExt(p, ".ppm")) return Pnm;
49   if (CmpFormatExt(p, ".pgm")) return Pnm;
50   if (CmpFormatExt(p, ".pbm")) return Pnm;
51   if (CmpFormatExt(p, ".pnm")) return Pnm;
52   if (CmpFormatExt(p, ".jpg")) return Jpg;
53   if (CmpFormatExt(p, ".jpeg")) return Jpg;
54   if (CmpFormatExt(p, ".tif")) return Tiff;
55   if (CmpFormatExt(p, ".tiff")) return Tiff;
56 
57   return Unknown;
58 }
59 
ReadImage(const char * filename,std::vector<unsigned char> * ptr,int * w,int * h,int * depth)60 int ReadImage(const char *filename,
61               std::vector<unsigned char> * ptr,
62               int * w,
63               int * h,
64               int * depth){
65   const Format f = GetFormat(filename);
66 
67   switch (f) {
68     case Pnm:
69       return ReadPnm(filename, ptr, w, h, depth);
70     case Png:
71       return ReadPng(filename, ptr, w, h, depth);
72     case Jpg:
73       return ReadJpg(filename, ptr, w, h, depth);
74     case Tiff:
75       return ReadTiff(filename, ptr, w, h, depth);
76     default:
77       return 0;
78   };
79 }
80 
WriteImage(const char * filename,const std::vector<unsigned char> & ptr,int w,int h,int depth)81 int WriteImage(const char * filename,
82               const std::vector<unsigned char> & ptr,
83               int w,
84               int h,
85               int depth){
86   Format f = GetFormat(filename);
87 
88   switch (f) {
89     case Pnm:
90       return WritePnm(filename, ptr, w, h, depth);
91     case Png:
92       return WritePng(filename, ptr, w, h, depth);
93     case Jpg:
94       return WriteJpg(filename, ptr, w, h, depth);
95     case Tiff:
96       return WriteTiff(filename, ptr, w, h, depth);
97     default:
98       return 0;
99   };
100 }
101 
ReadJpg(const char * filename,std::vector<unsigned char> * ptr,int * w,int * h,int * depth)102 int ReadJpg(const char * filename,
103             std::vector<unsigned char> * ptr,
104             int * w,
105             int * h,
106             int * depth) {
107 
108   FILE *file = fopen(filename, "rb");
109   if (!file) {
110     OPENMVG_LOG_ERROR << "Couldn't open " << filename << " fopen returned 0";
111     return 0;
112   }
113   const int res = ReadJpgStream(file, ptr, w, h, depth);
114   fclose(file);
115   return res;
116 }
117 
118 struct my_error_mgr {
119   struct jpeg_error_mgr pub;
120   jmp_buf setjmp_buffer;
121 };
122 
123 METHODDEF(void)
jpeg_error(j_common_ptr cinfo)124 jpeg_error (j_common_ptr cinfo)
125 {
126   my_error_mgr *myerr = (my_error_mgr*) (cinfo->err);
127   (*cinfo->err->output_message) (cinfo);
128   longjmp(myerr->setjmp_buffer, 1);
129 }
130 
ReadJpgStream(FILE * file,std::vector<unsigned char> * ptr,int * w,int * h,int * depth)131 int ReadJpgStream(FILE * file,
132                   std::vector<unsigned char> * ptr,
133                   int * w,
134                   int * h,
135                   int * depth) {
136   jpeg_decompress_struct cinfo;
137   struct my_error_mgr jerr;
138   cinfo.err = jpeg_std_error(&jerr.pub);
139   jerr.pub.error_exit = &jpeg_error;
140 
141   if (setjmp(jerr.setjmp_buffer)) {
142     OPENMVG_LOG_ERROR << "Error JPG: Failed to decompress.";
143     jpeg_destroy_decompress(&cinfo);
144     return 0;
145   }
146 
147   jpeg_create_decompress(&cinfo);
148   jpeg_stdio_src(&cinfo, file);
149   jpeg_read_header(&cinfo, TRUE);
150   jpeg_start_decompress(&cinfo);
151 
152   const int row_stride = cinfo.output_width * cinfo.output_components;
153 
154   *h = cinfo.output_height;
155   *w = cinfo.output_width;
156   *depth = cinfo.output_components;
157   ptr->resize((*h)*(*w)*(*depth));
158 
159   unsigned char *ptrCpy = &(*ptr)[0];
160 
161   while (cinfo.output_scanline < cinfo.output_height) {
162     JSAMPROW scanline[1] = { ptrCpy };
163     jpeg_read_scanlines(&cinfo, scanline, 1);
164     ptrCpy += row_stride;
165   }
166 
167   jpeg_finish_decompress(&cinfo);
168   jpeg_destroy_decompress(&cinfo);
169   return 1;
170 }
171 
172 
WriteJpg(const char * filename,const std::vector<unsigned char> & array,int w,int h,int depth,int quality)173 int WriteJpg(const char * filename,
174              const std::vector<unsigned char> & array,
175              int w,
176              int h,
177              int depth,
178              int quality) {
179   FILE *file = fopen(filename, "wb");
180   if (!file) {
181     OPENMVG_LOG_ERROR << "Couldn't open " << filename << " fopen returned 0";
182     return 0;
183   }
184   int res = WriteJpgStream(file, array, w, h, depth, quality);
185   fclose(file);
186   return res;
187 }
188 
WriteJpgStream(FILE * file,const std::vector<unsigned char> & array,int w,int h,int depth,int quality)189 int WriteJpgStream(FILE *file,
190                    const std::vector<unsigned char> & array,
191                    int w,
192                    int h,
193                    int depth,
194                    int quality) {
195   if (quality < 0 || quality > 100)
196     OPENMVG_LOG_ERROR << "The quality parameter should be between 0 and 100";
197 
198   struct jpeg_compress_struct cinfo;
199   struct jpeg_error_mgr jerr;
200 
201   cinfo.err = jpeg_std_error(&jerr);
202   jpeg_create_compress(&cinfo);
203   jpeg_stdio_dest(&cinfo, file);
204 
205   cinfo.image_width = w;
206   cinfo.image_height = h;
207   cinfo.input_components = depth;
208 
209   if (cinfo.input_components==3) {
210     cinfo.in_color_space = JCS_RGB;
211   } else if (cinfo.input_components==1) {
212     cinfo.in_color_space = JCS_GRAYSCALE;
213   } else {
214     OPENMVG_LOG_ERROR << "Unsupported number of channels in file";
215     jpeg_destroy_compress(&cinfo);
216     return 0;
217   }
218 
219   jpeg_set_defaults(&cinfo);
220   jpeg_set_quality(&cinfo, quality, TRUE);
221   jpeg_start_compress(&cinfo, TRUE);
222 
223   const unsigned char *ptr = &array[0];
224   int row_bytes = cinfo.image_width*cinfo.input_components;
225 
226   JSAMPLE *row = new JSAMPLE[row_bytes];
227 
228   while (cinfo.next_scanline < cinfo.image_height) {
229     std::memcpy(&row[0], &ptr[0], row_bytes * sizeof(unsigned char));
230     jpeg_write_scanlines(&cinfo, &row, 1);
231     ptr += row_bytes;
232   }
233 
234   delete [] row;
235 
236   jpeg_finish_compress(&cinfo);
237   jpeg_destroy_compress(&cinfo);
238   return 1;
239 }
240 
ReadPng(const char * filename,std::vector<unsigned char> * ptr,int * w,int * h,int * depth)241 int ReadPng(const char *filename,
242             std::vector<unsigned char> * ptr,
243             int * w,
244             int * h,
245             int * depth) {
246   FILE *file = fopen(filename, "rb");
247   if (!file) {
248     OPENMVG_LOG_ERROR << "Couldn't open " << filename << " fopen returned 0";
249     return 0;
250   }
251   int res = ReadPngStream(file, ptr, w, h, depth);
252   fclose(file);
253   return res;
254 }
255 
ReadPngStream(FILE * file,std::vector<unsigned char> * ptr,int * w,int * h,int * depth)256 int ReadPngStream(FILE *file,
257                   std::vector<unsigned char> * ptr,
258                   int * w,
259                   int * h,
260                   int * depth)  {
261 
262   // first check the eight byte PNG signature
263   png_byte  pbSig[8];
264   size_t readcnt = fread(pbSig, 1, 8, file);
265   (void) readcnt;
266   if (png_sig_cmp(pbSig, 0, 8))
267   {
268     return 0;
269   }
270 
271   // create the two png(-info) structures
272   png_structp png_ptr = nullptr;
273   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
274     (png_error_ptr)nullptr, (png_error_ptr)nullptr);
275   if (!png_ptr)
276   {
277     return 0;
278   }
279   png_infop info_ptr = nullptr;
280   info_ptr = png_create_info_struct(png_ptr);
281   if (!info_ptr)
282   {
283     png_destroy_read_struct(&png_ptr, nullptr, nullptr);
284     return 0;
285   }
286 
287   // initialize the png structure
288   png_init_io(png_ptr, file);
289   png_set_sig_bytes(png_ptr, 8);
290 
291   // read all PNG info up to image data
292 
293   png_read_info(png_ptr, info_ptr);
294 
295   // get width, height, bit-depth and color-type
296   png_uint_32 wPNG, hPNG;
297   int                 iBitDepth;
298   int                 iColorType;
299   png_get_IHDR(png_ptr, info_ptr, &wPNG, &hPNG, &iBitDepth,
300     &iColorType, nullptr, nullptr, nullptr);
301 
302   // expand images of all color-type to 8-bit
303 
304   if (iColorType == PNG_COLOR_TYPE_PALETTE)
305     png_set_expand(png_ptr);
306   if (iBitDepth < 8)
307     png_set_expand(png_ptr);
308   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
309     png_set_expand(png_ptr);
310   if (iBitDepth == 16) // convert 16-bit to 8-bit on the fly
311     png_set_strip_16(png_ptr);
312 
313   double dGamma;
314   // if required set gamma conversion
315   if (png_get_gAMA(png_ptr, info_ptr, &dGamma))
316     png_set_gamma(png_ptr, (double) 2.2, dGamma);
317 
318   // after the transformations are registered, update info_ptr data
319 
320   png_read_update_info(png_ptr, info_ptr);
321 
322   // get again width, height and the new bit-depth and color-type
323 
324   png_get_IHDR(png_ptr, info_ptr, &wPNG, &hPNG, &iBitDepth,
325     &iColorType, nullptr, nullptr, nullptr);
326 
327   // Get number of byte along a tow
328   png_uint_32         ulRowBytes;
329   ulRowBytes = png_get_rowbytes(png_ptr, info_ptr);
330 
331   // and allocate memory for an array of row-pointers
332   png_byte   **ppbRowPointers = nullptr;
333   if ((ppbRowPointers = (png_bytepp) malloc(hPNG
334     * sizeof(png_bytep))) == nullptr)
335   {
336     OPENMVG_LOG_ERROR << "Cannot allocate the image memory buffer";
337     return 0;
338   }
339 
340   *w = wPNG;
341   *h = hPNG;
342   *depth = png_get_channels(png_ptr, info_ptr);
343 
344   // now we can allocate memory to store the image
345   ptr->resize((*h)*(*w)*(*depth));
346 
347   // set the individual row-pointers to point at the correct offsets
348   for (png_uint_32 i = 0; i < hPNG; i++)
349     ppbRowPointers[i] = &((*ptr)[0]) + i * ulRowBytes;
350 
351   // now we can go ahead and just read the whole image
352   png_read_image(png_ptr, ppbRowPointers);
353 
354   // read the additional chunks in the PNG file (not really needed)
355   png_read_end(png_ptr, nullptr);
356 
357   free (ppbRowPointers);
358 
359   png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
360   return 1;
361 }
362 
WritePng(const char * filename,const std::vector<unsigned char> & ptr,int w,int h,int depth)363 int WritePng(const char * filename,
364              const std::vector<unsigned char> & ptr,
365              int w,
366              int h,
367              int depth) {
368   FILE *file = fopen(filename, "wb");
369   if (!file) {
370     OPENMVG_LOG_ERROR << "Couldn't open " << filename << " fopen returned 0";
371     return 0;
372   }
373   const int res = WritePngStream(file, ptr, w, h, depth);
374   fclose(file);
375   return res;
376 }
377 
WritePngStream(FILE * file,const std::vector<unsigned char> & ptr,int w,int h,int depth)378 int WritePngStream(FILE * file,
379                    const std::vector<unsigned char> & ptr,
380                    int w,
381                    int h,
382                    int depth) {
383   png_structp png_ptr =
384       png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
385 
386   if (!png_ptr)
387     return 0;
388 
389   png_infop info_ptr = png_create_info_struct(png_ptr);
390   if (!info_ptr)
391     return 0;
392 
393   png_init_io(png_ptr, file);
394 
395   // color types are defined at png.h:841+.
396   char colour;
397   switch (depth)
398   {
399     case 4: colour = PNG_COLOR_TYPE_RGBA;
400       break;
401     case 3: colour = PNG_COLOR_TYPE_RGB;
402       break;
403     case 1: colour = PNG_COLOR_TYPE_GRAY;
404       break;
405     default:
406     {
407       png_destroy_write_struct(&png_ptr, &info_ptr);
408       return 0;
409     }
410   }
411 
412   png_set_IHDR(png_ptr, info_ptr, w, h,
413       8, colour, PNG_INTERLACE_NONE,
414       PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
415 
416   png_write_info(png_ptr, info_ptr);
417 
418   for (int y = 0; y < h; ++y)
419     png_write_row(png_ptr, (png_byte*) (&ptr[0]) + w * depth * y);
420 
421   png_write_end(png_ptr, nullptr);
422   png_destroy_write_struct(&png_ptr, &info_ptr);
423   return 1;
424 }
425 
ReadPnm(const char * filename,std::vector<unsigned char> * array,int * w,int * h,int * depth)426 int ReadPnm(const char * filename,
427             std::vector<unsigned char> * array,
428             int * w,
429             int * h,
430             int * depth)  {
431   FILE *file = fopen(filename, "rb");
432   if (!file) {
433     OPENMVG_LOG_ERROR << "Couldn't open " << filename << " fopen returned 0";
434     return 0;
435   }
436   const int res = ReadPnmStream(file, array, w, h, depth);
437   fclose(file);
438   return res;
439 }
440 
441 
442 // Comment handling as per the description provided at
443 //   http://netpbm.sourceforge.net/doc/pgm.html
444 // and http://netpbm.sourceforge.net/doc/pbm.html
ReadPnmStream(FILE * file,std::vector<unsigned char> * array,int * w,int * h,int * depth)445 int ReadPnmStream(FILE *file,
446                   std::vector<unsigned char> * array,
447                   int * w,
448                   int * h,
449                   int * depth) {
450 
451   const int NUM_VALUES = 3;
452   const int INT_BUFFER_SIZE = 256;
453 
454   int magicnumber;
455   char intBuffer[INT_BUFFER_SIZE];
456   int values[NUM_VALUES], valuesIndex = 0, intIndex = 0, inToken = 0;
457   size_t res;
458 
459   // Check magic number.
460   res = size_t(fscanf(file, "P%d", &magicnumber));
461   if (res != 1) {
462     return 0;
463   }
464   if (magicnumber == 5) {
465     *depth = 1;
466   } else if (magicnumber == 6) {
467     *depth = 3;
468   } else {
469     return 0;
470   }
471 
472   // the following loop parses the PNM header one character at a time, looking
473   // for the int tokens width, height and maxValues (in that order), and
474   // discarding all comment (everything from '#' to '\n' inclusive), where
475   // comments *may occur inside tokens*. Each token must be terminate with a
476   // whitespace character, and only one whitespace char is eaten after the
477   // third int token is parsed.
478   while (valuesIndex < NUM_VALUES) {
479     char nextChar;
480     res = fread(&nextChar,1,1,file);
481     if (res == 0) return 0; // read failed, EOF?
482 
483     if (isspace(nextChar)) {
484       if (inToken) { // we were reading a token, so this white space delimits it
485         inToken = 0;
486         intBuffer[intIndex] = 0; // nullptr-terminate the string
487         values[valuesIndex++] = atoi(intBuffer);
488         intIndex = 0; // reset for next int token
489         // to conform with current image class
490         if (valuesIndex == 3 && values[2] > 255) return 0;
491       }
492     }
493     else if (isdigit(nextChar)) {
494       inToken = 1; // in case it's not already set
495       intBuffer[intIndex++] = nextChar;
496       if (intIndex == INT_BUFFER_SIZE) // tokens should never be this long
497         return 0;
498     }
499     else if (nextChar == '#') {
500       do { // eat all characters from input stream until newline
501         res = fread(&nextChar,1,1,file);
502       } while (res == 1 && nextChar != '\n');
503       if (res == 0) return 0; // read failed, EOF?
504     }
505     else {
506       // Encountered a non-whitespace, non-digit outside a comment - bail out.
507       return 0;
508     }
509   }
510 
511   // Read pixels.
512   (*array).resize( values[1] * values[0] * (*depth));
513   *w = values[0];
514   *h = values[1];
515   res = fread( &(*array)[0], 1, array->size(), file);
516   if (res != array->size()) {
517     return 0;
518   }
519   return 1;
520 }
521 
WritePnm(const char * filename,const std::vector<unsigned char> & array,int w,int h,int depth)522 int WritePnm(const char * filename,
523               const std::vector<unsigned char> & array,
524               int w,
525               int h,
526               int depth) {
527   FILE *file = fopen(filename, "wb");
528   if (!file) {
529     OPENMVG_LOG_ERROR << "Couldn't open " << filename << " fopen returned 0";
530     return 0;
531   }
532   const int res = WritePnmStream(file, array, w, h, depth);
533   fclose(file);
534   return res;
535 }
536 
537 
WritePnmStream(FILE * file,const std::vector<unsigned char> & array,int w,int h,int depth)538 int WritePnmStream(FILE * file,
539                    const std::vector<unsigned char> & array,
540                    int w,
541                    int h,
542                    int depth) {
543   // Write magic number.
544   if (depth == 1) {
545     fprintf(file, "P5\n");
546   } else if (depth == 3) {
547     fprintf(file, "P6\n");
548   } else {
549     return 0;
550   }
551 
552   // Write sizes.
553   fprintf(file, "%d %d %d\n", w, h, 255);
554 
555   // Write pixels.
556   const size_t res = fwrite( &array[0], 1, static_cast<int>(array.size()), file);
557   if (res != array.size()) {
558     return 0;
559   }
560   return 1;
561 }
562 
ReadTiff(const char * filename,std::vector<unsigned char> * ptr,int * w,int * h,int * depth)563 int ReadTiff(const char * filename,
564   std::vector<unsigned char> * ptr,
565   int * w,
566   int * h,
567   int * depth)
568 {
569   TIFF* tiff = TIFFOpen(filename, "r");
570   if (!tiff) {
571     OPENMVG_LOG_ERROR << "Couldn't open " << filename << " fopen returned 0";
572     return 0;
573   }
574   uint16 bps, spp;
575 
576   TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, w);
577   TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, h);
578   TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bps);
579   TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &spp);
580   *depth = bps * spp / 8;
581 
582   ptr->resize((*h)*(*w)*(*depth));
583 
584   if (*depth==4) {
585     if (ptr != nullptr) {
586       if (!TIFFReadRGBAImageOriented(tiff, *w, *h, (uint32*)&((*ptr)[0]), ORIENTATION_TOPLEFT, 0)) {
587         TIFFClose(tiff);
588         return 0;
589       }
590     }
591   } else {
592     for (size_t i=0; i<TIFFNumberOfStrips(tiff); ++i) {
593       if (TIFFReadEncodedStrip(tiff, i, ((uint8*)&((*ptr)[0]))+i*TIFFStripSize(tiff),(tsize_t)-1) ==
594         std::numeric_limits<tsize_t>::max()) {
595         TIFFClose(tiff);
596         return 0;
597       }
598     }
599   }
600   TIFFClose(tiff);
601   return 1;
602 }
603 
WriteTiff(const char * filename,const std::vector<unsigned char> & ptr,int w,int h,int depth)604 int WriteTiff(const char * filename,
605   const std::vector<unsigned char> & ptr,
606   int w,
607   int h,
608   int depth)
609 {
610   TIFF* tiff = TIFFOpen(filename, "w");
611   if (!tiff) {
612     OPENMVG_LOG_ERROR << "Couldn't open " << filename << " fopen returned 0";
613     return 0;
614   }
615   TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, w);
616   TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, h);
617   TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
618   TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
619   TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8);
620   TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, depth);
621   TIFFSetField(tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
622   TIFFSetField(tiff, TIFFTAG_COMPRESSION,COMPRESSION_NONE);
623   TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, 16);
624   for (uint32 y=0; y < static_cast<uint32>(h); ++y) {
625     if (TIFFWriteScanline(tiff,(tdata_t)(((uint8*)(&ptr[0]))+depth*w*y),y)<0) {
626       TIFFClose(tiff);
627       return 0;
628     }
629   }
630   TIFFClose(tiff);
631   return 1;
632 }
633 
ReadImageHeader(const char * filename,ImageHeader * imgheader)634 bool ReadImageHeader(const char * filename, ImageHeader * imgheader)
635 {
636   const Format f = GetFormat(filename);
637 
638   switch (f) {
639     case Pnm:
640       return Read_PNM_ImageHeader(filename, imgheader);
641     case Png:
642       return Read_PNG_ImageHeader(filename, imgheader);
643     case Jpg:
644       return Read_JPG_ImageHeader(filename, imgheader);
645     case Tiff:
646       return Read_TIFF_ImageHeader(filename, imgheader);
647     default:
648       OPENMVG_LOG_ERROR << "Unsupported image format header";
649       return false;
650   };
651 }
652 
Read_PNG_ImageHeader(const char * filename,ImageHeader * imgheader)653 bool Read_PNG_ImageHeader(const char * filename, ImageHeader * imgheader)
654 {
655   bool bStatus = false;
656   FILE *file = fopen(filename, "rb");
657   if (!file) {
658     OPENMVG_LOG_ERROR << "Cannot open the PNG file: " << filename << ".";
659     return false;
660   }
661 
662   // first check the eight byte PNG signature
663   png_byte  pbSig[8];
664   size_t readcnt = fread(pbSig, 1, 8, file);
665   (void) readcnt;
666   if (png_sig_cmp(pbSig, 0, 8)) {
667     fclose(file);
668     return false;
669   }
670 
671   // create the two png(-info) structures
672   png_structp png_ptr = nullptr;
673   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
674     (png_error_ptr)nullptr, (png_error_ptr)nullptr);
675   if (!png_ptr) {
676     fclose(file);
677     return false;
678   }
679   png_infop info_ptr = nullptr;
680   info_ptr = png_create_info_struct(png_ptr);
681   if (!info_ptr)  {
682     png_destroy_read_struct(&png_ptr, nullptr, nullptr);
683     fclose(file);
684     return false;
685   }
686 
687   // initialize the png structure
688   png_init_io(png_ptr, file);
689   png_set_sig_bytes(png_ptr, 8);
690 
691   // read all PNG info up to image data
692 
693   png_read_info(png_ptr, info_ptr);
694 
695   // get width, height, bit-depth and color-type
696   png_uint_32 wPNG, hPNG;
697   int iBitDepth;
698   int iColorType;
699   png_get_IHDR(png_ptr, info_ptr, &wPNG, &hPNG, &iBitDepth,
700     &iColorType, nullptr, nullptr, nullptr);
701 
702   if (imgheader)
703   {
704     imgheader->width = wPNG;
705     imgheader->height = hPNG;
706     bStatus = true;
707   }
708 
709   fclose(file);
710   return bStatus;
711 }
712 
Read_JPG_ImageHeader(const char * filename,ImageHeader * imgheader)713 bool Read_JPG_ImageHeader(const char * filename, ImageHeader * imgheader)
714 {
715   bool bStatus = false;
716 
717   FILE *file = fopen(filename, "rb");
718   if (!file) {
719     OPENMVG_LOG_ERROR << "Cannot open the JPG file: " << filename << ".";
720     return 0;
721   }
722 
723   jpeg_decompress_struct cinfo;
724   struct my_error_mgr jerr;
725   cinfo.err = jpeg_std_error(&jerr.pub);
726   jerr.pub.error_exit = &jpeg_error;
727 
728   if (setjmp(jerr.setjmp_buffer)) {
729     jpeg_destroy_decompress(&cinfo);
730     fclose(file);
731     return false;
732   }
733 
734   jpeg_create_decompress(&cinfo);
735   jpeg_stdio_src(&cinfo, file);
736   jpeg_read_header(&cinfo, TRUE);
737   jpeg_start_decompress(&cinfo);
738 
739   if (imgheader)
740   {
741     imgheader->width = cinfo.output_width;
742     imgheader->height = cinfo.output_height;
743     bStatus = true;
744   }
745 
746   jpeg_destroy_decompress(&cinfo);
747   fclose(file);
748   return bStatus;
749 }
750 
Read_PNM_ImageHeader(const char * filename,ImageHeader * imgheader)751 bool Read_PNM_ImageHeader(const char * filename, ImageHeader * imgheader)
752 {
753   const int NUM_VALUES = 3;
754   const int INT_BUFFER_SIZE = 256;
755 
756   int magicnumber;
757   char intBuffer[INT_BUFFER_SIZE];
758   int values[NUM_VALUES], valuesIndex = 0, intIndex = 0, inToken = 0;
759   size_t res;
760 
761   FILE *file = fopen(filename, "rb");
762   if (!file) {
763     OPENMVG_LOG_ERROR << "Cannot open the PNM file: " << filename << ".";
764     return false;
765   }
766 
767   // Check magic number.
768   res = size_t(fscanf(file, "P%d", &magicnumber));
769   if (res != 1) {
770     fclose(file);
771     return false;
772   }
773   // Test if we have a Gray or RGB image, else return false
774   switch (magicnumber)
775   {
776     case 5:
777     case 6:
778       break;
779     default:
780       fclose(file);
781       return false;
782   }
783 
784   // the following loop parses the PNM header one character at a time, looking
785   // for the int tokens width, height and maxValues (in that order), and
786   // discarding all comment (everything from '#' to '\n' inclusive), where
787   // comments *may occur inside tokens*. Each token must be terminate with a
788   // whitespace character, and only one whitespace char is eaten after the
789   // third int token is parsed.
790   while (valuesIndex < NUM_VALUES) {
791     char nextChar;
792     res = fread(&nextChar,1,1,file);
793     if (res == 0)
794     {
795       fclose(file);
796       return false; // read failed, EOF?
797     }
798 
799     if (isspace(nextChar)) {
800       if (inToken) { // we were reading a token, so this white space delimits it
801         inToken = 0;
802         intBuffer[intIndex] = 0; // nullptr-terminate the string
803         values[valuesIndex++] = atoi(intBuffer);
804         intIndex = 0; // reset for next int token
805         // to conform with current image class
806         if (valuesIndex == 3 && values[2] > 255)  {
807           fclose(file);
808           return false;
809         }
810       }
811     }
812     else if (isdigit(nextChar)) {
813       inToken = 1; // in case it's not already set
814       intBuffer[intIndex++] = nextChar;
815       if (intIndex == INT_BUFFER_SIZE) {// tokens should never be this long
816         fclose(file);
817         return false;
818       }
819     }
820     else if (nextChar == '#') {
821       do { // eat all characters from input stream until newline
822         res = fread(&nextChar,1,1,file);
823       } while (res == 1 && nextChar != '\n');
824       if (res == 0) {
825         fclose(file);
826         return false; // read failed, EOF?
827       }
828     }
829     else {
830       // Encountered a non-whitespace, non-digit outside a comment - bail out.
831       fclose(file);
832       return false;
833     }
834   }
835   fclose(file);
836   if (imgheader)
837   {
838     // Save value and return
839     imgheader->width = values[0];
840     imgheader->height = values[1];
841     return true;
842   }
843   return false;
844 }
845 
Read_TIFF_ImageHeader(const char * filename,ImageHeader * imgheader)846 bool Read_TIFF_ImageHeader(const char * filename, ImageHeader * imgheader)
847 {
848   bool bStatus = false;
849 
850   TIFF* tiff = TIFFOpen(filename, "r");
851   if (!tiff) {
852     OPENMVG_LOG_ERROR << "Cannot open TIFF image: " << filename << ".";
853     return false;
854   }
855 
856   if (imgheader)
857   {
858     TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &imgheader->width);
859     TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &imgheader->height);
860     bStatus = true;
861   }
862 
863   TIFFClose(tiff);
864   return bStatus;
865 }
866 
867 }  // namespace image
868 }  // namespace openMVG
869