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