A Use for Smartphone Photos
As a smartphone user, I take a lot of photos. Since I bought an iPhone 4 nearly two years ago, I’ve taken just over 6,000 photos with it. 47GB of memories. On average, 10 photos per day, every day, often of nothing in particular.
These photos aren’t good enough, or meaningful enough to anyone else, to post on Flickr. 500px would scoff at them. The few people on Facebook that would recognize the people, places and events in the photos wouldn’t see the point. They’re tiny fragments of my life, and that’s about it.
Instead of forcing these thousands of photos to stay hidden in my iPhoto
library, I found an outlet for them - my homepage. Crudely modelled after the stellar TED.com landing page, it’s supplied by a random set of hundreds of images, all of which I’ve taken, and until now, hand-cropped and hand-selected.
Michael Macias, in a submission to a Codebrawl last November, came up with a brilliantly simple method of content-aware image cropping. By measuring the greyscale entropy of a window as it slides over an image, the highest-interest thumbnail can be determined automatically. I took this solution, modified it (faster, uses ImageMagick, etc.), and hacked together a quick Ruby script that now lives on Github:
# Hacky random image thumbnailer.
# by Peter Sobot, April 21, 2012
# Based heavily on code by Michael Macias
# (https://gist.github.com/a54cd41137b678935c91)
require 'rmagick'
images = Dir.glob(ARGV[0] ? ARGV[0]
: '-default-input-paths-')
output_dir = (ARGV[1] ? ARGV[1]
: '-default-output-directory-')
num = Dir.glob(output_dir + '*.jpg').count
lim = 50
def entropy(image)
hist = image.quantize(256, Magick::GRAYColorspace).color_histogram
area = (image.rows * image.columns).to_f
-hist.values.reduce(0.0) do |e, freq|
p = freq / area
e + p * Math.log2(p)
end
end
def smart_crop(image, crop_width, crop_height)
x, y, width, height = 0, 0, image.columns, image.rows
slice_length = 16
while (width - x) > crop_width
slice_width = [width - x - crop_width, slice_length].min
left = image.crop(x, 0, slice_width, image.rows)
right = image.crop(width - slice_width, 0, slice_width, image.rows)
if entropy(left) < entropy(right)
x += slice_width
else
width -= slice_width
end
end
while (height - y) > crop_height
slice_height = [height - y - crop_height, slice_length].min
top = image.crop(0, y, image.columns, slice_height)
bottom = image.crop(0, height - slice_height, image.columns, slice_height)
if entropy(top) < entropy(bottom)
y += slice_height
else
height -= slice_height
end
end
im = image.crop(x, y, crop_width, crop_height)
if im.columns < crop_width or im.rows < crop_height
raise "Cropped image too small!"
end
im
end
puts "Matched #{images.length} images."
images.shuffle.slice(0, lim).each do |file|
output = output_dir + (num + 1).to_s + '.jpg'
begin
im = Magick::Image.read(file).first.auto_orient.resize(0.2)
r = smart_crop(im, 248, 248)
r.resize!(124, 124, Magick::QuadraticFilter)
r.strip!
r.write(output) { self.quality = 85 }
num = num + 1
puts "#{file} => #{output}"
rescue StandardError => e
puts "ERROR: #{file} didn't work: #{e}"
end
end
This script automatically chooses 50 random images from a given path (or shell glob) and crops them to their most “interesting” thumbnails. The thumbnails are scaled to size, and saved in incrementing order in the destination folder. It’s highly optimized for my personal workflow, but it does seem to work quite well. For example, take the following shot of Zameer Manji:
The original photo was poorly exposed, had no clear subject, and was, well, weird. After automatically cropping it down to a tiny thumbnail, it fits in nicely on my homepage as an artsy shot of a bike rack in the daylight.
Only one thing left to do: take more photos.