Flexible module system for Camera and Gui implementations added
This commit is contained in:
108
photobooth/camera/CameraGphoto2.py
Normal file
108
photobooth/camera/CameraGphoto2.py
Normal file
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import io, logging
|
||||
|
||||
from PIL import Image
|
||||
|
||||
import gphoto2 as gp
|
||||
|
||||
from . import Camera
|
||||
|
||||
|
||||
class CameraGphoto2(Camera):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
self.hasPreview = True
|
||||
self.hasIdle = False
|
||||
self._isActive = False
|
||||
|
||||
self._setupLogging()
|
||||
self._setupCamera()
|
||||
|
||||
|
||||
def cleanup(self):
|
||||
|
||||
self._cap.exit(self._ctxt)
|
||||
# self.setIdle()
|
||||
|
||||
|
||||
def _setupLogging(self):
|
||||
|
||||
logging.basicConfig(
|
||||
format='%(levelname)s: %(name)s: %(message)s',
|
||||
level=logging.ERROR)
|
||||
gp.check_result(gp.use_python_logging())
|
||||
|
||||
|
||||
def _setupCamera(self):
|
||||
|
||||
self._ctxt = gp.Context()
|
||||
self._cap = gp.Camera()
|
||||
self._cap.init(self._ctxt)
|
||||
|
||||
self._printSummary()
|
||||
|
||||
|
||||
# get configuration tree
|
||||
config = gp.check_result(gp.gp_camera_get_config(self._cap))
|
||||
|
||||
# find the image format config item
|
||||
OK, image_format = gp.gp_widget_get_child_by_name(config, 'imageformat')
|
||||
if OK >= gp.GP_OK:
|
||||
# get current setting
|
||||
value = gp.check_result(gp.gp_widget_get_value(image_format))
|
||||
# make sure it's not raw
|
||||
if 'raw' in value.lower():
|
||||
raise RuntimeError('Camera file format is set to RAW')
|
||||
|
||||
print(config)
|
||||
|
||||
|
||||
def _printSummary(self):
|
||||
|
||||
# self.setActive()
|
||||
|
||||
text = self._cap.get_summary(self._ctxt)
|
||||
print('Summary')
|
||||
print('=======')
|
||||
print(str(text))
|
||||
|
||||
# self.setIdle()
|
||||
|
||||
|
||||
# def setActive(self):
|
||||
|
||||
# self._cap.init(self._ctxt)
|
||||
# if not self._isActive:
|
||||
# self._cap.init(self._ctxt)
|
||||
# self._isActive = True
|
||||
|
||||
|
||||
# def setIdle(self):
|
||||
|
||||
# self._cap.exit(self._ctxt)
|
||||
# if self._isActive:
|
||||
# self._cap.exit(self._ctxt)
|
||||
# self._isActive = False
|
||||
|
||||
|
||||
def getPreview(self):
|
||||
|
||||
# self.setActive()
|
||||
camera_file = self._cap.capture_preview() #gp.check_result(gp.gp_camera_capture_preview(self._cap))
|
||||
file_data = camera_file.get_data_and_size() # gp.check_result(gp.gp_file_get_data_and_size(camera_file))
|
||||
return Image.open(io.BytesIO(file_data))
|
||||
|
||||
|
||||
def getPicture(self):
|
||||
|
||||
# self.setActive()
|
||||
file_path = self._cap.capture(gp.GP_CAPTURE_IMAGE)
|
||||
camera_file = self._cap.file_get(file_path.folder, file_path.name, gp.GP_FILE_TYPE_NORMAL)
|
||||
file_data = camera_file.get_data_and_size()
|
||||
return Image.open(io.BytesIO(file_data))
|
||||
|
||||
57
photobooth/camera/CameraGphoto2Cffi.py
Normal file
57
photobooth/camera/CameraGphoto2Cffi.py
Normal file
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import io, logging
|
||||
|
||||
from PIL import Image
|
||||
|
||||
import gphoto2cffi as gp
|
||||
|
||||
from . import Camera
|
||||
|
||||
|
||||
class CameraGphoto2Cffi(Camera):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
self.hasPreview = True
|
||||
self.hasIdle = True
|
||||
|
||||
# Avoid output cluttered
|
||||
logging.basicConfig(
|
||||
format='%(levelname)s: %(name)s: %(message)s',
|
||||
level=logging.CRITICAL)
|
||||
|
||||
self._setupCamera()
|
||||
|
||||
|
||||
def _setupCamera(self):
|
||||
|
||||
self._cap = gp.Camera()
|
||||
print(self._cap.supported_operations)
|
||||
|
||||
if 'raw' in self._cap.config['imgsettings']['imageformat'].value.lower():
|
||||
raise RuntimeError('Camera file format is set to RAW')
|
||||
|
||||
|
||||
def setActive(self):
|
||||
|
||||
self._cap._get_config()['actions']['viewfinder'].set(True)
|
||||
|
||||
|
||||
def setIdle(self):
|
||||
|
||||
self._cap._get_config()['actions']['viewfinder'].set(False)
|
||||
|
||||
|
||||
def getPreview(self):
|
||||
|
||||
return Image.open(io.BytesIO(self._cap.get_preview()))
|
||||
|
||||
|
||||
def getPicture(self):
|
||||
|
||||
return Image.open(io.BytesIO(self._cap.capture()))
|
||||
|
||||
40
photobooth/camera/CameraGphoto2CommandLine.py
Normal file
40
photobooth/camera/CameraGphoto2CommandLine.py
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from PIL import Image
|
||||
import os, subprocess
|
||||
|
||||
from . import Camera
|
||||
|
||||
class CameraGphoto2CommandLine(Camera):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
self.hasPreview = False
|
||||
self.hasIdle = False
|
||||
|
||||
if os.access('/dev/shm', os.W_OK):
|
||||
self._tmp_filename = '/dev/shm/photobooth.jpg'
|
||||
else:
|
||||
self._tmp_filename = '/tmp/photobooth.jpg'
|
||||
|
||||
self.setActive()
|
||||
|
||||
|
||||
def setActive(self):
|
||||
|
||||
print(self._callGphoto('-a', '/dev/null'))
|
||||
|
||||
|
||||
def getPicture(self):
|
||||
|
||||
self._callGphoto('--capture-image-and-download', self._tmp_filename)
|
||||
return Image.open(self._tmp_filename)
|
||||
|
||||
|
||||
def _callGphoto(self, action, filename):
|
||||
|
||||
cmd = 'gphoto2 --force-overwrite --quiet ' + action + ' --filename ' + filename
|
||||
return subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT)
|
||||
51
photobooth/camera/CameraOpenCV.py
Normal file
51
photobooth/camera/CameraOpenCV.py
Normal file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from PIL import Image
|
||||
|
||||
import cv2
|
||||
|
||||
from . import Camera
|
||||
|
||||
class CameraOpenCV(Camera):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super().__init__()
|
||||
|
||||
self.hasPreview = True
|
||||
self.hasIdle = True
|
||||
|
||||
self._cap = cv2.VideoCapture()
|
||||
|
||||
|
||||
def setActive(self):
|
||||
|
||||
if not self._cap.isOpened():
|
||||
self._cap.open(0)
|
||||
if not self._cap.isOpened():
|
||||
raise RuntimeError('Camera could not be opened')
|
||||
|
||||
|
||||
def setIdle(self):
|
||||
|
||||
if self._cap.isOpened():
|
||||
self._cap.release()
|
||||
|
||||
|
||||
def getPreview(self):
|
||||
|
||||
return self.getPicture()
|
||||
|
||||
|
||||
def getPicture(self):
|
||||
|
||||
self.setActive()
|
||||
status, frame = self._cap.read()
|
||||
if not status:
|
||||
raise RuntimeError('Failed to capture picture')
|
||||
|
||||
# OpenCV yields frames in BGR format, conversion to RGB necessary.
|
||||
# (See https://stackoverflow.com/a/32270308)
|
||||
return Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
|
||||
|
||||
99
photobooth/camera/__init__.py
Normal file
99
photobooth/camera/__init__.py
Normal file
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
# Available camera modules as tuples of (config name, module name, class name)
|
||||
modules = (
|
||||
('python-gphoto2', 'CameraGphoto2', 'CameraGphoto2'),
|
||||
('gphoto2-cffi', 'CameraGphoto2Cffi', 'CameraGphoto2Cffi'),
|
||||
('gphoto2-commandline', 'CameraGphoto2CommandLine', 'CameraGphoto2CommandLine'),
|
||||
('opencv', 'CameraOpenCV', 'CameraOpenCV') )
|
||||
|
||||
|
||||
class Camera:
|
||||
|
||||
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()
|
||||
|
||||
|
||||
def setIdle(self):
|
||||
|
||||
if not self.hasIdle:
|
||||
raise RuntimeError('Camera does not support idle state')
|
||||
|
||||
raise NotImplementedError()
|
||||
Reference in New Issue
Block a user