Different gPhoto2 backends implemented. Functionally is working on a very basic level

This commit is contained in:
Balthasar Reuter
2018-04-17 01:06:20 +02:00
parent 7a02d36c80
commit ee80244f69
8 changed files with 262 additions and 27 deletions

View File

@@ -1,3 +1,8 @@
pip install pyqt5 pip install pyqt5
pip install opencv-python pip install opencv-python
pip install Pillow pip install Pillow
apt install gphoto2 libgphoto2-dev
pip install gphoto2
-or-
pip install gphoto2-cffi

View File

@@ -8,6 +8,22 @@ class Camera:
self.hasPreview = False self.hasPreview = False
self.hasIdle = False self.hasIdle = False
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.cleanup()
def cleanup(self):
pass
@property @property
def hasPreview(self): def hasPreview(self):

108
photobooth/CameraGphoto2.py Normal file
View File

@@ -0,0 +1,108 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import io, logging
from PIL import Image
import gphoto2 as gp
from Camera 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))

View File

@@ -0,0 +1,52 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import io
from PIL import Image
import gphoto2cffi as gp
from Camera import Camera
class CameraGphoto2Cffi(Camera):
def __init__(self):
super().__init__()
self.hasPreview = True
self.hasIdle = True
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()))

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from Camera import Camera
from PIL import Image
import os, subprocess
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)

View File

@@ -9,8 +9,6 @@ from PictureDimensions import PictureDimensions
import Gui import Gui
from PyQt5Gui import PyQt5Gui from PyQt5Gui import PyQt5Gui
from CameraOpenCV import CameraOpenCV as Camera
from PIL import Image, ImageOps from PIL import Image, ImageOps
from multiprocessing import Pipe, Process from multiprocessing import Pipe, Process
@@ -21,11 +19,11 @@ from time import time, sleep, localtime, strftime
class Photobooth: class Photobooth:
def __init__(self, config): def __init__(self, config, camera):
picture_basename = strftime(config.get('Picture', 'basename'), localtime()) picture_basename = strftime(config.get('Picture', 'basename'), localtime())
self._cap = Camera() self._cap = camera
self._pic_dims = PictureDimensions(config, self._cap.getPicture().size) self._pic_dims = PictureDimensions(config, self._cap.getPicture().size)
self._pic_list = PictureList(picture_basename) self._pic_list = PictureList(picture_basename)
@@ -117,7 +115,9 @@ class Photobooth:
def showCounterNoPreview(self): def showCounterNoPreview(self):
for i in range(self.countdownTime): for i in range(self.countdownTime):
self._send.send( Gui.PreviewState(str(i)) ) self._send.send( Gui.PreviewState(
message = str(i),
picture = Image.new('RGB', (1,1), 'white') ) )
sleep(1) sleep(1)
@@ -165,8 +165,20 @@ class Photobooth:
def main_photobooth(config, send, recv): def main_photobooth(config, send, recv):
photobooth = Photobooth(config) if config.get('Camera', 'module') == 'python-gphoto2':
return photobooth.run(send, recv) from CameraGphoto2 import CameraGphoto2 as Camera
elif config.get('Camera', 'module') == 'gphoto2-cffi':
from CameraGphoto2Cffi import CameraGphoto2Cffi as Camera
elif config.get('Camera', 'module') == 'gphoto2-commandline':
from CameraGphoto2CommandLine import CameraGphoto2CommandLine as Camera
elif config.get('Camera', 'module') == 'opencv':
from CameraOpenCV import CameraOpenCV as Camera
else:
raise ImportError('Unknown camera module "' + config.get('Camera', 'module') + '"')
with Camera() as cap:
photobooth = Photobooth(config, cap)
return photobooth.run(send, recv)
def run(argv): def run(argv):

View File

@@ -324,26 +324,27 @@ class PyQt5Settings(QFrame):
global cfg global cfg
wrapper = QComboBox() self._camera_modules = [
wrapper.addItem('command line') ('gphoto2-commandline', 'gphoto2 via command line'),
wrapper.addItem('piggyphoto') # ('piggyphoto', 'piggyphoto'),
wrapper.addItem('gphoto2-cffi') ('gphoto2-cffi', 'gphoto2-cffi'),
('python-gphoto2', 'python-gphoto2'),
('opencv', 'OpenCV'),
('', 'none') ]
current_wrapper = cfg.get('Camera', 'gphoto2_wrapper') wrapper = QComboBox()
if current_wrapper == 'commandline': for m in self._camera_modules:
wrapper.setCurrentIndex(0) wrapper.addItem(m[1])
elif current_wrapper == 'piggyphoto':
wrapper.setCurrentIndex(1) current_wrapper = cfg.get('Camera', 'module')
elif current_wrapper == 'gphoto2-cffi': idx = [x for x, m in enumerate(self._camera_modules) if m[0] == current_wrapper]
wrapper.setCurrentIndex(2) wrapper.setCurrentIndex(idx[0] if len(idx) > 0 else -1)
else:
wrapper.setCurrentIndex(-1)
self._value_widgets['Camera'] = {} self._value_widgets['Camera'] = {}
self._value_widgets['Camera']['gphoto2_wrapper'] = wrapper self._value_widgets['Camera']['module'] = wrapper
layout = QFormLayout() layout = QFormLayout()
layout.addRow(QLabel('gPhoto2 wrapper:'), self._value_widgets['Camera']['gphoto2_wrapper']) layout.addRow(QLabel('Camera module:'), self._value_widgets['Camera']['module'])
widget = QGroupBox('Camera settings') widget = QGroupBox('Camera settings')
widget.setLayout(layout) widget.setLayout(layout)
@@ -467,8 +468,9 @@ class PyQt5Settings(QFrame):
cfg.set('Picture', 'min_dist_y', self._value_widgets['Picture']['min_dist_y'].text()) cfg.set('Picture', 'min_dist_y', self._value_widgets['Picture']['min_dist_y'].text())
cfg.set('Picture', 'basename', self._value_widgets['Picture']['basename'].text()) cfg.set('Picture', 'basename', self._value_widgets['Picture']['basename'].text())
wrapper_idx2val = [ 'commandline', 'piggyphoto', 'gphoto2-cffi' ] # wrapper_idx2val = [ 'commandline', 'piggyphoto', 'gphoto2-cffi', '' ]
cfg.set('Camera', 'gphoto2_wrapper', wrapper_idx2val[self._value_widgets['Camera']['gphoto2_wrapper'].currentIndex()]) # cfg.set('Camera', 'module', wrapper_idx2val[self._value_widgets['Camera']['module'].currentIndex()])
cfg.set('Camera', 'module', self._camera_modules[self._value_widgets['Camera']['module'].currentIndex()][0])
cfg.write() cfg.write()
self._gui.restart() self._gui.restart()

View File

@@ -7,8 +7,8 @@ width = 1024
height = 600 height = 600
[Camera] [Camera]
# Wrapper to use gPhoto2 (gphoto2-cffi, piggyphoto, commandline) # Camera module to use (python-gphoto2, gphoto2-cffi, gphoto2-commandline, opencv)
gphoto2_wrapper = commandline module = python-gphoto2
[Gpio] [Gpio]
# Enable use of GPIO (True/False) # Enable use of GPIO (True/False)