Let’s do something fun! In this post we’ll take a photo using your webcam in Ruby.

NOTE: This only works on OS X

I haven’t tried making it work on other operating systems. Not that I don’t like other operating systems, I just haven’t made it work. :-)

Installing the Gem

We’ll use the av_capture gem. It wraps the AVCapture framework on OS X. To install it, just do:

$ gem install av_capture

Using the Gem

I’ll paste the code here first, then explain it:

require ' av_capture ' session = AVCapture :: Session .new dev = AVCapture .devices.find(& :video? ) $stderr .puts dev.name session.run_with(dev) do |connection| $stdout .write connection.capture end

First the program creates a new capture session. OS X can capture from many multimedia devices, and we hook them together through a session. Next, it grabs the first device attached to the machine that has video capability. If your machine has multiple cameras, you may want to adjust this code. After it outputs the name of the camera, we tell the session to start and use that device.

The camera can only be used while the session is open, inside the block provided to run_with . Inside the block, we ask the connection to capture an image, then write the image to $stdout .

Running the code

I’ve saved the program in a file called thing.rb . If you run the program like this:

$ ruby thing.rb | open -f -a /Applications/Preview.app

it should open Preview.app with an image captured from the camera.

Taking Photos Interactively

Let’s make this program a little more interactive:

require ' av_capture ' require ' io/console ' session = AVCapture :: Session .new dev = AVCapture .devices.find(& :video? ) session.run_with(dev) do |connection| loop do case $stdin .getch when ' q ' then break else IO .popen( " open -g -f -a /Applications/Preview.app " , ' w ' ) do |f| f.write connection.capture end end end end

This program will just sit there until you press a button. Press ‘q’ to quit, any other button to take a photo and display it in Preview.app . Requiring io/console lets us read one character from $stdin as soon as possible, and the call to IO.popen lets us write the data to Preview.app .

A Photo Server using DRb

It takes a little time for the camera to turn on before the program can take a photo. This causes a little lag time when we want to take a photo. In the spirit of over-engineering things, lets create a photo server using DRb. The server will keep the camera on and ready to take photos. The client will ask the server for photos.

Server code

Here is our server code:

require ' av_capture ' require ' drb ' class PhotoServer attr_reader :photo_request , :photo_response def initialize @photo_request = Queue .new @photo_response = Queue .new @mutex = Mutex .new end def take_photo @mutex .synchronize do photo_request << " x " photo_response.pop end end end server = PhotoServer .new Thread .new do session = AVCapture :: Session .new dev = AVCapture .devices.find(& :video? ) session.run_with(dev) do |connection| while server.photo_request.pop server.photo_response.push connection.capture end end end URI = " druby://localhost:8787 " DRb .start_service URI , server DRb .thread.join

The PhotoServer object has a request queue and a response queue. When a client asks to take a photo by calling the take_photo method, it writes a request to the queue, then waits for a photo to be pushed on to the response queue.

The AVCapture session’s run block waits for a request to appear on the photo_request queue. When it gets a request on the queue, it takes a photo and writes the photo to the response queue.

At the bottom of the file, we connect the PhotoServer object to DRb on port 8787, and join the DRb server thread.

Client Code

Here is our client code:

require ' drb ' SERVER_URI = " druby://localhost:8787 " photoserver = DRbObject .new_with_uri SERVER_URI print photoserver.take_photo

The client code connects to the DRb server on port 8787, requests a photo, then writes the photo to $stdout .

Running the code

In one terminal, run the server code like this:

$ ruby server.rb

Then in another terminal, run the client code like this:

$ ruby client.rb | open -f -a /Applications/Preview.app

You should have a photo show up in Preview.app . You can kill the server program by doing Ctrl-C.

Speed comparison

Just for fun, let’s compare the speed of the first program to the speed of the client program just using time . Here is the first program:

$ time ruby thing.rb > /dev/null FaceTime HD Camera (Built-in) real 0m3.217s user 0m0.151s sys 0m0.069s

Here is the client program:

$ time ruby client.rb > /dev/null real 0m0.183s user 0m0.070s sys 0m0.038s

The first program takes about 3 seconds to take a photo where the second “client” program only takes 200ms or so. The reason the second program is much faster is because the server keeps the camera “hot”. Most of our time is spent getting the camera ready rather than taking photos.

Weird photos

Here are some weird photos that I made while I was writing this:

Happy Wednesday! <3<3<3<3