Camera module rewritten for new communicator scheme
This commit is contained in:
@@ -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):
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
89
photobooth/camera/CameraInterface.py
Normal file
89
photobooth/camera/CameraInterface.py
Normal 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()
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
Reference in New Issue
Block a user