Bob's Tools for Digital Photography
Digital cameras are great because they make it trivially easy to post
your pictures on the web, right? Well, not completely, as I learned in
my first two weeks using an Olympus
C2000-Z. Why? Because while it is easy to transfer your
photos from camera to PC, and then from PC to web site, this still
leaves two problems:
- The images take too long for people to download off the web. This
is because, if you're like me, you want to take photos in high
resolution, on the off chance your informal snapshot is a real
prize-winner. Maybe you'll want to print it out as an 8x10? Or do some
extensive cropping in your digital darkroom? In any event, you paid
good money for a camera that can shoot 800x600, 1024x 768, 1600x1200
or more... so you shoot high res. But even grandma won't want to look
at many pictures of junior if it takes 10 minutes per picture. You
need to scale the photos to a smaller size. Of course, any image
editing application can do this. Except that, manually resizing 40
pictures is a chore at best.
- Even once you have your photos appropriately sized, how will
people know on which photos to click? You want some kind of contact
sheet displaying a thumbnail preview image. Of course, I can think of
better things to do than creating HTML pages consisting merely of
small images and hyperlinks...
Enter perl to the rescue. I wrote the following two scripts to solve
the two problems described above. thumbnail.pl creates
small thumbnail preview images, and generates an HTML "contact sheet"
page. shrink.pl can take a whole batch of images and
shrink them so that none of them are bigger than a specified size.
You can see examples of both scripts wherever I have lots of photos on
my site. For example, see
www.lisbonne.com/david,
www.lisbonne.com/rainierpix, or
www.lisbonne.com/fathersday.
Feel free to use, copy, or modify these scripts. I use them regularly
on Linux, but can't vouch for them on any "lesser" operating
systems. Please send bug reports, modifications, or feature requests
to bob at lisbonne.com.
Note: I would like to post thumbnail.pl as a CGI on my
site so that folks could use it as a service, without having to
download or install anything. Unfortunately, my ISP doesn't provide
the best hosting environment for perl. If someone at best.com has the requisite perl modules
in their directory, please let me know. TIA.
thumbnail.pl
#!/usr/bin/perl -w
# Copyright (C) 1999, Bob Lisbonne
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# thumbnail.pl: Creates thumbnail images and an HTML "contact sheet"
# for easy previewing of images posted on the web.
#
# usage: thumbnail.pl URL, eg thumbnail.pl http://www.foo.com/photos/
#
# detailed usage:
# 1) Upload images from your digital camera to a new directory on your web site.
# 2) thumbnail.pl http://www.foo.com/photos/
# 3) Upload the thumbnails, which are called thumb_*
# 4) Upload the contact sheet index.html
#
# by Bob Lisbonne
# bob@lisbonne.com
# copyright 1999
#
# thumbnail.pl revision history:
# 7/2/99 version 1.0
# 7/10/99 v1.01 adds WIDTH and HEIGHT attributes to IMG tags
#
# The following modules may be obtained at http://www.perl.com/CPAN
use CGI;
use LWP::Simple;
use HTML::LinkExtor;
use File::Basename;
use Image::Magick;
# Set the following values to your preference. Sorry I haven't yet hooked them up to GetOpts.
my $maxx = 150; #max x pixels for thumbnail
my $maxy = 150; #max y pixels for thumbnail
my $colmax = 4; #max number of thumbnails per row in contact sheet
my @thumbs; #array of references to hashes. Each hash holds URL, filename, dimension, and filesize
my ($dirl,$imageobj,$nextimage,$base,$dir,$ext);
my ($status,$origx,$x,$y,$origy,$filesize,$colnum);
if (defined $ARGV[0]) {
#command line invokation
$dirurl = shift;
} else {
#CGI invokation
$q = new CGI;
print $q->header;
$dirurl = $q->param('dirurl');
}
#ensure that last char in $dirurl is a slash or get() will fail
$dirurl .= '/' unless (length($dirurl)==rindex($dirurl,'/')+1);
# Step 1: Build a list of thumbnails I need to make.
# So, fetch the $dirurl web page and parse the HTML.
$p = HTML::LinkExtor->new(undef, $dirurl);
$p->parse(get($dirurl))->eof;
@links = $p->links;
foreach $linkarray (@links) {
my @element = @$linkarray;
my $eltype = shift @element;
while (@element) {
my ($attr_name, $attr_val) = splice(@element,0,2);
$URL=URI->new_abs($attr_val,$dirurl);
unless ($URL =~ /.jpg$|.JPG$|.gif$|.GIF$/) {
#Image::Magick can handle many more image formats.
#Change the line above if you want to thumbnail PNG, TIFF, etc.
print "Skipping $URL since it doesn't end in jpg or gif.\n";
next;
}
my %thumb; #important to be in while loop, since reference added to @thumbs on each iteration
$thumb{"URL"}=$URL;
push (@thumbs,\%thumb);
}
}
# Step 2: Since Image::Magick only operates on local files,
# we need to http get each image and temporarily store locally.
foreach $thumbref (@thumbs) {
$URL = $$thumbref{"URL"};
($base, $dir, $ext) = fileparse($URL->path,'\..*');
$imagefilename = $base.$ext;
$nextimage = get($URL);
open(IMAGEFILE,">$imagefilename")
|| die "cannot open $imagefilename file\n";
print IMAGEFILE $nextimage;
close(IMAGEFILE);
# Step 3: Resize image and write out with new filename.
$imageobj = Image::Magick->new;
$status = $imageobj->Read($imagefilename);
warn "$status" if "$status";
$origx= $imageobj->Get('width');
$x = $origx;
$origy= $imageobj->Get('height');
$y = $origy;
$size = sprintf("%.0f",$imageobj->Get('filesize')/1024);
print "$imagefilename\twas $x"."x"."$y\t";
if ($x > $maxx) {
$y = $y*$maxx/$x;
$x = $maxx;
}
if ($y > $maxy) {
$x = $x*$maxy/$y;
$y = $maxy;
}
$x= int($x);
$y= int($y);
$imageobj->Scale($x."x".$y);
print "scaled to $x x $y.\n";
$status = $imageobj->Write("thumb_$imagefilename");
warn "$status" if "$status";
# Add remaining info to thumb hash before moving to next image.
@$thumbref{qw/filename width height thumbwidth thumbheight filesize/}= ($imagefilename,$origx,$origy,$x, $y, $size);
#Step 4: Cleanup
undef $imageobj; #releases data structure
unlink($imagefilename); #deletes local copy of unscaled image file
}
# Step 5: create the HTML page.
$colnum = 0;
open (HTMLFILE, ">index.html");
print HTMLFILE "\n";
while (defined($thumbref = shift(@thumbs))) {
($URL,$filename,$x,$y,$thumbx,$thumby,$filesize) = @$thumbref{qw/URL filename width height thumbwidth thumbheight filesize/};
if ($colnum == 0) {print HTMLFILE "\n";}
print HTMLFILE " \n";
print HTMLFILE " $filename $x x $y $filesize"."k | \n";
$colnum++;
if ($colnum == $colmax) {
print HTMLFILE "
\n";
$colnum = 0;
}
}
if ($colnum != 0) {print HTMLFILE "";}
print HTMLFILE "
\n";
close(HTMLFILE);
print "Your thumbnail is now available in the current local directory as index.html\n";
shrink.pl
#!/usr/bin/perl -w
# shrink.pl: Batch process shrinking of image files.
#
# usage: shrink.pl maxx maxy file-list, eg shrink.pl 800 600 *jpg
#
# by Bob Lisbonne
# bob@lisbonne.com
# copyright 1999
# shrink.pl version 7/2/99
#
# The following modules may be obtained at http://ww.perl.com/CPAN
use File::Basename;
use Image::Magick;
my ($file,$dir,$base,$ext,$maxx,$maxy,$imageobj,$status,$x,$y,$origx,$origy);
$maxx = shift;
$maxy = shift;
foreach $file (@ARGV) {
($base, $dir, $ext) = fileparse($file,'\..*');
if ((lc($ext) ne ".jpg") and (lc($ext) ne ".gif")) {
print "Skipping $file since extension $ext isn't jpg or gif.\n";
next;
}
$imageobj = Image::Magick->new;
$status = $imageobj->Read($file);
warn "$status" if "$status";
$origx= $imageobj->Get('width');
$x = $origx;
$origy= $imageobj->Get('height');
$y = $origy;
print "$file\twas $x"."x"."$y\t";
if ($x > $maxx) {
$y = $y*$maxx/$x;
$x = $maxx;
}
if ($y > $maxy) {
$x = $x*$maxy/$y;
$y = $maxy;
}
$scalefactor = int($x)."x".int($y);
print "scaled to $scalefactor.\n";
$imageobj->Scale($scalefactor);
$status = $imageobj->Write("$file");
warn "$status" if "$status";
undef $imageobj; #releases data structure
}