I wanted to build an awesome place for people to discuss module specific issues, but I don't have any more time for this, and there are much better places to discuss Perl-related issues. I'd recommend asking your question on Stack Overflow or on Perl Monks.
If you are looking for a Perl tutorial or Perl-related news, I hope these links will serve you well.
Posted on 2007-08-16 22:21:11-07 by geve
How to create sample picture, replacing large photo portion
Hi Phil,
In your example pictures you replace the large picture with a 1 pixel image.
I try to do the same:
I Created a 1_pixel.jpg image
then
c:\..\perl.exe exiftool -TagsFromFile Canon-PS-S50-3827.jpg 1_pixel.jpg

I get some extra things:
- extra tags in JPEG-APP01: ISO-speed / Contrast / Saturation / Sharpness
- an extra JPEG-APP00
- an extra JPEP-XMP part with adobe stuff.

What did I do wrong?
Ger
Direct Responses: 5906 | Write a response
Posted on 2007-08-16 23:40:03-07 by exiftool in response to 5905
Re: How to create sample picture, replacing large photo portion
Hi Ger,

Try this (adding "-all:all"):

c:\..\perl.exe exiftool -TagsFromFile Canon-PS-S50-3827.jpg -all:all 1_pixel.jpg

This will force tags to be written to their original group. Otherwise, by default, tags are written to the preferred group.

- Phil
Direct Responses: 5913 | Write a response
Posted on 2007-08-18 00:12:29-07 by geve in response to 5906
Re: How to create sample picture, replacing large photo portion
Hi Phil,

This works much better, I got rid off the XMP. And all the Exif seems copied.
But looking closer, the content has changed not the outcome.
'Shutter speed value' was '-118/32' which is 13 seconds is changed to '-11791/3196' this is also 13 seconds.

Maybe I have to explain what I was trying: I have a huge pile of photo's. I'am not interested in the actual photo's. So I thought of saving disk space, if I could minimize the picture without changing any EXIF data.

Is this possible?

--Ger.
Direct Responses: 5917 | Write a response
Posted on 2007-08-18 11:27:30-07 by exiftool in response to 5913
Re: How to create sample picture, replacing large photo portion
Hi Ger,

ExifTool writes tags individually, so the result will usually differ on a byte level from the original file. You can't use ExifTool to replace an image and leave the metadata exactly the same. To do this, I have written a custom script that may be of use to you:

I use this script to generate JPEG images for my meta information repository. Currently the script is written to insert the image from "t/images/Writer.jpg" into the specified file(s), but you can edit the script to change this to whatever you want.

- Phil

#!/usr/bin/perl -w my $help = <<_END_; # # File: swap_image # # Description: Swap image in JPEG file, preserving meta information # # Syntax: swap_image [OPTIONS] FILE/DIR [...] # # Options: -d - dummy mode (doesn't actually change the file) # -r - recursively process sub-directories # -i DIR - ignore specified directory # -f - fix truncated jpg # -v - verbose # # Revisions: 01/25/06 - PH Created from clean_jpg # 01/31/07 - PH Send warnings to stderr # _END_ # # Legal: Copyright (c) 2004-2005 Phil Harvey # You are free to use/modify this script for non-profit # purposes. Not responsible for loss or damages. # # Mail problems/comments to phil at owl.phy.queensu.ca # use strict; require 5.002; sub SwapDir($); sub SwapJpg($); my $newData; open FILE, "t/images/Writer.jpg" or die "error opening t/images/Writer.jpg\n"; seek FILE, 2, 0 or die "Error seeking\n"; read FILE, $newData, 65536 or die "Error reading from file\n"; close FILE; my @files; my $recurse = 0; my $verbose; my $dummy; my @ignore; my $count = 0; my $count_ok = 0; my $count_dir = 0; my $cleaned_bytes = 0; my $fix_truncated_jpg; my %jpegMarker = ( 0x01 => 'TEM', 0xc0 => 'SOF0', # to SOF15, with a few exceptions below 0xc4 => 'DHT', 0xc8 => 'JPGA', 0xcc => 'DAC', 0xd0 => 'RST0', 0xd8 => 'SOI', 0xd9 => 'EOI', 0xda => 'SOS', 0xdb => 'DQT', 0xdc => 'DNL', 0xdd => 'DRI', 0xde => 'DHP', 0xdf => 'EXP', 0xe0 => 'APP0', # to APP15 0xf0 => 'JPG0', 0xfe => 'COM', ); my $arg; while ($arg = shift) { if ($arg =~ /^-/) { for (my $i=1; ($_=substr $arg, $i, 1); ++$i) { /d/ and $dummy = 1, next; /f/ and $fix_truncated_jpg = 1, next; /i/ and push(@ignore,shift), next; /r/ and $recurse = 1, next; /v/ and $verbose = 1, next; print "Unknown option $_\n"; exit 1; } } else { push @files, $arg; } } unless (@files) { print $help; exit 1; } my $filename; foreach $filename (@files) { if (-d $filename) { SwapDir($filename); } elsif (-e $filename) { my $result = SwapJpg($filename); if ($result > 1) { ++$count; } elsif ($result > 0) { ++$count_ok; } } else { die "Can't open $filename\n"; } } if ($count or $count_ok or $count_dir>1) { printf("%5d directories scanned\n", $count_dir) if $count_dir > 1; printf("%5d JPG image swapped\n", $count) if $count; printf("%5d JPG files already swapped\n", $count_ok) if $count_ok; if ($cleaned_bytes >= 102400000) { printf("%5d MB saved\n", $cleaned_bytes / (1024*1024)); } elsif ($cleaned_bytes >= 100000) { printf("%5d KB saved\n", $cleaned_bytes / 1024); } else { printf("%5d bytes saved\n", $cleaned_bytes); } print "But this was dummy mode, so nothing was changed\n" if $dummy; } else { print "Nothing to do.\n"; } exit($count ? 0 : 1); #------------------------------------------------------------------------------ # Get JPEG marker name # Inputs: 0) Jpeg number # Returns: marker name sub JpegMarkerName($) { my $marker = shift; my $markerName = $jpegMarker{$marker}; unless ($markerName) { $markerName = $jpegMarker{$marker & 0xf0}; if ($markerName and $markerName =~ /^([A-Z]+)\d+$/) { $markerName = $1 . ($marker & 0x0f); } else { $markerName = sprintf("0x%.2x", $marker); } } return $markerName; } sub SwapDir($) { my $dir = shift; opendir(DIR_HANDLE, $dir) or die "Error opening directory $dir\n"; my @file_list = readdir(DIR_HANDLE); closedir(DIR_HANDLE); ++$count_dir; my $file; foreach $file (@file_list) { my $path = "$dir/$file"; if (-d $path) { next if $file =~ /^\./; # ignore dirs starting with "." next if grep /^$file$/, @ignore; $recurse and SwapDir($path); next; } if ($file =~ /\.jpg$/i) { my $result = SwapJpg($path); if ($result > 1) { ++$count; } elsif ($result > 0) { ++$count_ok; } } } } # rewrite a jpg file, swapping image while preserving all meta information # Inputs: 0) file name # - returns 0 on error, 1 if nothing done, 2 if any junk was removed sub SwapJpg($) { my $infile = shift; my $outfile = ''; my $success = 0; my $saved = 0; my ($s,$length,$buff); my ($ch,$data,$ord_ch,$done_meta); $verbose and print "File: $infile\n"; open(JPG_IN,$infile) or return $success; binmode( JPG_IN ); # create name of temporary file in same directory if ($infile =~ /(.*\/)/) { $outfile = $1; } $outfile .= "icat_CleanJpg.tmp"; unless (open(JPG_OUT,">$outfile")) { close(JPG_IN); return $success; } binmode( JPG_OUT ); # set input record separator to 0xff (the JPEG marker) to make reading quicker my $oldsep = $/; $/ = "\xff"; if (read(JPG_IN,$s,2)==2 and $s eq "\xff\xd8" and print JPG_OUT $s) { # read file until we reach an end of image (EOI) Marker: for (;;) { # Find next marker (JPEG markers begin with 0xff) $data = <JPG_IN>; defined($data) or last; chomp $data; # remove 0xff $saved += length($data); # print JPG_OUT $data or last; # JPEG markers can be padded with unlimited 0xff's for (;;) { read(JPG_IN, $ch, 1) or last Marker; $ord_ch = ord($ch); last if $ord_ch != 0xff; # exit loop before printing marker ++$saved; } my $hdr = "\xff" . $ch; # segment header # Now, $ord_ch is the value of the marker. if (($ord_ch >= 0xc0) && ($ord_ch <= 0xc3)) { # this is an image block read(JPG_IN, $buff, 7) == 7 or last; # print JPG_OUT $hdr,$buff or last; $saved += 9; # handle stand-alone markers 0x01 and 0xd0-0xd7 # (and the non-marker 0x00, which follows an 0xff if it exists in data) } elsif ($ord_ch==0x00 or $ord_ch==0x01 or ($ord_ch>=0xd0 and $ord_ch<=0xd7)) { # print JPG_OUT $hdr or last; $saved += 2; } elsif ($ord_ch==0xd9) { # end of image (EOI) $success = 1; # write our substitute image print JPG_OUT $newData or $success = 0; $saved += 2 - length $newData; # copy over anything after EOI my $preview_size = 0; while (read(JPG_IN, $buff, 65536)) { print JPG_OUT $buff or $success = 0; $preview_size += length $buff; } if ($verbose and $preview_size) { printf(" Preview image : %6d bytes -- Kept\n", $preview_size); } last; } else { # We **MUST** skip variables, since FF's within variable names are # NOT valid JPEG markers read(JPG_IN, $s, 2) == 2 or last; $length = unpack("n",$s); last if $length < 2; read(JPG_IN, $buff, $length-2) == $length-2 or last; # is this meta information (APP or COM segment)? my $is_meta; if (($ord_ch >= 0xe0 and $ord_ch <= 0xef) or $ord_ch == 0xfe) { my $mrkr = JpegMarkerName($ord_ch); $done_meta and warn " >>>> Segments out of order! - $infile\n"; $is_meta = 1; } else { $done_meta = 1; } my $mrkr; if ($verbose) { $mrkr = '(' . JpegMarkerName($ord_ch) . ')'; $mrkr .= ' ' if length($mrkr) < 6; } if ($is_meta) { $verbose and printf(" Marker 0x%x $mrkr: %6d bytes -- Kept\n", $ord_ch, $length); print JPG_OUT $hdr,$s,$buff or last; } else { if ($fix_truncated_jpg) { $success = 1; print JPG_OUT $newData; my $pos = tell JPG_IN; seek JPG_IN, 0, 2; $saved += 2 + tell(JPG_IN) - $pos - length $newData; $saved = 1 if $saved < 0; last; } $verbose and printf(" Marker 0x%x $mrkr: %6d bytes -- Cleaned\n", $ord_ch, $length); $saved += $length + 2; } } } } $/ = $oldsep; # return separator to original value close(JPG_IN); close(JPG_OUT) or $success = 0; if ($success and $saved > 0 and not $dummy) { unless (rename($outfile,$infile)) { $success = 0; unlink($outfile); } } else { unlink($outfile); warn " >>>>>>>>>> Error in $infile\n" unless $success; } $success and $verbose and printf(" Total saved :%7d bytes\n", $saved); if ($success and $saved > 0) { $cleaned_bytes += $saved; $saved = 1; } else { $saved = 0; } return $success + $saved; } # end
Direct Responses: Write a response