Project

General

Profile

How to correctly read all Exif tags

Added by Mikayel Egibyan over 6 years ago

Hi,

I have the following question:

Everything works just fine when trying to read all the available tags like:

@std::string res = "{ ";

Exiv2::ExifData::const_iterator end = exifData.end();
for (Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) {
res += i->key();
res += " > ";
res += toString(exifData[i
>key()]);
res += ", ";
}

if (res.length() >= 5) res = res.substr(0, res.size() - 2);
res += "}";
std::cout << res << "\n";@

But if I try to get any tag that doesn't has value (if the tag has value everything goes fine), e.g. GPSLatitudeRef, and only then read all the tags, it crashes:

@try{
std::cout << exifData["Exif.GPSInfo.GPSLatitudeRef"];
}
catch (Exiv2::Error& e) {
std::cout << "Caught Exiv2 exception '" << e.what() << "'\n";
}

std::string res = "{ ";

Exiv2::ExifData::const_iterator end = exifData.end();
for (Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) {
res += i->key();
res += " > ";
res += toString(exifData[i
>key()]);
res += ", ";
}

if (res.length() >= 5) res = res.substr(0, res.size() - 2);
res += "}";
std::cout << res << "\n";@

I noticed that doing exifData.clear() in the catch block helps, but it is not a solution.

Could anyone please lead me to have an idea what I am doing wrong?

Thanks in advance!
Mikayel


Replies (11)

RE: How to correctly read all Exif tags - Added by Robin Mills over 6 years ago

Mikayel

You cannot thank somebody in advance. You thank somebody when they have provided a solution.

Once more I refer you to our old friend samples/exifprint.cpp. You will see that he is writing the meta data to a stream - in this case std::cout. I suggest you use the stream type that is backed by a string (I can remember what it is called). When you've finished writing to that stream, the encapsulated string will be the formatted metadata.

Robin

RE: How to correctly read all Exif tags - Added by Mikayel Egibyan over 6 years ago

Robin,

"Thank in advance" is an alternative method to show respect when you ask a question, same as "Thanks", "Any assistance pleases", etc...

I don't want to write it using std::cout.
Could you be so kind please, if you noticed any bug in the code I wrote?

The issues here is that when I try to get a tag that doesn't have a value set, an Exiv2::Error& type error saying that the value is not set is rises, which somehow brakes the code that comes after.

Thanks!
Mikayel

RE: How to correctly read all Exif tags - Added by Robin Mills over 6 years ago

Make it easy for yourself. Use the ostringstream class.

// ***************************************************************** -*- C++ -*-
// exifprint.cpp, $Rev: 3090 $
// Sample program to print the Exif metadata of an image

#include <exiv2/exiv2.hpp>

#include <iostream>
#include <iomanip>
#include <cassert>

int main(int argc, char* const argv[])
try {

    if (argc != 2) {
        std::cout << "Usage: " << argv[0] << " file\n";
        return 1;
    }

    Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(argv[1]);
    assert(image.get() != 0);
    image->readMetadata();

    std::ostringstream out;

    Exiv2::ExifData &exifData = image->exifData();
    if (exifData.empty()) {
        std::string error(argv[1]);
        error += ": No Exif data found in the file";
        throw Exiv2::Error(1, error);
    }
    Exiv2::ExifData::const_iterator end = exifData.end();
    for (Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) {
        const char* tn = i->typeName();
        out << std::setw(44) << std::setfill(' ') << std::left
                  << i->key() << " " 
                  << "0x" << std::setw(4) << std::setfill('0') << std::right
                  << std::hex << i->tag() << " " 
                  << std::setw(9) << std::setfill(' ') << std::left
                  << (tn ? tn : "Unknown") << " " 
                  << std::dec << std::setw(3)
                  << std::setfill(' ') << std::right
                  << i->count() << "  " 
                  << std::dec << i->value()
                  << "\n";
    }

    std::cout << "The string = " << out.str();

    return 0;
}
//catch (std::exception& e) {
//catch (Exiv2::AnyError& e) {
catch (Exiv2::Error& e) {
    std::cout << "Caught Exiv2 exception '" << e.what() << "'\n";
    return -1;
}
I don't really want to debug your code, however if you're really stuck, please format into a "pre" block so I can read it easily - or attach code to this thread.

"Thanks in advance" to me means. "Don't expect me to thank because I want your help now and I am too busy to thank you later.". There is no respect or sincerity. How about "I will sincerely appreciate any help you can provide. Thanks".

RE: How to correctly read all Exif tags - Added by Robin Mills over 6 years ago

Mikayel

Your code is almost correct. You need to catch the exception. I've written a new sample $ showkey file key and here's the output on two files. ~/P.jpg is not geotagged. ~/Q.jpg is.

608 rmills@rmillsmbp:~/gnu/exiv2/trunk/samples $ exiv2 -pa -g GPS ~/P.jpg ~/Q.jpg
/Users/rmills/P.jpg   Exif.Image.GPSTag                            Long        1  15236
/Users/rmills/P.jpg   Exif.GPSInfo.GPSVersionID                    Byte        4  2.3.0.0

/Users/rmills/Q.jpg   Exif.Image.GPSTag                            Long        1  15236
/Users/rmills/Q.jpg   Exif.GPSInfo.GPSVersionID                    Byte        4  2.3.0.0
/Users/rmills/Q.jpg   Exif.GPSInfo.GPSLatitudeRef                  Ascii       2  North
/Users/rmills/Q.jpg   Exif.GPSInfo.GPSLatitude                     Rational    3  32deg 52.24230' 
/Users/rmills/Q.jpg   Exif.GPSInfo.GPSLongitudeRef                 Ascii       2  West
/Users/rmills/Q.jpg   Exif.GPSInfo.GPSLongitude                    Rational    3  96deg 56.11870' 
/Users/rmills/Q.jpg   Exif.GPSInfo.GPSTimeStamp                    Rational    3  22:50:32.5
/Users/rmills/Q.jpg   Exif.GPSInfo.GPSSatellites                   Ascii       3  04
/Users/rmills/Q.jpg   Exif.GPSInfo.GPSMapDatum                     Ascii      17  WGS-84          
/Users/rmills/Q.jpg   Exif.GPSInfo.GPSDateStamp                    Ascii      11  2015:01:29
When I ask for the key 'Exif.GPSInfo.GPSLatitude', here's the result:
609 rmills@rmillsmbp:~/gnu/exiv2/trunk/samples $ ../bin/showkey ~/P.jpg Exif.GPSInfo.GPSLatitude
key Exif.GPSInfo.GPSLatitude = *** no value ***
610 rmills@rmillsmbp:~/gnu/exiv2/trunk/samples $ ../bin/showkey ~/Q.jpg Exif.GPSInfo.GPSLatitude
key Exif.GPSInfo.GPSLatitude = 32deg 52.24230' 
611 rmills@rmillsmbp:~/gnu/exiv2/trunk/samples $ 
Here's the code.
// ***************************************************************** -*- C++ -*-
// showkey.cpp
// Sample program to print an Exif metadata key in a file

#include <exiv2/exiv2.hpp>

#include <iostream>
#include <iomanip>
#include <cassert>
#include <string>

int main(int argc, char* const argv[])
{
    if (argc != 3) {
        std::cout << "Usage: " << argv[0] << " file key\n";
        return 1;
    }
    const char* file = argv[1];
    const char* key  = argv[2];

    Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(file);
    assert(image.get() != 0);
    image->readMetadata();

    std::ostringstream out;
    Exiv2::ExifData &exifData = image->exifData();
    if (! exifData.empty()) {
        try {
            out << exifData[argv[2]];
        } catch ( ... ) {
            out << "*** no value ***" ;
        }
        std::cout << "key " << key << " = " << out.str() << std::endl;
    } else {
        std::cout << "no metadata found in file " << file << std::endl;
    }

    return 0;
}

RE: How to correctly read all Exif tags - Added by Mikayel Egibyan over 6 years ago

Robin,

Maybe I explained the issue bad, I will try to do it better.
@
#include <exiv2/exiv2.hpp>
#include <iostream>
#include <iomanip>
#include <cassert>
#include <string>

int main(int argc, char* const argv[]) {
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(file);
assert(image.get() != 0);
image->readMetadata();

std::string out;
Exiv2::ExifData &exifData = image->exifData();
if (! exifData.empty()) {
try {
std::cout << exifData["Exif.GPSInfo.GPSLatitudeRef"]; // Here can be any tag that doesn't have value
} catch ( Exiv2::Error e ) {
out += e.what();
}
Exiv2::ExifData::const_iterator end = exifData.end(); //Crashes here!
for (Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) {
out += i->key();
out += " > ";
out += toString(exifData[i
>key()]);
out += ", ";
}
std::cout << out;
} else {
...
}
return 0;
}@

Why does this crash?

Thank you!
Mikayel

RE: How to correctly read all Exif tags - Added by Robin Mills over 6 years ago

I'm not certain why you are crashing. I didn't write Exiv2. I'm the build/test engineer for the project. I don't know where you are located. I'm in England. I can give you some time 1-to-1 on Skype if you'd like to talk and I can mentor you through this.

Here are my thoughts about the code you have presented. A word of warning - I haven't debugged any of the code here. It's "about right", however it's straight from my head and fingers and not compiled or tested.

When you do this:

std::cout << exifData["Call.My.Bluff"]; 
...
exifData.end();
It may be crashing because the exifData object is in error. calling exifData.clear() before the exifData.end() may be required as you suggested earlier.

You cannot use exifData key/values as though they are strings. The are not strings. They are instances of the Exiv2::Key and Exiv2::MetaDatum classes. These classes have their << operators defined to write to streams. So you get the string value of a key or value by:

ostringstream key_stream ;
key_stream << key ;
key_stream.str() is your string!
and something similar for the value.

If you really want a toString(value) function, you could do something like:

std::string toString(Exiv2::Key key){
    ostringstream out ;
    out << key;
    return out.str();
}
However your strategy to append strings is flawed. Don't do this:
std::string out;
...
out += i->key ;
...
Go with the flow and let the classes work for you. Do this:
ostringstream out;
out << i->key ;
...
The string you want is out.str()
I hope this helps. Please consider my offer to have a 1-to-1 session on Skype. It will save us both time.

RE: How to correctly read all Exif tags - Added by Robin Mills over 6 years ago

Mikayel

I've updated the showkey.cpp file to build a string of comma separated keys in an image. It doesn't seem to crash on the ->end() statement when the key is unknown:

// ***************************************************************** -*- C++ -*-
// showkey.cpp, $Rev: 3090 $
// Sample program to print the Exif metadata of an image

#include <exiv2/exiv2.hpp>

#include <iostream>
#include <iomanip>
#include <cassert>
#include <string>

int main(int argc, char* const argv[])
{
    if (argc != 3) {
        std::cout << "Usage: " << argv[0] << " file key\n";
        return 1;
    }
    const char* file = argv[1];
    const char* key  = argv[2];

    Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(file);
    assert(image.get() != 0);
    image->readMetadata();

    std::ostringstream out;
    Exiv2::ExifData &exifData = image->exifData();
    if (! exifData.empty()) {
        try {
            out << exifData[argv[2]];
        } catch ( ... ) {
            out << "*** no value ***" ;
        }
        std::cout << "key " << key << " = " << out.str() << std::endl;

        std::ostringstream keys ;
        bool more = false ;
        Exiv2::ExifData::const_iterator end = exifData.end(); //Crashes here!
        for (Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) {
            if ( more ) keys << ", ";
            keys << i->key() ;
            more = true;
        }
        std::cout << "keys = " << keys.str() << std::endl ;
    } else {
        std::cout << "no metadata found in file " << file << std::endl;
    }

    return 0;
}
And here it's running on P.jpg (with no Latitude tag)
626 rmills@rmillsmbp:~/gnu/exiv2/trunk/samples $ ../bin/showkey ~/P.jpg Exif.GPSInfo.GPSLatitude
key Exif.GPSInfo.GPSLatitude = *** no value ***
keys = Exif.Image.Make, Exif.Image.Model, Exif.Image.Orientation, Exif.Image.XResolution, Exif.Image.YResolution, Exif.Image.ResolutionUnit, Exif.Image.Software, Exif.Image.DateTime, Exif.Image.YCbCrPositioning, Exif.Image.ExifTag, Exif.Photo.Exposure
...
And on Q.jpg with a latitude tag:
627 rmills@rmillsmbp:~/gnu/exiv2/trunk/samples $ ../bin/showkey ~/Q.jpg Exif.GPSInfo.GPSLatitude
key Exif.GPSInfo.GPSLatitude = 32deg 52.24230' 
keys = Exif.Image.Make, Exif.Image.Model, Exif.Image.Orientation, Exif.Image.XResolution, Exif.Image.YResolution, Exif.Image.ResolutionUnit, Exif.Image.Software, Exif.Image.DateTime, Exif.Image.YCbCrPositioning, Exif.Image.ExifTag, ...
We could even make it fancy with something like:
        for (Exiv2::ExifData::const_iterator i = exifData.begin(); i != end; ++i) {
            if ( more ) keys << ", ";
            keys << i->key() ;
            more = true;
            if ( keys.str().length() - keys.str().find_last_of('\n') > 60 ) {
                keys << std::endl;
                more = false;
            }
        }
And the output is beautifully formatted:
635 rmills@rmillsmbp:~/gnu/exiv2/trunk/samples $ ../bin/showkey ~/Q.jpg Exif.GPSInfo.GPSLatitude
key Exif.GPSInfo.GPSLatitude = 32deg 52.24230' 
keys = Exif.Image.Make, Exif.Image.Model, Exif.Image.Orientation, Exif.Image.XResolution
Exif.Image.YResolution, Exif.Image.ResolutionUnit, Exif.Image.Software
Exif.Image.DateTime, Exif.Image.YCbCrPositioning, Exif.Image.ExifTag
Exif.Photo.ExposureTime, Exif.Photo.FNumber, Exif.Photo.ExposureProgram
Exif.Photo.ISOSpeedRatings, Exif.Photo.SensitivityType, Exif.Photo.ExifVersion
Exif.Photo.DateTimeOriginal, Exif.Photo.DateTimeDigitized, Exif.Photo.ComponentsConfiguration
Exif.Photo.CompressedBitsPerPixel, Exif.Photo.ExposureBiasValue
Exif.Photo.MaxApertureValue, Exif.Photo.MeteringMode, Exif.Photo.LightSource
...

RE: How to correctly read all Exif tags - Added by Mikayel Egibyan over 6 years ago

Robin,

Thanks for your answer.

Still in your example, if you try to get exifData[i->key()] instead of i->key() it will crash.

Thanks,
Mikayel

RE: How to correctly read all Exif tags - Added by Mikayel Egibyan over 6 years ago

Robin,

Can you please add me on Skype - megibyan

Thanks,
Mikayel

RE: How to correctly read all Exif tags - Added by Robin Mills over 6 years ago

Mikayel Egibyan wrote:

Robin,

Thanks for your answer.

Still in your example, if you try to get exifData[i->key()] instead of i->key() it will crash.

Thanks,
Mikayel

Mikael

You know the answer!

exifData["duby.dubee.doo"] expects a std::string. i->key() is NOT a string! Ssshsshs.

I've added you on Skype. I am 'clanmills'.

Robin

RE: How to correctly read all Exif tags - Added by Robin Mills over 6 years ago

Mikayel

I've added two new programs to samples. I decided on the names:

  • exifdata for the program to format exif data into JSON/CSV/XML/Wolf (was showkeys)
  • exifvalue to show a single exif value (was showkey).

I've built and tested them on Mac and Windows (32 and 64 bit):

C:\cygwin64\home\rmills\gnu\exiv2\trunk\msvc2003\bin\Release>exifvalue
Usage: exifvalue file key

C:\cygwin64\home\rmills\gnu\exiv2\trunk\msvc2003\bin\Release>exifdata
Usage: exifdata file format
formats: csv | json | wolf | xml

C:\cygwin64\home\rmills\gnu\exiv2\trunk\msvc2003\bin\Release>exifdata y:\P.jpg json | grep GPS
  "Exif.Image.GPSTag":"15236",
  "Exif.GPSInfo.GPSVersionID":"2 3 0 0",

C:\cygwin64\home\rmills\gnu\exiv2\trunk\msvc2003\bin\Release>exifdata y:\Q.jpg json | grep GPS
  "Exif.Image.GPSTag":"15236",
  "Exif.GPSInfo.GPSVersionID":"2 3 0 0",
  "Exif.GPSInfo.GPSLatitudeRef":"N",
  "Exif.GPSInfo.GPSLatitude":"32/1 522423/10000 0/1",
  "Exif.GPSInfo.GPSLongitudeRef":"W",
  "Exif.GPSInfo.GPSLongitude":"96/1 561187/10000 0/1",
  "Exif.GPSInfo.GPSTimeStamp":"22/1 50/1 3245/100",
  "Exif.GPSInfo.GPSSatellites":"04",
  "Exif.GPSInfo.GPSMapDatum":"WGS-84          ",
  "Exif.GPSInfo.GPSDateStamp":"2015:01:29",

C:\cygwin64\home\rmills\gnu\exiv2\trunk\msvc2003\bin\Release>exifvalue
Usage: exifvalue file key

C:\cygwin64\home\rmills\gnu\exiv2\trunk\msvc2003\bin\Release>exifvalue y:\Q.jpg Exif.GPSInfo.GPSLatitude
32deg 52.24230'

C:\cygwin64\home\rmills\gnu\exiv2\trunk\msvc2003\bin\Release>exifvalue y:\P.jpg Exif.GPSInfo.GPSLatitude
Caught Exiv2 exception 'Value not set'

C:\cygwin64\home\rmills\gnu\exiv2\trunk\msvc2003\bin\Release>
I haven't been able to reproduce the crash we discussed concerning:
exifData["Exif.GPSInfo.GPSLatitude"]
when the value is not defined in the file. exifvalue is catching the exception 'Value not set'. If you continue to experience trouble with this, I'd like to ask you to build and test it with exifvalue.cpp. If you are able to reproduce this with exifvalue.cpp, please report an Issue on exiv2.org and it will be investigated. Please include all relevant platform information and a test file. The following command generates a very useful report:
exiv --verbose --version
You will appreciate that issues can be resolved more easily when you and I are working with the same code, the same platform and with the same image files.

Good Luck.

    (1-11/11)