parse projection type
This commit is contained in:
36
README.md
36
README.md
@@ -1,3 +1,39 @@
|
|||||||
# TinyEXIF: Tiny ISO-compliant C++ EXIF and XMP parsing library for JPEG
|
# TinyEXIF: Tiny ISO-compliant C++ EXIF and XMP parsing library for JPEG
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
TinyEXIF is a tiny, lightweight C++ library for parsing the metadata existing inside JPEG files. No third party dependencies are needed to parse EXIF data, however for accesing XMP data the TinyXML2 library is needed. TinyEXIF is easy to use, simply copy the two source files in you project and pass the JPEG data to EXIFInfo class. Currently common information like the camera make/model, original resolution, timestamp, focal length, lens info, F-stop/exposure time, GPS information, etc, embedded in the EXIF/XMP metadata are fetched. It is easy though to extend it and add any missing or new EXIF/XMP fields.
|
TinyEXIF is a tiny, lightweight C++ library for parsing the metadata existing inside JPEG files. No third party dependencies are needed to parse EXIF data, however for accesing XMP data the TinyXML2 library is needed. TinyEXIF is easy to use, simply copy the two source files in you project and pass the JPEG data to EXIFInfo class. Currently common information like the camera make/model, original resolution, timestamp, focal length, lens info, F-stop/exposure time, GPS information, etc, embedded in the EXIF/XMP metadata are fetched. It is easy though to extend it and add any missing or new EXIF/XMP fields.
|
||||||
|
|
||||||
|
## Usage example
|
||||||
|
|
||||||
|
```
|
||||||
|
#include "TinyEXIF.h"
|
||||||
|
#include <iostream> // std::cout
|
||||||
|
#include <fstream> // std::ifstream
|
||||||
|
#include <vector> // std::vector
|
||||||
|
|
||||||
|
int main(int argc, const char** argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
std::cout << "Usage: TinyEXIF <image_file>" << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read entire image file
|
||||||
|
std::ifstream file(argv[1], std::ifstream::in|std::ifstream::binary);
|
||||||
|
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(), length) == TinyEXIF::PARSE_EXIF_SUCCESS)
|
||||||
|
std::cout
|
||||||
|
<< "Image Description " << imageEXIF.ImageDescription << "\n"
|
||||||
|
<< "Image Resolution " << imageEXIF.ImageWidth << "x" << imageEXIF.ImageHeight << " pixels\n"
|
||||||
|
<< "Camera Model " << imageEXIF.Make << " - " << imageEXIF.Model << "\n"
|
||||||
|
<< "Focal Length " << imageEXIF.FocalLength << " mm" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
40
TinyEXIF.cpp
40
TinyEXIF.cpp
@@ -38,6 +38,15 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <tchar.h>
|
||||||
|
#else
|
||||||
|
#include <strings.h>
|
||||||
|
#define _tcsncmp strncmp
|
||||||
|
#define _tcsicmp strcasecmp
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
namespace TinyEXIF {
|
namespace TinyEXIF {
|
||||||
|
|
||||||
enum JPEG_MARKERS {
|
enum JPEG_MARKERS {
|
||||||
@@ -345,8 +354,8 @@ int EXIFInfo::parseFrom(const std::string &data) {
|
|||||||
// Do a sanity check by looking for bytes "Exif\0\0".
|
// Do a sanity check by looking for bytes "Exif\0\0".
|
||||||
// The marker has to contain at least the TIFF header, otherwise the
|
// The marker has to contain at least the TIFF header, otherwise the
|
||||||
// JM_APP1 data is corrupt. So the minimum length specified here has to be:
|
// JM_APP1 data is corrupt. So the minimum length specified here has to be:
|
||||||
// 6 bytes: "Exif\0\0" std::string
|
// 6 bytes: "Exif\0\0" string
|
||||||
// 2 bytes: TIFF header (either "II" or "MM" std::string)
|
// 2 bytes: TIFF header (either "II" or "MM" string)
|
||||||
// 2 bytes: TIFF magic (short 0x2a00 in Motorola byte order)
|
// 2 bytes: TIFF magic (short 0x2a00 in Motorola byte order)
|
||||||
// 4 bytes: Offset to first IFD
|
// 4 bytes: Offset to first IFD
|
||||||
// =========
|
// =========
|
||||||
@@ -671,7 +680,7 @@ int EXIFInfo::parseFromEXIFSegment(const uint8_t* buf, unsigned len) {
|
|||||||
parser.Fetch(m, 1);
|
parser.Fetch(m, 1);
|
||||||
parser.Fetch(s, 2);
|
parser.Fetch(s, 2);
|
||||||
char buffer[256];
|
char buffer[256];
|
||||||
snprintf(buffer, 256, "%f %f %f", h, m, s);
|
snprintf(buffer, 256, "%g %g %g", h, m, s);
|
||||||
this->GeoLocation.GPSTimeStamp = buffer;
|
this->GeoLocation.GPSTimeStamp = buffer;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -707,7 +716,7 @@ int EXIFInfo::parseFromEXIFSegment(const uint8_t* buf, unsigned len) {
|
|||||||
// Main parsing function for a XMP segment.
|
// Main parsing function for a XMP segment.
|
||||||
// Do a sanity check by looking for bytes "http://ns.adobe.com/xap/1.0/\0".
|
// Do a sanity check by looking for bytes "http://ns.adobe.com/xap/1.0/\0".
|
||||||
// So the minimum length specified here has to be:
|
// So the minimum length specified here has to be:
|
||||||
// 29 bytes: "http://ns.adobe.com/xap/1.0/\0" std::string
|
// 29 bytes: "http://ns.adobe.com/xap/1.0/\0" string
|
||||||
//
|
//
|
||||||
// PARAM: 'buf' start of the XMP header, which must be the bytes "http://ns.adobe.com/xap/1.0/\0".
|
// PARAM: 'buf' start of the XMP header, which must be the bytes "http://ns.adobe.com/xap/1.0/\0".
|
||||||
// PARAM: 'len' length of buffer
|
// PARAM: 'len' length of buffer
|
||||||
@@ -722,7 +731,7 @@ int EXIFInfo::parseFromXMPSegment(const uint8_t* buf, unsigned len) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
for (size_t i=len-needle_len; i-- > 0; ) {
|
for (size_t i=len-needle_len; i-- > 0; ) {
|
||||||
if (haystack[0] == needle[0] &&
|
if (haystack[0] == needle[0] &&
|
||||||
0 == strncmp(haystack, needle, needle_len))
|
0 == _tcsncmp(haystack, needle, needle_len))
|
||||||
return haystack;
|
return haystack;
|
||||||
haystack++;
|
haystack++;
|
||||||
}
|
}
|
||||||
@@ -748,19 +757,35 @@ int EXIFInfo::parseFromXMPSegment(const uint8_t* buf, unsigned len) {
|
|||||||
|
|
||||||
// Now try parsing the XML packet.
|
// Now try parsing the XML packet.
|
||||||
tinyxml2::XMLDocument doc;
|
tinyxml2::XMLDocument doc;
|
||||||
tinyxml2::XMLElement* document;
|
const tinyxml2::XMLElement* document;
|
||||||
if (doc.Parse(strXMP, len) != tinyxml2::XML_SUCCESS ||
|
if (doc.Parse(strXMP, len) != tinyxml2::XML_SUCCESS ||
|
||||||
((document=doc.FirstChildElement("x:xmpmeta")) == NULL && (document=doc.FirstChildElement("xmp:xmpmeta")) == NULL) ||
|
((document=doc.FirstChildElement("x:xmpmeta")) == NULL && (document=doc.FirstChildElement("xmp:xmpmeta")) == NULL) ||
|
||||||
(document=document->FirstChildElement("rdf:RDF")) == NULL ||
|
(document=document->FirstChildElement("rdf:RDF")) == NULL ||
|
||||||
(document=document->FirstChildElement("rdf:Description")) == NULL)
|
(document=document->FirstChildElement("rdf:Description")) == NULL)
|
||||||
return PARSE_EXIF_SUCCESS;
|
return PARSE_EXIF_SUCCESS;
|
||||||
|
|
||||||
// Now try parsing the XMP content of DJI.
|
// Now try parsing the XMP content for projection type.
|
||||||
|
{
|
||||||
|
const tinyxml2::XMLElement* const element(document->FirstChildElement("GPano:ProjectionType"));
|
||||||
|
if (element != NULL) {
|
||||||
|
const char* const szProjectionType(element->GetText());
|
||||||
|
if (szProjectionType != NULL) {
|
||||||
|
if (0 == _tcsicmp(szProjectionType, "perspective"))
|
||||||
|
ProjectionType = 1;
|
||||||
|
else if (0 == _tcsicmp(szProjectionType, "equirectangular") ||
|
||||||
|
0 == _tcsicmp(szProjectionType, "spherical"))
|
||||||
|
ProjectionType = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now try parsing the XMP content for DJI info.
|
||||||
document->QueryDoubleAttribute("drone-dji:AbsoluteAltitude", &GeoLocation.Altitude);
|
document->QueryDoubleAttribute("drone-dji:AbsoluteAltitude", &GeoLocation.Altitude);
|
||||||
document->QueryDoubleAttribute("drone-dji:RelativeAltitude", &GeoLocation.RelativeAltitude);
|
document->QueryDoubleAttribute("drone-dji:RelativeAltitude", &GeoLocation.RelativeAltitude);
|
||||||
document->QueryDoubleAttribute("drone-dji:FlightRollDegree", &GeoLocation.RollDegree);
|
document->QueryDoubleAttribute("drone-dji:FlightRollDegree", &GeoLocation.RollDegree);
|
||||||
document->QueryDoubleAttribute("drone-dji:FlightPitchDegree", &GeoLocation.PitchDegree);
|
document->QueryDoubleAttribute("drone-dji:FlightPitchDegree", &GeoLocation.PitchDegree);
|
||||||
document->QueryDoubleAttribute("drone-dji:FlightYawDegree", &GeoLocation.YawDegree);
|
document->QueryDoubleAttribute("drone-dji:FlightYawDegree", &GeoLocation.YawDegree);
|
||||||
|
|
||||||
return PARSE_EXIF_SUCCESS;
|
return PARSE_EXIF_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -834,6 +859,7 @@ void EXIFInfo::clear() {
|
|||||||
FocalLength = 0;
|
FocalLength = 0;
|
||||||
Flash = 0;
|
Flash = 0;
|
||||||
MeteringMode = 0;
|
MeteringMode = 0;
|
||||||
|
ProjectionType = 0;
|
||||||
ImageWidth = 0;
|
ImageWidth = 0;
|
||||||
ImageHeight = 0;
|
ImageHeight = 0;
|
||||||
|
|
||||||
|
|||||||
22
TinyEXIF.h
22
TinyEXIF.h
@@ -36,6 +36,20 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# ifdef TINYEXIF_EXPORT
|
||||||
|
# define TINYEXIF_LIB __declspec(dllexport)
|
||||||
|
# elif defined(TINYEXIF_IMPORT)
|
||||||
|
# define TINYEXIF_LIB __declspec(dllimport)
|
||||||
|
# else
|
||||||
|
# define TINYEXIF_LIB
|
||||||
|
# endif
|
||||||
|
#elif __GNUC__ >= 4
|
||||||
|
# define TINYEXIF_LIB __attribute__((visibility("default")))
|
||||||
|
#else
|
||||||
|
# define TINYEXIF_LIB
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace TinyEXIF {
|
namespace TinyEXIF {
|
||||||
|
|
||||||
enum ErrorCode {
|
enum ErrorCode {
|
||||||
@@ -47,10 +61,10 @@ 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 responsible for storing and parsing EXIF information from a JPEG blob
|
// Class responsible for storing and parsing EXIF information from a JPEG blob
|
||||||
//
|
//
|
||||||
class EXIFInfo {
|
class TINYEXIF_LIB EXIFInfo {
|
||||||
public:
|
public:
|
||||||
EXIFInfo() { clear(); }
|
EXIFInfo() { clear(); }
|
||||||
|
|
||||||
@@ -118,6 +132,10 @@ public:
|
|||||||
// 3: spot
|
// 3: spot
|
||||||
// 4: multi-spot
|
// 4: multi-spot
|
||||||
// 5: multi-segment
|
// 5: multi-segment
|
||||||
|
uint16_t ProjectionType; // Projection type
|
||||||
|
// 0: unknown projection
|
||||||
|
// 1: perspective projection
|
||||||
|
// 2: equirectangular/spherical projection
|
||||||
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
|
||||||
struct LensInfo_t { // Lens information
|
struct LensInfo_t { // Lens information
|
||||||
|
|||||||
Reference in New Issue
Block a user