Camera module rewritten for new communicator scheme

This commit is contained in:
Balthasar Reuter
2018-07-14 01:09:47 +02:00
parent f8918e895e
commit 809db26109
13 changed files with 242 additions and 89 deletions

View File

@@ -22,10 +22,10 @@ from colorsys import hsv_to_rgb
from PIL import Image
from . import Camera
from .CameraInterface import CameraInterface
class CameraDummy(Camera):
class CameraDummy(CameraInterface):
def __init__(self):

View File

@@ -24,10 +24,10 @@ from PIL import Image
import gphoto2 as gp
from . import Camera
from .CameraInterface import CameraInterface
class CameraGphoto2(Camera):
class CameraGphoto2(CameraInterface):
def __init__(self):

View File

@@ -24,10 +24,10 @@ from PIL import Image
import gphoto2cffi as gp
from . import Camera
from .CameraInterface import CameraInterface
class CameraGphoto2Cffi(Camera):
class CameraGphoto2Cffi(CameraInterface):
def __init__(self):

View File

@@ -23,10 +23,10 @@ import subprocess
from PIL import Image
from . import Camera
from .CameraInterface import CameraInterface
class CameraGphoto2CommandLine(Camera):
class CameraGphoto2CommandLine(CameraInterface):
def __init__(self):

View File

@@ -0,0 +1,89 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Photobooth - a flexible photo booth software
# Copyright (C) 2018 Balthasar Reuter <photobooth at re - web dot eu>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
class CameraInterface:
def __init__(self):
self.hasPreview = False
self.hasIdle = False
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.cleanup()
def cleanup(self):
pass
@property
def hasPreview(self):
return self._has_preview
@hasPreview.setter
def hasPreview(self, value):
if not isinstance(value, bool):
raise ValueError('Expected bool')
self._has_preview = value
@property
def hasIdle(self):
return self._has_idle
@hasIdle.setter
def hasIdle(self, value):
if not isinstance(value, bool):
raise ValueError('Expected bool')
self._has_idle = value
def setActive(self):
if not self.hasIdle:
pass
else:
raise NotImplementedError()
def setIdle(self):
if not self.hasIdle:
raise RuntimeError('Camera does not have idle functionality')
raise NotImplementedError()
def getPreview(self):
if not self.hasPreview:
raise RuntimeError('Camera does not have preview functionality')
raise NotImplementedError()
def getPicture(self):
raise NotImplementedError()

View File

@@ -23,10 +23,10 @@ from PIL import Image
import cv2
from . import Camera
from .CameraInterface import CameraInterface
class CameraOpenCV(Camera):
class CameraOpenCV(CameraInterface):
def __init__(self):

View File

@@ -24,10 +24,10 @@ from PIL import Image
from picamera import PiCamera
from . import Camera
from .CameraInterface import CameraInterface
class CameraPicamera(Camera):
class CameraPicamera(CameraInterface):
def __init__(self):

View File

@@ -17,6 +17,14 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from PIL import Image, ImageOps
from ..PictureDimensions import PictureDimensions
from .. import StateMachine
from ..Threading import Workers
# Available camera modules as tuples of (config name, module name, class name)
modules = (
('python-gphoto2', 'CameraGphoto2', 'CameraGphoto2'),
@@ -30,70 +38,94 @@ modules = (
class Camera:
def __init__(self):
def __init__(self, config, comm, CameraModule):
self.hasPreview = False
self.hasIdle = False
super().__init__()
def __enter__(self):
self._comm = comm
self._cap = CameraModule()
self._pic_dims = PictureDimensions(config, self._cap.getPicture().size)
return self
self._is_preview = (config.getBool('Photobooth', 'show_preview') and
self._cap.hasPreview)
self._is_keep_pictures = config.getBool('Photobooth', 'keep_pictures')
def __exit__(self, exc_type, exc_value, traceback):
logging.info('Using camera {} preview functionality'.format(
'with' if self.is_preview else 'without'))
self.cleanup()
self.setIdle()
def cleanup(self):
def teardown(self):
pass
self._cap.cleanup()
@property
def hasPreview(self):
def run(self):
return self._has_preview
for state in self._comm.iter(Workers.CAMERA):
self.handleEvent(state)
@hasPreview.setter
def hasPreview(self, value):
def handleEvent(self, event):
if not isinstance(value, bool):
raise ValueError('Expected bool')
self._has_preview = value
@property
def hasIdle(self):
return self._has_idle
@hasIdle.setter
def hasIdle(self, value):
if not isinstance(value, bool):
raise ValueError('Expected bool')
self._has_idle = value
if isinstance(event, StateMachine.GreeterState):
self.prepareCapture()
elif isinstance(event, StateMachine.CountdownState):
self.capturePreview()
elif isinstance(event, StateMachine.CaptureState):
self.capturePicture()
elif isinstance(event, StateMachine.AssembleState):
self.assemblePicture()
elif isinstance(event, StateMachine.TeardownState):
self.teardown()
def setActive(self):
if not self.hasIdle:
pass
else:
raise NotImplementedError()
self._cap.setActive()
def setIdle(self):
if not self.hasIdle:
raise RuntimeError('Camera does not have idle functionality')
if self._cap.hasIdle:
self._cap.setIdle()
raise NotImplementedError()
def prepareCapture(self):
def getPreview(self):
self.setActive()
self._pictures = []
if not self.hasPreview:
raise RuntimeError('Camera does not have preview functionality')
def capturePreview(self):
raise NotImplementedError()
if self._is_preview:
while self._comm.empty(Workers.CAMERA):
picture = ImageOps.mirror(self._cap.getPreview())
self._comm.send(Workers.GUI,
StateMachine.CameraEvent('preview', picture))
def getPicture(self):
def capturePicture(self):
raise NotImplementedError()
self.setIdle()
picture = self._cap.getPicture()
self._pictures.append(picture)
self.setActive()
if self._is_keep_pictures:
self._comm.send(Workers.WORKER,
StateMachine.CameraEvent('capture', picture))
if len(self._pictures) < self._pic_dims.totalNumPictures:
self._comm.send(Workers.MASTER,
StateMachine.CameraEvent('countdown', picture))
else:
self._comm.send(Workers.MASTER,
StateMachine.CameraEvent('assemble', picture))
def assemblePicture(self):
self.setIdle()
picture = Image.new('RGB', self._pic_dims.outputSize, (255, 255, 255))
for i in range(self._pic_dims.totalNumPictures):
resized = self._pictures[i].resize(self._pic_dims.thumbnailSize)
picture.paste(resized, self._pic_dims.thumbnailOffset[i])
self._comm.send(Workers.MASTER,
StateMachine.CameraEvent('review', picture))
self._pictures = []