Wednesday, April 18, 2012

Automating time-lapse photos with gphoto

Important update, December 2023: This post is quite old and hasn't been updated in more than 10 years. It might or might not be useful or accurate. I've moved to using a camera with a built-in intervalometer.

My article on installing and using gphoto introduced a great piece of software which lets you use a computer to control your camera. 
The purpose of this article is to show you how I used gphoto to automatically adjust the shutter speed in this time-lapse video of the sun setting behind the Portage Lake Lift Bridge.

Time lapse photos and videos aren't especially new, but the key to my video is the dramatically different exposures needed at the beginning and at the end. I was shooting around f/8 the whole time, but I began with a shutter speed of around 1/160 sec. and ended with exposures of 30 seconds each! How did I do this? With gphoto, of course!
First, a few words on what this article is not: It is not a tutorial on installing or the general use of gphoto. If you're interested in that, check out my article linked above. This is also not a tutorial on shell scripts, Linux, or any of the other technology necessary for making this stuff work -- while I'll introduce the basics of shell scripts, for the full details you should read this tutorial on Bash. Finally, gphoto gives different names to the settings on each camera -- you'll have to find these out on your own (although I'll give some hints there, as well!). There are just too many cameras out there, and each one works slightly differently.

Setting the exposure with gphoto

One of gphoto's most important features is the ability to control a camera's settings -- everything from shutter speed to image size, for most cameras.
With your camera plugged in to your computer's USB port, pull up a command line and type:
gphoto2 --list-config
This should dump a huge amount of information, listing every setting which can be read or configured with gphoto. A lot of this may be gibberish, but scroll around until you see something like these:
These are the settings for f/stop and shutter speed, respectively, on my Nikon D40x. As usual, your camera may be different -- but it's probably something sensibly named.
Now, we'll check out the possible values:
gphoto2 --get-config /main/capturesettings/f-number
This will list all possible values for the setting in question. In my case, it looks something like this:
Label: F-Number                                                               
Current: f/9
Choice: 0 f/3.8
Choice: 1 f/4
Choice: 2 f/4.5
Choice: 15 f/20
Choice: 16 f/22
A bit of interpretation: most of the settings that we'll be interested in are "radio", meaning that you can choose one setting from a list (like radio buttons). The "Current" line is the camera's current setting, and what follows is a list of all possible values for that setting. Every setting comes in two types: an index (that's the 0, 1, 2, etc. next to "Choice:") and a value (f/3.8, f/4, f/4.5, etc.). It's sometimes convenient to be able to say "set the aperture to f/8!", but because we want to automate the process of changing the exposure, we'll use the index which comes in more easily predictable values.
To set the aperture, first make sure that your camera is in a mode which allows the aperture to be changed (aperture priority/Av, or manual). You'll get an error if your camera doesn't want the aperture to change. Then type something like this:
gphoto2 --set-config-index /main/capturesettings/f-number=1
That would set the aperture to f/4 (setting number 1). We could equivalently do:
gphoto2 --set-config-value /main/capturesettings/f-number=f/4
This is more human-readable, but harder to automate.

Warning: gphoto lies!

You might have noticed the two settings for shutter speed which I listed above:
Why are there two? There's a perfectly good reason, actually. Listing the possible values:
gphoto2 --get-config /main/capturesettings/shutterspeed     
Label: Shutter Speed                                                           
Current: 0.0125s
Choice: 0 0.0002s
Choice: 1 0.0003s
Choice: 2 0.0004s
Choice: 50 25.0000s
Choice: 51 30.0000s
Choice: 52 429496.7295s
Now compare this to the other one:
gphoto2 --get-config /main/capturesettings/shutterspeed2
Label: Shutter Speed 2                                                         
Current: 1/80
Choice: 0 1/4000
Choice: 1 1/3200
Choice: 2 1/2500
Choice: 50 25
Choice: 51 30
Choice: 52 65535/65535
The first one lets you set shutter speed in decimal values, while the second is in fractions of a second. The values are the same index in each case (index 1 is always 0.0003 seconds, which is the same as 1/3200 second, for example). So, you could set your shutter speed any ol' way you like. All of the following have the same effect:
gphoto2 --set-config-value /main/capturesettings/shutterspeed=0.0002s
gphoto2 --set-config-value /main/capturesettings/shutterspeed=0.0002
gphoto2 --set-config-value /main/capturesettings/shutterspeed2=1/3200
gphoto2 --set-config-index /main/capturesettings/shutterspeed=1
gphoto2 --set-config-index /main/capturesettings/shutterspeed2=1
But... things aren't always quite so nice. For exmple, look at Choice 52... what's that? Turns out that it represents "bulb" mode, in which the shutter stays open as long as you command it to. Sadly, gphoto can't control Nikons in bulb mode -- you can set the shutter speed, but gphoto won't let you take pictures. This doesn't seem to be a problem with Canons.
But wait... there's more! For some reason, occasionally when I list my shutter speed, the list jumps from 1/30 sec at index 21 to a supposed 30 seconds at index 22. Setting the shutter speed to index 22, however, correctly sets the shutter speed to 1/25 sec. This eventually corrects itself. The lesson: gphoto lies sometimes. Beware, and watch out for funny-looking values.

Automating exposure changes

Now on to the real point of this article: automating the changes which we made by hand above. With those commands available, it's not too hard to write a script which calls gphoto for us at predefined intervals. The general idea of the script is:
  1. Choose a starting and ending shutter speed (or aperture, etc.) and a time interval.
  2. Set the shutter speed to the desired speed.
  3. Take a photo.
  4. Wait for the desired amount of time.
  5. Increase the shutter speed by one index, and go back to #3.
Here's my simple bash script which does exactly this:
# Captures a series of images using gphoto2
# $1: Starting shutter speed (index: 0 = 1/4000, 1 = 1/3200, ...
# $2: Ending shutter speed
# $3: Delay (in seconds) between exposures

for ((i=$1; i<=$2; i++))
gphoto2 --set-config-index /main/capturesettings/shutterspeed=$i;
gphoto2 --capture-image;
sleep $3;
If this is (for example) saved in a file named, then:

./ 1 10 2

will take 10 photos, one at each shutter speed from 1/4000 sec through 1/400 sec., waiting 2 seconds between each exposure.

To apply this to time lapse photos, you'll need to do the following:
  1. Determine the proper shutter speed for the beginning of your time lapse photo,
  2. Determine the proper shutter speed for the end of your time lapse, and
  3. Determine the amount of time (in seconds) from the beginning to end.
Then divide the amount of time by the number of different shutter speeds, which gives you the number of seconds between each photo. For example, for my lift bridge photo:
  1. My starting time was bright enough that I decided 1/250 sec. (index 12) would work.
  2. My ending time would be pitch black, so I decided that 30 sec. (index 51) was necessary.
  3. This lasted for about 1 hour (which is 3600 seconds) beginning at sunset.
I had 40 different shutter speeds between 1/250 sec. and 30 sec. (51-12 = 39, but that misses the ending value, so I had to add 1 -- just the same as there are 3 numbers from 1 to 3: 1, 2, 3, even though 3 - 1 = 2). So, I needed 3600/40 = 90 seconds between each exposure. Thus, I used:

./ 12 51 90


Details, refinements, and trouble

Naturally, there is a lot more than you can do with scripts like this. The simplest of these is to control other settings -- such as aperture -- in the same way as my script controls shutter speed. Here are some other considerations:

  • This script assumes that light fades in a nice even fashion during sunset -- which is hardly true, especially if you live in a hilly or mountainous area. There are several ways to handle this, such as modifying the "sleep" command, for example: sleep $3+$i;
  • This script also assumes that you want to take one photo per shutter speed. You could "smooth out" your exposures a bit by taking two (or more) photos. As the light changes, even photos at the same exposure will look a bit different. In fact, this is what I really did for the Lift Bridge Sunset time-lapse! Naturally, your "sleep" length should be a bit shorter in this case:
for ((i=$1; i<=$2; i++))
gphoto2 --set-config-index /main/capturesettings/shutterspeed=$i;
# Take first photo
gphoto2 --capture-image;
sleep $3;
# Take second photo
gphoto2 --capture-image;
sleep $3;
  • Another way to handle uneven lighting or non-regular changes in settings is to simply make your script into a list of commands, without need of a loop:
gphoto2 --set-config-index /main/capturesettings/shutterspeed=5;
gphoto2 --set-config-value /main/capturesettings/f-number=f/9;
sleep 5;
gphoto2 --set-config-index /main/capturesettings/shutterspeed=6;
gphoto2 --set-config-value /main/capturesettings/f-number=f/10;
  • For simpler applications, you may not need a script at all: gphoto has a built-in time-lapse mode for occasions when the settings will stay the same through all exposures. This is easy to use, such as this example which takes a photo every 30 seconds. It goes for 120 frames and then stops.
gphoto2 --interval=30 --frames=120 --capture-image-and-download
  • There is another way to get gphoto to do specific actions at specific times: the --hook-script option causes gphoto to call a specified script after certain events. Unfortunately, this script necessarily is called fresh after every download and so can't keep a "memory" (such as the number of the current shutter speed). There are probably ways around this, but it doesn't seem to help in the situation of this article.

That be it!

Thanks for reading! Hopefully you found this introduction to scripts and gphoto enlightening -- and if not, feel free to leave me a comment (or send me an email). I'll do my best to make suggestions.


thea said...

very cool time lapse photos.

Anonymous said...

Great article!
Thank you

Tiara said...

This is gorgeous!

Anonymous said...

I also had to use: gphoto2 --set-config-value /main/capturesettings/shootingmode=Manual to change the shooting mode.

Unknown said...

Thank you thank you thank you! I have been trying to figure out how to do precisely this for a while, and was about to start digging around with the notorious Nikon SDK to control my D3. This brings it all within reach of my meagre Python skills!