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.

My homepage, when this was written.

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)

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
      width -= slice_width

  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
      height -= slice_height

  im = image.crop(x, y, crop_width, crop_height)
  if im.columns < crop_width or im.rows < crop_height
    raise "Cropped image too small!"

puts "Matched #{images.length} images."

images.shuffle.slice(0, lim).each do |file|
  output = output_dir + (num + 1).to_s + '.jpg'
    im = Magick::Image.read(file).first.auto_orient.resize(0.2)
    r = smart_crop(im, 248, 248)
    r.resize!(124, 124, Magick::QuadraticFilter)
    r.write(output) { self.quality = 85 }

    num = num + 1
    puts "#{file} => #{output}"
  rescue StandardError => e
    puts "ERROR: #{file} didn't work: #{e}"

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:

That's Zameer. Yup.

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.


Now read this

The middle ground between form and function

I’ve noticed a distinct trend in all of my recent work. Not all of it is useful, and not all of it is feature-complete - but it all places a lot of importance on form over function. Let me give an example: Earlier this month, I put... Continue →