gPhoto Python bindings added to improve performance. This allows to show now a preview during countdown.
This commit is contained in:
@@ -114,13 +114,15 @@ A brief description on how to set-up a Raspberry Pi to use this photobooth softw
|
|||||||
```
|
```
|
||||||
and run `photobooth.py`
|
and run `photobooth.py`
|
||||||
|
|
||||||
8. Optionally make the software run automatically on startup. To do that, you must simply add a corresponding line in the autostart file of LXDE, which can be found at `~/.config/lxsession/LXDE-pi/autostart`. Assuming you cloned the Photobooth repository into `/home/pi/photobooth`, add the following line into the autostart-file:
|
8. Optional but highly recommended, as it improves performance significantly: Download the gPhoto2 Python-bindings [Piggyphoto](https://github.com/alexdu/piggyphoto) and put the folder `piggyphoto` into the Photobooth-directory.
|
||||||
|
|
||||||
|
9. Optionally make the software run automatically on startup. To do that, you must simply add a corresponding line in the autostart file of LXDE, which can be found at `~/.config/lxsession/LXDE-pi/autostart`. Assuming you cloned the Photobooth repository into `/home/pi/photobooth`, add the following line into the autostart-file:
|
||||||
```
|
```
|
||||||
lxterminal -e "/home/pi/photobooth/photobooth.sh set-time"
|
lxterminal -e "/home/pi/photobooth/photobooth.sh set-time"
|
||||||
```
|
```
|
||||||
For this to work you must install `gnome-control-center` by running `sudo apt-get install gnome-control-center` (Unfortunately, this brings along a lot of dependencies - however, I haven't found any lightweight alternative that would allow to simply set date and time using the touch screen).
|
For this to work you must install `gnome-control-center` by running `sudo apt-get install gnome-control-center` (Unfortunately, this brings along a lot of dependencies - however, I haven't found any lightweight alternative that would allow to simply set date and time using the touch screen).
|
||||||
|
|
||||||
9. Alternatively, you can also add a Desktop shortcut. Create a file `/home/pi/Desktop/Photobooth.desktop` and enter the following:
|
10. Alternatively, you can also add a Desktop shortcut. Create a file `/home/pi/Desktop/Photobooth.desktop` and enter the following:
|
||||||
```
|
```
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Encoding=UTF-8
|
Encoding=UTF-8
|
||||||
|
|||||||
36
camera.py
36
camera.py
@@ -6,9 +6,17 @@ import subprocess
|
|||||||
try:
|
try:
|
||||||
import cv2 as cv
|
import cv2 as cv
|
||||||
cv_enabled = True
|
cv_enabled = True
|
||||||
|
print('OpenCV available')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
cv_enabled = False
|
cv_enabled = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
import piggyphoto
|
||||||
|
piggyphoto_enabled = True
|
||||||
|
print('Piggyphoto available')
|
||||||
|
except ImportError:
|
||||||
|
piggyphoto_enabled = False
|
||||||
|
|
||||||
class CameraException(Exception):
|
class CameraException(Exception):
|
||||||
"""Custom exception class to handle camera class errors"""
|
"""Custom exception class to handle camera class errors"""
|
||||||
def __init__(self, message, recoverable=False):
|
def __init__(self, message, recoverable=False):
|
||||||
@@ -23,6 +31,12 @@ class Camera_cv:
|
|||||||
self.cap.set(3, picture_size[0])
|
self.cap.set(3, picture_size[0])
|
||||||
self.cap.set(4, picture_size[1])
|
self.cap.set(4, picture_size[1])
|
||||||
|
|
||||||
|
def has_preview(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def take_preview(self, filename="/tmp/preview.jpg"):
|
||||||
|
self.take_picture(filename)
|
||||||
|
|
||||||
def take_picture(self, filename="/tmp/picture.jpg"):
|
def take_picture(self, filename="/tmp/picture.jpg"):
|
||||||
if cv_enabled:
|
if cv_enabled:
|
||||||
r, frame = self.cap.read()
|
r, frame = self.cap.read()
|
||||||
@@ -39,9 +53,15 @@ class Camera_gPhoto:
|
|||||||
self.picture_size = picture_size
|
self.picture_size = picture_size
|
||||||
# Print the capabilities of the connected camera
|
# Print the capabilities of the connected camera
|
||||||
try:
|
try:
|
||||||
|
if piggyphoto_enabled:
|
||||||
|
self.cap = piggyphoto.camera()
|
||||||
|
print(self.cap.abilities)
|
||||||
|
else:
|
||||||
print(self.call_gphoto("-a", "/dev/null"))
|
print(self.call_gphoto("-a", "/dev/null"))
|
||||||
except:
|
except CameraException as e:
|
||||||
print("Warning: Can't list camera capabilities. Do you have gPhoto2 installed?")
|
print('Warning: Listing camera capabilities failed (' + e.message + ')')
|
||||||
|
except piggyphoto.libgphoto2error as e:
|
||||||
|
print('Warning: Listing camera capabilities failed (' + e.message + ')')
|
||||||
|
|
||||||
def call_gphoto(self, action, filename):
|
def call_gphoto(self, action, filename):
|
||||||
# Try to run the command
|
# Try to run the command
|
||||||
@@ -61,6 +81,18 @@ class Camera_gPhoto:
|
|||||||
raise CameraException("Unknown error!\n" + '\n'.join(e.output.split('\n')[1:3]), False)
|
raise CameraException("Unknown error!\n" + '\n'.join(e.output.split('\n')[1:3]), False)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
def has_preview(self):
|
||||||
|
return piggyphoto_enabled
|
||||||
|
|
||||||
|
def take_preview(self, filename="/tmp/preview.jpg"):
|
||||||
|
if piggyphoto_enabled:
|
||||||
|
self.cap.capture_preview(filename)
|
||||||
|
else:
|
||||||
|
raise CameraException("No preview supported!")
|
||||||
|
|
||||||
def take_picture(self, filename="/tmp/picture.jpg"):
|
def take_picture(self, filename="/tmp/picture.jpg"):
|
||||||
|
if piggyphoto_enabled:
|
||||||
|
self.cap.capture_image(filename)
|
||||||
|
else:
|
||||||
self.call_gphoto("--capture-image-and-download", filename)
|
self.call_gphoto("--capture-image-and-download", filename)
|
||||||
return filename
|
return filename
|
||||||
|
|||||||
4
gui.py
4
gui.py
@@ -53,7 +53,7 @@ class GUI_PyGame:
|
|||||||
def trigger_event(self, event_channel):
|
def trigger_event(self, event_channel):
|
||||||
EventModule.post(EventModule.Event(pygame.USEREVENT, channel=event_channel))
|
EventModule.post(EventModule.Event(pygame.USEREVENT, channel=event_channel))
|
||||||
|
|
||||||
def show_picture(self, filename, size=(0,0), offset=(0,0)):
|
def show_picture(self, filename, size=(0,0), offset=(0,0), flip=False):
|
||||||
# Use window size if none given
|
# Use window size if none given
|
||||||
if size == (0,0):
|
if size == (0,0):
|
||||||
size = self.size
|
size = self.size
|
||||||
@@ -74,6 +74,8 @@ class GUI_PyGame:
|
|||||||
# Create surface and blit the image to it
|
# Create surface and blit the image to it
|
||||||
surface = pygame.Surface(new_size)
|
surface = pygame.Surface(new_size)
|
||||||
surface.blit(image, (0,0))
|
surface.blit(image, (0,0))
|
||||||
|
if flip:
|
||||||
|
surface = pygame.transform.flip(surface, True, False)
|
||||||
self.surface_list.append((surface, offset))
|
self.surface_list.append((surface, offset))
|
||||||
|
|
||||||
def show_message(self, msg, color=(245,245,245), bg=(0,0,0), transparency=True):
|
def show_message(self, msg, color=(245,245,245), bg=(0,0,0), transparency=True):
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from time import sleep, clock
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from gui import GUI_PyGame as GuiModule
|
from gui import GUI_PyGame as GuiModule
|
||||||
|
#from camera import CameraException, Camera_cv as CameraModule
|
||||||
from camera import CameraException, Camera_gPhoto as CameraModule
|
from camera import CameraException, Camera_gPhoto as CameraModule
|
||||||
from events import Rpi_GPIO as GPIO
|
from events import Rpi_GPIO as GPIO
|
||||||
|
|
||||||
@@ -152,7 +153,8 @@ class Photobooth:
|
|||||||
# Do not catch KeyboardInterrupt and SystemExit
|
# Do not catch KeyboardInterrupt and SystemExit
|
||||||
except (KeyboardInterrupt, SystemExit):
|
except (KeyboardInterrupt, SystemExit):
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception as e:
|
||||||
|
print('SERIOUS ERROR: ' + repr(e))
|
||||||
self.handle_exception("SERIOUS ERROR!")
|
self.handle_exception("SERIOUS ERROR!")
|
||||||
|
|
||||||
def handle_gpio(self, channel):
|
def handle_gpio(self, channel):
|
||||||
@@ -277,6 +279,23 @@ class Photobooth:
|
|||||||
output_image.save(output_filename, "JPEG")
|
output_image.save(output_filename, "JPEG")
|
||||||
return output_filename
|
return output_filename
|
||||||
|
|
||||||
|
def show_counter(self, seconds):
|
||||||
|
if self.camera.has_preview():
|
||||||
|
tic, toc = clock(), 0
|
||||||
|
while toc < self.pose_time:
|
||||||
|
self.display.clear()
|
||||||
|
self.camera.take_preview("/tmp/photobooth_preview.jpg")
|
||||||
|
self.display.show_picture("/tmp/photobooth_preview.jpg", flip=True)
|
||||||
|
self.display.show_message(str(seconds - int(clock() - tic)))
|
||||||
|
self.display.apply()
|
||||||
|
toc = clock() - tic
|
||||||
|
else:
|
||||||
|
for i in range(seconds):
|
||||||
|
self.display.clear()
|
||||||
|
self.display.show_message(str(seconds - i))
|
||||||
|
self.display.apply()
|
||||||
|
sleep(1)
|
||||||
|
|
||||||
def take_picture(self):
|
def take_picture(self):
|
||||||
"""Implements the picture taking routine"""
|
"""Implements the picture taking routine"""
|
||||||
# Disable lamp
|
# Disable lamp
|
||||||
@@ -286,22 +305,18 @@ class Photobooth:
|
|||||||
self.display.clear()
|
self.display.clear()
|
||||||
self.display.show_message("POSE!\n\nTaking four pictures...");
|
self.display.show_message("POSE!\n\nTaking four pictures...");
|
||||||
self.display.apply()
|
self.display.apply()
|
||||||
sleep(self.pose_time - 3)
|
sleep(2)
|
||||||
|
|
||||||
# Extract display and image sizes
|
# Extract display and image sizes
|
||||||
size = self.display.get_size()
|
size = self.display.get_size()
|
||||||
outsize = (int(size[0]/2), int(size[1]/2))
|
outsize = (int(size[0]/2), int(size[1]/2))
|
||||||
|
|
||||||
# Countdown
|
|
||||||
for i in range(3):
|
|
||||||
self.display.clear()
|
|
||||||
self.display.show_message(str(3 - i))
|
|
||||||
self.display.apply()
|
|
||||||
sleep(1)
|
|
||||||
|
|
||||||
# Take pictures
|
# Take pictures
|
||||||
filenames = [i for i in range(4)]
|
filenames = [i for i in range(4)]
|
||||||
for x in range(4):
|
for x in range(4):
|
||||||
|
# Countdown
|
||||||
|
self.show_counter(self.pose_time)
|
||||||
|
|
||||||
# Try each picture up to 3 times
|
# Try each picture up to 3 times
|
||||||
remaining_attempts = 3
|
remaining_attempts = 3
|
||||||
while remaining_attempts > 0:
|
while remaining_attempts > 0:
|
||||||
@@ -329,7 +344,7 @@ class Photobooth:
|
|||||||
else:
|
else:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
# Sleep for a little bit if necessary
|
# Measure used time and sleep a second if too fast
|
||||||
toc = clock() - tic
|
toc = clock() - tic
|
||||||
if toc < 1.0:
|
if toc < 1.0:
|
||||||
sleep(1.0 - toc)
|
sleep(1.0 - toc)
|
||||||
|
|||||||
Reference in New Issue
Block a user