Time-lapse in the car

Andrew Booker 2008-04-10 17:33:11

I recently spent a week of evenings trying to write my own webcam image capture application in C++. It all seems fairly easy, now that I've got it working more or less the way I want, but really it was a dull slog to get anywhere at all. There isn't a lot of well presented expertise on this subject published on the web, so I've been working mainly from the Microsoft Developer Network (MSDN) documentation, which needs a reasonably strong stomach, but is indispensable.

What I've been trying to do is set up a silent time-lapse capture application using a webcam. I need a wide range of capture intervals, from a minute down to half a second. It needs to be able to run reliably on my laptop with the lid down for 24 hours or more. No doubt people have already written Windows applications that can do something like this. I wanted to write my own because

Originally I thought I'd be able to coerce some ordinary Windows applications into doing it for me. To recap, a couple of weekends ago I made a start by trying to use the basic Windows utility for taking pictures from a webcam. To see what I mean, plug in your webcam (with stream driver already installed), go to Control Panel, select Scanners and Cameras, double click on the icon for your webcam, and up comes the Scanner and Camera Wizard, which, if you're sitting at my desk, looks like this.

So far, so Windows for normal people. The nerdy thing I was trying to do last week was to get a Visual Basic application to control the Scanner and Camera Wizard remotely. I wanted it to press the Take Picture button, then press Next, then enter a file name, press a few other buttons and a tiny jpeg turns up somewhere on the hard disk.. all done automatically by Visual Basic every 10 seconds, running for long periods of time while I'm away doing something else.

Predictably this did not work well, because you have to guess how long the Wizard takes over the combined tasks of saving the file and consuming at least four inexplicable seconds deleting the temporary copy it made. By which point the Visual Basic Timer has completely lost it and you end up getting images... every now and then. If I'm going to take this around the M25 in the car, I want the images turning up on time, and I definitely need more than one every five seconds. Worse, if you even touch your computer while VB is controlling another window, as I might accidentally press a key in the car, it will probably break the fragile association between the Wizard and its controller, the VB messages will go to the wrong place, all sorts of useless help files will start popping up and taking over the desktop, and the image capture will stop before I get past the A10 interchange.

As an intial C++ alternative, I had a go at using the Windows Image Acquisition stuff. WIA has a COM interface, so it looked like I could get this working in VB, but I got nowhere. I got a bit further using WIA from C++. From the way it was numbering the files I guessed WIA is the underlying functionality beneath the Scanner and Camera Wizard. Saving to jpeg is compulsory, and this is not encouraging. Worse, it looked like I could only use WIA with a display window for the image. A display window would be completely pointless. Whether I'm driving, or I've gone off for the day, I will have no use for a monitor over the several-hour interval during which my pictures are being taken. Nor spare battery. So I gave up on WIA.

I eventually got it working using DirectShow video capture. Very basically, what the code does is connect a sample grabber to the webcam stream driver output. Allegedly my webcam can issue one 800 x 600 pixel frame every 30th of a second. I don't believe that, more likely that is its stream driver issuing a 30fps video blurred together from however much it can drag through the USB port. Anyway, the sample grabber gives you a callback on each of those frames, where it presents you with the image data and the exact time it was captured. So if I want to save one frame a second, I respond to the nearest callback that falls one second after the last one, and add that frame directly on to an AVI output file. Although I'm capturing one frame per second, my output AVI file is 25fps or more, so the result is a massively sped-up video.

This seems to be working well. My code takes 15-30ms to add each frame onto an AVI, and approximately twice as long to create a separate bitmap file of the frame. Although that imposes a practical limit on my capture rate, if I'm only taking one frame every minute, or even two every second, there is plenty of time for both. If my frame capture rate is one per minute, it probably means I will be running the application for many hours. So saving the frames as separate bitmaps is a useful backup, because if anything went wrong with the laptop I would still have the individual bitmap files even if I lost the AVI.

All this is looking promising for my sunrise-sunset time-lapse photography project (one frame per minute) and my M25 loop (2 frames per second) for Improvizone gig backdrop projections. I've been planning both for a couple of weeks, but I wasn't happy with my crappy VB attempt. Now I reckon I have something that will work. One huge advantage my C++ version has is that it can run as a completely invisible background process. It doesn't need a screen context in order to work. That means I can launch it as a scheduled task from a laptop in standby mode in the boot of my cleverly parked car. This is good, because the view from my house is not great for what I want, whereas there are a couple of promising spots just down the road.

Time-lapse photoblography << | >> Gig: Aimless Mules at the Plough, E17 on 17 April 2008