HDR photography with Raspberry Pi and gPhoto2 – Revision 2

Last August I started a small project to use my Raspberry Pi to do bracketed image capture for HDR using my ageing Nikon D40. My solution worked but was not complete. My solution required me to compose my scene and then connect and power on the RPi and wait for the script to complete and then shut down the RPi. I wanted something that I could trigger with a switch.

After much trial and error — and a bit of panic — I have a fully functional solution written in Perl. This HDR Raspberry Pi project is based on other projects and ideas from several places including the comments section of my original post from last August. In particular, I would like to recognize the following projects.

Zach Dwiel’s gphoto2-timelapse project is written in Python but I got the basic concept of what I needed to from his project. Using a startup script and usbreset ideas were borrowed from his project.

After searching around for how to use the GPIO on the Raspberry Pi I found a link to gpio from WiringPi. This is an implementation of most of the Arduino Wiring functions for the Raspberry Pi. The gpio binary plus a very easy to understand diagram and some simple code from the TNET Raspberry Pi site helped complete my project. I also found the RPi low-level peripherals page on the eLinux.org site very useful.

This project allows you to create bracketed images for use in HDR photography using the Raspberry Pi, a supported DSLR camera connected via USB to the Raspberry Pi, and the gphoto2 program. The script is a Perl rewrite of an earlier BASH script. The goals for the Perl rewrite was to make things a little easier.

I’ve included the code for the main script and usbreset but the rest you’ll have to download and install yourself. Download and compile the WiringPi code to create gpio. You will need this to control the GPIO pins on the RPi and a switch to signal the main script to do its work.

You will also need to install usbutils – Linux USB utilities – to get lsusb. lsusb is a utility for displaying information about USB buses in the system and the devices connected to them. I found lsusb more useful for detecting when a camera was attached to the RPi and turned on. I could have used gphoto2 with the autodetect switch but I found lsusb was faster.

You will also need to compile usbreset. This handy piece of code helped reset the USB interface to the Nikon camera between brackets sets. I found that without usbreset that gphoto2 would generate a lot of errors using the Nikon.

You will need to install Perl to run the HDR scripts. This might already be a part of your library.

Once everything is installed, you need to tweak the script a bit to get it to work with different cameras. For example, I used lsusb to specific USB ID for my Nikon D40. I used that in the script. You will also need to find the proper gphotos2 parameters for you camera. Once you have the camera specific parameters and functions in place, you can use the Perl scripts to begin taking images.

perl hdr.pl There is also an rc script which you can use to start the HDR script automatically when the computer (raspberry pi) is turned on. To setup, this feature runs the following commands:

sudo ln ./rc.hdr /etc/init.d/rc.hdr cd /etc/init.d/ sudo update-rc.d rc.hdr defaults

The script is made up one main loop and four functions. One function uses the lsusb utility to get the bus and device information for the Nikon. This information is then used by usbreset to re-initialise the Nikon’s USB connection in between bracket captures. I found that this eliminated a lot of the error messages from gphoto2.

Two more functions control the gpio so that I can trigger the capture event. One function takes the five brackets images.

That’s it in a nutshell. I did notice that the slowest part of the script is the gphoto2 capture. I can’t explain why but I notice that gphoto2 takes about 5 seconds to set up before it will start capturing images.

Moments after completing this project I had some ideas for improvement. I want to reduce the time between pushing the button and capturing the last image. Currently, this is five seconds. I suspect that calling out to gphoto2 from Perl is slowing things down. I think maybe writing Perl binding to the gphoto2 library may help or perhaps writing the entire program in C. I’m not a big fan of C — I try to avoid strongly typed languages in general — but if I can find the time and motivation I will try.

You can find all the code on GitHub.

HDR photography with Raspberry Pi

UPDATE: See my new blog post where I rewrote the code in Perl and added a trigger for the Pi.

I bought my Nikon D40 in 2006. The D40 is my first and only DSLR camera and I’ve loved using it every day that I’ve had it. At the time, it was Nikon’s entry-level DSLR and it suited my budget and photography skills. Over the last 6 years that I’ve owned the device my photography skills have improved but not my budget 1. I’d had to make do with the limitations of the D40 — 6-megapixel DX sensor, 200-3200 ISO range, no auto-bracketing — but the lack of auto-bracketing has been the most limiting.

I love making high dynamic range (HDR) 2 photographs. I love how my photographs come alive with the expanded range offered by HDR. HDR photography involves taking multiple photographs at varying exposure levels and combining the photos into a single final image. HDR photography requires a good tripod and a camera that can shoot bracketed photos. Having a camera body with auto-bracketing makes this easy. On the D40 I can manually adjust the exposure values (EV) between each capture but I risk moving the camera. A slight movement can make aligning the images much harder.

I’ve researched various solutions for my problem. I used a Mac app, Sofortbild, to tether my D40 to my MacBook via USB. Sofortbild provides complete remote control of a Nikon DSLR camera — like shutter speed, aperture, exposure, white balance, iso, image format — and automatically transfers captures images to the Mac hard drive. It can also auto-import images into iPhoto, Aperture or Adobe Lightroom. However, I was interested in its use for HDR photography.

Perform bracketing with an arbitrary number of exposure values by defining the minimum and maximum shutter speed and step. After bracketing high dynamic range images can automatically be calculated including auto image alignment and saved in all major high dynamic range image file formats. Filenames will get the same index including incrementing suffix for easier workflows with external high dynamic range applications.

Sofortbild is easy to use and fully functional but … the setup wasn’t only practical in a studio or controlled set. I just didn’t see myself lugging a laptop, tripod, camera, and lenses around. With a MacBook Air, maybe. A 7 lb MacBook. No. I wanted something lighter.

I started researching options to control the brackets using my iPhone. Searching the web I found a project called DSLR.bot. The creator wrote an iPhone/iPad (iOS) app that could trigger an IR LED to send the correct binary sequence to trigger the IR on the Nikon. The creator sells a pre-built IR remote but I built one myself. However, the author of the app limited the fastest shutter speed to 3/10 second. This was too slow for my daylight HDR. Most of my test shots were completely blown out in the highlights.

When the Raspberry Pi single-board computer was announced I could immediately see the usefulness of such a small. I ordered one and four months later I could start working on my first project. I planned to use the Raspberry Pi to control my Nikon via USB and capture bracketed images.

After much research and trial and error, I think I have a working solution.

![](https://islandinthenet.com/wp-content/uploads/2012/08/IMG_2830-960×960.jpg) My D40 auto-bracketing kit.

What you’ll need

Installing Raspbian “wheezy” Linux on an SD card

I used an OS X user account that has administrator privileges to install Raspbian “wheezy” Linux onto a 4GB SD card. The card was mounted on my OS X 10.8 iMac via a Moshi card reader. I followed the instruction on the Wiki but with some small tweaks. A little trial and error and I had a working RPi.

My disk showed up as follows:

/dev/disk4s1 3.7Gi 722Mi 3.0Gi 20% 0 0 100% /Volumes/NIKON D80

The Wiki did not make it clear that I needed to issue the commands from an account with system administrator privileges and that the diskutil commands must be executed using sudo. I was frustrated running these commands until I understood that little detail.

It is necessary to unmount the partition so that you will be allowed to overwrite the disc:

iMac-2:~ khurt$ sudo diskutil unmount /dev/disk4s1

Using the device name of the partition, in my case /dev/disk4s1, I got the raw device name for the entire disc, by omitting the last “s1” and replacing “disc” with “rdisk”. So my raw device name was /dev/rdisk4.

In the terminal I wrote the image to the card with this command, using the raw disc device name from above.

iMac-2:~ khurt$ sudo dd bs=1m if=~/Downloads/2012-07-15-wheezy-raspbian.img of=/dev/rdisk4

Once complete, I unmounted the SD card and inserted into the slot on my Raspberry Pi. I hooked up the Raspberry Pi to my home network via my AirPort Express via an Ethernet cable. My RPi was powered by a USB charger I picked up from Radio Shack. My plan was to get the device controlling my camera with a script and the power the device from the Mophie Juice Pack Powerstation battery/a>.

gPhoto2

Since my plan was to use the RPi to control my Nikon D40 for exposure bracketing of images for use in HDR photography I needed software that could send Picture Transfer Protocol (PTP) commands to my Nikon. My research led me to a library, gPhoto2, written for this purpose.

gPhoto2 is a free, redistributable, ready to use set of digital camera software applications for Unix-like systems … gPhoto2 runs on a large range of the UNIX-like operating system, including Linux, FreeBSD, NetBSD, MacOS X, etc.

Fortunately, someone had ported gPhoto2 to Raspian “wheezy”. My first order of business was to download and install the library.

I used the following command:

pi@raspberrypi ~ $ sudo apt-get install gphoto2 The following new packaged were installed (successfully): gphoto2 libcdk5 libexif12 libgd2-xpm libgphoto2-2 libgphoto2-l10n libgphoto2-port0 libltdl71

Once gPhoto2 was installed I tested to make sure things worked. I plugged my Nikon D40 into the RPi via a mini-USB to USB cable and ran the following command:

pi@raspberrypi ~ $ gphoto2 --auto-detect
Model                          Port                                            
----------------------------------------------------------
Nikon DSC D40 (PTP mode)       usb:001,006

Capturing images

I tested out a few of the commands to set ISO, exposure value (EV) and capture images. However, I needed a way to issue commands to capture five successive images at different exposure values without and I had no way to trigger. After much trial and error, I finally figured out the right combination of gPhoto2 and bash script commands to control the D40 when the camera is turned on.

#this command will set the exposure compensation vale to zero
--set-config /main/capturesettings/exposurecompensation 0

#this commands sets the camera to save images to the SD card
--set-config /main/settings/capturetarget=1

#this command will capture an image
—capture-image

#this command will set the ISO on the D40 to 200
--set-config /main/imgsettings/iso=0

I wanted a script that would wait until the camera was running, take 5 photos, then go back to waiting. I looked for ways to trigger the script when the camera shutter button was pressed but I could not find a way to do so. The best I could do was wait until the camera was turned on then capture the images and exit.

I used the following code to keep the script in a wait state until the camera is turned on.

# Wait for camera to be turned on
# by checking USB
DEVICE=$(gphoto2 --auto-detect | grep usb | cut -b 36-42 | sed 's/,///')
while [ -z ${DEVICE} ]
    do
    sleep 1
    DEVICE=$(gphoto2 --auto-detect | grep usb | cut -b 36-42 | sed 's/,///')
done

Once the RPi has detected the camera has been turned on I could initialize the camera and capture images.

echo "Camera has been turned on"
# Configure camera for gphoto2 access and later capture:
# NOTE: I'm not sure this is needed.

gphoto2 --set-config-value /main/capturesettings/exposurecompensation=0 
--capture-image-and-download 
--filename junk.jpg 
--force-overwrite

rm -f junk.jpg

# Assumes camera is in Aperture Priority mode
# Sets ISO to 200 and capture target to SD card
# see config.txt for other options

# Capture 5 images at EV 0,-2,-4,2,4
echo "Capturing five images"
gphoto2 --set-config /main/imgsettings/iso=0 
    --set-config /main/settings/capturetarget=1 
    --set-config-value /main/capturesettings/exposurecompensation=0 
    --capture-image 
    --set-config-value /main/capturesettings/exposurecompensation=-2000 
    --capture-image 
    --set-config-value /main/capturesettings/exposurecompensation=-4000 
    --capture-image 
    --set-config-value /main/capturesettings/exposurecompensation=2000 
    --capture-image 
    --set-config-value /main/capturesettings/exposurecompensation=4000 
    --capture-image

The script is executed by the pi user crontab every minute. The challenge is that I wanted the script to run, capture 5 images, and stop until the user was ready to capture another set. Remember, I had no way to trigger the script with a camera event. But I also wanted to make sure that the script would launch multiple copies of itself within the time it took to capture the images. I decided to set a status flag using a file to indicate to the script when it was already running. I used the useful bits of a script written by Jonathan Franzone

# ------------------------------------------------------------
# Setup Environment
# ------------------------------------------------------------

PDIR=${0%`basename $0`}
LCK_FILE=`basename $0`.lck

# ------------------------------------------------------------
# Am I Running
# ------------------------------------------------------------

if [ -f "${LCK_FILE}" ]; then

    # The file exists so read the PID
    # to see if it is still running
    MYPID=`head -n 1 "${LCK_FILE}"`

    TEST_RUNNING=`ps -p ${MYPID} | grep ${MYPID}`

    if [ -z "${TEST_RUNNING}" ]; then
        # The process is not running
        # Echo current PID into lock file
        echo "Not running"
        echo $ > "${LCK_FILE}"
    else
        echo "`basename $0` is already running [${MYPID}]"
        exit 0
    fi
else
    echo "Not running"
    echo $ > "${LCK_FILE}"
fi
`

The somewhere in the script after I’ve captured my images I could put this.

`# ------------------------------------------------------------
# Cleanup
# ------------------------------------------------------------

rm -f "${LCK_FILE}"

Putting it all together

Putting the thing together3 I have a complete script for capturing five images seconds after the D40 is turned on.

#!/bin/bash

#  hdr.sh
#
#
#  Created by Khürt L. Williams on 8/21/12.
#  Description: Script for executing gPhoto2 commands to record 5 bracketed images
#  Portions are from a script written by Jonathan Franzone: http://www.franzone.com/2007/09/23/how-can-i-tell-if-my-bash-script-is-already-running/

# ------------------------------------------------------------
# Setup Environment
# ------------------------------------------------------------

PDIR=${0%`basename $0`}
LCK_FILE=`basename $0`.lck

# ------------------------------------------------------------
# Am I Running
# ------------------------------------------------------------

if [ -f "${LCK_FILE}" ]; then

    # The file exists so read the PID
    # to see if it is still running
    MYPID=`head -n 1 "${LCK_FILE}"`

    TEST_RUNNING=`ps -p ${MYPID} | grep ${MYPID}`

    if [ -z "${TEST_RUNNING}" ]; then
        # The process is not running
        # Echo current PID into lock file
        echo "Not running"
        echo $ > "${LCK_FILE}"
    else
        echo "`basename $0` is already running [${MYPID}]"
        exit 0
    fi
else
    echo "Not running"
    echo $ > "${LCK_FILE}"
fi

# ------------------------------------------------------------
# Image capture
# ------------------------------------------------------------

# Wait for camera to be turned on
# by checking USB
DEVICE=$(gphoto2 --auto-detect | grep usb | cut -b 36-42 | sed 's/,///')
while [ -z ${DEVICE} ]
    do
    sleep 1
    DEVICE=$(gphoto2 --auto-detect | grep usb | cut -b 36-42 | sed 's/,///')
done

echo "Camera has been turned on"
# Configure camera for gphoto2 access and later capture:
# NOTE: I'm not sure this is needed.

gphoto2 --set-config-value /main/capturesettings/exposurecompensation=0 
--capture-image-and-download 
--filename junk.jpg 
--force-overwrite

rm -f junk.jpg

# Assumes camera is in Aperture Priority mode
# Sets ISO to 200 and capture target to SD card
# see config.txt for other options

# Capture 5 images at EV 0,-2,-4,2,4
echo "Capturing five images"
gphoto2 --set-config /main/imgsettings/iso=0 
    --set-config /main/settings/capturetarget=1 
    --set-config-value /main/capturesettings/exposurecompensation=0 
    --capture-image 
    --set-config-value /main/capturesettings/exposurecompensation=-2000 
    --capture-image 
    --set-config-value /main/capturesettings/exposurecompensation=-4000 
    --capture-image 
    --set-config-value /main/capturesettings/exposurecompensation=2000 
    --capture-image 
    --set-config-value /main/capturesettings/exposurecompensation=4000 
    --capture-image

# Wait for camera to be turned off
# by checking the USB
DEVICE=$(gphoto2 --auto-detect | grep usb | cut -b 36-42 | sed 's/,///')
while [ ${DEVICE} ]
    do
    echo "Turn off the camera"
    sleep 5
    DEVICE=$(gphoto2 --auto-detect | grep usb | cut -b 36-42 | sed 's/,///')
done

# ------------------------------------------------------------
# Cleanup
# ------------------------------------------------------------

rm -f "${LCK_FILE}"

# ------------------------------------------------------------
# Done
# ------------------------------------------------------------

exit 0

Next steps

My solution is useful but I’m not happy about the script and the way the camera is operated. Ideally, I would like a way to trigger the captures either via detecting the depression of the shutter button on the D40 or via a push button connected to the GPIO on the Raspberry Pi. The script could then be a simple loop that waits for a trigger event, captures five images, and then goes back to waiting.

I would also like to encase the Raspberry Pi and cables inside a compact exposure. Currently, a rubber band holds the package together. I’ve ordered the PiBox from Adafruit that should work nicely. Adafruit has a wide assortment of products designed for the Raspberry Pi.


  1. I’ve budgeted for a lens – Nikkor 35mm f/1.8 and Nikkor 50mm f/1.8 – and a studio lighting kit – AlienBees 800 – but not a new body. 
  2. I use Adobe Lightroom plug-ins Photomatix Pro app and HDR Efex Pro app
  3. You can download the script from my public Dropbox folder.