Feature #1137

Enable piping of selective copy of metadata between images

Added by Robin Mills over 1 year ago. Updated about 1 year ago.

Status:ClosedStart date:23 Nov 2015
Priority:NormalDue date:
Assignee:Robin Mills% Done:

100%

Category:designEstimated time:6.00 hours
Target version:0.26

Description

This was discussed on the forum. http://dev.exiv2.org/boards/3/topics/2329

I've added two features:
1 Option mfilename will access -m to mean standard-input
2 Option -PV is identical to -Pv AND adds the word 'set ' at the beginning of every output metadatum.

This enables the user to construct a pipeline such as this to copy the GPS (Latitude and Longitude) from one image to another.

$ exiv2 -PkV --grep GPSL http://dev.exiv2.org/attachments/download/805/DSC_7154.jpg | exiv2 -m- robin.jpg
In the discussion on the forum, I used utility 'sed' to generate the 'set ' word on every line of the output. For the convenience of Windows users (who will not have sed as a preinstalled command), the -PV option is provided.

In the discussion on the forum, I used a temporary file 'foo.cmd' to store the output of sed and read it into exiv2 with the mfoo.cmd option. The new feature that -m represents stdin is more convenient and applies to all platforms. The m option can of course be used by exiv2 and read from a pipe being filled by any application. So this is a useful scripting feature for exiv2.

Breaking this down:

$ exiv2 -PkV --grep GPSL http://dev.exiv2.org/attachments/download/805/DSC_7154.jpg 
set Exif.GPSInfo.GPSLatitudeRef                   N
set Exif.GPSInfo.GPSLatitude                      51/1 106969/10000 0/1
set Exif.GPSInfo.GPSLongitudeRef                  W
set Exif.GPSInfo.GPSLongitude                     1/1 495984/10000 0/1
And applying it to an empty image:
$ curl -O --silent http://clanmills.com/robin.jpg
$ exiv2 -pa robin.jpg
$ 
Using the pipe:
$ exiv2 -PkV --grep GPSL http://dev.exiv2.org/attachments/download/805/DSC_7154.jpg | exiv2 -m- robin.jpg
And examining the metadata:
$ exiv2 -pa robin.jpg
Exif.Image.GPSTag                            Long        1  26
Exif.GPSInfo.GPSLatitudeRef                  Ascii       2  North
Exif.GPSInfo.GPSLatitude                     Rational    3  51deg 10.69690' 
Exif.GPSInfo.GPSLongitudeRef                 Ascii       2  West
Exif.GPSInfo.GPSLongitude                    Rational    3  1deg 49.59840' 
$

1173.patch Magnifier (20.1 KB) Robin Mills, 24 Nov 2015 18:27

Associated revisions

Revision 4044
Added by Robin Mills over 1 year ago

#1137. Implemented.

Revision 4045
Added by Robin Mills over 1 year ago

#1137. Updated manpage to document m and -pV and Added regression detector for both new options to test suite.

Revision 4046
Added by Robin Mills over 1 year ago

#1137. Correction to r4045.

Revision 4047
Added by Robin Mills over 1 year ago

#1137. Correction to r4045.

Revision 4050
Added by Robin Mills over 1 year ago

#1137. Correction to r4045.

History

#1 Updated by Robin Mills over 1 year ago

  • % Done changed from 0 to 80
  • Estimated time changed from 2.00 to 3.00

I'm going to mark this as 80% done as something might come up in the next few weeks.

The code to read the command files is in src/exiv2.cpp/parseCmdFiles(). I was a little surprised to see that it doesn't use basicio, so it's not possible to use -mhttp://this/that. The parsing of the command files uses std::getline(). getline is not part of the basicio API. The code could be modified to "suck up" the data using basicio into a temporary file and parsed with std::getline().

It's possible to argue at the -PV option should also imply -Pk as a convenience.

#2 Updated by Phil Harvey over 1 year ago

Robin Mills wrote:

$ exiv2 -PkV --grep GPSL http://dev.exiv2.org/attachments/download/805/DSC_7154.jpg | exiv2 -m- robin.jpg

The equivalent ExifTool command is:

$ exiftool -tagsfromfile http://dev.exiv2.org/attachments/download/805/DSC_7154.jpg "-gpsl*" robin.jpg

which avoids the need for a pipe. I wonder if a similar Exiv2 feature would be useful. Copying specific tags from one file to another is a very common operation.

#3 Updated by Robin Mills over 1 year ago

  • % Done changed from 80 to 50
  • Estimated time changed from 3.00 to 6.00

Ah, that's interesting, Phil. If you've thought of this, I don't have a stupid idea! Mind you, to give credit for the idea, it's Nick who asked if we could do something like this.

I'll say this is 50% done and bump the estimate from 3 to 6 hours. Something to think about on a wet afternoon in England (shouldn't need to wait long for that in November).

The syntax of the exiv2 command is:

$ exiv2 [options] [action] file
We generally say "do abc ...." to one or more files and our insert/extract/preview/sidecar files are usually paired by name with the file. The pipe idea fits nicely into that model as we are typically reading, or writing. A command-option such as -tagsfromfile is a departure from our usual semantics as we are reading from one source and writing to one or more outputs. Maybe the syntax could be:
$ exiv2 -PkV1 --grep GPSL http://dev.exiv2.org/attachments/download/805/DSC_7154.jpg robin.jpg
The purpose of the '1' is to say read from the first, and apply to the rest. Without the '1', the command:
$ exiv2 -PkV --grep GPSL http://dev.exiv2.org/attachments/download/805/DSC_7154.jpg robin.jpg
Would say "Report by reading both DSC_7154.jpg AND robin.jpg". Oh, I'm getting excited. The command:
$ exiv2 -PkV+ --grep GPSL http://dev.exiv2.org/attachments/download/805/DSC_7154.jpg robin.jpg foo.jpg man.jpg
Would be to pair the files, so copy from DSC... into robin.jpg, copy from foo.jpg to man.jpg.

#4 Updated by Phil Harvey over 1 year ago

The pairing idea is interesting. ExifTool has an automated way to do this which allows, for example, someone to copy GPS from sidecar XMP files to the corresponding JPG's:

exiftool -tagsfromfile %d%f.xmp "-gps*" some_directory/*.jpg

Which is also very useful.

ExifTool also allows copying from multiple sources, ie) to copy Make and Model from another JPG, and GPS tags from the XMP sidecar:

exiftool -tagsfromfile other_directory/%f.jpg -make -model -tagsfromfile %d%f.xmp "-gps*" some_directory/*.jpg

but this feature is rarely used. However, if the syntax would allow it and it was easy to implement then it might be nice to have this ability too.

#5 Updated by Robin Mills over 1 year ago

  • File 1173.patchMagnifier added
  • % Done changed from 50 to 90

I have implemented this and attach a patch. I'm not going to submit this to trunk at the time, for a variety of reasons:

  1. There is an alternative to this using the pipe design.
  2. Let's wait for feedback concerning the 'set a.b.c value' commands. Life may be more complicated than this!
  3. The -P1 and -P+ options are new semantics for the exiv2 application. I'd like to pause and think before jumping into a major semantic change.
  4. We should avoid adding complicated stuff to the exiv2 application as its purpose is to be our test harness and "more complex" sample application.

So Phil: As always, I appreciate your input and encouragement. And I want you to know that I have spent a rainy afternoon implementing this. For the moment, I prefer to leave things as they are.

Demo:

$ curl -O --silent http://clanmills.com/robin.jpg ; exiv2 -pa robin.jpg ; exiv2 -P1V --grep GPSL ~/temp/Stonehenge.jpg  robin.jpg ; exiv2 -pa robin.jpg
Exif.Image.GPSTag                            Long        1  26
Exif.GPSInfo.GPSLatitudeRef                  Ascii       2  North
Exif.GPSInfo.GPSLatitude                     Rational    3  51deg 10.69690' 
Exif.GPSInfo.GPSLongitudeRef                 Ascii       2  West
Exif.GPSInfo.GPSLongitude                    Rational    3  1deg 49.59840' 
779 rmills@rmillsmbp:~/gnu/exiv2/trunk $ 
Implementing the -P+ option is a mod to the code in exiv2.cpp around line 930.
    // #1137
    // when -P1 is set (prFirst), pop the first file, print the metadata to stream and parse
    // set the action to modify to read the generated commands
    //
    // implementing the -P+ (prPair) feature is a simple variation of this code
    // to read (and subsequently remove) every other file in params->files_ to stream
    //
    if (rc == 0 && Params::instance().printItems_ & Params::prFirst ) {
        Params& params = Params::instance();
        // Cancel prFirst
        params.printItems_ |= !Params::prFirst;

        // Read and pop the first file
        std::string           file  = params.files_[0];
        Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(file);
        assert(image.get() != 0);
        image->readMetadata();
        params.files_.erase (Params::instance().files_.begin());

        // Write the 'set' commands and read them into modifyCmds_
        Action::Print* printAction = new Action::Print();
        std::ostringstream    os ;
        printAction->printMetadata(image.get(),os);
        delete printAction;
        std::istringstream is(os.str());
        parseCmdStream(modifyCmds_,is,file);

        // Change action to modify
        params.action_ = Action::modify;
    }

#6 Updated by Robin Mills over 1 year ago

Nick:

I've made you a watcher of this issue. So you'll get an email when this issue is updated. You'll see that Phil and I have had a little discussion about this.

#7 Updated by Nick Henshaw over 1 year ago

Robin Mills wrote:

Nick:

I've made you a watcher of this issue. So you'll get an email when this issue is updated. You'll see that Phil and I have had a little discussion about this.

Thanks Robin

I can see that you have a number of solution options, and it's dificult for me, as a newby, to comment on the suitability of piping, copying, pairing.

However from a "user" point of view, the typical scenario is:
I have some pics taken with my phone (with GPS and usually telling the right local time), and some with my camera (elderly DSLR, GPS-ignorant and usually telling the wrong local time). The camera pics are generally in clumps - I am less likely to wander and snap with this one, more likely (on balance) to get it out, take a few snaps in one place, put it away. So on the assumption that I have taken at least one pic with my phone in the same location, my typical requirement is to copy (in particular) GPS data from one phone pic to multiple camera pics.

Having said that, if I am generating exiv2 commands from lists, it is easier for me to generate multiple commands than to put multiple destinations on a single command line.

So:
exiv2 <params> Phonepic.JPG Camerapic1.JPG
exiv2 <params> Phonepic.JPG Camerapic2.JPG
exiv2 <params> Phonepic.JPG Camerapic3.JPG

is easier to generate than:
exiv2 <params> Phonepic.JPG Camerapic1.JPG Camerapic2.JPG Camerapic3.JPG

Don't know if this helps!

Nick

#8 Updated by Robin Mills over 1 year ago

Well you know how it is, Nick. These things are just features. They are horses for courses. At the moment, I haven't submitted the patch, so you'll have to generate the commands:

exiv2 -PkV --grep GPSL Phonepic.jpg | exiv2 -m- Camerapic1.jpg
exiv2 -PkV --grep GPSL Phonepic.jpg | exiv2 -m- Camerapic2.jpg
or
exiv2 -PkV --grep GPSL Phonepic.jpg > gps.txt
exiv2 -mgps.txt Camerapic1.jpg Camerpic2.jpg Camerapic3.jpg
or (and simplest)
exiv2 -PkV --grep GPSL Phonepic.jpg | exiv2 -m- Camerapic1.jpg Camerpic2.jpg Camerapic3.jpg
Incidentally, the reason I became interested in Exiv2 back in 2008 was to copy location data from the GPX file in my garmin running watch and update photos. I did that in Python and the script has been working without change for years. Today, I believe there are lots of applications that can read a GPX file and geotag your photos. I gave a 5 minute 'lightning' presentation about this at the Python 2010 Conference in Atlanta, GA. http://clanmills.com/2010/PyCon/RobinPyCon2010.mov

Last year I purchased a Nikon D5300 with a built-in GPS. Very convenient. I see the D5500 has removed the GPS feature. You can get standalone GPS devices which work with a variety of cameras. And it's possible that your phone can record a GPX file.

#9 Updated by Robin Mills over 1 year ago

I've just had another idea. We could write a script that calls exiv2 to get the time and location data from a directory of phone photos and writes a GPX file. So you do:

phone2gpx.bat c:\Pictures\From\Phone\ > foo.gpx
Then you drop foo.gpx into a directory of camera photos, and an use a geotagging application (such as my gps.py script). Snuffing to it.

#10 Updated by Robin Mills about 1 year ago

  • Status changed from Assigned to Closed
  • % Done changed from 90 to 100

Also available in: Atom PDF

Redmine Appliance - Powered by TurnKey Linux