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)
  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:

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.

 
18
Kudos
 
18
Kudos

Now read this

Macbook & Two Hard Drives

I have a lot of media on my laptop. Roughly 140GB of music, 40GB of photos, 40GB of games, video and other random stuff on my internal drive alone. Like any computer user, or lazy person, I suppose, I hate to wait. I want a faster... Continue →