add DigitalZoomRatio tag and refactor parsing

This commit is contained in:
cDc
2017-08-01 15:02:12 +03:00
parent 725c623d11
commit 8523cdcf3a
4 changed files with 509 additions and 355 deletions

View File

@@ -33,10 +33,11 @@
#include "TinyEXIF.h"
#include "tinyxml2.h"
#include <algorithm>
#include <cstdint>
#include <stdio.h>
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
#ifdef _MSC_VER
#include <tchar.h>
@@ -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<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
// parseFromEXIFSegment() or parseFromXMPSegment()
@@ -385,20 +726,20 @@ int EXIFInfo::parseFromEXIFSegment(const uint8_t* buf, unsigned len) {
// 8 bytes
if (offs + 8 > 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<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;
}
parser.ParseTag();
parseIFDExif(parser);
}
}
@@ -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.
if (gps_sub_ifd_offset + 4 <= len) {
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)
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;