Auto Photo Download

Environment

This work is all using my Canon PowerShot A480 cameras with SDM firmware loaded and my Fedora 14 linux box.

The physical requirement is a USB cable connected to the camera from the computer, but also some sort of switch on the +5V power in that USB connection in order to trigger the cameras to take pictures. On my computer, the power provided by the USB port seems to be enough to make it take a picture, but the switch mechanism is yet to be worked out.

The Process

It does appear to be possible to automatically download the images during the scanning process by taking advantage of the time you are lifting the platen and turning pages. If the SDM firmware switches to USB mode during that time, you can copy off the picture just taken and delete the image from the camera. The advantage of this is obviously that you'll never run out of space on the SD card, and you can start post processing on the images while still scanning the book.

Details

I haven't yet understood scripting of SDM enough to fully automate the process, but I have manually run a script to turn on USB mode and verified that it can trigger the downloading onto my linux box.

In most linux environments, the DBUS daemon is running and plugging USB devices will generate broadcast DBUS messages. When SDM activates the USB mode, it looks exactly like the camera was just plugged in. I take advantage of these messages to trigger running a script which digs up the information about the camera necessary to run gphoto2 and download the images.

For this I use a little program I wrote called xdbusd. If you start xdbusd in the background when you login (or sometime before you want to download images, anyway) it will listen for the dbus message and run a script every time the camera switches to usb mode. Here is the ~/.xdbusd/camera.xdbd file I cobbled up for my testing:

[General]
busName=system
interface=org.freedesktop.Hal.Manager
member=DeviceAdded
string=/org/freedesktop/Hal/devices/usb_device_4a9_31bf_F5DAF8F6E3034929A22D62C4EB7445E6_if0
exec=/zooty/projects/scanner/fetch-right-photos

The string parameter was obtained by running dbus-monitor --system and then plugging the camera in while it was in USB mode. It incorporates the serial number of the camera, so it is unique, and can therefore be used to distinguish between the right and left cameras. I'd need another file just like this for the left camera.

The /zooty/projects/scanner/fetch-right-photos it execs is a perl script I whipped out that runs lshal to determine the usb port parameter needed to identify the camera to gphoto2, then it runs gphoto2 to download all the images then delete all the images. It looks like:

#!/usr/bin/perl -w
#
use strict;

chdir('/zooty/projects/scanner/testing');

open(LOGFILE, ">>", '/zooty/projects/scanner/testing/gphoto2.log');

my $udi='/org/freedesktop/Hal/devices/usb_device_4a9_31bf_F5DAF8F6E3034929A22D62C4EB7445E6_if0';

my $camera_dir='/store_00010001/DCIM/100CANON';

# Prototype perl script to be run automagically by xdbusd when the camera
# switches to usb mode. The idea is to download all the images then delete
# them off the camera so we can keep shooting book pages forever and never
# run out of space on the SD card.

# First we find out what usb port the camera got assigned on this connection.
# We look for lines in the lshal output like:
#  usb.bus_number = 9  (0x9)  (int)
#  usb.linux.device_number = 12  (0xc)  (int)
# and pull out the 9 and 12 (in this example) to pass to gphoto2 to uniquely
# identify the camera we want it to talk to.

my $usb_bus;
my $linux_device;
my $lshal;
open($lshal, '-|', "lshal", "-u", $udi);
while (<$lshal>) {
   if (/^\s*usb\.bus_number\s*=\s*(\d+)\s/) {
      $usb_bus = $1;
      print LOGFILE $_;
   } elsif (/\s*usb\.linux\.device_number\s*=\s*(\d+)\s/) {
      $linux_device = $1;
      print LOGFILE $_;
   }
}
close($lshal);

# Now run gphoto2 once to fetch all the files, then again to delete all the
# files.

my $gphoto2_port = sprintf("usb:%03d,%03d", $usb_bus, $linux_device);

system("gphoto2", "--port", $gphoto2_port,
                  "--folder", $camera_dir,
                  "--no-recurse",
                  "--get-all-files");

system("gphoto2", "--port", $gphoto2_port,
                  "--folder", $camera_dir,
                  "--no-recurse",
                  "--delete-all-files");

If the script is timed correctly, the camera ought to be switching back out of USB mode and listening for the +5V power to be toggled again, and the process starts over.

SDM script

Not yet having a USB cable with a switch on the +5V line, I tried this script which just lets any old camera button click cause a new photo to be taken:

@title Shoot Books
:loop
 press "shoot_half"
 wait_click
 click "shoot_full"
 release "shoot_half"
 sleep 1500
 upload_images_for 10
goto "loop"
end

It actually seems to work OK. Every time I click a button on the camera it takes another picture and goes into USB mode for 10 seconds (it retracts the lens while it does that, which is a little disconcerting - don't know if I can avoid that or not).

While in USB mode, my xdbusd script runs automatically and gets the image file off the camera.

Using the file browser after taking a few pictures shows that it does indeed delete the images on the SD card, so everything seems to be working well. Just need to fiddle with the USB cable to use it as a remote switch.

Or maybe, adopt one of the anti motion detection scripts I see on the forum to just make it take the picture automatically after I stop fiddling with the page turning, then I don't need any kind of external trigger.

Future

Naturally, this all begs to be packaged up into a slick GUI app that does the listening for the DBUS messages itself, uses libgphoto2 to fetch the photos itself, and has a fancy preview screen to show you the most recently fetched images. Don't know how likely I am to do that because the cobbled up prototype seems to work quite well.

For even more fun, I ordered one of these:

A usb controlled relay and digital I/O device. I should be able to use one of the 5V digital outputs to replace the +5V power signal on a USB cable so my computer will be able to trigger the cameras then bring the 5V back online when using the USB cable as an interface to transfer the pictures while the cameras are in USB mode.

I should also be able to use the relays to turn on and off the lights so I can use less electricity and avoid getting them overheated.

Integrating this controller with everything makes the idea of writing a slick GUI for it all even more appealing.

Wed Feb 16, 2011 update: The controller board arrived, and it works perfectly. I found a 9VDC wall wart laying around and used it to provide power for the relays. I also hooked a voltmeter to the digital outputs and tested everything by talking to /dev/ttyACM0 with kermit. I could send commands to turn on the relays and listen to them click and watch the LEDs change. I could send commands to turn on and off the digital outputs and watch the voltmeter reading change, so this looks like the perfect little gimmick for automating the camera and light control from my fedora 14 linux box.

I should be able to substitute the +5V line in a USB cable with one of the digital outputs from the controller board (the ground should already be common since the controller is getting power from a USB port on the same computer). I can then toggle the USB power to the cameras on and off by sending commands to the controller. (I tried fooling with this some, but couldn't get it to work, so just using a relay to toggle the power seems simplest).

I should also be able to use a relay to control the lights, turning them on just before I take a picture.

And I should be able to use one of the digital lines on the controller as an input to check for me pusing a button I can mount on the scanner where it is easy to reach.

Page last modified Wed Feb 23 18:46:36 2011