News:

2023-03-15 Major improvements to the new Geolocation feature

Main Menu

How to disambiguate tag names within a single group?

Started by dae65, July 21, 2020, 04:54:06 AM

Previous topic - Next topic

dae65

Hi.

Some tag names appear in multiple groups, and we can disambiguate them by specifying a group. However, some tags are ambiguous within a group, not only across groups, but within groups. For instance, in the QuickTime ItemList group, a single tag name Title maps to two tag IDs, namely, titl and ©nam; tag name Album maps to albm and ©alb, etc.

Is it possible for the Image::ExifTool API to disambiguate between tag names within groups?

For example, the following script successfully writes "My Album" as an Album tag. How can I force it to write it either as albm or ©alb?


use Image::ExifTool;
my $file = shift;
my $exiftool = new Image::ExifTool ($file);
$exiftool->SetNewValue('Album', 'My Album', Group => 'ItemList');
$exiftool->WriteInfo($file);


What about reading ambiguous tag names: how can I know which underlying tag ID the content of $info->{Album} was read from?


#!/usr/bin/perl
use Image::ExifTool;
my $file = shift;
my $info = Image::ExifTool::ImageInfo ($file, {Group1 => 'ItemList'});
say STDOUT $info->{Album};


Thank you.

Phil Harvey

...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 ($).

dae65

I've read it. Thanks! :) I'd like to use the renaming strategy you mentioned at the end of that post. Now, would it be safe to redefine keys at run-time in the %Image::ExifTool::QuickTime::ItemList hash directly, instead of using an external configuration file, or a BEGIN block, with %Image::ExifTool::UserDefined {Image::ExifTool::QuickTime::ItemList}?

For my purposes, I was wondering if I would run into any problems if my scripts looped through all keys in %Image::ExifTool::QuickTime::ItemList and ...UserData, and replaced ambiguous tags names with new names, say, Album1, Album2, etc., like this:


use Image::ExifTool;
use Image::ExifTool::QuickTime;

my %groups = (
  ItemList => \%Image::ExifTool::QuickTime::ItemList,
  UserData => \%Image::ExifTool::QuickTime::UserData,
);

my @groups = qw/ItemList UserData/;

my (%names, %index, @renamed);

# find duplicates

foreach my $group (@groups) {
  my $href = $groups{$group} or next;
  foreach my $key (sort keys %$href) {
    my $value = $href->{$key};
    next if ref $value and ref $value ne 'HASH' or length $key != 4;
    my $name = (ref $value) ? $value->{Name} : $value;
    $names{$name}++;
  }
}

# rename

foreach my $group (@groups) {
  my $href = $groups{$group} or next;
  foreach my $key (sort keys %$href) {
    my $value = $href->{$key};
    next if ref $value and ref $value ne 'HASH' or length $key != 4;
    my $name = (ref $value) ? $value->{Name} : $value;
    if ($names{$name} > 1) {
      my $new_name = $name . ++$index{$name};
      if (ref $value) {
        $value->{Name} = $new_name;
        delete $value->{Avoid};
      } else {
        $href->{$key} = $new_name;
      }
      push @renamed, [$group, $key, $new_name];
    }
  }
}

binmode STDOUT, ':utf8';
say STDOUT join ' ', @$_ for sort { $a->[2] cmp $b->[2] } @renamed;


This prints the following output:

ItemList albm Album1
ItemList ©alb Album2
UserData albm Album3
UserData ©alb Album4
ItemList aART AlbumArtist1
UserData albr AlbumArtist2
ItemList ©ART Artist1
UserData ©ART Artist2
ItemList auth Author1
ItemList ©aut Author2
UserData auth Author3
ItemList ©cmt Comment1
UserData ©cmt Comment2
ItemList ©com Composer1
ItemList ©wrt Composer2
UserData ©com Composer3
UserData ©wrt Composer4
ItemList ©day ContentCreateDate1
UserData ©day ContentCreateDate2
ItemList cprt Copyright1
ItemList ©cpy Copyright2
UserData cprt Copyright3
UserData ©cpy Copyright4
ItemList desc Description1
ItemList dscp Description2
ItemList ©des Description3
UserData dscp Description4
ItemList ©too Encoder1
UserData ©too Encoder2
UserData CNFV FirmwareVersion1
UserData FIRM FirmwareVersion2
ItemList ©xyz GPSCoordinates1
UserData ©xyz GPSCoordinates2
ItemList gnre Genre1
ItemList ©gen Genre2
UserData gnre Genre3
UserData ©gen Genre4
ItemList grup Grouping1
ItemList ©grp Grouping2
UserData ©grp Grouping3
UserData Lvlm LevelMeter1
UserData lvlm LevelMeter2
ItemList ©lyr Lyrics1
UserData ©lyr Lyrics2
UserData manu Make1
UserData ©mak Make2
UserData CNMN Model1
UserData cmnm Model2
UserData modl Model3
UserData ©mdl Model4
UserData ©mod Model5
ItemList perf Performer1
UserData perf Performer2
UserData ptch Pitch1
UserData ©fpt Pitch2
ItemList rtng Rating1
UserData rtng Rating2
UserData roll Roll1
UserData ©frl Roll2
UserData SNum SerialNumber1
UserData slno SerialNumber2
ItemList titl Title1
ItemList ©nam Title2
UserData titl Title3
UserData ©nam Title4
ItemList ©trk Track1
UserData ©trk Track2
UserData _yaw Yaw1
UserData ©fyw Yaw2
ItemList yrrc Year1
UserData yrrc Year2


Thanks again. :)

Phil Harvey

Quote from: dae65 on July 21, 2020, 09:38:13 AM
I've read it. Thanks! :) I'd like to use the renaming strategy you mentioned at the end of that post. Now, would it be safe to redefine keys at run-time in the %Image::ExifTool::QuickTime::ItemList hash directly, instead of using an external configuration file, or a BEGIN block, with %Image::ExifTool::UserDefined {Image::ExifTool::QuickTime::ItemList}?

To do this you would use AddUserDefinedTags.

But note that you will find a side-effect of changing a tag name even if you do it the way I recommended: The tag with the original name will still be writable.  Fixing this "quirk" is possible at my end, but would be some work.

- 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 ($).

dae65

Thanks!  :D With AddUserDefinedTags, I'm now able to disambiguate all tags in the ItemList and UserData groups using (normalized) tag IDs. In other words, in order to set ©alb or albm separately, I use 'Alb' or 'Albm' respectively.

$exiftool->SetNewValue ( 'Alb', 'foo', Group => 'ItemList' );
$exiftool->SetNewValue ( 'Albm', 'bar', Group => 'ItemList' );
$exiftool->WriteInfo ($file)


And then, Image::ExifTool::ImageInfo ($file) returns a hash with user-defined tag names as expected. So far, so good.

Strangely, the GetValue method is not working though. I mean, having just set Alb to 'foo', written it to a file, and read it with ImageInfo, still:

say STDOUT $exiftool->GetValue ( 'Alb', { Group1 => 'ItemList' } );

prints nothing. Would help me figure out what I'm doing wrong now, please? Thanks! :)

Here is the code I'm using to disambiguiate all tags in the ItemList and UserData groups;


use Image::ExifTool;
use Image::ExifTool::QuickTime;

my %tables = (
  ItemList => \%Image::ExifTool::QuickTime::ItemList,
  UserData => \%Image::ExifTool::QuickTime::UserData,
);

foreach my $group ( keys %tables ) {
  my %tags;
  foreach my $id ( grep { length == 4 } keys %{ $tables{$group} } ) {
    for ( $tables{$group}->{$id} ) {

      next if ref and ( ref ) ne 'HASH' || ! $_->{Name};
      next if not ref and /^\d+$/ || ! /\w/;

      my $name = $id;
      $name =~ s/\W//g;
      $name = ucfirst $name;

      if ( ref ) {
        my ( $key, $value );
        $tags{$id}->{$key} = $value while ( $key, $value ) = each %$_;
        $tags{$id}->{Name} = $name;
        delete $tags{$id}->{Avoid};
      } else {
        $tags{$id} = $name;
      }
    }
  }
  Image::ExifTool::AddUserDefinedTags ( "Image::ExifTool::QuickTime::$group", %tags);
}

Phil Harvey

Are you loading your user-defined tags and calling ImageInfo() before GetInfo()?

This works for me:

#!/usr/bin/perl -w
use strict;
use Image::ExifTool;

my $testfile = 'a.mov';

Image::ExifTool::AddUserDefinedTags ( 'Image::ExifTool::QuickTime::ItemList', desc => 'MyDesc' );

my $et = new Image::ExifTool;

$et->SetNewValue(MyDesc => 'testing 1 2 3');
$et->WriteInfo($testfile);

$et->ImageInfo($testfile);

print $et->GetValue ( 'MyDesc', { Group1 => 'ItemList' } );

# end


- 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 ($).

dae65

Now it works. Thanks. :) Actually, I was calling ImageInfo at the right point but wrongly as a function, not as an object method. It seems GetValue won't get a value unless ImageInfo is called first and as a method.

Thanks a lot for your help, Phil. I believe other users have already requested methods to access tags by means of IDs, instead of names, something like SetNewValueByID and GetValueByID? If no one has yet, I'd like to request it, please.

Phil Harvey

Quote from: dae65 on July 22, 2020, 12:49:33 PM
It seems GetValue won't get a value unless ImageInfo is called first and as a method.

Yes.

QuoteI believe other users have already requested methods to access tags by means of IDs, instead of names, something like SetNewValueByID and GetValueByID? If no one has yet, I'd like to request it, please.

I don't know how this would work.  You want to provide a tag ID and what?...  A table reference?  A group name?  And what about the case where multiple tags have the same ID in the same table (which happens a lot)?

- 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 ($).

dae65

Quote from: Phil Harvey on July 22, 2020, 05:09:30 PM
You want to provide a tag ID and what?...  A table reference?  A group name?
A tag ID and a group name. I suppose that at some level this has to be enough, unless due to how Image::ExifTool was designed, it's just impossible to do without Tag names.

$et->SetNewValueByID ( '©alb', 'MyAlbum', Group => 'ItemList' );.

Quote from: Phil Harvey on July 22, 2020, 05:09:30 PM
And what about the case where multiple tags have the same ID in the same table (which happens a lot)?
I didn't know this could happen. I thought IDs were unique in a table.

Phil Harvey

Quote from: dae65 on July 22, 2020, 06:15:07 PMI didn't know this could happen. I thought IDs were unique in a table.

MakerNotes a particularly bad for this.  There are many cases where the tag represented by a given ID depends on other factors (camera model or software version number for example).

- 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 ($).

Phil Harvey

This problem has been resolved with the addition of family 7 group names in ExifTool 12.03.

- 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 ($).