Different gPhoto2 backends implemented. Functionally is working on a very basic level
This commit is contained in:
@@ -1,3 +1,8 @@
|
||||
pip install pyqt5
|
||||
pip install opencv-python
|
||||
pip install Pillow
|
||||
pip install Pillow
|
||||
apt install gphoto2 libgphoto2-dev
|
||||
|
||||
pip install gphoto2
|
||||
-or-
|
||||
pip install gphoto2-cffi
|
||||
@@ -8,6 +8,22 @@ class Camera:
|
||||
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):
|
||||
|
||||
|
||||
108
photobooth/CameraGphoto2.py
Normal file
108
photobooth/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 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))
|
||||
|
||||
52
photobooth/CameraGphoto2Cffi.py
Normal file
52
photobooth/CameraGphoto2Cffi.py
Normal 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()))
|
||||
|
||||
40
photobooth/CameraGphoto2CommandLine.py
Normal file
40
photobooth/CameraGphoto2CommandLine.py
Normal 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)
|
||||
@@ -9,8 +9,6 @@ from PictureDimensions import PictureDimensions
|
||||
import Gui
|
||||
from PyQt5Gui import PyQt5Gui
|
||||
|
||||
from CameraOpenCV import CameraOpenCV as Camera
|
||||
|
||||
from PIL import Image, ImageOps
|
||||
|
||||
from multiprocessing import Pipe, Process
|
||||
@@ -21,11 +19,11 @@ from time import time, sleep, localtime, strftime
|
||||
|
||||
class Photobooth:
|
||||
|
||||
def __init__(self, config):
|
||||
def __init__(self, config, camera):
|
||||
|
||||
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_list = PictureList(picture_basename)
|
||||
|
||||
@@ -117,7 +115,9 @@ class Photobooth:
|
||||
def showCounterNoPreview(self):
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@@ -165,8 +165,20 @@ class Photobooth:
|
||||
|
||||
def main_photobooth(config, send, recv):
|
||||
|
||||
photobooth = Photobooth(config)
|
||||
return photobooth.run(send, recv)
|
||||
if config.get('Camera', 'module') == 'python-gphoto2':
|
||||
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):
|
||||
|
||||
@@ -324,26 +324,27 @@ class PyQt5Settings(QFrame):
|
||||
|
||||
global cfg
|
||||
|
||||
wrapper = QComboBox()
|
||||
wrapper.addItem('command line')
|
||||
wrapper.addItem('piggyphoto')
|
||||
wrapper.addItem('gphoto2-cffi')
|
||||
self._camera_modules = [
|
||||
('gphoto2-commandline', 'gphoto2 via command line'),
|
||||
# ('piggyphoto', 'piggyphoto'),
|
||||
('gphoto2-cffi', 'gphoto2-cffi'),
|
||||
('python-gphoto2', 'python-gphoto2'),
|
||||
('opencv', 'OpenCV'),
|
||||
('', 'none') ]
|
||||
|
||||
current_wrapper = cfg.get('Camera', 'gphoto2_wrapper')
|
||||
if current_wrapper == 'commandline':
|
||||
wrapper.setCurrentIndex(0)
|
||||
elif current_wrapper == 'piggyphoto':
|
||||
wrapper.setCurrentIndex(1)
|
||||
elif current_wrapper == 'gphoto2-cffi':
|
||||
wrapper.setCurrentIndex(2)
|
||||
else:
|
||||
wrapper.setCurrentIndex(-1)
|
||||
wrapper = QComboBox()
|
||||
for m in self._camera_modules:
|
||||
wrapper.addItem(m[1])
|
||||
|
||||
current_wrapper = cfg.get('Camera', 'module')
|
||||
idx = [x for x, m in enumerate(self._camera_modules) if m[0] == current_wrapper]
|
||||
wrapper.setCurrentIndex(idx[0] if len(idx) > 0 else -1)
|
||||
|
||||
self._value_widgets['Camera'] = {}
|
||||
self._value_widgets['Camera']['gphoto2_wrapper'] = wrapper
|
||||
self._value_widgets['Camera']['module'] = wrapper
|
||||
|
||||
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.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', 'basename', self._value_widgets['Picture']['basename'].text())
|
||||
|
||||
wrapper_idx2val = [ 'commandline', 'piggyphoto', 'gphoto2-cffi' ]
|
||||
cfg.set('Camera', 'gphoto2_wrapper', wrapper_idx2val[self._value_widgets['Camera']['gphoto2_wrapper'].currentIndex()])
|
||||
# wrapper_idx2val = [ 'commandline', 'piggyphoto', 'gphoto2-cffi', '' ]
|
||||
# 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()
|
||||
self._gui.restart()
|
||||
|
||||
@@ -7,8 +7,8 @@ width = 1024
|
||||
height = 600
|
||||
|
||||
[Camera]
|
||||
# Wrapper to use gPhoto2 (gphoto2-cffi, piggyphoto, commandline)
|
||||
gphoto2_wrapper = commandline
|
||||
# Camera module to use (python-gphoto2, gphoto2-cffi, gphoto2-commandline, opencv)
|
||||
module = python-gphoto2
|
||||
|
||||
[Gpio]
|
||||
# Enable use of GPIO (True/False)
|
||||
|
||||
Reference in New Issue
Block a user