Added saving of assembled pictures and more robust error handling
This commit is contained in:
@@ -21,6 +21,41 @@ class GuiState:
|
||||
assert not kwargs
|
||||
|
||||
|
||||
|
||||
class ErrorState(GuiState):
|
||||
|
||||
def __init__(self, title, message, **kwargs):
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.title = title
|
||||
self.message = message
|
||||
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
|
||||
return self._title
|
||||
|
||||
|
||||
@title.setter
|
||||
def title(self, title):
|
||||
|
||||
self._title = title
|
||||
|
||||
|
||||
@property
|
||||
def message(self):
|
||||
|
||||
return self._message
|
||||
|
||||
|
||||
@message.setter
|
||||
def message(self, message):
|
||||
|
||||
self._message = message
|
||||
|
||||
|
||||
class IdleState(GuiState):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
@@ -35,7 +70,7 @@ class PictureState(GuiState):
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self._pic = picture
|
||||
self.picture = picture
|
||||
|
||||
|
||||
@property
|
||||
@@ -56,7 +91,7 @@ class MessageState(GuiState):
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self._msg = message
|
||||
self.message = message
|
||||
|
||||
|
||||
@property
|
||||
|
||||
@@ -2,21 +2,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from Config import Config
|
||||
|
||||
from PictureList import PictureList
|
||||
|
||||
import Gui
|
||||
from PyQt5Gui import PyQt5Gui
|
||||
|
||||
from CameraOpenCV import CameraOpenCV as Camera
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from multiprocessing import Pipe, Process
|
||||
|
||||
from time import time, sleep
|
||||
from time import time, sleep, localtime, strftime
|
||||
|
||||
|
||||
output_size = (1920, 1080)
|
||||
min_distance = (10, 10)
|
||||
num_pictures = (2, 2)
|
||||
pose_time = 2
|
||||
picture_basename = strftime('%Y-%m-%d/photobooth', localtime())
|
||||
|
||||
class Photobooth:
|
||||
|
||||
@@ -25,6 +30,20 @@ class Photobooth:
|
||||
self._cap = Camera()
|
||||
|
||||
|
||||
@property
|
||||
def getNextFilename(self):
|
||||
|
||||
return self._get_next_filename
|
||||
|
||||
@getNextFilename.setter
|
||||
def getNextFilename(self, func):
|
||||
|
||||
if not callable(func):
|
||||
raise ValueError('getNextFilename must be callable')
|
||||
|
||||
self._get_next_filename = func
|
||||
|
||||
|
||||
def run(self, send, recv):
|
||||
|
||||
self._send = send
|
||||
@@ -35,7 +54,11 @@ class Photobooth:
|
||||
except EOFError:
|
||||
return 1
|
||||
else:
|
||||
self.trigger()
|
||||
try:
|
||||
self.trigger()
|
||||
except RuntimeError as e:
|
||||
print('Camera error: ' + str(e))
|
||||
self._send.send( Gui.ErrorState('Camera error', str(e)) )
|
||||
|
||||
return 0
|
||||
|
||||
@@ -71,10 +94,10 @@ class Photobooth:
|
||||
( output_size[i] - (num_pictures[i] + 1) * min_distance[i] ) /
|
||||
( num_pictures[i] * picture_size[i]) ) for i in range(2) ) )
|
||||
|
||||
output_picture_size = tuple( int(picture_size[0] * resize_factor)
|
||||
output_picture_size = tuple( int(picture_size[i] * resize_factor)
|
||||
for i in range(2) )
|
||||
output_picture_dist = tuple( int( ( output_size[i] - num_pictures[i] *
|
||||
output_picture_size[i] ) / (num_pictures[i] + 1) )
|
||||
output_picture_dist = tuple( ( output_size[i] - num_pictures[i] *
|
||||
output_picture_size[i] ) // (num_pictures[i] + 1)
|
||||
for i in range(2) )
|
||||
|
||||
output_image = Image.new('RGB', output_size, (255, 255, 255))
|
||||
@@ -93,7 +116,8 @@ class Photobooth:
|
||||
|
||||
def capturePictures(self):
|
||||
|
||||
pictures = [self.captureSinglePicture() for i in range(2) for _ in range(num_pictures[i])]
|
||||
pictures = [self.captureSinglePicture()
|
||||
for i in range(2) for _ in range(num_pictures[i])]
|
||||
return self.assemblePictures(pictures)
|
||||
|
||||
|
||||
@@ -103,7 +127,9 @@ class Photobooth:
|
||||
|
||||
sleep(2)
|
||||
|
||||
self._send.send(Gui.PictureState(self.capturePictures()))
|
||||
img = self.capturePictures()
|
||||
img.save(self.getNextFilename(), 'JPEG')
|
||||
self._send.send(Gui.PictureState(img))
|
||||
|
||||
sleep(5)
|
||||
|
||||
@@ -112,7 +138,11 @@ class Photobooth:
|
||||
|
||||
def main_photobooth(send, recv):
|
||||
|
||||
picture_list = PictureList(picture_basename)
|
||||
|
||||
photobooth = Photobooth()
|
||||
photobooth.getNextFilename = picture_list.getNext
|
||||
|
||||
return photobooth.run(send, recv)
|
||||
|
||||
|
||||
|
||||
66
photobooth/PictureList.py
Normal file
66
photobooth/PictureList.py
Normal file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
|
||||
from glob import glob
|
||||
|
||||
|
||||
class PictureList:
|
||||
"""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)
|
||||
|
||||
self.findExistingFiles()
|
||||
|
||||
|
||||
def findExistingFiles(self):
|
||||
|
||||
# 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
|
||||
else:
|
||||
pictures.sort()
|
||||
last_picture = pictures[-1]
|
||||
self.counter = int(last_picture[-(self.count_width+len(self.suffix)):-len(self.suffix)])
|
||||
|
||||
# Print initial infos
|
||||
print('Info: Number of last existing file: ' + str(self.counter))
|
||||
print('Info: Saving assembled pictures as: ' + self.basename + (self.count_width * 'X') + '.jpg')
|
||||
|
||||
|
||||
def getFilename(self, count):
|
||||
|
||||
return self.basename + str(count).zfill(self.count_width) + self.suffix
|
||||
|
||||
|
||||
def getLast(self):
|
||||
|
||||
return self.getFilename(self.counter)
|
||||
|
||||
|
||||
def getNext(self):
|
||||
|
||||
self.counter += 1
|
||||
return self.getFilename(self.counter)
|
||||
@@ -67,6 +67,8 @@ class PyQt5Gui(Gui.Gui):
|
||||
# img = QImage(state.picture, state.picture.shape[1], state.picture.shape[0], QImage.Format_RGB888)
|
||||
img = ImageQt.ImageQt(state.picture)
|
||||
self._p.setCentralWidget(PyQt5PictureMessage('', img))
|
||||
elif isinstance(state, Gui.ErrorState):
|
||||
self.showError(state.title, state.message)
|
||||
else:
|
||||
raise ValueError('Unknown state')
|
||||
|
||||
@@ -89,6 +91,13 @@ class PyQt5Gui(Gui.Gui):
|
||||
self._p.setCentralWidget(PyQt5PictureMessage('Hit the button!', 'homer.jpg'))
|
||||
|
||||
|
||||
def showError(self, title, message):
|
||||
|
||||
if QMessageBox.warning(self._p, title,message, QMessageBox.Ok,
|
||||
QMessageBox.Ok) == QMessageBox.Ok:
|
||||
self.showIdle()
|
||||
|
||||
|
||||
class PyQt5Receiver(QThread):
|
||||
|
||||
notify = pyqtSignal(object)
|
||||
|
||||
Reference in New Issue
Block a user