Automatic tag adding depending on camera and lens model

Started by VlCTOR, November 06, 2019, 04:21:17 PM

Previous topic - Next topic

VlCTOR

Hi, everyone!
My family uses multiple cameras with interchangeable lenses and multiple cameras with built-in lenses. The photo storage (Synology) does work with EXIF and XMP tags, but it is not able to read tags from MakerNotes.
My idea is to use Exiftool to process all JPEG files in folder (*.jpg) at once:
1) if the model=DSLR-A500, then write multiple values in multiple tags, for example:
                 "Victor"       to  EXIF:OwnerName, IPTC:By-line, XMP:CopyrightOwnerName, etc.
                 "4634060"   to  EXIF:SerialNumber, XMP:CameraSerialNumber
                Then copy LensType and LensSpec from MakerNotes to EXIF and XMP.
                Then:
                       if MakerNotes:LensSpec=DT 16-50mm F2. 8 SSM, then write multiple values to multiple tags, for example:
                           EXIF:LensMake=SONY,
                           XMP:LensManufacturer=SONY,
                           XMP:LensModel=Sony DT 16-50mm F2. 8 SSM (SAL1650),
                           EXIF:LensSerialNumber=1881834
                      if MakerNotes:LensSpec=DT 50mm F1. 8 SAM, then write multiple values to multiple tags, for example:
                           EXIF:LensMake=SONY,
                           XMP:LensModel=Sony DT 50mm F1. 8 SAM (SAL50F18),
                           EXIF:LensSerialNumber=1833114

2) if model=ILCA-77M2, then the steps are the same as DSLR-A500, but with different Owner and Serial values.

3) if model=DSC-V1, then write multiple values in multiple tags, for example:
              "Victor" to EXIF:OwnerName, IPTC:By-line, XMP:CopyrightOwnerName...
              EXIF:FocalLengthIn35mmFormat<${FocalLength#;$_*=4.8}
              XMP: Lens=Carl Zeiss Vario-Sonnar 7-28mm F2. 8-4
              EXIF:LensMake=Carl Zeiss
              EXIF:LensSerialNumber=5970555

I tried to implement this algorithm in config_file, but I failed in Perl programming.
Will appreciate your help.

StarGeek

It might be possible to do this in a config file using the WriteAlso function, but I wouldn't want to attempt that.  It would just be to complicated.  This would be a case where using multiple commands would be best, IMO.  You could simplify the actual command by using ARGS files and combine with the -execute option.
"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

VlCTOR

Hello, StarGeek!
Thank you for your response.

That's the way I do it now. I created a common ARGS file and ARGS files for each camera and each lens. The Windows Command File (CMD) combines multiple ARGS files into one and Exiftool runs with the following parameters:
exiftool -config %my_cfg% -v0 -stay_open True -@ %my_args% *.jpg >>%log
To switch to other argument files, I use commands:
             -stay_open
             True
             -@ Body1_ARGSFILE 
(Body2_ARGSFILE, Body3_ARGSFILE, Lens1_ARGSFILE, Lens2_ARGSFILE)
But I manually sorting files into different folders now. And also manually specify the files of the arguments to be used. I want to automate this process.

My idea was to create a procedure in the CFGFILE that would read - EXIF:Model and MakerNotes: LensSpec.
Based on the data obtained, in accordance with the algorithm described in the first post, assign values to some variable. For example:
    MyLens= Sony DT 50mm F1.8 SAM (SAL50F18)
    MyLensSerial=1833114
    MyBodyOwner=Victor
    MyBodySerial=4634060

In the ARGS file use the following commands:
-EXIF:OwnerName < MyBodyOwner
-IPTC:By-line < MyBodyOwner
-XMP:CopyrightOwnerName < MyBodyOwner
-EXIF:SerialNumber < MyBodySerial
-XMP:CameraSerialNumber < MyBodySerial
...
-EXIF:LensSerialNumber < MyLensSerial


greybeard

If I was doing it I would probably write a short perl program using the exiftool interface.

Something like this (not intended to be complete and doesn't have exactly the same cameras and lenses but close enough to give an idea)

#!/usr/bin/perl

use strict;
use warnings;
use File::Find::Rule;
use File::Basename;
use Image::ExifTool;

my $dpath="/cygdrive/c/users/andy/downloads/tagsamp";
my @image_files = File::Find::Rule->file()
      ->name("*.jpg")
      ->maxdepth( 1 )
      ->in($dpath);

foreach my $fl (@image_files) {

  my $fl = shift;
  my ($filename, $dirs) = fileparse($fl,".jpg");
  my $exifTool = new Image::ExifTool;
  my $info     = $exifTool->ImageInfo($fl);
  my ($model,$lensinfo);
  if (!($model    = $$info{"Model"}))    { $model    = "Unknown"; }
  if (!($lensinfo = $$info{"LensInfo"})) { $lensinfo = "Unknown"; }
  printf("%-60s %-20s %-20s %-20s\n",$dirs,$filename,$model,$lensinfo);

  my ($tag,$value,$ok);

  if ($model eq "ILCA-77M2") {
     $tag = "EXIF:OwnerName"; $value = "Victor";
     $ok  = $exifTool->SetNewValue($tag, $value);
     if ($lensinfo eq "16-50mm f/2.8") {
        $tag = "EXIF:LensSerialNumber"; $value = "1833225";
        $ok  = $exifTool->SetNewValue($tag, $value);
     } elsif ($lensinfo eq "70-200mm f/2.8") {
        $tag = "EXIF:LensSerialNumber"; $value = "1881887";
        $ok  = $exifTool->SetNewValue($tag, $value);
     }
  } elsif ($model eq "DSLR-A580") {
     $tag = "EXIF:OwnerName"; $value = "Alice";
     $ok  = $exifTool->SetNewValue($tag, $value);
     if ($lensinfo eq "18-55mm f/3.5-5.6") {
        $tag = "EXIF:LensSerialNumber"; $value = "1833114";
        $ok  = $exifTool->SetNewValue($tag, $value);
     } elsif ($lensinfo eq "75-300mm f/4.5-5.6") {
        $tag = "EXIF:LensSerialNumber"; $value = "1881834";
        $ok  = $exifTool->SetNewValue($tag, $value);
     }
  } elsif ($model eq "DSC-V1") {
     $tag = "EXIF:OwnerName"; $value = "Eric";
     $ok  = $exifTool->SetNewValue($tag, $value);
     $tag = "XMP:Lens"; $value = "Carl Zeiss Vario-Sonnar 7-28mm F2. 8-4";
     $ok  = $exifTool->SetNewValue($tag, $value);
     $tag = "EXIF:LensMake"; $value = "Carl Zeiss";
     $ok  = $exifTool->SetNewValue($tag, $value);
     $tag = "EXIF:LensSerialNumber"; $value = "5970555";
     $ok  = $exifTool->SetNewValue($tag, $value);
  }

  $exifTool->WriteInfo($fl);

}

exit;

StarGeek

Quote from: VlCTOR on November 07, 2019, 07:45:59 AM
My idea was to create a procedure in the CFGFILE that would read - EXIF:Model and MakerNotes: LensSpec.
Based on the data obtained, in accordance with the algorithm described in the first post, assign values to some variable. For example:
    MyLens= Sony DT 50mm F1.8 SAM (SAL50F18)
    MyLensSerial=1833114
    MyBodyOwner=Victor
    MyBodySerial=4634060

In the ARGS file use the following commands:
-EXIF:OwnerName < MyBodyOwner
-IPTC:By-line < MyBodyOwner
-XMP:CopyrightOwnerName < MyBodyOwner
-EXIF:SerialNumber < MyBodySerial
-XMP:CameraSerialNumber < MyBodySerial
...
-EXIF:LensSerialNumber < MyLensSerial


Ok, sorry for misunderstanding. It sound as if you wanted it all written in as a single tag.  That is a much more reasonable goal and I actually have made a tag like that in the past.  I would get some photos from a few friends of mine and had a tag that just listed their name based upon the camera serial number, very much like your MyBodyOwner example.  Your config would just require some copy/pasted/edited of the same procedure.

At the core, each tag would be something like:
MyBodyOwner => {
Require => {
0 => 'Model',
},
Desire => {
1 => 'MakerNotes:LensSpec',
},
ValueConv => q{
if ($val[0] eq 'DSLR-A500') { return "John Smith"; }
if ($val[0] eq 'Something Else') { return "Jane Doe"; }
if ($val[0] eq 'Another') { return "Arthur Dent"; }
return undef;
},
},


Put in the Desire for tags which may not be there but in which you can continue without.  Require for any tags in which the tag would have to fail if they don't exist.  You would put your conditions in the parenthesis after the if, and they would be the same as you would have on an exiftool -if command.  So in the case of Model eq DSLR-A500 and Lens eq DT 16-50mm F2. 8 SSM, you would put
if ($val[0] eq 'DSLR-A500' and $val[1] eq 'DT 16-50mm F2. 8 SSM') { return "Resut"; }

It would just take careful examination of the tags you want to use and a bit of trial and error, but this would be the basic idea.  Copy/paste/edit for each of the tags you want to create.

"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

VlCTOR

Thank you so much!
Very clearly you have written everything.
That's exactly what I want.
Below is my fragment of code:
%Image::ExifTool::UserDefined = (
    'Image::ExifTool::Composite' => {
MyLens => {
Require => {
0 => 'Model',
},
Desire => {
1 => 'MakerNotes:LensSpec',
2 => 'MakerNotes:LensType',
},
ValueConv => q{
if ($val[0] eq 'DSC-V1') { return "Carl Zeiss Vario-Sonnar 7-28mm F2. 8-4"; }
                                if ($val[0] eq 'DSLR-A500' or $val[0] eq 'ILCA-77M2') { return $val[1]"; }
return undef;
},
},

MyBodyOwner => {
Require => {
0 => 'Model',
},
Desire => {
1 => 'MakerNotes:LensSpec',
},
ValueConv => q{
if ($val[0] eq 'DSLR-A500') { return "Victor"; }
if ($val[0] eq 'ILCA-77M2') { return "Zoya"; }
if ($val[0] eq 'DSC-V1') { return "Victor"; }
return undef;
},
},
    },
);
1; # end


I also have a few questions for you:
1) Does the headline procedure need and what should it be?
Like this: %Image::ExifTool::UserDefined = (
                 'Image::ExifTool::Composite' => {[/code]

2) Can i return multiple values in one procedure?
For example, if  model=DSC-V1, I need to assign ten variables. Maybe it will be an array. But each element of the array needs to be written in a separate tag. For example:
-XMP:CopyrightOwnerName<MyCamera[1]
...
-EXIF:SerialNumber<MyCamera[8]

StarGeek

Quote from: VlCTOR on November 07, 2019, 05:55:00 PM
1) Does the headline procedure need and what should it be?
Like this: %Image::ExifTool::UserDefined = (
                 'Image::ExifTool::Composite' => {

I don't understand.  What you have listed is what should be in order for exiftool to be able to use User Defined tags.

Quote2) Can i return multiple values in one procedure?
For example, if  model=DSC-V1, I need to assign ten variables. Maybe it will be an array. But each element of the array needs to be written in a separate tag.

No.  If you want you can return the value as a list, like keywords, but you can't really treat it as an array by itself.
"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

VlCTOR

Ok. Thank you very much, StarGeek.
I will try to use this in practice.
After all, it's not that hard to assign values to variables individually.

VlCTOR

Quote from: greybeard on November 07, 2019, 08:19:41 AM
If I was doing it I would probably write a short perl program using the exiftool interface.

Something like this (not intended to be complete and doesn't have exactly the same cameras and lenses but close enough to give an idea)
...

Thank you, Greybeard!
As far as I can understand your program, Exiftool will be called so many times, as many files in the folder.
This is valid for <100 files, but not acceptable when the number of files is several tens of thousands.
However, in your program I saw a lot of useful things for me.

greybeard

Fair enough Victor - I mostly wrote it to see how easy it would be.

With my aging computer I'd estimate 10,000 images would take 25 minutes to process.

VlCTOR

My simple CONFIGFILE:
MyBodyOwner => {
Require => {
0 => 'Model',
},
Desire => {
1 => 'MakerNotes:LensSpec',
},
ValueConv => q{
if ($val[0] eq 'DSC-V1') { return "Victor"; }
return undef;
},
},

My simple ARGSFILE:
-m
-overwrite_original
-EXIF:OwnerName < ${MyBodyOwner}


My comand line:
exiftool -config CONFIGFILE -@ARGSFILE -v3 *.jpg

It doesn't work properly.

EXIF:OwnerName is empty
Writing ExifIFD:OwnerName
Writing ExifIFD:OwnerName if tag exists
...
  Rewriting InteropIFD
    - ExifIFD:OwnerName = ''
    + ExifIFD:OwnerName = ''



Where did I make the mistake?

Phil Harvey

Try this config file:

%Image::ExifTool::UserDefined = (
'Image::ExifTool::Composite' => {
MyBodyOwner => {
Require => {
0 => 'Model',
},
Desire => {
1 => 'MakerNotes:LensSpec',
},
ValueConv => q{
if ($val[0] eq 'DSC-V1') { return "Victor"; }
return undef;
},
},
},
);
1; #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 ($).

VlCTOR

Hi, Phil!
It works great! Thank you very much.

I've added a few features to my config file.
Most of the features work well, but there are a few problems:
1) I haven't found a way to print convert the lens digital code to LensType (Name as string).                                                                                                        #  -LensModel < ${MyLensType}
2) LensSpec returns too long string. I need to cut off the first three and the last three characters. Like this: '01 18 135 3.5 5.6 02' -> '18 135 3.5 5.6'                     #   -LensInfo < ${MyLensSpec}
3) To automatically generate the LensMake tag, I need to take the first word of the LensType tag and convert it to upper case.                                                       #    -LensMake < ${MyLensMake}

Below is my configuration file. Can be optimise the configuration file to be compact?
%Image::ExifTool::UserDefined = (

'Image::ExifTool::Composite' => {
# -------------- LENS SETTING --------------
#  -LensModel < ${MyLensType}
MyLensType => {
Require => {
0 => 'Model',
},
Desire => {
1 => 'MakerNotes:LensType',
},
ValueConv => q{
if ($val[0] eq 'DSC-V1') { return "Carl Zeiss Vario-Sonnar 7-28mm F2.8-4"; }
if ($val[0] eq 'DSC-S700') { return "Sony Lens Optical 3x  5.8-17.4mm F2.8-4.8"; }
                                if ($val[0] eq 'DSLR-A500' or $val[0] eq 'DSLR-A550') { return $val[1]; }
return undef;
},
},

#  -LensInfo < ${MyLensSpec}
MyLensSpec => {
Require => {
0 => 'Model',
},
Desire => {
1 => 'MakerNotes:LensType',
2 => 'MakerNotes:LensSpec',
},
ValueConv => q{
if ($val[0] eq 'DSC-V1') { return "7 28 2.8 4"; }
if ($val[0] eq 'DSC-S700') { return "5.8 17.4 2.8 4.8"; }
                                if ($val[0] eq 'DSLR-A500' or $val[0] eq 'DSLR-A550') {
if ($val[1] eq '18') { return "28 80 3.5 5.6"; }
return $val[2];
}
return undef;
},
},

#  -LensMake < ${MyLensMake}
#  -XMP:XMP-microsoft:LensManufacturer < ${MyLensMake}
MyLensMake => {
Require => {
0 => 'Model',
},
Desire => {
1 => 'MakerNotes:LensType',
},
ValueConv => q{
if ($val[0] eq 'DSC-V1') { return "CARL ZEISS"; }
if ($val[0] eq 'DSC-S700') { return "SONY"; }
                                if ($val[0] eq 'DSLR-A500' or $val[0] eq 'DSLR-A550') { return $val[1]; }
return undef;
},
},

#  -LensSerialNumber < ${MyLensSerial}
MyLensSerial => {
Require => {
0 => 'Model',
},
Desire => {
1 => 'MakerNotes:LensType',
},
ValueConv => q{
if ($val[0] eq 'DSC-V1') { return "5970555"; }
if ($val[0] eq 'DSC-S700') { return "0029516"; }
                                if ($val[0] eq 'DSLR-A500' or $val[0] eq 'DSLR-A550') {
if ($val[1] eq '63') { return "1881834"; }
if ($val[1] eq '57') { return "1833114"; }
if ($val[1] eq '55') { return "4164868"; }
if ($val[1] eq '65') { return "1883815"; }
if ($val[1] eq '56') { return "1881834"; }
if ($val[1] eq '18') { return "54843232"; }
}
return undef;
},
},

# -------------- BODY SETTING --------------
#  -EXIF:ExifIFD:OwnerName < ${MyBodyOwner}
MyBodyOwner => {
Require => {
0 => 'Model',
},
ValueConv => q{
if ($val[0] eq 'DSLR-A500') { return "Victor"; }
if ($val[0] eq 'DSLR-A550') { return "Zoya"; }
if ($val[0] eq 'DSC-V1') { return "Andrey"; }
if ($val[0] eq 'DSC-S700') { return "Andrey"; }
return undef;
},
},

#  -SerialNumber < ${MyBodySerial}
MyBodySerial => {
Require => {
0 => 'Model',
},
ValueConv => q{
if ($val[0] eq 'DSLR-A500') { return "4634060"; }
if ($val[0] eq 'DSLR-A550') { return "1570055"; }
if ($val[0] eq 'DSC-V1') { return "5970555"; }
if ($val[0] eq 'DSC-S700') { return "0029516"; }
return undef;
},
},


},
);
1; #end