diff --git a/README.md b/README.md index a1e19d1..a8e4fec 100644 --- a/README.md +++ b/README.md @@ -37,3 +37,4 @@ int main(int argc, const char** argv) { return 0; } ``` +See `main.cpp` for more details. diff --git a/TinyEXIF.cpp b/TinyEXIF.cpp index 79596fb..d6f1af5 100644 --- a/TinyEXIF.cpp +++ b/TinyEXIF.cpp @@ -33,10 +33,11 @@ #include "TinyEXIF.h" #include "tinyxml2.h" -#include #include -#include +#include +#include #include +#include #ifdef _MSC_VER #include @@ -130,19 +131,19 @@ private: public: EntryParser(const uint8_t* _buf, unsigned _len, unsigned _tiff_header_start, bool _alignIntel) - : buf(_buf), tiff_header_start(_tiff_header_start), len(_len), alignIntel(_alignIntel), offs(0) {} + : buf(_buf), len(_len), tiff_header_start(_tiff_header_start), alignIntel(_alignIntel), offs(0) {} void Init(unsigned _offs) { offs = _offs - 12; } - uint16_t ParseTag() { + void ParseTag() { offs += 12; tag = parse16(buf + offs, alignIntel); format = parse16(buf + offs + 2, alignIntel); length = parse32(buf + offs + 4, alignIntel); - return tag; } + uint16_t GetTag() const { return tag; } uint32_t GetLength() const { return length; } uint32_t GetData() const { return parse32(buf + offs + 8, alignIntel); } @@ -153,19 +154,19 @@ public: bool IsRational() const { return format == 5 || format == 10; } bool Fetch(std::string& val) const { - if (format != 2) + if (format != 2 || length == 0) return false; val = parseEXIFString(buf, length, GetData(), tiff_header_start, len, alignIntel); return true; } bool Fetch(uint8_t& val) const { - if (format != 1 && format != 2 && format != 6) + if ((format != 1 && format != 2 && format != 6) || length == 0) return false; val = parse8(buf + offs + 8); return true; } bool Fetch(uint16_t& val) const { - if (!IsShort()) + if (!IsShort() || length == 0) return false; val = parse16(buf + offs + 8, alignIntel); return true; @@ -177,13 +178,13 @@ public: return true; } bool Fetch(uint32_t& val) const { - if (!IsLong()) + if (!IsLong() || length == 0) return false; val = parse32(buf + offs + 8, alignIntel); return true; } bool Fetch(double& val) const { - if (!IsRational()) + if (!IsRational() || length == 0) return false; val = parseEXIFRational(buf + GetSubIFD(), alignIntel); return true; @@ -241,16 +242,356 @@ public: } else if (base+data+num_components <= len) { const char* const sz((const char*)buf+base+data); - if (sz[--num_components] == '\0') - while (num_components && sz[num_components-1] == '\0') - --num_components; - value.assign(sz, num_components); + unsigned num(0); + while (num < num_components && sz[num] != '\0') + ++num; + value.assign(sz, num); } return value; } }; +// Parse tag as Image IFD +void EXIFInfo::parseIFDImage(EntryParser& parser, unsigned& exif_sub_ifd_offset, unsigned& gps_sub_ifd_offset) { + switch (parser.GetTag()) { + case 0x0102: + // Bits per sample + parser.Fetch(BitsPerSample); + break; + + case 0x010e: + // Image description + parser.Fetch(ImageDescription); + break; + + case 0x010f: + // Camera maker + parser.Fetch(Make); + break; + + case 0x0110: + // Camera model + parser.Fetch(Model); + break; + + case 0x0112: + // Orientation of image + parser.Fetch(Orientation); + break; + + case 0x011a: + // XResolution + parser.Fetch(XResolution); + break; + + case 0x011b: + // YResolution + parser.Fetch(YResolution); + break; + + case 0x0128: + // Resolution Unit + parser.Fetch(ResolutionUnit); + break; + + case 0x0131: + // Software used for image + parser.Fetch(Software); + break; + + case 0x0132: + // EXIF/TIFF date/time of image modification + parser.Fetch(DateTime); + break; + + case 0x1001: + // Original Image width + if (!parser.Fetch(RelatedImageWidth)) { + uint16_t _RelatedImageWidth; + if (parser.Fetch(_RelatedImageWidth)) + RelatedImageWidth = _RelatedImageWidth; + } + break; + + case 0x1002: + // Original Image height + if (!parser.Fetch(RelatedImageHeight)) { + uint16_t _RelatedImageHeight; + if (parser.Fetch(_RelatedImageHeight)) + RelatedImageHeight = _RelatedImageHeight; + } + break; + + case 0x8298: + // Copyright information + parser.Fetch(Copyright); + break; + + case 0x8769: + // EXIF SubIFD offset + exif_sub_ifd_offset = parser.GetSubIFD(); + break; + + case 0x8825: + // GPS IFS offset + gps_sub_ifd_offset = parser.GetSubIFD(); + break; + + default: + // Try to parse as EXIF tag, as some images store them in here + parseIFDExif(parser); + break; + } +} + +// Parse tag as Exif IFD +void EXIFInfo::parseIFDExif(EntryParser& parser) { + switch (parser.GetTag()) { + case 0x829a: + // Exposure time in seconds + parser.Fetch(ExposureTime); + break; + + case 0x829d: + // FNumber + parser.Fetch(FNumber); + break; + + case 0x8822: + // Exposure Program + parser.Fetch(ExposureProgram); + break; + + case 0x8827: + // ISO Speed Rating + parser.Fetch(ISOSpeedRatings); + break; + + case 0x9003: + // Original date and time + parser.Fetch(DateTimeOriginal); + break; + + case 0x9004: + // Digitization date and time + parser.Fetch(DateTimeDigitized); + break; + + case 0x9201: + // Shutter speed value + parser.Fetch(ShutterSpeedValue); + ShutterSpeedValue = 1.0/exp(ShutterSpeedValue*log(2)); + break; + + case 0x9202: + // Aperture value + parser.Fetch(ApertureValue); + ApertureValue = exp(ApertureValue*log(2)*0.5); + break; + + case 0x9203: + // Brightness value + parser.Fetch(BrightnessValue); + break; + + case 0x9204: + // Exposure bias value + parser.Fetch(ExposureBiasValue); + break; + + case 0x9206: + // Subject distance + parser.Fetch(SubjectDistance); + break; + + case 0x9207: + // Metering mode + parser.Fetch(MeteringMode); + break; + + case 0x9208: + // Light source + parser.Fetch(LightSource); + break; + + case 0x9209: + // Flash info + parser.Fetch(Flash); + break; + + case 0x920a: + // Focal length + parser.Fetch(FocalLength); + break; + + case 0x9214: + // Subject area + if (parser.IsShort() && parser.GetLength() > 1) { + SubjectArea.resize(parser.GetLength()); + for (uint32_t i=0; i len) return PARSE_EXIF_ERROR_CORRUPT; - const unsigned tiff_header_start = offs; + bool alignIntel; if (buf[offs] == 'I' && buf[offs+1] == 'I') - ByteAlign = 1; - else { - if (buf[offs] == 'M' && buf[offs+1] == 'M') - ByteAlign = 0; - else - return PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN; - } + alignIntel = true; // 1: Intel byte alignment + else + if (buf[offs] == 'M' && buf[offs+1] == 'M') + alignIntel = false; // 0: Motorola byte alignment + else + return PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN; + EntryParser parser(buf, len, offs, alignIntel); offs += 2; - if (0x2a != EntryParser::parse16(buf + offs, alignIntel())) + if (0x2a != EntryParser::parse16(buf + offs, alignIntel)) return PARSE_EXIF_ERROR_CORRUPT; offs += 2; - const unsigned first_ifd_offset = EntryParser::parse32(buf + offs, alignIntel()); + const unsigned first_ifd_offset = EntryParser::parse32(buf + offs, alignIntel); offs += first_ifd_offset - 4; if (offs >= len) return PARSE_EXIF_ERROR_CORRUPT; @@ -411,107 +752,15 @@ int EXIFInfo::parseFromEXIFSegment(const uint8_t* buf, unsigned len) { // bytes of data. if (offs + 2 > len) return PARSE_EXIF_ERROR_CORRUPT; - int num_entries = EntryParser::parse16(buf + offs, alignIntel()); + int num_entries = EntryParser::parse16(buf + offs, alignIntel); if (offs + 6 + 12 * num_entries > len) return PARSE_EXIF_ERROR_CORRUPT; unsigned exif_sub_ifd_offset = len; unsigned gps_sub_ifd_offset = len; - EntryParser parser(buf, len, tiff_header_start, alignIntel()); parser.Init(offs+2); while (--num_entries >= 0) { - switch (parser.ParseTag()) { - case 0x0102: - // Bits per sample - parser.Fetch(BitsPerSample); - break; - - case 0x010e: - // Image description - parser.Fetch(ImageDescription); - break; - - case 0x010f: - // Camera maker - parser.Fetch(Make); - break; - - case 0x0110: - // Camera model - parser.Fetch(Model); - break; - - case 0x0112: - // Orientation of image - parser.Fetch(Orientation); - break; - - case 0x011a: - // XResolution - parser.Fetch(XResolution); - break; - - case 0x011b: - // YResolution - parser.Fetch(YResolution); - break; - - case 0x0128: - // Resolution Unit - parser.Fetch(ResolutionUnit); - break; - - case 0x0131: - // Software used for image - parser.Fetch(Software); - break; - - case 0x0132: - // EXIF/TIFF date/time of image modification - parser.Fetch(DateTime); - break; - - case 0x1001: - // Original Image width - if (!parser.Fetch(RelatedImageWidth)) { - uint16_t _RelatedImageWidth; - if (parser.Fetch(_RelatedImageWidth)) - RelatedImageWidth = _RelatedImageWidth; - } - break; - - case 0x1002: - // Original Image height - if (!parser.Fetch(RelatedImageHeight)) { - uint16_t _RelatedImageHeight; - if (parser.Fetch(_RelatedImageHeight)) - RelatedImageHeight = _RelatedImageHeight; - } - break; - - case 0x8298: - // Copyright information - parser.Fetch(Copyright); - break; - - case 0x8769: - // EXIF SubIFD offset - exif_sub_ifd_offset = parser.GetSubIFD(); - break; - - case 0x8825: - // GPS IFS offset - gps_sub_ifd_offset = parser.GetSubIFD(); - break; - - case 0xa405: - // Focal length in 35mm film - if (!parser.Fetch(LensInfo.FocalLengthIn35mm)) { - uint16_t _FocalLengthIn35mm; - if (parser.Fetch(_FocalLengthIn35mm)) - LensInfo.FocalLengthIn35mm = (double)_FocalLengthIn35mm; - } - break; - } + parser.ParseTag(); + parseIFDImage(parser, exif_sub_ifd_offset, gps_sub_ifd_offset); } // Jump to the EXIF SubIFD if it exists and parse all the information @@ -520,161 +769,13 @@ int EXIFInfo::parseFromEXIFSegment(const uint8_t* buf, unsigned len) { // typical user might want. if (exif_sub_ifd_offset + 4 <= len) { offs = exif_sub_ifd_offset; - int num_entries = EntryParser::parse16(buf + offs, alignIntel()); + int num_entries = EntryParser::parse16(buf + offs, alignIntel); if (offs + 6 + 12 * num_entries > len) return PARSE_EXIF_ERROR_CORRUPT; parser.Init(offs+2); while (--num_entries >= 0) { - switch (parser.ParseTag()) { - case 0x829a: - // Exposure time in seconds - parser.Fetch(ExposureTime); - break; - - case 0x829d: - // FNumber - parser.Fetch(FNumber); - break; - - case 0x8822: - // Exposure Program - parser.Fetch(ExposureProgram); - break; - - case 0x8827: - // ISO Speed Rating - parser.Fetch(ISOSpeedRatings); - break; - - case 0x9003: - // Original date and time - parser.Fetch(DateTimeOriginal); - break; - - case 0x9004: - // Digitization date and time - parser.Fetch(DateTimeDigitized); - break; - - case 0x9201: - // Shutter speed value - parser.Fetch(ShutterSpeedValue); - break; - - case 0x9202: - // Aperture value - parser.Fetch(ApertureValue); - break; - - case 0x9203: - // Brightness value - parser.Fetch(BrightnessValue); - break; - - case 0x9204: - // Exposure bias value - parser.Fetch(ExposureBiasValue); - break; - - case 0x9206: - // Subject distance - parser.Fetch(SubjectDistance); - break; - - case 0x9207: - // Metering mode - parser.Fetch(MeteringMode); - break; - - case 0x9208: - // Light source - parser.Fetch(LightSource); - break; - - case 0x9209: - // Flash info - parser.Fetch(Flash); - break; - - case 0x920a: - // Focal length - parser.Fetch(FocalLength); - break; - - case 0x9214: - // Subject area - if (parser.IsShort() && parser.GetLength() > 1) { - SubjectArea.resize(parser.GetLength()); - for (uint32_t i=0; i len) return PARSE_EXIF_ERROR_CORRUPT; parser.Init(offs+2); while (--num_entries >= 0) { - switch (parser.ParseTag()) { - case 1: - // GPS north or south - parser.Fetch(GeoLocation.LatComponents.direction); - break; - - case 2: - // GPS latitude - if (parser.IsRational() && parser.GetLength() == 3) { - parser.Fetch(GeoLocation.LatComponents.degrees, 0); - parser.Fetch(GeoLocation.LatComponents.minutes, 1); - parser.Fetch(GeoLocation.LatComponents.seconds, 2); - } - break; - - case 3: - // GPS east or west - parser.Fetch(GeoLocation.LonComponents.direction); - break; - - case 4: - // GPS longitude - if (parser.IsRational() && parser.GetLength() == 3) { - parser.Fetch(GeoLocation.LonComponents.degrees, 0); - parser.Fetch(GeoLocation.LonComponents.minutes, 1); - parser.Fetch(GeoLocation.LonComponents.seconds, 2); - } - break; - - case 5: - // GPS altitude reference (below or above sea level) - parser.Fetch((uint8_t&)GeoLocation.AltitudeRef); - break; - - case 6: - // GPS altitude - parser.Fetch(GeoLocation.Altitude); - break; - - case 7: - // GPS timestamp - if (parser.IsRational() && parser.GetLength() == 3) { - double h,m,s; - parser.Fetch(h, 0); - parser.Fetch(m, 1); - parser.Fetch(s, 2); - char buffer[256]; - snprintf(buffer, 256, "%g %g %g", h, m, s); - GeoLocation.GPSTimeStamp = buffer; - } - break; - - case 11: - // Indicates the GPS DOP (data degree of precision) - parser.Fetch(GeoLocation.GPSDOP); - break; - - case 18: - // GPS geodetic survey data - parser.Fetch(GeoLocation.GPSMapDatum); - break; - - case 29: - // GPS date-stamp - parser.Fetch(GeoLocation.GPSDateStamp); - break; - - case 30: - // GPS differential indicates whether differential correction is applied to the GPS receiver - parser.Fetch(GeoLocation.GPSDifferential); - break; - } + parser.ParseTag(); + parseIFDGPS(parser); } GeoLocation.parseCoords(); } @@ -901,7 +932,6 @@ void EXIFInfo::clear() { Copyright = ""; // Shorts / unsigned / double - ByteAlign = 0; ImageWidth = 0; ImageHeight = 0; RelatedImageWidth = 0; @@ -929,6 +959,7 @@ void EXIFInfo::clear() { LensInfo.FocalLengthMin = 0; LensInfo.FStopMax = 0; LensInfo.FStopMin = 0; + LensInfo.DigitalZoomRatio = 0; LensInfo.FocalLengthIn35mm = 0; LensInfo.FocalPlaneXResolution = 0; LensInfo.FocalPlaneYResolution = 0; diff --git a/TinyEXIF.h b/TinyEXIF.h index 23784a1..5b93ef7 100644 --- a/TinyEXIF.h +++ b/TinyEXIF.h @@ -62,6 +62,8 @@ enum ErrorCode { PARSE_EXIF_ERROR_CORRUPT = 5, // EXIF header was found, but data was corrupted }; +class EntryParser; + // // Class responsible for storing and parsing EXIF information from a JPEG blob // @@ -91,11 +93,16 @@ public: // Set all data members to default values. void clear(); - // Return true if the memory alignment is Intel format. - bool alignIntel() const { return this->ByteAlign != 0; } +private: + // Parse tag as Image IFD. + void parseIFDImage(EntryParser&, unsigned&, unsigned&); + // Parse tag as Exif IFD. + void parseIFDExif(EntryParser&); + // Parse tag as GPS IFD. + void parseIFDGPS(EntryParser&); - // Data fields filled out by parseFrom() - uint8_t ByteAlign; // 0: Motorola byte alignment, 1: Intel +public: + // Data fields uint32_t ImageWidth; // Image width reported in EXIF data uint32_t ImageHeight; // Image height reported in EXIF data uint32_t RelatedImageWidth; // Original image width reported in EXIF data @@ -202,10 +209,11 @@ public: double FStopMax; // Max aperture (f-stop) double FocalLengthMin; // Min focal length (mm) double FocalLengthMax; // Max focal length (mm) + double DigitalZoomRatio; // Digital zoom ratio when the image was shot double FocalLengthIn35mm; // Focal length in 35mm film - double FocalPlaneXResolution; // Indicates the number of pixels in the image width (X) direction per FocalPlaneResolutionUnit on the camera focal plane (may not exist) - double FocalPlaneYResolution; // Indicates the number of pixels in the image width (Y) direction per FocalPlaneResolutionUnit on the camera focal plane (may not exist) - uint16_t FocalPlaneResolutionUnit;// Indicates the unit for measuring FocalPlaneXResolution and FocalPlaneYResolution (may not exist) + double FocalPlaneXResolution; // Number of pixels in the image width (X) direction per FocalPlaneResolutionUnit on the camera focal plane (may not exist) + double FocalPlaneYResolution; // Number of pixels in the image width (Y) direction per FocalPlaneResolutionUnit on the camera focal plane (may not exist) + uint16_t FocalPlaneResolutionUnit;// Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution (may not exist) // 0: unspecified in EXIF data // 1: no absolute unit of measurement // 2: inch @@ -222,12 +230,12 @@ public: double RollDegree; // Flight roll in degrees double PitchDegree; // Flight pitch in degrees double YawDegree; // Flight yaw in degrees - double GPSDOP; // Indicates the GPS DOP (data degree of precision) - uint16_t GPSDifferential; // Indicates whether differential correction is applied to the GPS receiver (may not exist) + double GPSDOP; // GPS DOP (data degree of precision) + uint16_t GPSDifferential; // Differential correction applied to the GPS receiver (may not exist) // 0: measurement without differential correction // 1: differential correction applied - std::string GPSMapDatum; // Indicates the geodetic survey data (may not exist) - std::string GPSTimeStamp; // Indicates the time as UTC (Coordinated Universal Time) (may not exist) + std::string GPSMapDatum; // Geodetic survey data (may not exist) + std::string GPSTimeStamp; // Time as UTC (Coordinated Universal Time) (may not exist) std::string GPSDateStamp; // A character string recording date and time information relative to UTC (Coordinated Universal Time) YYYY:MM:DD (may not exist) struct Coord_t { double degrees; diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..199b180 --- /dev/null +++ b/main.cpp @@ -0,0 +1,114 @@ +// Defines the entry point for the console application. + +#ifdef _MSC_VER +#include +#endif +#include "TinyEXIF.h" +#include // std::cout +#include // std::ifstream +#include // std::vector +#include // std::setprecision + +int main(int argc, const char** argv) +{ + if (argc != 2) { + std::cout << "Usage: TinyEXIF " << "\n"; + return -1; + } + + // read entire image file + std::ifstream file(argv[1], std::ifstream::in|std::ifstream::binary); + if (!file.is_open()) { + std::cout << "error: can not open '" << argv[1] << "'\n"; + return -2; + } + file.seekg(0,std::ios::end); + std::streampos length = file.tellg(); + file.seekg(0,std::ios::beg); + std::vector data(length); + file.read((char*)data.data(), length); + + // parse image EXIF and XMP metadata + TinyEXIF::EXIFInfo imageEXIF; + if (imageEXIF.parseFrom(data.data(), (unsigned)length) == TinyEXIF::PARSE_EXIF_SUCCESS) { + if (imageEXIF.ImageWidth || imageEXIF.ImageHeight) + std::cout << "ImageResolution " << imageEXIF.ImageWidth << "x" << imageEXIF.ImageHeight << " pixels" << "\n"; + if (imageEXIF.RelatedImageWidth || imageEXIF.RelatedImageHeight) + std::cout << "RelatedImageResolution " << imageEXIF.RelatedImageWidth << "x" << imageEXIF.RelatedImageHeight << " pixels" << "\n"; + if (!imageEXIF.ImageDescription.empty()) + std::cout << "Description " << imageEXIF.ImageDescription << "\n"; + if (!imageEXIF.Make.empty() || !imageEXIF.Model.empty()) + std::cout << "CameraModel " << imageEXIF.Make << " - " << imageEXIF.Model << "\n"; + std::cout << "Orientation " << imageEXIF.Orientation << "\n"; + std::cout << "Resolution " << imageEXIF.XResolution << "x" << imageEXIF.YResolution << "\n"; + std::cout << "ResolutionUnit " << imageEXIF.ResolutionUnit << "\n"; + std::cout << "BitsPerSample " << imageEXIF.BitsPerSample << "\n"; + if (!imageEXIF.Software.empty()) + std::cout << "Software " << imageEXIF.Software << "\n"; + if (!imageEXIF.DateTime.empty()) + std::cout << "DateTime " << imageEXIF.DateTime << "\n"; + if (!imageEXIF.DateTimeOriginal.empty()) + std::cout << "DateTimeOriginal " << imageEXIF.DateTimeOriginal << "\n"; + if (!imageEXIF.DateTimeDigitized.empty()) + std::cout << "DateTimeDigitized " << imageEXIF.DateTimeDigitized << "\n"; + if (!imageEXIF.SubSecTimeOriginal.empty()) + std::cout << "SubSecTimeOriginal " << imageEXIF.SubSecTimeOriginal << "\n"; + if (!imageEXIF.Copyright.empty()) + std::cout << "Copyright " << imageEXIF.Copyright << "\n"; + std::cout << "ExposureTime " << std::setprecision(10) << imageEXIF.ExposureTime << " s" << "\n"; + std::cout << "FNumber " << imageEXIF.FNumber << "\n"; + std::cout << "ExposureProgram " << imageEXIF.ExposureProgram << "\n"; + std::cout << "ISOSpeed " << imageEXIF.ISOSpeedRatings << "\n"; + std::cout << "ShutterSpeedValue " << std::setprecision(10) << imageEXIF.ShutterSpeedValue << "\n"; + std::cout << "ApertureValue " << std::setprecision(10) << imageEXIF.ApertureValue << "\n"; + std::cout << "BrightnessValue " << std::setprecision(10) << imageEXIF.BrightnessValue << "\n"; + std::cout << "ExposureBiasValue " << imageEXIF.ExposureBiasValue << "\n"; + std::cout << "SubjectDistance " << imageEXIF.SubjectDistance << "\n"; + std::cout << "FocalLength " << imageEXIF.FocalLength << " mm" << "\n"; + std::cout << "Flash " << imageEXIF.Flash << "\n"; + if (!imageEXIF.SubjectArea.empty()) { + std::cout << "SubjectArea"; + for (uint16_t val: imageEXIF.SubjectArea) + std::cout << " " << val; + std::cout << "\n"; + } + std::cout << "MeteringMode " << imageEXIF.MeteringMode << "\n"; + std::cout << "LightSource " << imageEXIF.LightSource << "\n"; + std::cout << "ProjectionType " << imageEXIF.ProjectionType << "\n"; + std::cout << "LensInfo.FStopMin " << imageEXIF.LensInfo.FStopMin << "\n"; + std::cout << "LensInfo.FStopMax " << imageEXIF.LensInfo.FStopMax << "\n"; + std::cout << "LensInfo.FocalLengthMin " << imageEXIF.LensInfo.FocalLengthMin << " mm" << "\n"; + std::cout << "LensInfo.FocalLengthMax " << imageEXIF.LensInfo.FocalLengthMax << " mm" << "\n"; + std::cout << "LensInfo.DigitalZoomRatio " << imageEXIF.LensInfo.DigitalZoomRatio << "\n"; + std::cout << "LensInfo.FocalLengthIn35mm " << imageEXIF.LensInfo.FocalLengthIn35mm << "\n"; + std::cout << "LensInfo.FocalPlaneXResolution " << std::setprecision(10) << imageEXIF.LensInfo.FocalPlaneXResolution << "\n"; + std::cout << "LensInfo.FocalPlaneYResolution " << std::setprecision(10) << imageEXIF.LensInfo.FocalPlaneYResolution << "\n"; + std::cout << "LensInfo.FocalPlaneResolutionUnit " << imageEXIF.LensInfo.FocalPlaneResolutionUnit << "\n"; + if (!imageEXIF.LensInfo.Make.empty() || !imageEXIF.LensInfo.Model.empty()) + std::cout << "LensInfo.Model " << imageEXIF.LensInfo.Make << " - " << imageEXIF.LensInfo.Model << "\n"; + if (imageEXIF.GeoLocation.hasLatLon()) { + std::cout << "GeoLocation.Latitude " << std::setprecision(10) << imageEXIF.GeoLocation.Latitude << "\n"; + std::cout << "GeoLocation.Longitude " << std::setprecision(10) << imageEXIF.GeoLocation.Longitude << "\n"; + } + if (imageEXIF.GeoLocation.hasAltitude()) { + std::cout << "GeoLocation.Altitude " << imageEXIF.GeoLocation.Altitude << " m" << "\n"; + std::cout << "GeoLocation.AltitudeRef " << (int)imageEXIF.GeoLocation.AltitudeRef << "\n"; + } + if (imageEXIF.GeoLocation.hasRelativeAltitude()) + std::cout << "GeoLocation.RelativeAltitude " << imageEXIF.GeoLocation.RelativeAltitude << "\n"; + if (imageEXIF.GeoLocation.hasOrientation()) { + std::cout << "GeoLocation.RollDegree " << imageEXIF.GeoLocation.RollDegree << "\n"; + std::cout << "GeoLocation.PitchDegree " << imageEXIF.GeoLocation.PitchDegree << "\n"; + std::cout << "GeoLocation.YawDegree " << imageEXIF.GeoLocation.YawDegree << "\n"; + } + std::cout << "GeoLocation.GPSDOP " << imageEXIF.GeoLocation.GPSDOP << "\n"; + std::cout << "GeoLocation.GPSDifferential " << imageEXIF.GeoLocation.GPSDifferential << "\n"; + if (!imageEXIF.GeoLocation.GPSMapDatum.empty()) + std::cout << "GeoLocation.GPSMapDatum " << imageEXIF.GeoLocation.GPSMapDatum << "\n"; + if (!imageEXIF.GeoLocation.GPSTimeStamp.empty()) + std::cout << "GeoLocation.GPSTimeStamp " << imageEXIF.GeoLocation.GPSTimeStamp << "\n"; + if (!imageEXIF.GeoLocation.GPSDateStamp.empty()) + std::cout << "GeoLocation.GPSDateStamp " << imageEXIF.GeoLocation.GPSDateStamp << "\n"; + } + return 0; +}