├── .gitattributes ├── .gitignore ├── README.md └── photobooth.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Raspberry Pi Photobooth Project 2 | ===================== 3 | 4 | Here is my blog post with full instructions, pictures and a circuit diagram: 5 | 6 | http://contractorwolf.com/raspberry-pi-high-def-photobooth/ 7 | 8 | This photobooth needs a folder called "/images" in the same path as the photobooth.py file, my version also has 4 buttons connected from a 315mhx receiver to operate. Since the 315mhz outputs 5v and the RPI can only take 3v I have the lines running through 4 optoisolators and only the 3v from the RPi are run into the optoisolators. 9 | 10 | You must also have PyGame and GPhoto2 installed at the minimum, and my full notes (sorry, they are rough) are here: 11 | 12 | This project aim to replicate the functionality of a photobooth screen with the hardware of a Raspberry Pi and some other custom parts for wireless control and updating the cameras usage based on whether a person is standing in front of the screen or not. 13 | 14 | The basic functionality will be the same as shown in this project: 15 | 16 | http://www.youtube.com/watch?v=jiQrpruA4-E 17 | 18 | Except the netbook running windows XP (to use the Canon SDK) and Arduino (for wireless control and PIR input) used in this video will be replaced with a single Raspberry Pi that will both listen for incoming wireless control and an onboard PIR sensor. One USB is used to send commands to the camera over USB and one for the Wifi. 19 | 20 | The project has several functions that must be controlled by the Raspberry Pi: 21 | connection to an HDMI monitor (or HDTV) to display the slideshow of images stored on the device as well as any photos that have already been taken. 22 | listening to the wireless button controller to operate the cameras “capture image” functionality as well as controlling the slideshow (prev, next, show last, etc). 23 | listening to the PIR sensor to control the cameras open and close lens functions, as well as turning the software from running as a photobooth to a slideshow only. This is to protect the camera and save battery as well as hide the photobooth functionality until there are people to capture in images. 24 | 25 | Cameras that work with gphoto2: 26 | 27 | http://www.gphoto.org/proj/libgphoto2/support.php 28 | 29 | 30 | PROCESS 31 | 32 | 33 | Links: 34 | 35 | Download Raspian 36 | 37 | http://www.raspberrypi.org/downloads 38 | 39 | Use WinDiskImager to flash the SD card with the disk image (.img file): 40 | 41 | http://sourceforge.net/projects/win32diskimager/ 42 | 43 | Update/Upgrade 44 | 45 | sudo apt-get update 46 | 47 | sudo apt-get upgrade 48 | 49 | 50 | Install GPhoto2 51 | 52 | sudo apt-get install gphoto2 53 | 54 | ISSUE: gphoto2 could not lock the device camera is already in use, app starts but shows last photo, doesnt stream 55 | SOLUTION: pkill gvfs 56 | 57 | http://gphoto-software.10949.n7.nabble.com/Nikon-D800-Camera-is-already-in-use-td13616.html 58 | 59 | 60 | Update Gphoto2 to the latest version (currently 2.5.2) 61 | 62 | https://github.com/gonzalo/gphoto2-updater 63 | 64 | 65 | $sudo ./gphoto2-updater.sh 66 | 67 | (for some reason their page doesn’t have the “./” but it errored when i didn’t use that specific path. even running it from inside that same folder, linux is weird 68 | 69 | The updater basically solves all previous issues that I had previously with the RPi and gphoto2. The camera no doesnt close after taking pictures, it remains in its ready state. 70 | 71 | 72 | Install VSFTP 73 | 74 | http://www.instantsupportsite.com/self-help/raspberry-pi/raspberry-install-ftp/ 75 | 76 | Install Adafruit GPIO Library 77 | 78 | http://learn.adafruit.com/adafruits-raspberry-pi-lesson-4-gpio-setup/adafruit-pi-code 79 | 80 | sudo apt-get install git 81 | 82 | git clone http://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code.git 83 | 84 | Install the Python GPIO libraries for accessing the pins in python 85 | 86 | sudo apt-get install python-dev 87 | 88 | sudo apt-get install python-rpi.gpio 89 | 90 | 91 | Uploading to Google Drive (untested) 92 | 93 | https://developers.google.com/drive/web/quickstart/quickstart-python 94 | 95 | IMPORTANT: 96 | 97 | sudo apt-get install python-setuptools 98 | 99 | (installs easy_install, and other tools) 100 | 101 | sudo easy_install --upgrade google-api-python-client 102 | 103 | (needs first step to install east_install to run second step) 104 | 105 | ------------------------------------------------ 106 | ADDITIONAL NOTES AND LINKS TO OTHER USEFUL PAGES BELOW 107 | ------------------------------------------------ 108 | 109 | To do: 110 | 111 | CAPTURE 112 | 113 | 1) take a picture using capture-image-and-download 114 | 115 | (done) 116 | 117 | 2) use capture-movie to capture stream of images 118 | 119 | status: the camera will capture-movie which stores the mjpg stream, but when trying to use that file in pygame it seems to only grab the first file and then reuse that file instead of grabbing it over and over. this may just be a caching issue in pygame and there might be a way around it. 120 | 121 | other ideas: 122 | 123 | 3) display stream from capture-movie 124 | 125 | 126 | CONTROL 127 | 128 | 1) use pygame button to take a picture 129 | 130 | 2) connect 4 button wireless controller to raspberry pi GPIO 131 | 132 | 3) take picture with the wireless controller (done) 133 | 134 | 4) show slideshow with images stored on the RPi (done) 135 | 136 | 5) flip between images using the wireless controller (done) 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | usage of capture-movie converting to a jpeg on the fly, try this 145 | 146 | https://github.com/damyon/picam/blob/master/picam/gphoto2.py 147 | 148 | gphoto2 --capture-movie --stdout | avconv -f mjpeg -timelimit 60 -i pipe:0 -vsync 1 -r 1 /var/lib/picam/preview/preview%02d.jpg 149 | 150 | uses “avconv” 151 | 152 | 153 | Use mplayer to show mjpg streams: 154 | 155 | sudo apt-get install mplayer 156 | 157 | gphoto2 --stdout --capture-movie | someotherprogram 158 | 159 | gphoto2 --capture-movie --stdout>>/dev/fakevideo1 160 | 161 | 162 | RESEARCH LINKS 163 | 164 | virtual video device: 165 | 166 | http://stackoverflow.com/questions/18011304/how-to-make-a-virtual-video-device-for-testing-and-debugging-which-has-dev-vide 167 | 168 | creating a virtual video device: 169 | 170 | http://moozing.wordpress.com/2011/12/26/ip-camera-gstreamer-and-virtual-video-devices/ 171 | 172 | using pygame camera module: 173 | 174 | http://www.pygame.org/docs/tut/camera/CameraIntro.html 175 | 176 | 177 | my post on nabble (gphoto): 178 | 179 | http://gphoto-software.10949.n7.nabble.com/capture-preview-VS-liveview-on-gphoto-2-5-2-td13957.html#a13964 180 | 181 | mjpg streaming on the rpi 182 | 183 | http://blog.miguelgrinberg.com/post/how-to-build-and-run-mjpg-streamer-on-the-raspberry-pi 184 | 185 | 186 | ffserver -f /etc/ffserver.conf | gphoto2 --stdout --capture-movie | ffmpeg -f mjpeg -r 2 -i - http://localhost:81/canon.ffm 187 | 188 | I have found you need to set the permissions right: 189 | 190 | i have made a new directory: /usr/local/www 191 | 192 | sudo chmod 755 /usr/local/www 193 | 194 | sudo chmod 644 /usr/local/www/* 195 | mjpg_streamer -i "input_uvc.so -d /dev/video0" -o "output_http.so -p 8080 -w /usr/local/www" 196 | 197 | 198 | 199 | FULL API C++ APP 200 | 201 | https://github.com/scheckmedia/CameraControllerApi 202 | 203 | -------------------------------------------------------------------------------- /photobooth.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # EXECUTE THIS FILE LIKE THIS: 4 | # > sudo python photobooth.py 5 | 6 | from time import sleep 7 | 8 | import os 9 | import os.path 10 | import subprocess as sub 11 | import datetime 12 | import pygame 13 | import pygbutton 14 | import RPi.GPIO as GPIO 15 | import glob 16 | import sys 17 | 18 | bounceMillis = 800 #waits 800 ms before noticing another button press 19 | fps = 0; #frames per second 20 | photos_taken = 0 21 | index = 0 22 | width = 1280 23 | height = 980 24 | continue_loop = True 25 | 26 | image_name = "" 27 | delay_time = .005 28 | flash_time = .1 29 | last_image_taken = "" 30 | waiting_on_download = False #if this is true, look for last_image_taken 31 | 32 | current_image = 0 33 | in_process = False 34 | image_count = 0; 35 | object_list = [] #list of preloaded images 36 | change_ticks = 0 37 | last_image_number = 0 38 | last_preview = {} 39 | 40 | 41 | #***************FUNCTIONS****************** 42 | 43 | def APressed(channel): 44 | print("A button pressed: " + str(index)) 45 | TakePicture() 46 | 47 | def BPressed(channel): 48 | print("B button pressed: " + str(index)) 49 | 50 | global change_ticks 51 | change_ticks = pygame.time.get_ticks() + 20000 52 | 53 | FlashLEDs(2) 54 | 55 | LastPicture() 56 | 57 | def CPressed(channel): 58 | print("C button pressed: " + str(index)) 59 | 60 | global change_ticks 61 | change_ticks = pygame.time.get_ticks() + 20000 62 | 63 | PrevPicture() 64 | 65 | def DPressed(channel): 66 | print("D button pressed: " + str(index)) 67 | 68 | global change_ticks 69 | change_ticks = pygame.time.get_ticks() + 20000 70 | 71 | NextPicture() 72 | 73 | 74 | def RenderOverlay(): 75 | #drow title and buttons 76 | 77 | #app name 78 | screen.blit(pygame.font.SysFont("freeserif",30,bold=0).render(app_name, 1, white),((width-350),10)) 79 | 80 | #button 81 | take_pic_button.draw(screen) 82 | 83 | #button 84 | quit_button.draw(screen) 85 | 86 | pygame.display.update() 87 | 88 | def LoadImageToObjectList(image_name): 89 | #load image by filename to the list of image objects for fast switching 90 | 91 | global object_list 92 | global image_count 93 | global last_image_number 94 | 95 | print "LoadImageToObjectList: " + image_name 96 | print "before load: " + str(pygame.time.get_ticks()) 97 | load = pygame.image.load(image_name).convert_alpha() 98 | print "loaded: " + str(pygame.time.get_ticks()) 99 | scale = pygame.transform.scale(load,(width,height)) 100 | print "scaled: " + str(pygame.time.get_ticks()) 101 | print "before append" 102 | object_list.append(scale) 103 | 104 | 105 | last_image_number = image_count 106 | 107 | image_count = image_count + 1 108 | 109 | 110 | print "after append" + str(pygame.time.get_ticks()) 111 | print "added to object_list: " + str(len(object_list)) 112 | print "end of LoadImageToObjectList" 113 | 114 | pygame.display.update() 115 | 116 | print "sent to google drive: " + image_name 117 | 118 | 119 | def LoadImageObjectToScreen(image): 120 | #load the image object from the list to the screen 121 | 122 | print "begin LoadImageObjectToScreen" 123 | 124 | print "before load: " + str(pygame.time.get_ticks()) 125 | screen.blit(image,(0,0)) 126 | print "added to screen: " + str(pygame.time.get_ticks()) 127 | pygame.display.update() 128 | 129 | print "end LoadImageObjectToScreen" 130 | 131 | 132 | def FlashLEDs(iterations): 133 | #flashes LED for iterations 134 | index = 0 135 | 136 | while(index (len(object_list)-1): 222 | current_image = 0 223 | 224 | DrawCenterMessage("LOADING NEXT IMAGE: " + str(current_image),550,70,((width/2)-220),((height)-100)) 225 | 226 | LoadImageObjectToScreen(object_list[current_image]) 227 | 228 | screen.blit(last_preview,((width-320),(height - 240))) 229 | 230 | RenderOverlay() 231 | 232 | in_process = False 233 | 234 | print "end NextPicture" 235 | 236 | def LastPicture(): 237 | #draws the last picture in the list to the screen 238 | global current_image 239 | global in_process 240 | 241 | print "LastPicture" 242 | 243 | if not in_process: 244 | in_process = True 245 | 246 | FlashLEDs(1) 247 | 248 | DrawCenterMessage("LOADING LAST TAKEN: " +str(last_image_number),600,70,((width/2)-220),((height)-100)) 249 | 250 | LoadImageObjectToScreen(object_list[last_image_number]) 251 | 252 | RenderOverlay() 253 | 254 | in_process = False 255 | 256 | print "end LastPicture" 257 | 258 | 259 | def GetDateTimeString(): 260 | #format the datetime for the time-stamped filename 261 | dt = str(datetime.datetime.now()).split(".")[0] 262 | clean = dt.replace(" ","_").replace(":","_") 263 | return clean 264 | 265 | def DrawCenterMessage(message,width,height,x,y): 266 | #displays notification messages onto the screen 267 | 268 | backgroundCenterSurface = pygame.Surface((width,height))#size 269 | backgroundCenterSurface.fill(black) 270 | 271 | screen.blit(backgroundCenterSurface,(x,y))#position 272 | screen.blit(pygame.font.SysFont("freeserif",40,bold=1).render(message, 1, white),(x+10,y+10)) 273 | pygame.display.update() 274 | 275 | 276 | def FlashLEDs(max_iterations): 277 | #flash red led (GPIO pin 23) 278 | index = 0 279 | 280 | while(index