ExifTool Forum

ExifTool => The Image::ExifTool API => Topic started by: Tom Cunningham on April 07, 2016, 09:58:02 PM

Title: Setting new values
Post by: Tom Cunningham on April 07, 2016, 09:58:02 PM
This is just out of idle curiosity, since I have a workaround.  I have the following code (yes, I know I'm not checking return codes, yadda-yadda :-D):


    my $setNameType = 1;
    ...
    {
        ...
        $success = $exifTool->SetNewValue('RegionName' => \@regionName);
        $success = $exifTool->SetNewValue('RegionType' => \@regionType);
        $setNameType = 0;
        $success = $exifTool->SetNewValue('RegionAreaUnit' => \@regionAreaUnit);
        $success = $exifTool->SetNewValue('RegionAreaX' => \@regionAreaX);
        $success = $exifTool->SetNewValue('RegionAreaY' => \@regionAreaY);
        $success = $exifTool->SetNewValue('RegionAreaW' => \@regionAreaW);
        $success = $exifTool->SetNewValue('RegionAreaH' => \@regionAreaH);
        $exifTool->WriteInfo($file);
$info = $exifTool->ImageInfo($file);
    }

    if (defined $$info{RegionName} && @{$$info{RegionName}} > 0) {
        @regionName = uniq(@{$$info{RegionName}});
        # if RegionName exists, assume corresponding RegionType exists as well
        @regionType = @{$$info{RegionType}};
        if (@regionName < @{$$info{RegionName}}) {
            splice @regionType, 1, @{$$info{RegionName}} - @regionName;
        }
        # use the regionName as the basis for Keywords, Subject, etc.
        $updates = 1;  # we'll always update
        if ($setNameType) {
            $success = $exifTool->SetNewValue('RegionName' => \@regionName);
            $success = $exifTool->SetNewValue('RegionType' => \@regionType);
        }
        $success = $exifTool->SetNewValue('Keywords' => \@regionName);
        $success = $exifTool->SetNewValue('Subject' => \@regionName);
    }
    ...
    $exifTool->WriteInfo($file);
   


Assume that going in @regionName is [foo, bar, baz], and @regionType is [Face, Face, Face].  If I remove the lines referring to $setNameType (i.e., no assignment and no conditional), the RegionName tag eventually gets written as [foo, bar, baz, foo, bar, baz], and RegionType is [Face, Face, Face, Face, Face, Face].  The duplication is what surprises me, as I though SetNewValue overwrites by default.  If I use setNameType as shown, I get what I expect (e.g. no duplication).  Can someone clue me to what I'm missing?
Title: Re: Setting new values
Post by: Hayo Baan on April 08, 2016, 01:28:01 AM
Multiple setNewValue calls add up for list types. To prevent that, set the Replace option. See the documentation (https://exiftool.org/ExifTool.html#SetNewValue)
Title: Re: Setting new values
Post by: Phil Harvey on April 08, 2016, 07:42:46 AM
In general I would recommend writing these as structured tags (ie. add structures to XMP-mwg-rs:RegionList).  Usually it is easier this way to ensure that you are making the proper associations.

- Phil
Title: Re: Setting new values
Post by: Tom Cunningham on April 08, 2016, 09:54:22 PM
Thanks, guys!  I saw "0 = Overwrite existing value(s)" in AddValue as the default and thought that would take care of it, completely missed Replace.  The structured tags look good, I'll give them a whirl.
Title: Re: Setting new values
Post by: Tom Cunningham on April 13, 2016, 09:40:43 AM
Totally confused.  So I do: my $info = $exifTool->ImageInfo($file);I thought that when I used@{$$info{RegionName}}I would get a count of the elements in the RegionName array.  Instead, I get what I consider to be the first element of the array, a string.  I think I'm getting lost in the levels of indirection.
Title: Re: Setting new values
Post by: Hayo Baan on April 13, 2016, 10:24:23 AM
Perl's handling of list variables is probably what was confusing you ;D

When you use an array (list) variable in scalar context, you get the number of elements in the array, but when you use it in list context, you get the elements themselves.

Example:
my @list = ( "One", 2 ,3 ,4, "Five" );
my $list = @list;
print "$list\n";
print "@list\n";

will give the following output:
5
One 2 3 4 Five

(print by default just shows all elements with a space in between. To do this comma separated, do something like this:
print join(", ", @list) . "\n";

Hope this helps,
Hayo
Title: Re: Setting new values
Post by: Phil Harvey on April 13, 2016, 10:34:02 AM
I agree with Hayo's assessment.  But you must be careful about the context.  Your @{$$info{RegionName}} should return the array, provided that $$info{RegionName} was an ARRAY reference.  Then what you get depends on the context:

$var = @{$$info{RegionName}};  # scalar context returns number of elements in the array

($var) = @{$$info{RegionName}};  # list context returns first element of the array

- Phil
Title: Re: Setting new values
Post by: Tom Cunningham on April 13, 2016, 10:59:00 AM
OK, maybe it's context confusion.  :P  Here's some more detail:

    if (!defined $$info{RegionName} || @{$$info{RegionName}} == 0)

Perl complains:

Can't use string ("Joe Smith") as an ARRAY ref while "strict refs" in use at ../../../../Metadata/EXIF/ExifTool/exiftidy.pl line 216.

So what's the syntax for getting the number of elements from $$info{RegionName} (or, erm, what it refers to)?
Title: Re: Setting new values
Post by: Phil Harvey on April 13, 2016, 11:37:13 AM
OK.  I think we have to step back a bit.  I said:

Quoteprovided that $$info{RegionName} was an ARRAY reference

You must check this since it may be a scalar if there is just one element:

if (ref $$info{RegionName} eq 'ARRAY') {
    $num = @{$$info{RegionName}};
} elsif (defined $$info{RegionName}) {
    $num = 1;
} else {
    $num = 0;
}


- Phil
Title: Re: Setting new values
Post by: Tom Cunningham on April 13, 2016, 11:46:17 AM
Wait, so if a list/array has only one element, it's always interpreted as a scalar?  BTW, thanks for your help with this, Phil (Hayo too!).
Title: Re: Setting new values
Post by: Hayo Baan on April 13, 2016, 12:58:49 PM
Hi Tom,

No, arrays are always interpreted as arrays. However, a reference can point to anything so you have to know what it is pointing to. Normally you do know what it is, but in this case it turns out it can be either. exiftool treats it as a scalar if there is just one element which I hadn't expected myself either, but it is probably mentioned in the documentation ;D
Title: Re: Setting new values
Post by: Phil Harvey on April 13, 2016, 01:12:59 PM
Quote from: Hayo Baan on April 13, 2016, 12:58:49 PM
it is probably mentioned in the documentation ;D

Hmmm.  If it is, I can't find it, other than this quote:

    an array reference may be used to indicate a list

(emphasis added)

Depending on the options, a list with a single item may sometimes be returned as an ARRAY ref (eg. if Struct is used), and a list with multiple items may be returned as a SCALAR (eg. if the List option isn't set).

- Phil