A bit of cleanup. Failed pictures can now produce recoverable errors (e.g., failed focus) and retry up to 3 times
This commit is contained in:
21
camera.py
21
camera.py
@@ -11,7 +11,9 @@ except ImportError:
|
||||
|
||||
class CameraException(Exception):
|
||||
"""Custom exception class to handle camera class errors"""
|
||||
pass
|
||||
def __init__(self, message, recoverable=False):
|
||||
self.message = message
|
||||
self.recoverable = recoverable
|
||||
|
||||
|
||||
class Camera_cv:
|
||||
@@ -35,23 +37,28 @@ class Camera_gPhoto:
|
||||
|
||||
def __init__(self, picture_size):
|
||||
self.picture_size = picture_size
|
||||
# Print the abilities of the connected camera
|
||||
# print(self.call_gphoto("-a", "/dev/null"))
|
||||
# Print the capabilities of the connected camera
|
||||
try:
|
||||
print(self.call_gphoto("-a", "/dev/null"))
|
||||
except:
|
||||
print("Warning: Can't list camera capabilities. Do you have gPhoto2 installed?")
|
||||
|
||||
def call_gphoto(self, action, filename):
|
||||
# Try to run the command
|
||||
try:
|
||||
cmd = "gphoto2 --force-overwrite --quiet " + action + " --filename " + filename
|
||||
cmd = [ "gphoto2", "--force-overwrite", "--quiet", action, "--filename " + filename ]
|
||||
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
|
||||
if "ERROR" in output:
|
||||
raise subprocess.CalledProcessError(returncode=0, cmd=cmd, output=output)
|
||||
except subprocess.CalledProcessError as e:
|
||||
if "Canon EOS Capture failed: 2019" in e.output:
|
||||
raise CameraException("Can't focus! Move and try again!")
|
||||
raise CameraException("Can't focus! Move a little bit!", True)
|
||||
elif "No camera found" in e.output:
|
||||
raise CameraException("No (supported) camera detected!")
|
||||
raise CameraException("No (supported) camera detected!", False)
|
||||
elif "command not found" in e.output:
|
||||
raise CameraException("gPhoto2 not found!", False)
|
||||
else:
|
||||
raise CameraException("Unknown error!\n" + '\n'.join(e.output.split('\n')[1:3]))
|
||||
raise CameraException("Unknown error!\n" + '\n'.join(e.output.split('\n')[1:3]), False)
|
||||
return output
|
||||
|
||||
def take_picture(self, filename="/tmp/picture.jpg"):
|
||||
|
||||
@@ -49,19 +49,31 @@ display_time = 10
|
||||
###############
|
||||
|
||||
class PictureList:
|
||||
"""Class to manage images and count them"""
|
||||
"""A simple helper class.
|
||||
|
||||
It provides the filenames for the assembled pictures and keeps count
|
||||
of taken and previously existing pictures.
|
||||
"""
|
||||
|
||||
def __init__(self, basename):
|
||||
"""Initialize filenames to the given basename and search for
|
||||
existing files. Set the counter accordingly.
|
||||
"""
|
||||
|
||||
# Set basename and suffix
|
||||
self.basename = basename
|
||||
self.suffix = ".jpg"
|
||||
self.count_width = 5
|
||||
|
||||
# Ensure directory exists
|
||||
dirname = os.path.dirname(self.basename)
|
||||
if not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
|
||||
# Find existing files
|
||||
count_pattern = "[0-9]" * self.count_width
|
||||
pictures = glob(self.basename + count_pattern + self.suffix)
|
||||
|
||||
# Get number of latest file
|
||||
if len(pictures) == 0:
|
||||
self.counter = 0
|
||||
@@ -69,8 +81,10 @@ class PictureList:
|
||||
pictures.sort()
|
||||
last_picture = pictures[-1]
|
||||
self.counter = int(last_picture[-(self.count_width+len(self.suffix)):-len(self.suffix)])
|
||||
print("Number of last existing file: " + str(self.counter) + "(" + str(len(pictures)) + ")")
|
||||
print("Saving as: " + self.basename)
|
||||
|
||||
# Print initial infos
|
||||
print("Info: Number of last existing file: " + str(self.counter))
|
||||
print("Info: Saving assembled pictures as: " + self.basename + "XXXXX.jpg")
|
||||
|
||||
def get(self, count):
|
||||
return self.basename + str(count).zfill(self.count_width) + self.suffix
|
||||
@@ -84,6 +98,11 @@ class PictureList:
|
||||
|
||||
|
||||
class Photobooth:
|
||||
"""The main class.
|
||||
|
||||
It contains all the logic for the photobooth.
|
||||
"""
|
||||
|
||||
def __init__(self, display_size, picture_basename, picture_size, pose_time, display_time,
|
||||
trigger_channel, shutdown_channel, lamp_channel):
|
||||
self.display = GuiModule('Photobooth', display_size)
|
||||
@@ -277,16 +296,34 @@ class Photobooth:
|
||||
# Take pictures
|
||||
filenames = [i for i in range(4)]
|
||||
for x in range(4):
|
||||
self.display.clear()
|
||||
self.display.show_message("S M I L E !!!\n\n" + str(x+1) + " of 4")
|
||||
self.display.apply()
|
||||
# Try each picture up to 3 times
|
||||
for attempt in range(3):
|
||||
self.display.clear()
|
||||
self.display.show_message("S M I L E !!!\n\n" + str(x+1) + " of 4")
|
||||
self.display.apply()
|
||||
|
||||
tic = clock()
|
||||
filenames[x] = self.camera.take_picture("/tmp/photobooth_%02d.jpg" % x)
|
||||
toc = clock() - tic
|
||||
tic = clock()
|
||||
|
||||
if toc < 1.0:
|
||||
sleep(1.0 - toc)
|
||||
try:
|
||||
filenames[x] = self.camera.take_picture("/tmp/photobooth_%02d.jpg" % x)
|
||||
break
|
||||
except CameraException as e:
|
||||
# On recoverable errors: display message and retry
|
||||
if e.recoverable:
|
||||
if attempt < 3:
|
||||
self.display.clear()
|
||||
self.display.show_message(e.message)
|
||||
self.display.apply()
|
||||
sleep(1)
|
||||
else:
|
||||
raise CameraException("Giving up! Please start again!", False)
|
||||
else:
|
||||
raise e
|
||||
|
||||
# Sleep for a little bit if necessary
|
||||
toc = clock() - tic
|
||||
if toc < 1.0:
|
||||
sleep(1.0 - toc)
|
||||
|
||||
# Show 'Wait'
|
||||
self.display.clear()
|
||||
|
||||
Reference in New Issue
Block a user