Magic Lantern Firmware Wiki
Advertisement

Good news for Python lovers! You can control your DSLR camera with Python scripts via the USB cable, PTP protocol.

I've written a Python binding for gphoto2, called piggyphoto, available here:

http://github.com/alexdu/piggyphoto

The binding is based on ctypes, which means (only in theory) that you don't have to compile anything in order to use it. In practice, the stable version is almost useless, so you have to compile the latest SVN snapshot of gphoto2.

This is a test bed for various enhancements which can be implemented in Magic Lantern.

Related page: PTP

Installing the SVN version of gphoto2[]

On Ubuntu Karmic, I had to follow these steps:

svn co https://gphoto.svn.sourceforge.net/svnroot/gphoto/trunk gphoto

The README files from (lib)gphoto say that the default prefix for installation is /usr/local. Doing so resulted in conflicts with the stable version from Ubuntu package manager. So I've changed the prefix to /usr:

cd gphoto/trunk
cd libgphoto2
autoreconf --install --symlink
./configure --with-prefix=/usr --with-camlibs=canon
make
sudo make install

cd ../gphoto2
autoreconf -is
./configure --with-libgphoto2=/usr
make
sudo make install
gphoto2 -v
... 
gphoto2         2.4.99.2       gcc, popt(m), exif, no cdk, aa, jpeg, readline
libgphoto2      2.4.99.7       INCOMPLETE CAMLIB SET (canon ptp2), gcc, ltdl, EXIF
libgphoto2_port 0.10.0         gcc, ltdl, USB, serial without locking

On Osx Lion, Homebrew can install it:

brew install gphoto2


Quick Start[]

After you have installed the latest libgphoto2, get piggyphoto from github:

git clone git://github.com/alexdu/piggyphoto.git
cd piggyphoto

Now run the test scripts from the piggyphoto main directory. Let's try a few of them:

Autodetect the camera[]

python autodetect.py

libgphoto2 version:
2.4.99.7
INCOMPLETE CAMLIB SET (canon ptp2)
gcc (C compiler used)
ltdl (for portable loading of camlibs)v
EXIF (for special handling of EXIF files)

detected cameras:
[('Canon EOS 550D', 'usb:002,039')]

Take a picture[]

python snap.py

Model : Canon EOS 550D
Status : 0
Port : 4
Operations : 25
File Operations : 10
Folder Operations : 14
USB (vendor/product) : 0x4a9/0x31ea
USB class : 0x0/0x0/0x0
Library : /usr/lib/libgphoto2/2.4.99.7/ptp2
Id : PTP

Saving preview.jpg
Saving snap.jpg

Live Preview[]

Piggyphoto-preview

Live preview from 550D with piggyphoto

For this one you need Pygame:

sudo easy_install pygame
python preview.py

Now, your DSLR is behaving like a webcam :)

Look at camera settings[]

Changing settings does not seem to work well, so for now we'll just reading the settings:

python list-config.py 

Model : Canon EOS 550D
Status : 0
Port : 4
Operations : 25
File Operations : 10
Folder Operations : 14
USB (vendor/product) : 0x4a9/0x31ea
USB class : 0x0/0x0/0x0
Library : /usr/lib/libgphoto2/2.4.99.7/ptp2
Id : PTP

main.actions.focuslock = 2
main.actions.bulb = 2
main.actions.uilock = 2
main.actions.syncdatetime = None
main.actions.autofocusdrive = None
main.actions.manualfocusdrive = None
main.actions.eoszoom = 0
main.actions.eoszoomposition = 0,0
main.settings.datetime = 1286126968
main.settings.reviewtime = Hold
main.settings.output = Unknown value 0000
main.settings.evfmode = 1
main.settings.ownername = 
main.settings.artist = 
main.settings.copyright = 
main.settings.capturetarget = Internal RAM
main.settings.capture = 2
main.status.model = 21########
main.status.ptpversion = 256
main.status.batterylevel = 100%
main.status.lensname = EF50mm f/1.8 II
main.status.serialnumber = 83#######
main.status.shuttercounter = 12648    # after less than a month! wtf?!
main.status.availableshots = 42450
main.imgsettings.imageformat = Large Fine JPEG
main.imgsettings.imageformatsd = Large Fine JPEG
main.imgsettings.iso = 1600
main.imgsettings.whitebalance = Cloudy
main.imgsettings.whitebalanceadjusta = 0
main.imgsettings.whitebalanceadjustb = 0
main.imgsettings.whitebalancexa = 0
main.imgsettings.whitebalancexb = 0
main.capturesettings.exposurecompensation = 1.0
main.capturesettings.autoexposuremode = AV
main.capturesettings.drivemode = Continuous
main.capturesettings.picturestyle = User defined 3
main.capturesettings.aperture = 1.8
main.capturesettings.meteringmode = Evaluative
main.capturesettings.bracketmode = 0
main.capturesettings.aeb = off
main.other.d402 = Canon EOS 550D
main.other.d407 = 1
main.other.d406 = Unknown Initiator
main.other.d303 = 1
main.other.5001 = 100

Trap Focus[]

Trap focus functionality was requested by Magic Lantern users:

http://bitbucket.org/hudson/magic-lantern/issue/160/trap-focus-functionality

"Trap Focus" is great functionality found in Nikons [and Pentax] is missing in entire Canon lineup. When enabled it delays firing shutter until central point becomes in focus. From the user perspective when MF (together with "Trap Focus") is enabled user presses (and holds) shutter button. Nothing happens until central point becomes in focus. When it happens - camera takes picture. It's great in any situation with manual lens, but especially suited for sports and macro photography.

Here is an experiment for implementing trap focus in tethered shooting mode, with piggyphoto. It's not really useful in the current state, but if you like it, it may be implemented into Magic Lantern. It's really useful for macro shots and old manual lenses.

How to try it[]

Connect the camera via USB and run the script focus-snap.py:

python focus-snap.py

It will display a live preview. If you want to change settings, close the window, press the Live View button on the camera (while it's still connected to USB) and you can change shooting settings. Put a short exposure time, like 1/250, 1/500, to avoid motion blur. Then run the script again.


Look at the title bar of the preview window. It should display "Looking for focus peak". Now point the camera at some object and rotate the focus ring. The object will be focused; turn the ring a bit farther, and the object will become blurred. The title bar will say "Focus peak found". Now turn the focus ring back sloooooooowly and camera will take a picture when the object will come back into focus.

How it works[]

My implementation estimates the amount of focus on the live preview image, and takes a picture when it detects a peak (well, almost).

Estimating focus for an image[]

The 550D outputs live preview frames which are 1056 x 704 pixels. The focus is computed from the central area of the image, having 100x100 pixels.

The function for evaluating the focus is implemented in focus.py:

def estimate(file, s=5):
    """Estimates the amount of focus of an image file.
    Returns a real number: higher values indicate better focus.
    Bug: a high-contrast, blurry image can be considered with better focus
    than a low-contrast, perfectly focused image.
    """
    im = Image.open(file).convert("L")
    w,h = im.size
    box = (w/2 - 50, h/2 - 50, w/2 + 50, h/2 + 50)
    im = im.crop(box)
    imf = im.filter(ImageFilter.MedianFilter(s))
    d = ImageChops.subtract(im, imf, 1, 100)
    return ImageStat.Stat(d).stddev[0]

The main idea is that if you blur a focused image, you will lose lots of details. But if you blur a blurred image, it won't change much. So, a measure for how much the image is in focus will be the difference between the original and slightly blurred image.

Problems:

  • The focus measure is not absolute. Ideally, it should return a number between 0 and 1, with 0 = extremely blurry and 1 = perfectly focused. But not all lenses are able to give perfect focus, and the sampling rate is finite, so the algorithm may miss the sweet spot.
  • The focus measure is dependent on the scene analyzed. A high-contrast, blurry image will be evaluated as being better than a low-contrast, perfect-focus image. This function just returns a number which, for the same scene, is greater when the image is focused.

Another solution, which seems better, is to use morphological opening or closing. I'll experiment with it.


Improvements to the focus estimation function (estimate in focus.py) are welcome.

Detecting focus peak[]

As a workaround for the focus measure not being absolute, I've implemented a slightly less easy-to-use procedure: you have to bring the object into focus, then defocus it, and then bring the object into focus again. This allows the algorithm to detect the peak value for the current scene, and snaps when the focus value is greater than 98% of the peak. If you turn the focus ring slowly enough, this seems to work (well... almost).

Limitations and known bugs[]

  • If you turn the ring too fast, the image will be out of focus (because the picture is took too late). This is mainly due the communication delay between camera and PC, and slow implementation of focus evaluation function. It shouldn't be a problem if the method is implemented on the camera.
  • If you change the scene, the camera may snap randomly, even if nothing is in focus.
  • You have to bring the object into focus and continue turning the ring until the image blurs, and then turn the ring back.

TODO[]

  • document the API
  • wrap missing functions from gphoto2 (only a few are implemented right now)
  • add support for Magic Lantern's PTP functions (by wrapping libptp with mweerden's patches?)
  • allow uploading files to camera's card
  • send ARM code to be executed, poke/peek memory locations, read/set properties...
  • review memory leak when running Live Preview [?! what memory leak? ] Canon 7D, live preview for a few minutes and my system (ubuntu 12.04 & python 2.7) ran out of memory :S please help!!
    • This is due to the camera file object (cfile) never being removed from memory. The object has a function to fix this error, it just needs to be called after the file is saved.
  • <put your wishlist here>

libptp/win32 support[]

I've been able to compile libptp/ptpcam (the one with CHDK support, from [1]) as a DLL under windows, and call some functions from Python via ctypes. Experimental code is here:

https://github.com/alexdu/piggyPTP

However, I don't know how to compile ptpcam as a shared library (.so) under Linux. Does anyone know?

Advertisement