Image processing on the XO laptop


The XO laptop from the One Laptop Per Child organization is a wonderful toy. Sure it’s not very powerful (430Mhz AMD Geode/256M RAM/1G flash HD), but it does have 3 USB slots, an SD drive, a builtin camera and WiFi 802.11b. It is possible to expand the file system through USB sticks or SD disks or even NFS (although this might be a little slow over WiFi).

It’s not a replacement for a work PC or even a netbook, but it’s an amazing device for experimental stuff (like building your own spycam). Think of it this way: the XO is a battery operated, WiFi enabled platform in a tough plastic case that you can build a variety of software toys with.

Without putting additional libraries onto the XO, I discovered that it is possible to do some image processing on the XO using pygame, the python bindings for SDL. For my spy cam project, I wanted to avoid transferring “blank” pictures to the degree that I could. Pictures can be nearly totally white or totally black. Programmatically, this can be determined in a number of ways. I choose to look at the average brightness of the photo.

Each photograph taken by the spycam is stored on disk as a PNG file. Each pixel in these PNG files has an 8-bit value for the Red, Green and Blue components. By adding each of these values together and dividing by three, a number is generated between 0 and 255. All of these averages can be added together and divided by the number of pixels to get the brightness of the average pixel in the photo. If the average is very high (greater than 250), the photo is mostly white and probably useless. If the average is very low (less than 10), the photo is mostly black and probably uninteresting.

The code below is a python script that runs on my XO. It uses the pygame library to do the photographic analysis described above.

import pygame
import sys
import os

if len(sys.argv) < 2 :
   print "Supply an image file name\n"

path = sys.argv[1]
if not os.path.exists(path) :
    print "File '" + path + "' does not exist\n";

surf = pygame.image.load(path)

bright_sum = 0.0
for y in range(0,surf.get_height()-1) :
  for x in range(0,surf.get_width()-1) :
      pix = surf.get_at((x,y))
      # average brightness
      bright_sum += (int(pix[0]) 
                 + int(pix[1]) 
             + int(pix[2]))/3.0

avg = (bright_sum + 0.0) 
      / (surf.get_height() * surf.get_width())
print("%.2f" % avg)

The script is remarkably brief. It expects a filename to be passed on the command line. It loads the file to create a pygame.Surface object, which provides access to each pixel in the photo. The average brightness for each pixel is summed together. After all the pixels have been summed, the average brightness of the photo can be calculated and printed.

One gotcha that’s worth mentioning is that all the math done in this process should be done with floats or doubles. There is usually a fractional component of averaging. Since I’m taking the sum of many averages, losing this fractional component is a problem. Therefore, make sure that all integers have a fractional component. This forces python to do the arithmatic with floats or doubles, which preserves precision.

More interesting analysis can be done. The pygame library provides a kind of beachhead for additional exploration!