Sun, 13 Mar 2011
Fedora Photobooth @ SXSW

This is the first year that Fedora will have a booth at SXSW! Sadly, I am not going to be attending since it conflicts with PyCon. However, my code will be running at our booth. Usually the Fedora booth at conferences is comprised of a bunch of flyers, media, swag, and some people to help answer questions and tell the Fedora story. However at SXSW, things are going to be a little different.
Aside from the amazing flyers that Máirín created, there will also be a Fedora Photobooth. Someone (probably Spot or Jared) will be dressed in a full Tux costume, and people can come and get their photo taken with them. Spot came to me the other day and asked if I could write some code to streamline the whole process.
An hour or so later, photobooth.py was born. There are definitely lots of improvements that can be made, but here is what it currently does in its initial incarnation:
- Uses gphoto2 to automatically detect your camera
- When Enter is pressed, it snaps a photo and downloads it locally
- A Fedora watermark is applied to the bottom right corner of the image
- The photo is uploaded to a server
- A QRCode is generated that points to the image URL
- A TinyURL is generated for the image
- HTML is generated that shows the image, the QRCode, and TinyURL
- The page is then displayed in the web browser
See Mo's blog for photos of this code in action at the Fedora SXSW booth!
* SXSW Expo Day 1 from the show floor
* SXSW Expo Day 2
* A Beefy, Miraculous Day at SXSW (Expo Day 3)

The Code
I threw this in a git repo and tossed it up on GitHub:
github.com/lmacken/photobooth.py
#!/usr/bin/python # photobooth.py - version 0.3 # Requires: python-imaging, qrencode, gphoto2, surl # Author: Luke Macken <lmacken@redhat.com> # License: GPLv3 import os import surl import Image import subprocess from uuid import uuid4 from os.path import join, basename, expanduser # Where to spit out our qrcode, watermarked image, and local html out = expanduser('~/Desktop/sxsw') # The watermark to apply to all images watermark_img = expanduser('~/Desktop/fedora.png') # This assumes ssh-agent is running so we can do password-less scp ssh_image_repo = 'fedorapeople.org:~/public_html/sxsw/' # The public HTTP repository for uploaded images http_image_repo = 'http://lmacken.fedorapeople.org/sxsw/' # Size of the qrcode pixels qrcode_size = 10 # Whether or not to delete the photo after uploading it to the remote server delete_after_upload = True # The camera configuration # Use gphoto2 --list-config and --get-config for more information gphoto_config = { '/main/imgsettings/imagesize': 3, # small '/main/imgsettings/imagequality': 0, # normal '/main/capturesettings/zoom': 70, # zoom factor } # The URL shortener to use shortener = 'tinyurl.com' class PhotoBooth(object): def initialize(self): """ Detect the camera and set the various settings """ cfg = ['--set-config=%s=%s' % (k, v) for k, v in gphoto_config.items()] subprocess.call('gphoto2 --auto-detect ' + ' '.join(cfg), shell=True) def capture_photo(self): """ Capture a photo and download it from the camera """ filename = join(out, '%s.jpg' % str(uuid4())) cfg = ['--set-config=%s=%s' % (k, v) for k, v in gphoto_config.items()] subprocess.call('gphoto2 ' + '--capture-image-and-download ' + '--filename="%s" ' % filename, shell=True) return filename def process_image(self, filename): print "Processing %s..." % filename print "Applying watermark..." image = self.watermark(filename) print "Uploading to remote server..." url = self.upload(image) print "Generating QRCode..." qrcode = self.qrencode(url) print "Shortening URL..." tiny = self.shorten(url) print "Generating HTML..." html = self.html_output(url, qrcode, tiny) subprocess.call('firefox "%s"' % html, shell=True) print "Done!" def watermark(self, image): """ Apply a watermark to an image """ mark = Image.open(watermark_img) im = Image.open(image) if im.mode != 'RGBA': im = im.convert('RGBA') layer = Image.new('RGBA', im.size, (0,0,0,0)) position = (im.size[0] - mark.size[0], im.size[1] - mark.size[1]) layer.paste(mark, position) outfile = join(out, basename(image)) Image.composite(layer, im, layer).save(outfile) return outfile def upload(self, image): """ Upload this image to a remote server """ subprocess.call('scp "%s" %s' % (image, ssh_image_repo), shell=True) if delete_after_upload: os.unlink(image) return http_image_repo + basename(image) def qrencode(self, url): """ Generate a QRCode for a given URL """ qrcode = join(out, 'qrcode.png') subprocess.call('qrencode -s %d -o "%s" %s' % ( qrcode_size, qrcode, url), shell=True) return qrcode def shorten(self, url): """ Generate a shortened URL """ return surl.services.supportedServices()[shortener].get({}, url) def html_output(self, image, qrcode, tinyurl): """ Output HTML with the image, qrcode, and tinyurl """ html = """ <html> <center> <table> <tr> <td colspan="2"> <b><a href="%(tinyurl)s">%(tinyurl)s</a></b> </td> </tr> <tr> <td><img src="%(image)s" border="0"/></td> <td><img src="%(qrcode)s" border="0"/></td> </tr> </table> </center> </html> """ % {'image': image, 'qrcode': qrcode, 'tinyurl': tinyurl} outfile = join(out, basename(image) + '.html') output = file(outfile, 'w') output.write(html) output.close() return outfile if __name__ == "__main__": photobooth = PhotoBooth() try: photobooth.initialize() while True: raw_input("Press enter to capture photo.") filename = photobooth.capture_photo() photobooth.process_image(filename) except KeyboardInterrupt: print "\nExiting..."
posted at: 02:57 | link | | 20 comments
Posted by emichan at Sun Mar 13 16:04:26 2011
Luke you rock! Thank you so much for making this for us. Spot will be wearing the costume primarily, and it looks great on him from the pictures I've seen so far. Giant penguin ftw! :)
Posted by Ryan Rix at Mon Mar 14 05:56:03 2011
You should pass this along to http://www.fauxbooth.com/ . They're a localish outfit that has a photobooth similar to this, I'd imagine, and they run Fedora on their setup. Probably very similar to yours, in fact.
Posted by tom Callaway at Wed Mar 16 12:33:05 2011
Luke, your code has been working fantastic for us! Thanks again.
Posted by Sylvain at Thu Mar 17 22:44:12 2011
Hi,
Sadly it doesn't work for me :/
$ python photobooth.py
Modèle Port
----------------------------------------------------------
Nikon DSC D5000 (PTP mode) usb:002,011
*** Erreur ***
L'élément graphique /main/capturesettings/zoom n'est pas configurable.
*** Erreur ***
La propriétée 'Taille de l'image' / 0x5003 n'etait pas réglée, PTP eurror code 0x02ff.
*** Erreur ***
Échec lors du positionnement de la nouvelle valeur de configuration 3 pour l'entrée de configuration /main/imgsettings/imagesize.
Any idea to help me ?
(I use Fedora 14 and install
correctly requires: python-imaging, qrencode, gphoto2, surl)
Thanks,
Sylvain
Posted by Luke Macken at Fri Mar 18 04:25:00 2011
Sylvain,
It looks like your camera doesn't support the default `gphoto_config` options that I set in there by default. Try commenting the imagequality/imagesize/zoom out.
Posted by Sylvain at Fri Mar 18 07:16:45 2011
OK
If i disable all the gphoto config line, my camera take a photo.
gphoto_config = {
#'/main/imgsettings/imagesize': 3, # small
#'/main/imgsettings/imagequality': 0, # normal
#'/main/capturesettings/zoom': 70, # zoom factor
}
But of course the file is quite big..
I also saw the watermark, so it's better than yesterday !
I also don't kwon how to replace the line for the upload to my personnal FTP. Any idea too ?
Thanks for your help.
Sylvain
Posted by Luke Macken at Fri Mar 18 17:41:06 2011
@Sylvain: You can run `gphoto2 --list-config --auto-config` to see all of the configuration options for your specific camera; it may have a different type of sizing/resolution option.
As for FTP, this script currently only supports SCP'ing it to a remove SSH/SFTP server. Adding support for FTP shouldn't be /too/ difficult though, if someone wants to write a patch :)
Posted by Sylvain at Sat Mar 19 22:04:03 2011
Thanks.
For SFTP it's OK, I just try and find the correct syntax to reach my provider. It work's fine :)
But I'm sorry, I don't see how can 'gphoto2 --list-config --auto-config' help me :
$ gphoto2 --list-config --auto-config
Utilisation: gphoto2 [-?valLnPTDR] [-?|--help] [--usage] [--debug]
[--debug-logfile=FILENAME] [--quiet] [--hook-script=FILENAME]
[--stdout] [--stdout-size] [--auto-detect] [--show-exif=CHAINE]
[--show-info=CHAINE] [--summary] [--manual] [--about]
[--storage-info] [--shell] [-v|--version] [--list-cameras]
[--list-ports] [-a|--abilities] [--port=FILENAME] [--speed=SPEED]
[--camera=MODEL] [--usbid=USBIDs] [--list-config]
[--get-config=CHAINE] [--set-config=CHAINE]
[--set-config-index=CHAINE] [--set-config-value=CHAINE]
[--wait-event=COUNT] [--wait-event-and-download=COUNT]
[--capture-preview] [-B|--bulb SECONDS] [-F|--frames COUNT]
[-I|--interval SECONDS] [--reset-interval] [--capture-image]
[--capture-image-and-download] [--capture-movie] [--capture-sound]
[--capture-tethered=COUNT] [-l|--list-folders] [-L|--list-files]
[-m|--mkdir DIRNAME] [-r|--rmdir DIRNAME] [-n|--num-files]
[-p|--get-file RANGE] [-P|--get-all-files] [-t|--get-thumbnail RANGE]
[-T|--get-all-thumbnails] [--get-metadata=RANGE] [--get-all-metadata]
[--upload-metadata=CHAINE] [--get-raw-data=RANGE]
[--get-all-raw-data] [--get-audio-data=RANGE] [--get-all-audio-data]
[-d|--delete-file RANGE] [-D|--delete-all-files]
[-u|--upload-file FILENAME] [--filename=FILENAME_PATTERN]
[-f|--folder FOLDER] [-R|--recurse] [--no-recurse] [--new]
[--force-overwrite]
But thanks for your previous help !
Posted by luke at Sun Mar 20 03:23:20 2011
@Sylvain, maybe try doing --auto-detect first, and then --list-config ?
Posted by Sylvain at Sun Mar 20 09:09:49 2011
Well, it's near perfect :)
I understand a little how to run gphoto. I'll read gPhoto Doc too (http://www.gphoto.org/doc/remote/)
For exemple, to know what parameter is supported by my camero for imagesize, this is this command line :
$ gphoto2 --get-config imagesize
Current: 4288x2848
Choice: 0 4288x2848
Choice: 1 3216x2136
Choice: 2 2144x1424
So, visibly, the lower resolution I can have is with choice 2, a litter big perhaps, but anayway I can run better photobooth :)
Now I'll try to config the HTML generate file.
Thanks for all.
Posted by Sylvain at Sun Mar 20 15:09:16 2011
Do you know if it is possible to have the code of Fedora Photobooth ? It look like very nice !
Posted by Luke at Fri Mar 25 20:17:26 2011
@Sylvain, I don't have that template, but I'll see if I can track it down.
Posted by IncernMaync at Fri May 27 15:29:38 2011
stД›hovГЎnГ pЕ™erov kontaktnГ ДЌoДЌky pro dД›ti parfГ©my keton http://praguevo1036-bioten.co.tv - letenky granada kontaktnГ ДЌoДЌky historie
prodluЕѕovГЎnГ Е™as praha 1 parfГ©my yves rocher kontaktnГ ДЌoДЌky druhy letenky uganda , parfГ©my fendi parfГ©my clinique letenky do londГЅna parfГ©my joop .
parfГ©my na lГ©to letenky winnipeg stД›hovГЎnГ fofr letenky chorvatsko parfГ©my ostrava parfГ©my eu parfГ©my akce
stД›hovГЎnГ klavГrЕЇ brno letenky washington prodluЕѕovГЎnГ Е™as pГsek stД›hovГЎnГ nonstop parfГ©my top 10 .
parfГ©my lr , kontaktnГ ДЌoДЌky oasys parfГ©my ysl .
http://letenkyvu866-bose.co.tv - parfГ©my hugo boss stД›hovГЎnГ nonstop
http://liposukcehy1418-ofnut.co.tv - parfГ©my oriflame letenky varЕЎava
kontaktnГ ДЌoДЌky upГr kontaktnГ ДЌoДЌky praha stД›hovГЎnГ trezorЕЇ letenky pelikan parfГ©my boss .
parfГ©my versace , stД›hovГЎnГ brno cena kontaktnГ ДЌoДЌky frequency xc .
Posted by marcoelgordo at Tue Mar 27 20:42:07 2012
Hi,
I installed libsurl.i686 but I get the following message File "./photobooth.py", line 8, in <module>
import surl
ImportError: No module named surl. Am I missing any dependencies?
Posted by Luke at Mon Apr 9 14:37:45 2012
marcoelgordo,
You need to install `surl`. https://launchpad.net/surl
Posted by Marcoelgordo at Wed May 9 12:13:46 2012
Lewk,
I have sent you an email with some suggestion to the code to:
1. integrate Arduino push button to start the sequence
2. have a preview before taking the shot using piggyphoto
3. refresh the same firefox tab (using remote add-on) to avoid having multiple tabs open.
Have you received the email or do I need to resend it?
Marc
Posted by Marcoelgordo at Wed May 9 12:31:30 2012
Lewk,
I have sent you an email with some suggestion to the code to:
1. integrate Arduino push button to start the sequence
2. have a preview before taking the shot using piggyphoto
3. refresh the same firefox tab (using remote add-on) to avoid having multiple tabs open.
Have you received the email or do I need to resend it?
Marc
Posted by Marcoelgordo at Wed May 9 12:49:41 2012
Lewk,
I have sent you an email with some suggestion to the code to:
1. integrate Arduino push button to start the sequence
2. have a preview before taking the shot using piggyphoto
3. refresh the same firefox tab (using remote add-on) to avoid having multiple tabs open.
Have you received the email or do I need to resend it?
Marc








