PreviewImage and SetNewValuesFromFile

Started by alyda, September 15, 2014, 03:39:36 PM

Previous topic - Next topic

alyda

Hi Phil,

I'm adding an additional bit of functionality to an older perl script, and it's working as desired. I'm fairly good at copying and tweaking bits and pieces I find in the Image::ExifTool Perl Library Module and this forum, but I really haven't a clue what some of it means.

Here's the new piece (copied, in part, from https://exiftool.org/forum/index.php/topic,19.msg33.html#msg33):

  my $out = $jpg_dir . $dto . $ssto . $sc . '.jpg';
  # extract the PreviewImage tag explicitly
  my $previewInfo = $exifTool->ImageInfo($file, 'PreviewImage');
  my $val = $$previewInfo{PreviewImage};
  if ($val) {
    open OUT, ">$out";
    print OUT $$val and close OUT;
    # copy all EXIF information, preserving the original IFD
    # (without '>*.*' tags would be copied to the preferred EXIF IFD)
    my $info = $exifTool->SetNewValuesFromFile($file, 'EXIF:*>*:*');
    $exifTool->WriteInfo($out);
  }


I understand that references are commonly used when you are returning a large object or data structure (for example an array or hash) from a subroutine, and that I can also use references to subroutines and scalars. When I want to actually use the values in these variables, I need to dereference the pointer. (http://perlmeme.org/howtos/using_perl/dereferencing.html)

In your reply to Janc you state, "Notice that I dereference $val because I know both JpgFromRaw and PreviewImage will return a scalar reference, but to be safe you should probably test for this."

Question 1: What do you mean by, "but to be safe you should probably test for this."

Question 2: Open, print and close feel so clumsy and old-fashioned compared to the object-oriented method of the rest of the code. Is there a 'prettier' way to write this part?

Here's the entire script:

use strict;
use Image::ExifTool;
require 'Image/ExifTool/Shift.pl';

# camera set to UTC0; will shift [-]10 for Hawaii
my $tz = 10;

my $numArgs = $#ARGV;

my $raw_dir = $ARGV[$numArgs - 1] . '/';
my $jpg_dir = $ARGV[$numArgs] . '/';

my $exifTool = new Image::ExifTool;

$exifTool->Options('DateFormat' => '%Y%m%d-%H%M%S');

foreach my $i (0..($numArgs - 2)) {
  $exifTool = new Image::ExifTool;
  # extract meta information from the image
  my $file = $ARGV[$i];
  $exifTool->ExtractInfo($file);
  # get date and time
  my $exifdto = $exifTool->GetValue('DateTimeOriginal');
  # set GPS date and time stamps
  $exifTool->SetNewValue('GPSDateStamp' => $exifdto);
  $exifTool->SetNewValue('GPSTimeStamp' => $exifdto);
  # camera set to UTC0, so shift the dates to reflect proper locale
  # for convenience, a shortcut tag called AllDates has been defined to represent these three tags: DateTimeOriginal, CreateDate and ModifyDate
  # http://www.exiftool.org/index.html#shift
  $exifTool->SetNewValue('AllDates' => $tz, 'Shift' => -1);
  # get values for use in new filename
  # set desired date format
  $exifTool->Options('DateFormat' => '%Y%m%d-%H%M%S');
  # to *use* a shifted date/time value the shift must be applied to the unformatted (ValueConv) value
  # then you must do the DateFormat conversion yourself
  my $dto = $exifTool->GetValue('DateTimeOriginal', 'ValueConv');
  Image::ExifTool::ShiftTime($dto, $tz, -1);
  $dto = $exifTool->ConvertDateTime($dto);
  # Nikon records multiple frames per second; get 'SubSecTimeOriginal' to avoid filename collision
  my $ssto = $exifTool->GetValue('SubSecTimeOriginal');
  # get shutter count
  my $sc = $exifTool->GetValue('ShutterCount');
  # pad the shutter count number
  $sc = '_' . sprintf('%07d', $sc);
  # concatenate all the pieces of the new filename
  # i.e., 20100126-08040600_0020213.nef
  my $nn = $dto . $ssto . $sc . '.nef';
  # add information to source file, writing output to new file
  # https://exiftool.org/ExifTool.html#WriteInfo
  # note use of full path in filenames
  $exifTool->WriteInfo($file, $raw_dir . $nn);

  my $out = $jpg_dir . $dto . $ssto . $sc . '.jpg';
  # extract the PreviewImage tag explicitly
  my $previewInfo = $exifTool->ImageInfo($file, 'PreviewImage');
  my $val = $$previewInfo{PreviewImage};
  if ($val) {
    open OUT, ">$out";
    print OUT $$val and close OUT;
    # copy all EXIF information, preserving the original IFD
    # (without '>*.*' tags would be copied to the preferred EXIF IFD)
    my $info = $exifTool->SetNewValuesFromFile($file, 'EXIF:*>*:*');
    # overwrite file
    $exifTool->WriteInfo($out);
  }
}


Phil Harvey

Quote from: alyda on September 15, 2014, 03:39:36 PM
In your reply to Janc you state, "Notice that I dereference $val because I know both JpgFromRaw and PreviewImage will return a scalar reference, but to be safe you should probably test for this."

Question 1: What do you mean by, "but to be safe you should probably test for this."

    $val = $$val if ref $val eq 'SCALAR';

QuoteQuestion 2: Open, print and close feel so clumsy and old-fashioned compared to the object-oriented method of the rest of the code. Is there a 'prettier' way to write this part?

You could maybe make it more object oriented with some other Perl library, but the open/print/close is the way I would do it (although I would do a binmode(OUT); after opening the file so the code would work on Windows platforms.

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