-IF expression: Can I have 2 conditions in it? (e.g. X and Y both true)

Started by robertklink, March 26, 2021, 08:49:45 PM

Previous topic - Next topic

robertklink

My basic endeavor is to go through my collection of 100k+ images and associate each one with the person that took them, mostly based off the camera model from the EXIF -Model tag and recollections about who was using them and when. My collection is basically ordered folder wise by Year (e.g. 2010) then multiple sub folders by time frame and subject (e.g., 2010-12-22 to -28 Xmas).

So in the simplest form I'm successfully using the following example command and running it over & over with different bits of data, 1 year directory at a time:

exiftool -overwrite_original -r -m -Artist="Robert Klink" -OwnerName="Robert Klink" -if "$Model=~/DMC-TZ5/i" "D:\Folders Synced from NAS\P_Picasa(P)\Pictures\Pictures by Year\2010"

   84 directories scanned
9794 files failed condition
  779 image files updated
    0 image files read


Now to get to the point of this topic:
My next desired step up in efficiency is to evolve to an "IF (X=true AND Y=true) THEN write these tags" capability. (Where X is the -Model tag value and Y is the DateTimeOriginal value.)

This improvement is desired to cope with the fact the cameras in our family have passed from person to person over time.

I think this involves implementing Perl regular expressions (regex) in some fashion - which I'm quite novice at. Can those be used within the exiftool command? Or am I looking at higher order scripting?

In my ultimate "dream" script I'd associate each of ~30 camera models and start/end dates with a person's name to automate the tag writing in far fewer passes and manual cmd edits. Would welcome suggestions/examples towards that end. I'm no wiz at such stuff, but I can generally work it out if I have some pertinent examples to work from.

Windows 10

StarGeek

Quote from: robertklink on March 26, 2021, 08:49:45 PM
My next desired step up in efficiency is to evolve to an "IF (X=true AND Y=true) THEN write these tags" capability. (Where X is the -Model tag value and Y is the DateTimeOriginal value.)
<...>
I think this involves implementing Perl regular expressions (regex) in some fashion - which I'm quite novice at. Can those be used within the exiftool command? Or am I looking at higher order scripting?

It doesn't necessarily need to be RegEx, it can be simple comparisons.  For example (digging into my clipboard history program)
exiftool -if "defined $XMP-dc:Description and defined $IPTC:Caption-Abstract and $XMP-dc:Description ne $IPTC:Caption-Abstract" -XMP-dc:Description -IPTC:Caption-Abstract  /path/to/files/
This command checks to see if both Caption-Abstract and Description exist (using the Perl Defined function) and if they are not the same value.  As you can see, you can string things together with your basic AND, OR, and NOT.

Just about any Perl code can be used.  One of the main ways I learned Perl was to have the idea of what I wanted to do and then searched the various StackExchange sites for a code snippet that did the job.  Make it work and then figure out what I actually did.

QuoteIn my ultimate "dream" script I'd associate each of ~30 camera models and start/end dates with a person's name to automate the tag writing in far fewer passes and manual cmd edits. Would welcome suggestions/examples towards that end. I'm no wiz at such stuff, but I can generally work it out if I have some pertinent examples to work from.

This sounds like a bigger project than something you would run on the command line.  So you would probably want to make a user-defined function for this.  See the example.config file for some details.

Here's a simple example of a tag in my config file.  It checks the SerialNumber to see if it can be linked to a specific camera and returns the name of the owner of that camera.
%Image::ExifTool::UserDefined = (
'Image::ExifTool::Composite' => {
CopyrightBySerialNumber => {
Require => 'SerialNumber',
ValueConv => q{
if ($val == '123456') { return "John Smith"; }
if ($val == '789012') { return "Jane Doe"; }
if ($val == '567890') { return "StarGeek"; }
if ($val == '8675309') { return "Jenny"; }
if ($val == '42') { return "Life, Universe, Everything"; }
return undef;
},
},
############
},
);
"It didn't work" isn't helpful. What was the exact command used and the output.
Read FAQ #3 and use that cmd
Please use the Code button for exiftool output

Please include your OS/Exiftool version/filetype

robertklink

After a brief refresh on regular expressions, I believe I was off-base on thinking of those in the context of performing flow control logic operations. More just a means of formatting variables that flow control logic (etc) would use.

So I think I may be needing a Win CMD or Powershell script - which I'm just barely literate with. I believe that Perl would be better apart from the fact that I'm totally illiterate with it.

Or just continue to work through it manually.

I do also have the capability to use CSV files to export the pertinent tags, manually massage in a spreadsheet and write new tag values back via CSV, but that's a lot of manual massaging.


robertklink

The big "AHA" for me in your reply is that I can string together multiple conditions within the IF function. I'll try that out tomorrow; it should give me a lot more power in working through my endeavor.

As to the "It checks the serial number" code, it looks valuable to me to. But I'm not seeing the bigger picture of how to employ it.

I presume that it's called (with arguments?) from an overarching Perl script, returns the value back to it, and the script then uses an exiftool command to write the tag?

StarGeek

Quote from: robertklink on March 26, 2021, 10:16:23 PM
As to the "It checks the serial number" code, it looks valuable to me to. But I'm not seeing the bigger picture of how to employ it.

It's an example of how you might do the "associate each of ~30 camera models and start/end dates".  The require line would become
Desire => { 0 => 'DateTimeOriginal', 1 => 'Model' },
then you would have rows of
if ($val[1] eq 'Canon Camera' and $val[0] gt '2001' and $val[0] lt '2005') { return "Jane Doe used this camera"; }
where you would check the camera model (held in $val[1] in this example) and check the date range (DateTimeOriginal held in $val[0]).  This would return the value at the end and you could do whatever you want with the result.  Copy the result into another tag, parse the result in a batch file, etc.

QuoteI presume that it's called (with arguments?) from an overarching Perl script, returns the value back to it, and the script then uses an exiftool command to write the tag?

It would be called on the command line.  You would save out the example code into a text file, tell exiftool to include it with the -Config option, and then you treat the new CopyrightBySerialNumber as you would any other exiftool tag.  In my case, I use it like this:
exiftool -P -overwrite_original -d "%Y" -if "$CopyrightBySerialNumber and not (defined $CopyrightNotice and defined $rights and defined $Copyright)" "-MyCopyRight<© $datetimeoriginal $CopyrightBySerialNumber, All rights reserved." /path/to/files
This command checks to see if the CopyrightBySerialNumber exists and at least one of the three copyright tags is empty.  It then will write the full copyright text to all three tags (MyCopyRight is a shortcut I made for all three tags).
"It didn't work" isn't helpful. What was the exact command used and the output.
Read FAQ #3 and use that cmd
Please use the Code button for exiftool output

Please include your OS/Exiftool version/filetype

robertklink

I went with StarGeek's "make a user defined function via a Config file" suggestion for this and was successful in defining ~ 40 different "Model to Owner" relationships, including ones that vary with time. Works great, but ...

My next complication to deal with is the fact that my wife & I simultaneously use identical model cell phones as cameras and there's no EXIF data (e.g. serial number) to differentiate them. (Lesson learned - edit OwnerName in when I first move them from phone into my PC master collection.) But for probably 90% of them I have at least kept them in separate subfolders bearing 'Name 1' (me)' or 'Name 2' (wife).

So now I'm trying to add FilePath as a 3rd criteria (Val[2]) into my function, in addition to DateTimeOriginal and Model. More specifically to find desired 'Name' (e.g. "Pat's") in the Filepath tag within the IF logic construct. I know the syntax to do that in an exiftool command, but that doesn't seem to work in my config file - nor did I really expect it would.

Here's a shortened version of my "OwnerByModelAndDate" function.
The # commented out "if ($val[1] eq 'SM-G935T' and $val[2] =~/Pat's/)  { return "Pat's S7 Edge"; }" is my "blows it up" conundrum.
Help please.


%Image::ExifTool::UserDefined = (
'Image::ExifTool::Composite' => {
OwnerByModelAndDate => {
Desire => { 0 => 'DateTimeOriginal', 1 => 'Model', 2 => 'FilePath' },
ValueConv => q{
# if ($val[1] eq 'SM-G935T' and $val[2] =~/Pat's/)  { return "Pat's S7 Edge"; }
if ($val[1] eq 'SM-G935T')  { return "Bob or Pat's S7 Edge"; }
if ($val[1] eq 'PENTAX Optio 555' and $val[0] lt '2008:03:04') { return "Robert Klink"; }
if ($val[1] eq 'PENTAX Optio 555' and $val[0] gt '2008:03:04') { return "Patricia Klink"; }
if ($val[1] eq 'DMC-TZ5' and $val[0] lt '2009:12:13') { return "Robert Klink"; }
return undef;
},
},
############
},
);

robertklink

Okay, never mind, I fixed it - after much tinkering with different permutations of the syntax.

Unsettling feeling though is I really don't know what the fix was because it looks the same as I started with to me.
Here's an abbreviated version of the "working now" code:

%Image::ExifTool::UserDefined = (
'Image::ExifTool::Composite' => {
OwnerByModelAndDate => {
Desire => { 0 => 'DateTimeOriginal', 1 => 'Model', 2 => 'FilePath'  },
ValueConv => q{
   if ($val[1] eq 'SM-G935T' and $val[2] =~/Bob's/)  { return "Bob's S7 Edge"; }
   if ($val[1] eq 'SM-G935T' and $val[2] =~/Pat's/)  { return "Pat's S7 Edge"; }
   if ($val[1] eq 'SM-G935T')  { return "Bob or Pat's S7 Edge"; }
   if ($val[1] eq 'SM-G965U' and $val[2] =~/Bob's/)  { return "Bob's S9+"; }
   if ($val[1] eq 'SM-G965U' and $val[2] =~/Pat's/)  { return "Pat's S9+"; }
   if ($val[1] eq 'SM-G965U')  { return "Bob or Pat's S9+"; }
return undef;
},
},
############
}, 


And a bit of "testing it" output:

C:\Users\rober>exiftool -Config C:\Users\rober\.Exiftool_config.txt -r -m -if "$OwnerByModelAndDate" -OwnerByModelAndDate -model -filepath "D:\Folders Synced from NAS\P_Picasa(P)\Pictures\Pictures by Year\2016\2016-05-10 to -11 Pat's Shanghai"
======== D:/Folders Synced from NAS/P_Picasa(P)/Pictures/Pictures by Year/2016/2016-05-10 to -11 Pat's Shanghai/20160508_093730.jpg
Owner By Model And Date         : Pat's S7 Edge
Camera Model Name               : SM-G935T
File Path                       : D:/Folders Synced from NAS/P_Picasa(P)/Pictures/Pictures by Year/2016/2016-05-10 to -11 Pat's Shanghai/20160508_093730.jpg
======== D:/Folders Synced from NAS/P_Picasa(P)/Pictures/Pictures by Year/2016/2016-05-10 to -11 Pat's Shanghai/20160508_100117.jpg
Owner By Model And Date         : Pat's S7 Edge
Camera Model Name               : SM-G935T
File Path                       : D:/Folders Synced from NAS/P_Picasa(P)/Pictures/Pictures by Year/2016/2016-05-10 to -11 Pat's Shanghai/20160508_100117.jpg
======== D:/Folders Synced from NAS/P_Picasa(P)/Pictures/Pictures by Year/2016/2016-05-10 to -11 Pat's Shanghai/P3180544.JPG
Owner By Model And Date         : Patricia Klink
Camera Model Name               : DMC-ZS20
File Path                       : D:/Folders Synced from NAS/P_Picasa(P)/Pictures/Pictures by Year/2016/2016-05-10 to -11 Pat's Shanghai/P3180544.JPG
======== D:/Folders Synced from NAS/P_Picasa(P)/Pictures/Pictures by Year/2016/2016-05-10 to -11 Pat's Shanghai/P3180546.JPG
Owner By Model And Date         : Patricia Klink
Camera Model Name               : DMC-ZS20


All's well that ends well.

robertklink

Cancel that never mind  ???

I'm having trouble with the IF conditions where I'm looking for Model to contain string1 AND FilePath to contain String2 in my UserDefined "QwnerByModelAndDate" tag.

Here's the beginning part of it. The problematic IFs are the 4 in bold. I intentionally have both =~'Bobs' and =~/Bob's/ variations as tryout cases in this version.

%Image::ExifTool::UserDefined = (
   'Image::ExifTool::Composite' => {
      OwnerByModelAndDate => {
         Desire => { 0 => 'DateTimeOriginal', 1 => 'Model', 2 => 'FilePath'  },
         ValueConv => q{
            if ($val[1] =~ 'SM-G965U' and $val[2] =~'Bobs')  { return "Bobs S9+"; }
            if ($val[1] =~ 'SM-G965U' and $val[2] =~/Bob's/)  { return "Bob's S9+"; }
            if ($val[1] =~ 'SM-G965U' and $val[2] =~'Pats')  { return "Pats S9+"; }
            if ($val[1] =~ 'SM-G965U' and $val[2] =~/Pat's/)  { return "Pat's S9+"; }

            if ($val[1] =~ 'SM-G965U')  { return "Bob or Pat's S9+"; }
            if ($val[1] =~ 'PENTAX Optio 555' and $val[0] lt '2008:03:04') { return "Robert Klink"; }
            if ($val[1] =~ 'PENTAX Optio 555' and $val[0] gt '2008:03:04') { return "Patricia Klink"; }
            if ($val[1] =~ 'DMC-TZ5' and $val[0] lt '2009:12:13') { return "Robert Klink"; }
            if ($val[1] =~ 'DMC-TZ5' and $val[0] gt '2009:12:13') { return "Patricia Klink"; }
            if ($val[1] =~ 'DMC-ZS3' and $val[0] lt '2010:08:05') { return "Robert Klink"; }
            if ($val[1] =~ 'DMC-ZS3' and $val[0] gt '2010:08:05') { return "Patricia Klink"; }
            if ($val[1] =~ 'DMC-ZS7' and $val[0] lt '2012:08:24') { return "Robert Klink"; }
            if ($val[1] =~ 'DMC-ZS7' and $val[0] gt '2012:08:24') { return "Patricia Klink"; }
            if ($val[1] =~ 'DMC-ZS20' and $val[0] lt '2015:06:12') { return "Robert Klink"; }
            if ($val[1] =~ 'DMC-ZS20' and $val[0] gt '2015:06:12') { return "Patricia Klink"; }
            if ($val[1] =~ 'SPH-L720')  { return "Robert Klink"; }



I'm running this against a simple "Scratch" Directory setup to test each of those four conditions. It has four subfolders, each having a single JPG image:
Bobs GS9
Bob's GS9
Pats GS9
Pat's GS9

(I have both 's and (no')s variations of the name of interest in my master collection folders so need to cover both cases.)

The run result is as follows:
C:\Users\rober\Documents\Batch_Scripts\CMD scripts>exiftool -Config C:\Users\rober\.Exiftool_config.txt -r -OwnerByModelAndDate "D:\Folders Synced from NAS\P_Picasa(P)\Pictures\Pictures by Year\scratch"
======== D:/Folders Synced from NAS/P_Picasa(P)/Pictures/Pictures by Year/scratch/Bob's GS9/20191128_143703.jpg
Owner By Model And Date         : Bob or Pat's S9+
======== D:/Folders Synced from NAS/P_Picasa(P)/Pictures/Pictures by Year/scratch/Bobs GS9/20191123_084941.jpg
Owner By Model And Date         : Bob or Pat's S9+
======== D:/Folders Synced from NAS/P_Picasa(P)/Pictures/Pictures by Year/scratch/Pat's GS9/20191222_122922.jpg
Owner By Model And Date         : Bob or Pat's S9+
======== D:/Folders Synced from NAS/P_Picasa(P)/Pictures/Pictures by Year/scratch/Pats GS9/20191222_122920.jpg
Owner By Model And Date         : Bob or Pat's S9+
    5 directories scanned
    4 image files read


As you can see it always falls through to the 5th IF, which is the default for no string2 match. Whereas I'm expecting all 4 files to be caught by the IFs above.







Phil Harvey

I don't have time to read through this in detail, but you will need to set the RequestAll API option for FilePath to be generated.

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

robertklink

Yea, that worked - once I figured out I had to put it in the exiftool cmd, not in my user-defined tag in Config.txt. Found the clue in FAQ 30.

I found various places in the forum referring to RequestAll=2, or =3. But no documentation what =2 or =3 mean. I presume different levels of how deep to dig? I used "-api RequestAll" without any =.

Speaking of FAQ 30, I understand from it that this is going to slow down my process. I'll live with that, but wondering if I might speed it up in another way.

My basic batch job is:
(Pass 1) exiftool do blah_1 recursively in directory YYYY (~ 50 subfolders / 10,000 files +/-) (Pass 1 needs the Selectall)
(Pass 2) exiftool do blah_2 to same directory
(Pass 3) exiftool do blah_3 to same directory

Repeat on Subfolders 2003 thru 2021

Wondering if I can combine the 3 different commands into a single pass and perhaps save processing / disk access time? I believe I saw something like that in the forum once, but hard to find it again.

If you think that may have merit, I'll open a separate post with more details.

StarGeek

Quote from: robertklink on April 08, 2021, 11:25:58 PM
I found various places in the forum referring to RequestAll=2, or =3. But no documentation what =2 or =3 mean. I presume different levels of how deep to dig? I used "-api RequestAll" without any =.

The RequestAll API entry says
     May be set to 2 or 3 to enable generation of some additional tags as mentioned in the tag name documentation

Using Google to search the Tag Name pages (site:exiftool.org/TagNames "RequestAll") only shows entries on the Extra tags page and the MacOS tags page.  You can search on those pages for RequestAll and check the Notes column.
"It didn't work" isn't helpful. What was the exact command used and the output.
Read FAQ #3 and use that cmd
Please use the Code button for exiftool output

Please include your OS/Exiftool version/filetype

Phil Harvey

ExifTool command may be combined into a single command as long as subsequent commands don't rely on changes made by earlier commands.  But even if this is the case, multiple commands may be combined into a single command line using the -execute option.  Using -execute avoids the overhead of loading exiftool for each command, but doesn't avoid processing the files multiple times.

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

StarGeek

Quote from: Phil Harvey on April 12, 2021, 08:44:13 AMUsing -execute avoids the overhead of loading exiftool for each command, but doesn't avoid processing the files multiple times.

In my experience, when running only a few commands, like three in this case, this overhead is minimal, especially if you're running it on a large number of files.  I only shifted to using -execute when I was running 15-20 commands in sequence and on a small number of files each time.  Using -execute is harder to troubleshoot and maintain commands, so you have to decide if the trade off is going to be worth it.
"It didn't work" isn't helpful. What was the exact command used and the output.
Read FAQ #3 and use that cmd
Please use the Code button for exiftool output

Please include your OS/Exiftool version/filetype