Setting new values

Started by Tom Cunningham, April 07, 2016, 09:58:02 PM

Previous topic - Next topic

Tom Cunningham

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?

Hayo Baan

#1
Multiple setNewValue calls add up for list types. To prevent that, set the Replace option. See the documentation
Hayo Baan – Photography
Web: www.hayobaan.nl

Phil Harvey

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

Tom Cunningham

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.

Tom Cunningham

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.

Hayo Baan

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
Hayo Baan – Photography
Web: www.hayobaan.nl

Phil Harvey

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

Tom Cunningham

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)?

Phil Harvey

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

Tom Cunningham

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!).

Hayo Baan

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
Hayo Baan – Photography
Web: www.hayobaan.nl

Phil Harvey

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