xmp:datetimeoriginal

Started by Alan Clifford, March 15, 2011, 06:55:52 PM

Previous topic - Next topic

Alan Clifford

I decided to do my next bit of photo fiddling in Perl.  So first learn Perl.  Or at least a bit more. And as it seem perverse to call the command line exiftool from a Perl script, learn the API.
The idea is to take the exif:datetimeoriginal and create an xmp:datetimeoriginal (as recommended by Phil) based on the timezone when the photo was taken and the timezone of the camera.  The latter is usually UTC but not alway.

I have managed to do it but I have some questions.


use Image::ExifTool;
my $exifTool = new Image::ExifTool;
$exifTool->Options(DateFormat => "%S, %M, %H, %d, %m, %Y");
# my $info = $exifTool->ImageInfo($photofile, 'DateTimeOriginal', 'DatetimeOriginal (1)');
my $info = $exifTool->ImageInfo($photofile, 'exif:DateTimeOriginal', 'xmp:DatetimeOriginal');


Both forms of ->ImageInfo appear to work but I am unsure about the (1).  Is "xmp:datetimeoriginal" always "Datetimeoriginal (1)"?


print "exif            $$info{DateTimeOriginal}\n" ;
print "xmp             $$info{'DateTimeOriginal (1)'}\n" if $$info{'DateTimeOriginal (1)'};
my $xmp1 = $exifTool->GetValue('DateTimeOriginal (1)') ;
print "xmp (1)         $xmp1\n" if $xmp1;


Both the $$info way and the ->GetValue way won't accept "xmp:datetimeoriginal" so I really need to understand the (1) business.


my @dateno = split /,/, $exifTool->GetValue('DateTimeOriginal') ;


How can I be sure which datetimeoriginal I am getting?

At this point, $datestr is created, eg 2010:11:11 02:01:30+02:00.  The code is not relevant here.


my $rc = $exifTool->SetNewValue('xmp:DateTimeOriginal', $datestr) ;
print "set new value rc = $rc\n";
my $newxmp1 = $exifTool->GetValue('DateTimeOriginal (1)') ;
print "new xmp (1)     $newxmp1\n" if $newxmp1;


->Getvalue doesn't get the new value


my $rawval = $exifTool->GetNewValues('DateTimeOriginal') ;
print "raw val         $rawval\n" if $rawval;


$rawval returns the changed xmp:datetimeoriginal but it has to be called with Datetimeoriginal rather than xmp:datetimeoriginal or "datetimeoriginal (1)".  This is a bit confusing when ->getvalue uses datetimeoriginal for the exif:datetimeoriginal.


$rc = $exifTool->WriteInfo($photofile);
print "write rc = $rc\n";


It works.


Alan


Phil Harvey

Hi Alan,

I'll try to answer your questions:

Quote from: AlanClifford on March 15, 2011, 06:55:52 PM

use Image::ExifTool;
my $exifTool = new Image::ExifTool;
$exifTool->Options(DateFormat => "%S, %M, %H, %d, %m, %Y");
# my $info = $exifTool->ImageInfo($photofile, 'DateTimeOriginal', 'DatetimeOriginal (1)');
my $info = $exifTool->ImageInfo($photofile, 'exif:DateTimeOriginal', 'xmp:DatetimeOriginal');


Both forms of ->ImageInfo appear to work but I am unsure about the (1).  Is "xmp:datetimeoriginal" always "Datetimeoriginal (1)"?

The first version is wrong.  You call ImageInfo with tag names, not tag keys.  Tag keys may have instance numbers in brackets, but that is an implementation detail and you shouldn't be using these via the API.  You should let ExifTool generate the tag keys for you, like this:

my @tags = (  'exif:DateTimeOriginal', 'xmp:DatetimeOriginal' );
$info = $exifTool->ImageInfo($photofile, \@tags);


Then upon return the @tags array will contain the tag keys corresponding to the tags you requested.

Then you get the values you requested with $$info{$tags[0]} and $$info{$tags[1]}.

Quote
print "exif            $$info{DateTimeOriginal}\n" ;
print "xmp             $$info{'DateTimeOriginal (1)'}\n" if $$info{'DateTimeOriginal (1)'};
my $xmp1 = $exifTool->GetValue('DateTimeOriginal (1)') ;
print "xmp (1)         $xmp1\n" if $xmp1;


Both the $$info way and the ->GetValue way won't accept "xmp:datetimeoriginal" so I really need to understand the (1) business.

I hope my previous answer clears this up.

Quote

my @dateno = split /,/, $exifTool->GetValue('DateTimeOriginal') ;


How can I be sure which datetimeoriginal I am getting?

This too.

Quote

my $rawval = $exifTool->GetNewValues('DateTimeOriginal') ;
print "raw val         $rawval\n" if $rawval;


$rawval returns the changed xmp:datetimeoriginal but it has to be called with Datetimeoriginal rather than xmp:datetimeoriginal or "datetimeoriginal (1)".  This is a bit confusing when ->getvalue uses datetimeoriginal for the exif:datetimeoriginal.

True.  I admit that GetNewValues isn't a fully functional routine, but very few people (if any) use it.  But since you mention it I will look into adding the ability to specify the group name.  It won't ever accept tag keys like the reader routines because the tag keys are generated for extracted information, while GetNewValues returns information that has been queued for writing.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Alan Clifford

Thanks for that.

The only reason I tried getnewvalues was because getvalue didn't return the new value after I had set it with setnewvalue.  I guess I made an assumption that I shouldn't have about what setnewvalue does.  I am not used to this style of programming.

Alan

Alan Clifford

Phil, I found in your documentation that timezones are ignored in setting options for date values.  So no %z  :(

I came up with this to chop it out of the normal string returned for xmp:datetimeoriginal.

if ($xmp =~ /\+|-/) {
    $xmptz = $&.$' ;
    print STDERR "tz $xmptz\n" ;
 
I still need my Perl book open for this stuff so I hope that is a reasonable solution.

Alan

Phil Harvey

In general it is best to avoid using $1, $& and $' if possible because these are very inefficient.

Instead, I would have captured the interesting string like this:

$xmp =~ /([-+].*)/ and print STDERR "tz $1\n";

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Alan Clifford

Perl lessons as well!

I'd headed straight for a section in my book entitled "Grabbing parts of a string."  The $1 stuff was in the section prior to that.

It struck me that I could use this parentheses stuff to do a basename, dirname split instead of some longwinded rindex stuff from an example in the book.

$string = "/photographs/photo/somewhere/something.jpg" ;
$string =~ m|(.*/)(.*)| and print "dirname $1\nbasename $2\n";