Skip to content

Instantly share code, notes, and snippets.

@klang
Created August 16, 2010 16:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save klang/527284 to your computer and use it in GitHub Desktop.
Save klang/527284 to your computer and use it in GitHub Desktop.
change size and quality of jpg/bmp pictures while keeping exifdata. The script makes a 'lightweight' version of original off-the-camera jpg pictures. The copy can be used for picture frames without taking up too much space.
#!/usr/bin/perl
use Getopt::Long;
use strict;
my $src = $ENV{'PWD'};
my $dst = "/cygdrive/c/pictures/export/digital";
my $quality = 75;
sub usage {
print STDERR "$0\n";
print STDERR " [--verbose]\twrites everything that happens (2 levels)\n";
print STDERR " [--dry]\tdo NOT execute any commands\n";
print STDERR " [--quality]\treduce pictures to this jpg quality (default: $quali
ty)\n";
print STDERR " [--dst]\talt. destination (default: $dst)\n";
print STDERR " [--src]\talt. source (default: $src)\n\n";
print STDERR " [--exif]\twill check destination files for exif data and move if
missing\t";
print STDERR "the command can be run without commands to reduce quality to $qual
ity\n";
print STDERR "using current directory as source and default as destination\n\n";
}
my ($help, $verbose , $dry, $moveExifData);
GetOptions( 'help|?' => \$help,
"verbose+" => \$verbose,
"quality:$quality" => \$quality,
"src:s" => \$src,
"dst:s" => \$dst,
"dry!" => \$dry,
"exif!" => \$moveExifData,
);
if ($help) {
usage();
exit;
}
print "Reading from: $src".$/;
print "Exporting to: $dst".$/;
if ($moveExifData) {
print "checking EXIF data in all exported files (and moving missing data from so
urce)\n";
moveExifData($src);
}else{
print "converting source files to ${quality}% quality and moving EXIF data\n";
convert($src);;
}
exit;
sub convert {
my $dir = shift;
local *DIR;
makeDestinationDir($dir);
print "$dir$/" or print "error - $!";
opendir DIR, $dir or die "opendir $dir: $!";
my $found = 0;
while ($_ = readdir DIR) {
next if /^\.{1,2}$/;
#next if -f $path $_!~/JPG$/i || $_ !~ /BMP$/i;
my $path = "$dir/$_";
reduceJPGQuality($path) if -f $path && /JPG$/i;
reduceBMPQuality($path) if -f $path && /BMP$/i;
convert($path) if -d $path;
}
closedir DIR;
}
# http://www.mactricksandtips.com/2008/07/convert-images-using-terminal.html
sub moveExifData {
my $dir = shift;
local *DIR;
makeDestinationDir($dir);
print "$dir$/" or print "error - $!";
opendir DIR, $dir or die "opendir $dir: $!";
my $found = 0;
while ($_ = readdir DIR) {
next if /^\.{1,2}$/;
my $path = "$dir/$_";
if ( -f $path && /JPG$/i ) {
my $file = relativePath($path);
my $exiflines = `exiftool '$dst$file' | wc -l`;
chomp($exiflines);
if ( $exiflines <= 13 ) {
moveEXIFdata($path);
} else {
print " EXIF OK for $dst$file\n" if $verbose > 1;
}
}
moveExifData($path) if -d $path;
}
closedir DIR;
}
### *** TODO *** ###
# rsync -varuHpogt --filter='+ Picasa.ini' --filter='- *.*' /cygdrive/g/pictures/d40
x /cygdrive/c/pictures/export/digital/d40x
# rsync -varuHpogt --filter='+ Picasa.ini' --filter='- *.*' /cygdrive/g/pictures/dig
ital /cygdrive/c/pictures/export/digital
sub movePicasaData {
my $dir = shift;
local *DIR;
makeDestinationDir($dir);
print "$dir$/" or print "error - $!";
opendir DIR, $dir or die "opendir $dir: $!";
my $found = 0;
while ($_ = readdir DIR) {
next if /^\.{1,2}$/;
my $path = "$dir/$_";
if ( -f $path && /INI$/i ) {
my $file = relativePath($path);
# check date of $dst$file
# move the newest file
my $exiflines = `exiftool '$dst$file' | wc -l`;
chomp($exiflines);
if ( $exiflines <= 13 ) {
movePicasadata($path);
} else {
print " EXIF OK for $dst$file\n" if $verbose > 1;
}
}
movePicasaData($path) if -d $path;
}
closedir DIR;
}
sub makeDestinationDir {
my $dir = relativePath(shift);
if (! -e "$dst$dir" ) {
print "mkdir -p $dst$dir".$/ if $verbose > 2;
mkdir("$dst$dir", 0755) || `mkdir -m 0755 -p $dst$dir`;
die "Can not mkdir $dst$dir: $!" if ! -e "$dst$dir";
} else {
print "$dst$dir exists$/" if $verbose > 2;
}
}
sub filename {
my $file = shift;
$file =~ s/.*\/(.+\.JPG)/\1/;
return $file;
}
sub relativePath {
my $relative = shift;
$relative =~ s/$src//e;
return $relative;
}
sub reduceJPGQuality {
my $file = relativePath(shift);
my $filename = filename($file);
if ( ! -e "$dst$file" && $file =~ /JPG$/i ) {
print " $filename quality reduced to $quality%$/";
print "djpeg '$src$file' | cjpeg -verbose -quality $quality -outfile '$dst$f
ile'".$/ if $dry || $verbose > 2;
`djpeg '$src$file' | cjpeg -verbose -quality $quality -outfile '$dst$file' 2
>/dev/null` if !$dry;
moveEXIFdata($file);
} else {
print " $filename already converted$/" if $verbose > 1;
}
}
sub reduceJPGsize {
my $file = relativePath(shift);
my $filename = filename($file);
if ( ! -e "$dst$file" && $file =~ /JPG$/i ) {
print " $filename quality reduced to $quality%$/";
print "djpeg '$src$file' | cjpeg -verbose -quality $quality -outfile '$dst$f
ile'".$/ if $dry || $verbose > 2;
`djpeg -fast -scale 1/8 '$src$file' | cjpeg -verbose -quality $quality -outf
ile '$dst$file' 2>/dev/null` if !$dry;
moveEXIFdata($file);
} else {
print " $filename already converted$/" if $verbose > 1;
}
}
sub moveEXIFdata {
my $file = relativePath(shift);
if ( -e "$dst$file" ) {
print " moving EXIF data$/";
print "exiftool -TagsFromFile '$src$file' -all:all '$dst$file'$/" if $dry ||
$verbose > 2;
# exiftool -TagsFromFile src.jpg -all:all dst.jpg
# Copy the values of all writable tags from "src.jpg" to "dst.jpg",
# preserving the original tag groups.
`exiftool -TagsFromFile '$src$file' -all:all '$dst$file'` if !$dry;
unlink("$dst$file"."_original") if !$dry;
}
}
sub reduceBMPQuality {
my $file = relativePath(shift);
my $filename = filename($file);
my $jpgfile = $file; $jpgfile =~ s/BMP$/jpg/;
if ( ! -e "$dst$jpgfile" && $file =~ /BMP$/i ) {
print " $filename quality reduced to $quality%$/";
print "cjpeg -verbose -quality $quality -outfile '$dst$file' '$src$file'".$/
if $dry || $verbose > 2;
`cjpeg -verbose -quality $quality -outfile '$dst$jpgfile' '$src$file' 2>/dev
/null` if !$dry;
} else {
print " $filename already converted$/" if $verbose > 1;
}
}
sub nef2jpg {
my $file = relativePath(shift);
if ( ! -e "$dst$file" && $file =~ /NEF$/i ) {
print "\tcreating jpg file$/";
print "\t\t$/";
# dcraw -c -w crw_0001.crw | cjpeg > crw_0001.jpeg
# print "djpeg '$src$file' | cjpeg -verbose -quality $quality -outfile '$dst$f
ile'".$/ if $dry || $verbose;
# `djpeg '$src$file' | cjpeg -verbose -quality $quality -outfile '$dst$file'`
if !$dry;
moveEXIFdata($file);
}
}
__END__;
=head1 NAME
exportjpg
=head1 SYNOPSIS
Create a lightweight copy of large photo collections while maintaining EXIF data. Or
, how to anoy the family with endless slideshows in viewable quality.
=head1 DESCRIPTION
Normally, when working on pictures using C<djpeg>, C<djpeg> or similar, EXIF data is
not copied to the resulting files. I found that anoying as I have all my pictues on
my portable computer (limited space, always limited space) for easy and fast refere
nce.
It is possible to check the existence of EXIF data in exported (by exportjpg) jpgs.
During implementation of this program, I fucked up and did not know which exported f
iles contained EXIF data and which did not. The B<--exif> option checks the copies t
o determine the presence of a full set of EXIF data. If something is missing in the
copy, the program will find the data in the original file.
=head1 USAGE
exportjpg
exportjpg --exif
exportjpg --verbose --verbose --dry
=head1 DEPENDENCIES
djpeg - http://www.linuxcommand.org/man_pages/djpeg1.html
cjpeg - http://www.linuxcommand.org/man_pages/cjpeg1.html
exiftool - http://www.sno.phy.queensu.ca/~phil/exiftool/
Windows users will benefit greatly form B<Cygwin> http://www.cygwin.com/
=head1 OPTIONS
=over 4
=item B<no options> - normal operation
cd to the directory containing the folders with pictures you want to export. Write C
<exportjpg>, wait.
=item B<--verbose> - verbose
several uses of B<verbose> is possible (and when I say 'several' I mean, two.)
=item B<--dry> - dry run
do not execute any time consuming commands (conversions or exif inspections)
=item B<--exif> - exif tester
if an execution of the command produced errors, it is possible to recheck the presen
ce of the exif data in the exported files with this option. When running in B<exif>
mode, nothing else is done (no conversions)
=item B<--src> - source directory
If source directory is not the current directory.
=item B<--dst> - destination directory
To change the destination directory specified in the $dst variable on the fly.
=item B<--help> - help
short option help
[--verbose] writes everything that happens (2 levels)
[--dry] do NOT execute any commands
[--quality] reduce pictures to this jpg quality (default: 75)
[--dst] alt. destination (default: $dst)
[--src] alt. source (default: $ENV{'PWD'})
[--exif] will check destination files for exif data and move
if missing
the command can be run without commands to reduce quality to 75 using current direct
ory as source and default as destination
=back
=head TODO
Extend the program to accept RAW files as input using C<dcraw>.
Finish the BMP section (bmp2jpg) and add options for this
=head1 AUTHOR
Karsten Lang Pedersen <karsten@lang.dk>
=head1 COPYRIGHT
Copyright 2006 by Karsten Lang Pedersen (karsten@lang.dk). All rights reserved.
=cut
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment