add DigitalZoomRatio tag and refactor parsing
This commit is contained in:
@@ -37,3 +37,4 @@ int main(int argc, const char** argv) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
See `main.cpp` for more details.
|
||||||
|
|||||||
713
TinyEXIF.cpp
713
TinyEXIF.cpp
@@ -33,10 +33,11 @@
|
|||||||
|
|
||||||
#include "TinyEXIF.h"
|
#include "TinyEXIF.h"
|
||||||
#include "tinyxml2.h"
|
#include "tinyxml2.h"
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <stdio.h>
|
#include <cstdio>
|
||||||
|
#include <cmath>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#include <tchar.h>
|
#include <tchar.h>
|
||||||
@@ -130,19 +131,19 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
EntryParser(const uint8_t* _buf, unsigned _len, unsigned _tiff_header_start, bool _alignIntel)
|
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) {
|
void Init(unsigned _offs) {
|
||||||
offs = _offs - 12;
|
offs = _offs - 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t ParseTag() {
|
void ParseTag() {
|
||||||
offs += 12;
|
offs += 12;
|
||||||
tag = parse16(buf + offs, alignIntel);
|
tag = parse16(buf + offs, alignIntel);
|
||||||
format = parse16(buf + offs + 2, alignIntel);
|
format = parse16(buf + offs + 2, alignIntel);
|
||||||
length = parse32(buf + offs + 4, alignIntel);
|
length = parse32(buf + offs + 4, alignIntel);
|
||||||
return tag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t GetTag() const { return tag; }
|
uint16_t GetTag() const { return tag; }
|
||||||
uint32_t GetLength() const { return length; }
|
uint32_t GetLength() const { return length; }
|
||||||
uint32_t GetData() const { return parse32(buf + offs + 8, alignIntel); }
|
uint32_t GetData() const { return parse32(buf + offs + 8, alignIntel); }
|
||||||
@@ -153,19 +154,19 @@ public:
|
|||||||
bool IsRational() const { return format == 5 || format == 10; }
|
bool IsRational() const { return format == 5 || format == 10; }
|
||||||
|
|
||||||
bool Fetch(std::string& val) const {
|
bool Fetch(std::string& val) const {
|
||||||
if (format != 2)
|
if (format != 2 || length == 0)
|
||||||
return false;
|
return false;
|
||||||
val = parseEXIFString(buf, length, GetData(), tiff_header_start, len, alignIntel);
|
val = parseEXIFString(buf, length, GetData(), tiff_header_start, len, alignIntel);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool Fetch(uint8_t& val) const {
|
bool Fetch(uint8_t& val) const {
|
||||||
if (format != 1 && format != 2 && format != 6)
|
if ((format != 1 && format != 2 && format != 6) || length == 0)
|
||||||
return false;
|
return false;
|
||||||
val = parse8(buf + offs + 8);
|
val = parse8(buf + offs + 8);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool Fetch(uint16_t& val) const {
|
bool Fetch(uint16_t& val) const {
|
||||||
if (!IsShort())
|
if (!IsShort() || length == 0)
|
||||||
return false;
|
return false;
|
||||||
val = parse16(buf + offs + 8, alignIntel);
|
val = parse16(buf + offs + 8, alignIntel);
|
||||||
return true;
|
return true;
|
||||||
@@ -177,13 +178,13 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool Fetch(uint32_t& val) const {
|
bool Fetch(uint32_t& val) const {
|
||||||
if (!IsLong())
|
if (!IsLong() || length == 0)
|
||||||
return false;
|
return false;
|
||||||
val = parse32(buf + offs + 8, alignIntel);
|
val = parse32(buf + offs + 8, alignIntel);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool Fetch(double& val) const {
|
bool Fetch(double& val) const {
|
||||||
if (!IsRational())
|
if (!IsRational() || length == 0)
|
||||||
return false;
|
return false;
|
||||||
val = parseEXIFRational(buf + GetSubIFD(), alignIntel);
|
val = parseEXIFRational(buf + GetSubIFD(), alignIntel);
|
||||||
return true;
|
return true;
|
||||||
@@ -241,16 +242,356 @@ public:
|
|||||||
} else
|
} else
|
||||||
if (base+data+num_components <= len) {
|
if (base+data+num_components <= len) {
|
||||||
const char* const sz((const char*)buf+base+data);
|
const char* const sz((const char*)buf+base+data);
|
||||||
if (sz[--num_components] == '\0')
|
unsigned num(0);
|
||||||
while (num_components && sz[num_components-1] == '\0')
|
while (num < num_components && sz[num] != '\0')
|
||||||
--num_components;
|
++num;
|
||||||
value.assign(sz, num_components);
|
value.assign(sz, num);
|
||||||
}
|
}
|
||||||
return value;
|
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<parser.GetLength(); ++i)
|
||||||
|
parser.Fetch(SubjectArea[i], i);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x9291:
|
||||||
|
// Fractions of seconds for DateTimeOriginal
|
||||||
|
parser.Fetch(SubSecTimeOriginal);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xa002:
|
||||||
|
// EXIF Image width
|
||||||
|
if (!parser.Fetch(ImageWidth)) {
|
||||||
|
uint16_t _ImageWidth;
|
||||||
|
if (parser.Fetch(_ImageWidth))
|
||||||
|
ImageWidth = _ImageWidth;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xa003:
|
||||||
|
// EXIF Image height
|
||||||
|
if (!parser.Fetch(ImageHeight)) {
|
||||||
|
uint16_t _ImageHeight;
|
||||||
|
if (parser.Fetch(_ImageHeight))
|
||||||
|
ImageHeight = _ImageHeight;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xa20e:
|
||||||
|
// Focal plane X resolution
|
||||||
|
parser.Fetch(LensInfo.FocalPlaneXResolution);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xa20f:
|
||||||
|
// Focal plane Y resolution
|
||||||
|
parser.Fetch(LensInfo.FocalPlaneYResolution);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xa210:
|
||||||
|
// Focal plane resolution unit
|
||||||
|
parser.Fetch(LensInfo.FocalPlaneResolutionUnit);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xa215:
|
||||||
|
// Exposure Index and ISO Speed Rating are often used interchangeably
|
||||||
|
if (ISOSpeedRatings == 0) {
|
||||||
|
double ExposureIndex;
|
||||||
|
if (parser.Fetch(ExposureIndex))
|
||||||
|
ISOSpeedRatings = (uint16_t)ExposureIndex;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xa404:
|
||||||
|
// Digital Zoom Ratio
|
||||||
|
parser.Fetch(LensInfo.DigitalZoomRatio);
|
||||||
|
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;
|
||||||
|
|
||||||
|
case 0xa432:
|
||||||
|
// Focal length and FStop.
|
||||||
|
if (parser.Fetch(LensInfo.FocalLengthMin, 0))
|
||||||
|
if (parser.Fetch(LensInfo.FocalLengthMax, 1))
|
||||||
|
if (parser.Fetch(LensInfo.FStopMin, 2))
|
||||||
|
parser.Fetch(LensInfo.FStopMax, 3);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xa433:
|
||||||
|
// Lens make.
|
||||||
|
parser.Fetch(LensInfo.Make);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xa434:
|
||||||
|
// Lens model.
|
||||||
|
parser.Fetch(LensInfo.Model);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse tag as GPS IFD
|
||||||
|
void EXIFInfo::parseIFDGPS(EntryParser& parser) {
|
||||||
|
switch (parser.GetTag()) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Locates the JM_APP1 segment and parses it using
|
// Locates the JM_APP1 segment and parses it using
|
||||||
// parseFromEXIFSegment() or parseFromXMPSegment()
|
// parseFromEXIFSegment() or parseFromXMPSegment()
|
||||||
@@ -385,20 +726,20 @@ int EXIFInfo::parseFromEXIFSegment(const uint8_t* buf, unsigned len) {
|
|||||||
// 8 bytes
|
// 8 bytes
|
||||||
if (offs + 8 > len)
|
if (offs + 8 > len)
|
||||||
return PARSE_EXIF_ERROR_CORRUPT;
|
return PARSE_EXIF_ERROR_CORRUPT;
|
||||||
const unsigned tiff_header_start = offs;
|
bool alignIntel;
|
||||||
if (buf[offs] == 'I' && buf[offs+1] == 'I')
|
if (buf[offs] == 'I' && buf[offs+1] == 'I')
|
||||||
ByteAlign = 1;
|
alignIntel = true; // 1: Intel byte alignment
|
||||||
else {
|
else
|
||||||
if (buf[offs] == 'M' && buf[offs+1] == 'M')
|
if (buf[offs] == 'M' && buf[offs+1] == 'M')
|
||||||
ByteAlign = 0;
|
alignIntel = false; // 0: Motorola byte alignment
|
||||||
else
|
else
|
||||||
return PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN;
|
return PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN;
|
||||||
}
|
EntryParser parser(buf, len, offs, alignIntel);
|
||||||
offs += 2;
|
offs += 2;
|
||||||
if (0x2a != EntryParser::parse16(buf + offs, alignIntel()))
|
if (0x2a != EntryParser::parse16(buf + offs, alignIntel))
|
||||||
return PARSE_EXIF_ERROR_CORRUPT;
|
return PARSE_EXIF_ERROR_CORRUPT;
|
||||||
offs += 2;
|
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;
|
offs += first_ifd_offset - 4;
|
||||||
if (offs >= len)
|
if (offs >= len)
|
||||||
return PARSE_EXIF_ERROR_CORRUPT;
|
return PARSE_EXIF_ERROR_CORRUPT;
|
||||||
@@ -411,107 +752,15 @@ int EXIFInfo::parseFromEXIFSegment(const uint8_t* buf, unsigned len) {
|
|||||||
// bytes of data.
|
// bytes of data.
|
||||||
if (offs + 2 > len)
|
if (offs + 2 > len)
|
||||||
return PARSE_EXIF_ERROR_CORRUPT;
|
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)
|
if (offs + 6 + 12 * num_entries > len)
|
||||||
return PARSE_EXIF_ERROR_CORRUPT;
|
return PARSE_EXIF_ERROR_CORRUPT;
|
||||||
unsigned exif_sub_ifd_offset = len;
|
unsigned exif_sub_ifd_offset = len;
|
||||||
unsigned gps_sub_ifd_offset = len;
|
unsigned gps_sub_ifd_offset = len;
|
||||||
EntryParser parser(buf, len, tiff_header_start, alignIntel());
|
|
||||||
parser.Init(offs+2);
|
parser.Init(offs+2);
|
||||||
while (--num_entries >= 0) {
|
while (--num_entries >= 0) {
|
||||||
switch (parser.ParseTag()) {
|
parser.ParseTag();
|
||||||
case 0x0102:
|
parseIFDImage(parser, exif_sub_ifd_offset, gps_sub_ifd_offset);
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jump to the EXIF SubIFD if it exists and parse all the information
|
// 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.
|
// typical user might want.
|
||||||
if (exif_sub_ifd_offset + 4 <= len) {
|
if (exif_sub_ifd_offset + 4 <= len) {
|
||||||
offs = exif_sub_ifd_offset;
|
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)
|
if (offs + 6 + 12 * num_entries > len)
|
||||||
return PARSE_EXIF_ERROR_CORRUPT;
|
return PARSE_EXIF_ERROR_CORRUPT;
|
||||||
parser.Init(offs+2);
|
parser.Init(offs+2);
|
||||||
while (--num_entries >= 0) {
|
while (--num_entries >= 0) {
|
||||||
switch (parser.ParseTag()) {
|
parser.ParseTag();
|
||||||
case 0x829a:
|
parseIFDExif(parser);
|
||||||
// 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<parser.GetLength(); ++i)
|
|
||||||
parser.Fetch(SubjectArea[i], i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x9291:
|
|
||||||
// Fractions of seconds for DateTimeOriginal
|
|
||||||
parser.Fetch(SubSecTimeOriginal);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xa002:
|
|
||||||
// EXIF Image width
|
|
||||||
if (!parser.Fetch(ImageWidth)) {
|
|
||||||
uint16_t _ImageWidth;
|
|
||||||
if (parser.Fetch(_ImageWidth))
|
|
||||||
ImageWidth = _ImageWidth;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xa003:
|
|
||||||
// EXIF Image height
|
|
||||||
if (!parser.Fetch(ImageHeight)) {
|
|
||||||
uint16_t _ImageHeight;
|
|
||||||
if (parser.Fetch(_ImageHeight))
|
|
||||||
ImageHeight = _ImageHeight;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xa20e:
|
|
||||||
// Focal plane X resolution
|
|
||||||
parser.Fetch(LensInfo.FocalPlaneXResolution);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xa20f:
|
|
||||||
// Focal plane Y resolution
|
|
||||||
parser.Fetch(LensInfo.FocalPlaneYResolution);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xa210:
|
|
||||||
// Focal plane resolution unit
|
|
||||||
parser.Fetch(LensInfo.FocalPlaneResolutionUnit);
|
|
||||||
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;
|
|
||||||
|
|
||||||
case 0xa432:
|
|
||||||
// Focal length and FStop.
|
|
||||||
if (parser.Fetch(LensInfo.FocalLengthMin, 0))
|
|
||||||
if (parser.Fetch(LensInfo.FocalLengthMax, 1))
|
|
||||||
if (parser.Fetch(LensInfo.FStopMin, 2))
|
|
||||||
parser.Fetch(LensInfo.FStopMax, 3);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xa433:
|
|
||||||
// Lens make.
|
|
||||||
parser.Fetch(LensInfo.Make);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xa434:
|
|
||||||
// Lens model.
|
|
||||||
parser.Fetch(LensInfo.Model);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -682,83 +783,13 @@ int EXIFInfo::parseFromEXIFSegment(const uint8_t* buf, unsigned len) {
|
|||||||
// there. Note that it's possible that the GPS SubIFD doesn't exist.
|
// there. Note that it's possible that the GPS SubIFD doesn't exist.
|
||||||
if (gps_sub_ifd_offset + 4 <= len) {
|
if (gps_sub_ifd_offset + 4 <= len) {
|
||||||
offs = gps_sub_ifd_offset;
|
offs = gps_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)
|
if (offs + 6 + 12 * num_entries > len)
|
||||||
return PARSE_EXIF_ERROR_CORRUPT;
|
return PARSE_EXIF_ERROR_CORRUPT;
|
||||||
parser.Init(offs+2);
|
parser.Init(offs+2);
|
||||||
while (--num_entries >= 0) {
|
while (--num_entries >= 0) {
|
||||||
switch (parser.ParseTag()) {
|
parser.ParseTag();
|
||||||
case 1:
|
parseIFDGPS(parser);
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
GeoLocation.parseCoords();
|
GeoLocation.parseCoords();
|
||||||
}
|
}
|
||||||
@@ -901,7 +932,6 @@ void EXIFInfo::clear() {
|
|||||||
Copyright = "";
|
Copyright = "";
|
||||||
|
|
||||||
// Shorts / unsigned / double
|
// Shorts / unsigned / double
|
||||||
ByteAlign = 0;
|
|
||||||
ImageWidth = 0;
|
ImageWidth = 0;
|
||||||
ImageHeight = 0;
|
ImageHeight = 0;
|
||||||
RelatedImageWidth = 0;
|
RelatedImageWidth = 0;
|
||||||
@@ -929,6 +959,7 @@ void EXIFInfo::clear() {
|
|||||||
LensInfo.FocalLengthMin = 0;
|
LensInfo.FocalLengthMin = 0;
|
||||||
LensInfo.FStopMax = 0;
|
LensInfo.FStopMax = 0;
|
||||||
LensInfo.FStopMin = 0;
|
LensInfo.FStopMin = 0;
|
||||||
|
LensInfo.DigitalZoomRatio = 0;
|
||||||
LensInfo.FocalLengthIn35mm = 0;
|
LensInfo.FocalLengthIn35mm = 0;
|
||||||
LensInfo.FocalPlaneXResolution = 0;
|
LensInfo.FocalPlaneXResolution = 0;
|
||||||
LensInfo.FocalPlaneYResolution = 0;
|
LensInfo.FocalPlaneYResolution = 0;
|
||||||
|
|||||||
30
TinyEXIF.h
30
TinyEXIF.h
@@ -62,6 +62,8 @@ enum ErrorCode {
|
|||||||
PARSE_EXIF_ERROR_CORRUPT = 5, // EXIF header was found, but data was corrupted
|
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
|
// Class responsible for storing and parsing EXIF information from a JPEG blob
|
||||||
//
|
//
|
||||||
@@ -91,11 +93,16 @@ public:
|
|||||||
// Set all data members to default values.
|
// Set all data members to default values.
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
// Return true if the memory alignment is Intel format.
|
private:
|
||||||
bool alignIntel() const { return this->ByteAlign != 0; }
|
// 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()
|
public:
|
||||||
uint8_t ByteAlign; // 0: Motorola byte alignment, 1: Intel
|
// Data fields
|
||||||
uint32_t ImageWidth; // Image width reported in EXIF data
|
uint32_t ImageWidth; // Image width reported in EXIF data
|
||||||
uint32_t ImageHeight; // Image height reported in EXIF data
|
uint32_t ImageHeight; // Image height reported in EXIF data
|
||||||
uint32_t RelatedImageWidth; // Original image width 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 FStopMax; // Max aperture (f-stop)
|
||||||
double FocalLengthMin; // Min focal length (mm)
|
double FocalLengthMin; // Min focal length (mm)
|
||||||
double FocalLengthMax; // Max 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 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 FocalPlaneXResolution; // 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)
|
double FocalPlaneYResolution; // 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)
|
uint16_t FocalPlaneResolutionUnit;// Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution (may not exist)
|
||||||
// 0: unspecified in EXIF data
|
// 0: unspecified in EXIF data
|
||||||
// 1: no absolute unit of measurement
|
// 1: no absolute unit of measurement
|
||||||
// 2: inch
|
// 2: inch
|
||||||
@@ -222,12 +230,12 @@ public:
|
|||||||
double RollDegree; // Flight roll in degrees
|
double RollDegree; // Flight roll in degrees
|
||||||
double PitchDegree; // Flight pitch in degrees
|
double PitchDegree; // Flight pitch in degrees
|
||||||
double YawDegree; // Flight yaw in degrees
|
double YawDegree; // Flight yaw in degrees
|
||||||
double GPSDOP; // Indicates the GPS DOP (data degree of precision)
|
double GPSDOP; // GPS DOP (data degree of precision)
|
||||||
uint16_t GPSDifferential; // Indicates whether differential correction is applied to the GPS receiver (may not exist)
|
uint16_t GPSDifferential; // Differential correction applied to the GPS receiver (may not exist)
|
||||||
// 0: measurement without differential correction
|
// 0: measurement without differential correction
|
||||||
// 1: differential correction applied
|
// 1: differential correction applied
|
||||||
std::string GPSMapDatum; // Indicates the geodetic survey data (may not exist)
|
std::string GPSMapDatum; // Geodetic survey data (may not exist)
|
||||||
std::string GPSTimeStamp; // Indicates the time as UTC (Coordinated Universal Time) (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)
|
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 {
|
struct Coord_t {
|
||||||
double degrees;
|
double degrees;
|
||||||
|
|||||||
114
main.cpp
Normal file
114
main.cpp
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
// Defines the entry point for the console application.
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
#include "TinyEXIF.h"
|
||||||
|
#include <iostream> // std::cout
|
||||||
|
#include <fstream> // std::ifstream
|
||||||
|
#include <vector> // std::vector
|
||||||
|
#include <iomanip> // std::setprecision
|
||||||
|
|
||||||
|
int main(int argc, const char** argv)
|
||||||
|
{
|
||||||
|
if (argc != 2) {
|
||||||
|
std::cout << "Usage: TinyEXIF <image_file>" << "\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<uint8_t> 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user