Archive for the ‘photos’ Category
Photos, CoverFlow, QuickLook, and Spotlight
February 9th, 2008
I take a lot of photos for work. I organize the photo files according to project and the date on which they were taken. For example, the photos I take on April 23, 2007 on the Smith Industries project will be kept in the folder /Users/drang/projects/smith.industries/Photos/20070423.1 I don’t use iPhoto, because:
- I don’t like having all my photos lumped together, which is iPhoto’s default behavior.
- Setting up an iPhoto library for each project is a hassle, as is telling iPhoto which library to use.
- I often need to burn all the photos for a particular project onto a CD or DVD and send them to a client. This is much more convenient when all the photos are in a single directory.
I used to use a combination of the Finder and Xee to review photos, and I outlined my system here and here. Basically, I’d use the Finder’s Icon View with 128 pixel icons to quickly scroll through a folder of photos (I wrote an AppleScript to toggle between the large icon size and the 48 pixel size I usually use), and I’d use Xee for more detailed viewing. This worked well, but with Leopard there’s a better way.
First, I can use CoverFlow in the Finder to scroll through a folder of photos, and I get to see them at a much larger size than in Icon View.
Often, this view is good enough for my purposes. If I need to see a picture at a larger size, a quick press of the spacebar brings it up at a larger size in QuickLook. Option-clicking in the QuickLook window will zoom in further, if necessary. Although it’s a fine program that’s served me well, Xee now seems unnecessary.
I’m trying to make better use of Spotlight with my photos, too. You’ll notice in the screenshot above that the file list below the CoverFlow area has a Comments field just after the Name field. I’m using this as a way of captioning the photos. The captions are easy to read as I flip through the photos, and I can use Spotlight to show just those photos that have a keyword in their comment. Comments aren’t visible by default; you have to select Show View Options from the View menu at click the Comments checkbox.

Once the Comments are visible, you can reorder and resize the columns.
The normal way of adding Spotlight comments to a file is to open the file’s Info window and typing in the little Spotlight Comments box. This is a lot of busywork if you have dozens or hundreds of photos to caption. So I wrote an AppleScript that looks for a file called “captions.txt” in the same folder as the photos and sets the comments according to the contents of that file. You can write “captions.txt” in your favorite text editor, making use of whatever shortcuts and timesavers it provides.
The “captions.txt” file is a plain text file with the following format:
20080127-073.jpg|passenger area
20080127-074.jpg|LF hub linkage
20080127-075.jpg|RF hub linkage
20080127-076.jpg|LF hub linkage
20080127-077.jpg|LR hub linkage
20080127-078.jpg|RR hub linkage
20080127-079.jpg|sight glass after filling
20080127-080.jpg|front of crane; fluid pools
20080127-081.jpg|front of crane; fluid pools
Each line represents a single file. The name of the photo file and its caption are separated by the vertical pipe character (which I chose because I don’t expect it to ever show up in my file names or captions).
Here’s a hint for automatically generating all the photo file names: open Terminal, cd to the photo folder, and run this command:
ls *.jpg | pbcopy
The ls lists all the files that end with “.jpg”, and the pbcopy puts that list onto the Clipboard. Now you can Paste in your text editor, and the files will appear, one per line. You may need to change the *.jpg to *.JPG if that’s how your files are named.
The AppleScript that processes the captions, cleverly entitled “Caption photos,” looks like this:
1: tell application "Finder"
2: set photoFolder to target of front Finder window as alias
3: set cFiles to files of photoFolder whose name is "captions.txt"
4: if (count of cFiles) = 0 then
5: return -- stop if there's no captions file
6: end if
7:
8: display dialog "Do you want to append or replace existing comments?" buttons ["Append", "Replace"] default button 2
9: set dialogResult to result
10: set cFile to item 1 of cFiles as alias
11: set capLines to read cFile as text using delimiter "
12: "
13: set savedTextItemDelimiters to AppleScript's text item delimiters
14: set AppleScript's text item delimiters to {"|"}
15: repeat with theLine in capLines
16: set fileName to text item 1 of theLine
17: set fileComment to text item 2 of theLine
18: if button returned of dialogResult is "Append" then
19: set oldComment to comment of file fileName in folder photoFolder
20: set comment of file fileName in folder photoFolder to (oldComment & return & fileComment)
21: else
22: set comment of file fileName in folder photoFolder to fileComment
23: end if
24: end repeat
25: set AppleScript's text item delimiters to savedTextItemDelimiters
26:
27: end tell
It’s meant to be run when the folder of photos is open and is the frontmost Finder window. If there’s no “captions.txt” file in the folder, nothing happens. If there is a “captions.txt” file, the script asks (in Line 8) if you want the captions in the file to be appended to or replace any existing comments. Replace is the default. The append or replace question is not asked for each individual file, because
I just started using Spotlight comments for photo captions. There may be some downside to it that I haven’t discovered yet, but so far it seems very useful.
-
I got into the habit of capitalizing the
Photosfolder back in my Linux days. The Linux file managers I used sorted contents in ASCII order, so items with initial capital letters always came first. MyPhotosfolder was therefore always near the top of the project folder’s contents list—easy to find and open. The Mac’s “Arrange by Name” scheme is not case sensitive, so there’s no point in capitalizing anymore. I just haven’t dropped the habit yet. ↩
Digital photos and me, Part V
October 19th, 2006
In this fifth and—I think—last post in the series, I’m going to describe the program I use to prepare sheets of photos for printing at my local Costco. You could use the same procedure for printing at, say, Wal-Mart or any of the various online photo printing shops.
It’s not really necessary for you to read the first four posts in the series (one, two, three, and four), but since the program uses the RMagick library for Ruby, reading the fourth post would probably be a good idea if you’re using a Macintosh, as it explains a fairly simple way to get RMagick installed. The program I’ll describe isn’t Mac-specific, so if you can get RMagick installed on your Windows or Linux machine, it should work fine. (The should in that last sentence is the tipoff that I haven’t tested it on Windows or Linux. If you do, let me know how it works.)
The idea behind the program is to create a JPEG file that:
- contains several photographs, each labeled with its file name;
- has an optional title and a pair of subtitles;
- it formatted to print out nicely on an 8×10, with room for binding or punched holes along one of the long edges.
An example would be something like this, which you can click on to see a larger version.
Back in 2000, I wrote a similar program in Python. I used it first to organize family photos, but then adapted it to handle the digital photos I was taking at work. I chose Python because of Tkinter, Python’s built-in interface to the Tk library of user interface widgets. I wanted the program to have a GUI that worked in Windows so my wife and coworkers could use it. The program worked pretty well, and I wrote a post about it some time ago. But I began to dislike it because:
- it was written with an 8½×11 sheet in mind, and when I switched from inkjet printing my own sheets on 8½×11 paper to sending the files off to Costco for photographic printing on 8×10 paper, the difference in aspect ratio changed the placement of the images and messed up the binding margin;
- it used the Python Imaging Library (PIL) and a set of high-resolution bitmap font files, and I wanted the flexibility to change fonts and the resolution of the output file;
- I’d rather just use a command-line program; and
- my fling with Python was over, and I didn’t want to be maintaining a Python program anymore.
The result is the following Ruby program. I’m not really a Ruby programmer—not yet, anyway—and it shows. The program is very much a procedural program, not object-oriented in any way, so I’m sure it would be considered poor Ruby style. No matter. For now, the important thing is that it works; the logic and data contained in it can be rewritten into a more Rubyish form as I learn the language and its styles and idioms.
I call the program photosheets because that’s what it makes. Here’s the listing.
#!/usr/bin/env ruby
require 'optparse'
require 'RMagick'
include Magick
Geometry = {
4 => {
'name' => 'Quad',
'rows' => 2,
'cols' => 2,
'image_size' => [1280, 960],
'dpi' => 300,
'height' => 2400,
'width' => 3000,
'left_margin' => 145,
'right_margin' => 2855,
'top_margin' => 180,
'title_size' => 16*4,
'subtitle_size' => 10*4,
'title_clearance' => 45,
'label_size' => 9*4,
'label_clearance' => 4,
'horizontal_clearance' => 150,
'vertical_clearance' => 100,
'bfont' => 'Helvetica-Bold',
'font' => 'Helvetica'
},
9 => {
'name' => 'Nine',
'rows' => 3,
'cols' => 3,
'image_size' => [800, 600],
'dpi' => 300,
'height' => 2400,
'width' => 3000,
'left_margin' => 150,
'right_margin' => 2850,
'top_margin' => 180,
'title_size' => 16*4,
'subtitle_size' => 10*4,
'title_clearance' => 45,
'label_size' => 9*4,
'label_clearance' => 4,
'horizontal_clearance' => 150,
'vertical_clearance' => 100,
'bfont' => 'Helvetica-Bold',
'font' => 'Helvetica'
}
}
def place_photo(photo, pos, per_page, lo)
pos = (pos - 1) % per_page
$img.read(photo)
# Strip out the photo's directory path and extension.
base = File::basename(photo, ".*")
# Rotate if necesary and scale
if ($img.rows > $img.columns)
$img.rotate!(270)
end
scale_x = lo['image_size'][0]*1.0/$img.columns
scale_y = lo['image_size'][1]*1.0/$img.rows
scale = [scale_x, scale_y].min
h = (scale*$img.columns).round
v = (scale*$img.rows).round
$img.resize!(h,v, HammingFilter)
# Determine placement on page.
row = pos/lo['cols'] # zero-indexed
col = pos - row*lo['cols'] # zero-indexed
x = lo['left_margin'] +
col * (lo['image_size'][0] + lo['horizontal_clearance']) +
lo['image_size'][0] - h
y = lo['top_margin'] + lo['title_size'] + lo['title_clearance'] +
row * (lo['image_size'][1] + lo['vertical_clearance']) +
lo['image_size'][1] - v
lx = x + h
ly = y + v + lo['label_clearance'] + lo['label_size']
# Place the photo.
# $sheet.composite!($thumb, x, y, OverCompositeOp)
$sheet.composite!($img, x, y, OverCompositeOp)
# Place the label.
label = Draw.new
label.font(lo['font'])
label.pointsize(lo['label_size'])
label.text_align(RightAlign)
label.text(lx, ly, base)
label.draw($sheet)
$img.clear
STDERR.print "#{pos+1}..." if $verbose
end
def make_sheet(sheet_count, photo_list, per_page, lo)
# Determine the range of photos for this sheet.
first_photo = (sheet_count - 1) * per_page
last_photo = first_photo + per_page - 1
if (last_photo >= photo_list.size)
last_photo = photo_list.size - 1
end
sublist = photo_list[first_photo..last_photo]
# Initialize the sheet.
# sheet = Image.new(lo['width'], lo['height'])
$sheet.erase!
# $sheet = $sheet.texture_floodfill(100,100, $granite)
# Draw the title block.
tx = lo['width']/2
ty = lo['top_margin'] + lo['title_size']
title = Draw.new
title.font(lo['bfont'])
title.pointsize(lo['title_size'])
title.text_align(CenterAlign)
title.text(tx, ty, $ctitle) unless $ctitle == ''
title.pointsize(lo['subtitle_size'])
title.font(lo['font'])
title.font_weight(NormalWeight)
tx = lo['left_margin']
title.text_align(LeftAlign)
title.text(tx, ty, $ltitle) unless $ltitle == ''
tx = lo['right_margin']
title.text_align(RightAlign)
title.text(tx, ty, $rtitle) unless $rtitle == ''
title.draw($sheet)
# Place the photos.
pos = 0
sublist.each do |photo|
pos += 1
place_photo(photo, pos, per_page, lo)
end
# Save the sheet.
name = "Sheet-%02d.jpg" % sheet_count
$sheet.write(name)
STDERR.puts "#{name} done!" if $verbose
end
# Start of main program.
# Defaults.
per_page = 9
$ctitle = ''
$ltitle = ''
$rtitle = ''
$verbose = FALSE
# Process the options.
opts = OptionParser.new
opts.banner = "Usage: photosheets [options] files"
opts.separator("Create one or more 8x10 sheets of photos.\nOptions:")
opts.on("-n N", Integer, "photos per page (4 or 9; default: 9)") {
|num| per_page = num }
opts.on("-t TITLE", String, "main title (centered)") {
|str| $ctitle = str }
opts.on("-l SUBTITLE", String, "left subtitle (project)") {
|str| $ltitle = str }
opts.on("-r SUBTITLE", String, "right subtitle (date)") {
|str| $rtitle = str }
opts.on("-v", "verbose output (default: off)") {
|str| $verbose = TRUE }
opts.on("-h", "--help", "print this message") {
|str| puts opts.help; exit }
photo_list = opts.parse(ARGV) #everything after the options
photo_list = photo_list.delete_if { |f| f =~ /^Sheet-/ }
layout = Geometry[per_page]
# Get the title and subtitles interactively if no main title is given.
if ($ctitle == '')
STDERR.print "Main title: "
$ctitle = STDIN.gets
STDERR.print "Left subtitle (project): "
$ltitle = STDIN.gets
STDERR.print "Right subtitle (date): "
$rtitle = STDIN.gets
end
# Initialize the counts and the images.
num_photos = photo_list.size;
num_sheets = num_photos/per_page
num_sheets =num_sheets + 1 if (num_photos % per_page > 0)
$granite = Magick::Image.read('granite:').first
# $granite = $granite.level(0, 1.50)
$sheet = Image.new(layout['width'], layout['height'])#{
# self.background_color = '#DDD'
# }
$img = ImageList.new
# Make the sheets.
1.upto(num_sheets) do |i|
make_sheet(i, photo_list, per_page, layout)
end
As you can see, it’s about 200 lines long, with the first 50 or so lines taken up with defining the geometry of the layout.
Thanks to the optparse library, you can get a good idea of how the program works by typing photosheets --help at the command line. The response will be
Usage: photosheets [options] files
Create one or more 8x10 sheets of photos.
Options:
-n N photos per page (4 or 9; default: 9)
-t TITLE main title (centered)
-l SUBTITLE left subtitle (project)
-r SUBTITLE right subtitle (date)
-v verbose output (default: off)
-h, --help print this message
which tells you that you can use the -n option to specify either 4 or 9 photos per sheet and you can give the main title with the -t option and the two subtitles with the -l and -r options. Since at work I usually use the left subtitle for the project name and the right subtitle for the date or date range of the photos, those conventions are mentioned parenthetically in the help. Nine photos per sheet is the default because that’s what I use the most. You can change any of this stuff by fiddling with the parts of the program labeled # Defaults and # Process the options.
The program creates a series of JPEG files in the directory you are in when you call it. The files are named Sheet-01.jpg, Sheet-02.jpg`, etc. If you already have files with those names in the current directory, they’ll be overwritten (a ruthlessness that is a legacy of my Linux days).
Normally, the program doesn’t give any feedback as it runs—you just wait until the output files are created. But if you call the program with the -v option, it will give you a running account of its progress, like this
1...2...3...4...5...6...7...8...9...Sheet-01.jpg done!
1...2...3...4...5...6...7...8...9...Sheet-02.jpg done!
1...2...3...4...5...6...7...8...9...Sheet-03.jpg done!
1...2...3...4...5...6...7...8...9...Sheet-04.jpg done!
1...2...3...Sheet-05.jpg done!
An important feature not mentioned in the help is interactive input. If you call the program without the -t option, it assumes you want to provide the title and subtitles interactively and begins prompting you for them. Here’s an example of the prompts and the answers I gave to produce the sheet shown above.
Main title: Main Title
Left subtitle (project): Project Name
Right subtitle (date): October 8, 2006
This behavior is set up to make my life easier and require the least amount of typing and thinking. My most common use of the program is to open the Terminal, change to a directory of photos, and call photosheets *.jpg. I then type in the titles (any of which can be blank) when prompted and wait for the multi-photo sheets to be created. These get uploaded to Costco and printed on 8×10 paper.
If you’re wondering why I bother printing out my photos when I can just view them on my computer, you need to get more experience with that big analog thing we older folks call the “real world.” Riffling through sheets of photos, or laying them all out on a big table, is much more efficient than looking at just a few at a time on your screen. No matter how big your screen is, I’ll be able to find a table that’s bigger.
So that’s it. These five posts, taken together, explain how I deal with digital photos on screen and off. No iPhoto necessary, and in fact, no iPhoto wanted because iPhoto’s method of organizing photos isn’t convenient for the way I work. Perhaps I should say “iPhoto’s old method of organizing photos,” as this way of working was developed when iPhoto ’05 was the current thing. I’ve read somewhere that the current iPhoto doesn’t force you to organize your photos its way. If that’s the case, I might be willing to revisit it. But right now I don’t feel any compelling need to.
Installing RMagick on the Macintosh
October 13th, 2006
This should be considered the fourth post in the “Digital photos and me” series. I thought this might interest a broader audience than the previous posts in the series, so I gave it title that would stand out more.
Well, it’s been a few weeks since the first three installments in this series. The demands of work are the primary reason for the delay, but the new iMac has also played a role. The prerequisites for installing RMagick—our topic for the day—are many, and when you have an older computer with lots of installed software, the prerequisites are easy to forget because they may have been installed months earlier for another purpose. But with a virgin computer, I could figure out exactly what was needed—no more, no less. So I held off on the post until I had a chance to do the installation on the new iMac. That chance came today, so I’m writing it up before I forget what I did.
First, I suppose I should mention the purpose of installing RMagick and what it has to do with digital photos. RMagick is the Ruby interface for accessing the ImageMagick library, a large set of routines for manipulating, combining, and converting digital image files. I wanted a cleaner, command-line version of my old photopage program, and I wanted to write it in Ruby rather than Python. I had used PIL, the Python Imaging Library to write photopage, a library that wasn’t going to be available for Ruby. RMagick seems to be the standard image manipulation library for Ruby, so I went with it.
Next, I’ll warn you that this article, which was linked on Daring Fireball and is ranked fairly high in a Google search on “RMagick os x,” is likely to give you trouble. As near as I can tell, it doesn’t install the fonts that ImageMagick and RMagick need, so you’ll end up with a library that can’t put text in your images. If I’m wrong about this, please educate me.
On the other hand, this article, which is also has a high Google ranking, is very good. I kept it up on one of my Safari tabs to refer to as I did my installation.
Step 1—Install the XCode development tools
These will be on your Tiger installation disk. You can also download them if you’ve signed up (for free) with Apple’s Developer Connection. The downloaded version is the best bet, as it will be the most recent. This is a normal package-type installation: you double-click on the package and it leads you through a few simple steps.
Step 2—Install DarwinPorts
The DarwinPorts project is in flux right now as it moves from one host to another. It’s also changing its name to MacPorts, but the change isn’t reflected everywhere. Don’t let the apparent schizophrenia deter you; DarwinPorts is, in my opinion, the best way to install Unix/Linux software on your Mac. If you’re not familiar with it, here is a nutshell description: it’s an automated way of downloading, configuring, compiling, and installing software with a single command.
I’ve used DarwinPorts’ main competitor, Fink, in the past, but not any more. DarwinPorts packages are usually more up-to-date and give me fewer installation problems. Yes, compiling everything does take more time (Fink can install pre-compiled packages), but I end up with a package tuned for my system. With the change to Intel processors, this is a bigger deal than it used to be.
So anyway, download the latest DarwinPorts from this repository. At present, version 1.3.2 is the latest, and because it doesn’t come in .dmg form you’ll have to download the source code, unzip it, then manually configure and compile it using the directions in the README file. This isn’t such a big deal. Open a Terminal window, go into the DarwinPorts directory and issue the commands
./configure
make
sudo make install
in that order. This will install DarwinPorts in /opt/local. All of the software you will now install with DarwinPorts will also go in that directory, so you need to make sure it’s in your path. I added the line
export PATH=/opt/local/bin:$PATH
to my ~/.bashrc file, then restarted the Terminal to make sure that line was read.
The main DarwinPorts program is called port, and it has many options. You’ll typically run it under sudo because it needs to read and write in places that only an administrator has permission to. The first port command you should run is
sudo port selfupdate
which will update the list of available software (which are known, somewhat confusingly, as “ports”) and do some other housekeeping. Now you’re ready to get into the meat of things.
Step 3—Install ImageMagick and its prerequisites
DarwinPorts is supposed to handle software “dependencies,” which is when one software package depends on another (what I’ve been calling prerequisites). If you ask DarwinPorts to install program A, and program A needs libraries X and Y to work, it’s supposed to install X and Y first, then go ahead and install A. All with just the one command to install A. Generally speaking, this works very well, but ImageMagick can be built in so many different ways (called “variants” in DarwinPorts-speak), I find it easier to just install the prerequisites first, then ImageMagick.
Here are the commands you need to issue:
sudo port install ghostscript
sudo port install bzip2
sudo port install zlib
sudo port install libxml2
sudo port install libiconv
sudo port install freetype
sudo port install jpeg
sudo port install libpng
sudo port install libwmf
sudo port install tiff
sudo port install lcms
You don’t have to do it in this order. In fact, if you install lcms first, you’ll find that several others will be installed automatically. In any event, don’t forget ghostscript; that’s where the fonts will come from.
Now you can install ImageMagick:
sudo port install ImageMagick
If there are errors, you should check to make sure you didn’t forget any of the prerequisites. The command
port installed
will give you a list of everything you’ve installed. You can check it against the list above.
One of the great things about DarwinPorts is how everything it installs is in the /opt/local directory. If you think you’ve really screwed up your installation, you can just delete that entire directory and start over (yes, I’ve done it—did it today, in fact). There won’t be any weird little configuration files left hanging around.
Optional Step 3a—Install a better Ruby
The Ruby that comes with OS X Tiger is version 1.8.2. This will work fine with RMagick (we’re installing RMagick, remember?), but Ruby is a fast-moving language, and 1.8.2 won’t do a lot of the cool new things with, for example, Rails or Mongrel. Also, the interactive Ruby environment, irb, packaged with Tiger wouldn’t work on my iMac. So you may want to install the DarwinPorts version of Ruby, which is currently at version 1.8.5. Just do
sudo install ruby
and you’ll soon have new Ruby at /opt/local/bin/ruby. If you’ve changed your $PATH as I suggested above, this version of Ruby will be the one that launches when you type ruby or irb at the command line. And the Apple-issued version will still be at /usr/bin/ruby if you need it.
Step 4—Install RubyGems
OK, now it’s time to install another package manager, this one specific to Ruby called RubyGems. You can download it from its rubyforge site (rubyforge, as its name implies, is like a sourceforge for ruby), unpack it somewhere convenient, and issue the command
sudo ruby setup.rb
from within the rubygems directory. This will install a new program, called gem, which works a lot like port. As with port, the first thing you should have gem update itself:
sudo gem update --system
Now you’re ready for RMagick.
Step 5—Install RMagick
sudo gem install rmagick
After about 1000 words of setup, it comes down to a very simple command. The great thing, though, is that the work you did installing DarwinPorts and RubyGems will be money in the bank if you want to install SQLite-Ruby, Rails, Rake, etc.
The next (and final, I think) post in the series will describe a Ruby program that uses RMagick to create index sheets, each containing several photos.
Online photo printing and sharing
January 17th, 2006
For the past couple of months I’ve been using Costco’s online photo center to print my digital photos, and it’s worked out well. My only gripe is that I can’t save my finishing preference. I prefer the “lustre” finish to glossy, but glossy is the default, so I have to remember to change to lustre every time I send a print job. So far, I’ve only forgotten once… Also, I’ve learned to upload photos that are already cropped to fit the aspect ratio of the prints I want; e.g. photos to be printed as 4x6s should be cropped to a 3:2 aspect ratio, 8x10s to a 5:4 aspect ratio, etc. Otherwise, the Costco software/hardware does the cropping for you and you may not like where it chooses to cut.
The photos I upload to Costco can be shared with other, sort of the way Flickr works, but without the tagging, commenting and other cool stuff. Earlier last year, I had set up a Flickr account (in my real name) so I could let my mom see and maybe download our latest family photos. I didn’t think downloading would work too well for her, because she lives in a rural area and has only a dialup connection. Megabyte downloads are a big and painful deal for her. Also, she’s not really a whiz with her computer and printer. Flickr recently began offering photo printing services through Target, which would be perfect except that there are no Targets near her. And no Costcos, either.
Enter the Evil Empire. There are, of course, two or three Wal-Marts relatively close to Mom and she’s at one of them or another a couple of times a week. I hate Wal-Mart, but it’s really the best option for Mom, so today I set up an account with the Wal-Mart photo system and uploaded a few files as a test. I did some dry runs to see how easy it is to order prints and, as you might expect, it was very easy to choose the files to print, the number and sizes of the prints, and which store you want to pick up from. They even have a way for you to choose how you want your photo cropped. No choice of finish, but Mom has never been as picky as I am about that. And she’s been having her film processed and printed at Wal-Mart for years, so she’s used to whatever they typically do.
Wal-Mart is cheaper than Costco for 4x6s, but not by much. Costco charges $0.17 per print and Wal-Mart charges $0.15 for in-store pickup. You may have heard that Wal-Mart charges $0.12, but that’s if you choose to have your prints mailed to you and it doesn’t include shipping. In my dry runs of different quantities, shipping always worked out to 7 cents per print.
If Mom is happy with this new account, I will succumb to the Wal-Mart hegemony. My only solace is that she’ll be paying them for the prints, not me.
More photo stuff
August 18th, 2005
To continue on from my last
post,
the other program in my photo organization scheme is a program that
takes the photo files, which have names like DSCnnnn.JPG, and gives
them new names based on the day they were taken. The format for the
names is yymmddnn.jpg, where
yy is the last 2 digits of the year. I didn’t get a digital camera until after the year 2000, and I’m not too concerned about the Y2.1k problem.
mm is the two-digit month. January through September have a leading zero: 01-09.
dd is the two-digit day of the month.
nn is the two-digit number of the photograph taken on that day. I’m screwed if I take more than ninety-nine photos on any given day, but for personal photos I’ve never even come close.
I kept the name to the old DOS “8.3” format because I read somewhere that the ISO 9660 standard for CDROMs requires it. And although there are (obviously) extensions to that standard that allow longer names, the short naming system didn’t seem like too much of a burden.
The name of the program is canonize, a mild pun: the file name is being put in canonical form, and our camera is made by Canon.
Here’s the program.
#!/usr/bin/perl
use Image::Info qw(image_info);
use Getopt::Std;
$help = <<HELP;
canonize: rename photos to reflect the date on which they were taken.
Use the date embedded in the EXIF file. The format for the
file name is yymmddnn.jpg, where yy is the year, mm is
the month, dd is the day, and nnn is the photo number for
that day. This is specialized for the EXIF format of our
Canon PowerShot S30.
usage:
canonize [options] file-list
option:
-s num: the starting number for the photos (default: 1)
-p: print the new photo names, but don't rename the files
-h: display this help message
HELP
getopts('hps:');
$number = $opt_s || 1; # default starting number is 1
if ($opt_h || !@ARGV) { die $usage; }
#initialize count and date string
$i = 1;
$olddatestring = "1989:03:25"; # can be any date that will certainly
# be before the date the photo was taken
while ($oldname = shift) {
my $info = image_info($oldname);
$datestring = $info->{DateTimeOriginal};
if ($datestring =~ /^\d\d(\d\d):(\d\d):(\d\d)/) {
$datestring = substr($datestring,0,10);
if ($datestring ne $olddatestring) {
$olddatestring = $datestring;
$i = $number;
} else {
$i++;
}
$newname = sprintf("%02d%02d%02d%02d.jpg", $1,$2,$3,$i);
}
if ($opt_p) {
print "$oldname -> $newname\n";
} else {
rename ($oldname, $newname) or print "Couldn't change $oldname to $newname\n";
}
}
The program uses the Image::Info module from CPAN to extract the date and time of the photo from the file. (Although we call our photo files “JPEGs,” JPEG is actually a compression method, not a file format. Digital cameras save their images in EXIF files which use JPEG compression for the image data and also holds a bunch of other info, like the aperture setting, shutter speed, focal length. And date and time.)
As I’m trying to do in all my utility programs, canonize has a usage string near the top that I can print as a help message from the command line and which I see immediately upon opening the file for editing or study.
The -s option lets you start your photo numbering at something other
than 01. This may seem weird, but I thought it might be needed in case I
downloaded part of a day’s photos and renamed them before downloading
the rest. So far, I haven’t used that option. I used the -p option
(which prints out the old and new file names, but doesn’t actually
change the name) a lot during debugging and decided to leave it in the
final code (is any program really final?). It turns out I have used it a
few times to make sure things were going to go the way I thought they
should.
The program doesn’t actually sort the files in date order. The intention
is to invoke the program as canonize *.JPG, so it receives the file
names in alphabetical order from the shell. Because of the way the
camera names the files, alphabetical order is also chronological order.
This is potential flaw, and I suppose I should suck in all the file
names and sort them by date before assigning new names to them. But it
works with my personal camera (the Canon) and with the cameras I use a
work (three different models of Nikon), so I’m not inclined to dig in
and change it.
You’ll notice a date of 1989:03:25 embedded in the code. The logic of the program needed a date that was always going to be earlier than the earliest photo being renamed. Any date before I bought the camera would have been fine; March 25, 1989 is my daughter’s birth date.
Photopage
August 18th, 2005
A few years ago, I wanted to get a digital camera for family photographs. To convince my wife—who was in charge of organizing our photos—that this was a good idea, I told her that she would never have to do any labeling or dating of photos again. I got the camera and then had to deliver on the promise. To that end, I’ve developed a simple system of file organization and written two programs. The first, and most important, of these programs is Photopage.
Photopage looks into a directory and creates a series of index sheets, JPEG files with several individual photos laid out in rows and columns and labeled with their file name. At the top of the sheet is a title, which I use to display the month in which the photos were taken. The sheets are printed out and kept in three-ring binders, as easy to flip through as any photo album and made with very little effort. Some subsized sample sheets are given below. You can click on them to get a larger version; the original JPEGs from Photopage were about 300 dpi when printed on a letter-sized sheet of paper.
As I was writing Photopage, I realized it would be just as good for indexing my digital photo files at work. And my coworkers would probably like it, too. So I expanded the program beyond my original design, adding new layouts, subtitles, and extra captioning. Since it was going to be used by people other than me, and on Windows as well as Linux, it needed a GUI.
I wrote Photopage in Python because Python is cross-platform, comes with a GUI (Tkinter, Python’s version of Tk), and has PIL, the Python Imaging Library for dealing with JPEGs and TIFFs.
Photopage was my first significant Python program and it shows. There’s no object-oriented stuff, except for that imposed on me by Tkinter and PIL. And it’s probably not very Pythony, although I haven’t programmed enough Python to really understand its idioms. Still, it works.
To use Photopage, you need Python, PIL, a set of bitmapped font files
(PIL couldn’t use system fonts when I wrote Photopage; I think it can
now, but I haven’t rewritten Photopage to take advantage of them), and
the Photopage source itself. I’ve made a zip file of Photopage, the
fonts, and some elementary
documentation
available on the leancrew site. Linux users will probably have Python
installed already, and there are PIL packages for the major
distributions (Debian’s is python2.3-imaging). Windows users can get a
Python installer from the Python download
site and a PIL installer from
PythonWare. The
installation part of the Photopage
documentation was
written for Windows users. I haven’t written a Linux version because I
figure Linux users are smart enough to translate from the Windows
instructions (basically, you just need to copy—as root—the directory
with the bitmapped fonts into your PIL library directory).
I didn’t make the bitmapped fonts and don’t want to leave the impression that I did. I found them on the ‘net when I first wrote the program. I didn’t make note of the site, and now I can’t find it. If you know who made them, please let me know so I can give credit and thanks.





