Compare commits
1 Commits
stopi2-mul
...
gphoto
Author | SHA1 | Date |
---|---|---|
|
5fef8eed03 |
162
dslr_helper.py
162
dslr_helper.py
|
@ -1,162 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# python-gphoto2 - Python interface to libgphoto2
|
|
||||||
# http://github.com/jim-easterbrook/python-gphoto2
|
|
||||||
# Copyright (C) 2015-22 Jim Easterbrook jim@jim-easterbrook.me.uk
|
|
||||||
#
|
|
||||||
# This file is part of python-gphoto2.
|
|
||||||
#
|
|
||||||
# python-gphoto2 is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Lesser General Public License as
|
|
||||||
# published by the Free Software Foundation, either version 3 of the
|
|
||||||
# License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# python-gphoto2 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
|
|
||||||
# Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public
|
|
||||||
# License along with python-gphoto2. If not, see
|
|
||||||
# <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import locale
|
|
||||||
import os
|
|
||||||
# ~ import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import gphoto2 as gp
|
|
||||||
|
|
||||||
camera_current_settings = {
|
|
||||||
'capturemode' : dict(min=0, max=4, default=0, value=3), # 0: single,1: burst,2:Timer,3:2S Remote,4:Quick remote
|
|
||||||
'imagesize' : dict(min=0, max=2, default=0, value=2), # 0:L, 1:M, 2: S (1936x1296)
|
|
||||||
'whitebalance' : dict(min=0, max=7, default=0, value=1), # 0 Automatic 1 Daylight 2 Fluorescent 3 Tungsten 4 Flash 5 Cloudy 6 Shade 7 Preset
|
|
||||||
'capturetarget' : dict(min=0, max=1, default=0, value=0), # Internal memory
|
|
||||||
# ~ 'nocfcardrelease' : dict(min=0, max=1, default=0, value=0), # Allow capture without sd card
|
|
||||||
# ~ 'recordingmedia' : dict(min=0, max=1, default=0, value=1), # Write to RAM
|
|
||||||
}
|
|
||||||
|
|
||||||
def initialize_camera():
|
|
||||||
camera = gp.check_result(gp.gp_camera_new())
|
|
||||||
try:
|
|
||||||
gp.check_result(gp.gp_camera_init(camera))
|
|
||||||
# get configuration tree
|
|
||||||
current_camera_config = gp.check_result(gp.gp_camera_get_config(camera))
|
|
||||||
except:
|
|
||||||
camera.exit()
|
|
||||||
camera = None
|
|
||||||
current_camera_config = None
|
|
||||||
return camera, current_camera_config
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: find how to use gp.CameraWidget.get_range() and check parameter value is in range before applying.
|
|
||||||
|
|
||||||
def apply_gphoto_setting(config, setting, new_value, inc=False):
|
|
||||||
# find the $setting config item
|
|
||||||
try:
|
|
||||||
# Get widget with name $setting
|
|
||||||
cur_setting = gp.check_result(gp.gp_widget_get_child_by_name(config, setting))
|
|
||||||
# Get a list of available choices
|
|
||||||
choices = list(gp.check_result(gp.gp_widget_get_choices(cur_setting)))
|
|
||||||
# Build dict with name/value equivalence
|
|
||||||
choices_dict = {choices.index(i):i for i in list(choices)}
|
|
||||||
# Increment mode : current value is increased or looped
|
|
||||||
if inc:
|
|
||||||
# Get current setting value
|
|
||||||
new_value = gp.check_result(gp.gp_widget_get_value(cur_setting))
|
|
||||||
# Check current value + 1 is in range
|
|
||||||
if new_value in range(0, len(choices)):
|
|
||||||
# Apply or loop value accordingly
|
|
||||||
pass
|
|
||||||
# If new_value exists in list, apply
|
|
||||||
if new_value['value'] in choices_dict:
|
|
||||||
cur_setting_choice = gp.check_result(gp.gp_widget_get_choice(cur_setting, new_value['value']))
|
|
||||||
# set config value
|
|
||||||
gp.check_result(gp.gp_widget_set_value(cur_setting, cur_setting_choice))
|
|
||||||
except:
|
|
||||||
print("Configuration error while setting {} to {}".format(setting, new_value))
|
|
||||||
|
|
||||||
|
|
||||||
def apply_dslr_settings(camera_settings):
|
|
||||||
camera, config = initialize_camera()
|
|
||||||
# iterate over the settings dictionary
|
|
||||||
for setting in camera_settings:
|
|
||||||
apply_gphoto_setting(config, setting, camera_settings[setting])
|
|
||||||
# validate config
|
|
||||||
status = gp.check_result(gp.gp_camera_set_config(camera, config))
|
|
||||||
# close camera
|
|
||||||
camera.exit()
|
|
||||||
return status
|
|
||||||
|
|
||||||
|
|
||||||
def check_status_value(config, value, optimal_value=None):
|
|
||||||
cur_check = gp.check_result(gp.gp_widget_get_child_by_name(config, value))
|
|
||||||
cur_check_value = gp.check_result(gp.gp_widget_get_value(cur_check))
|
|
||||||
if optimal_value is not None:
|
|
||||||
cur_check_choice = gp.check_result(gp.gp_widget_get_choice(cur_check, optimal_value[value]))
|
|
||||||
return [cur_check_value, cur_check_choice]
|
|
||||||
else:
|
|
||||||
return cur_check_value
|
|
||||||
|
|
||||||
|
|
||||||
# ~ def check_status(camera, config):
|
|
||||||
# ~ for value in camera_status:
|
|
||||||
# ~ cur_check_value, cur_check_choice = check_status_value(config, value, camera_status)
|
|
||||||
# ~ if cur_check_value == cur_check_choice:
|
|
||||||
# ~ return True
|
|
||||||
# ~ else:
|
|
||||||
# ~ # Some values are not optimal
|
|
||||||
# ~ return False
|
|
||||||
|
|
||||||
|
|
||||||
def find_file_ext(gp_name:str, full_path:str):
|
|
||||||
# extract dir path
|
|
||||||
dirname = os.path.dirname(full_path)
|
|
||||||
# extract filename from path
|
|
||||||
new_name = os.path.basename(full_path)
|
|
||||||
# if the path does'nt contain file name, return camera's FS filename
|
|
||||||
if not full_path.endswith(('.jpg', '.JPG', '.raw')):
|
|
||||||
return gp_name
|
|
||||||
suffix = gp_name.split('.')[-1]
|
|
||||||
prefix = new_name.split('.')[:-1]
|
|
||||||
prefix.insert(len(prefix), suffix)
|
|
||||||
print(suffix)
|
|
||||||
print(prefix)
|
|
||||||
return os.path.join(dirname, '.'.join(prefix))
|
|
||||||
|
|
||||||
|
|
||||||
def capture_and_download(target:str='/tmp'):
|
|
||||||
camera, current_camera_config = initialize_camera()
|
|
||||||
# Check battery level
|
|
||||||
battery_level = int(check_status_value(current_camera_config, 'batterylevel')[:-1])
|
|
||||||
if battery_level < 10:
|
|
||||||
print("Battery level is too low, shutter disabled.")
|
|
||||||
return False
|
|
||||||
file_path = camera.capture(gp.GP_CAPTURE_IMAGE)
|
|
||||||
print('Camera file path: {0}/{1}'.format(file_path.folder, file_path.name))
|
|
||||||
print(target)
|
|
||||||
target = find_file_ext(file_path.name, target)
|
|
||||||
print('Copying image to', target)
|
|
||||||
try:
|
|
||||||
camera_file = camera.file_get(
|
|
||||||
file_path.folder, file_path.name, gp.GP_FILE_TYPE_NORMAL)
|
|
||||||
except:
|
|
||||||
print("Camera error. Check Battery and try restarting the camera.")
|
|
||||||
return False
|
|
||||||
try:
|
|
||||||
camera_file.save(target)
|
|
||||||
except:
|
|
||||||
print('File access error.')
|
|
||||||
return False
|
|
||||||
camera.exit()
|
|
||||||
return True
|
|
||||||
|
|
||||||
def main():
|
|
||||||
apply_dslr_settings(camera_current_settings)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
logging.basicConfig(
|
|
||||||
format='%(levelname)s: %(name)s: %(message)s', level=logging.ERROR)
|
|
||||||
callback_obj = gp.check_result(gp.use_python_logging())
|
|
||||||
sys.exit(main())
|
|
|
@ -386,14 +386,14 @@ class dslr():
|
||||||
# ~ import gphoto2 as gp
|
# ~ import gphoto2 as gp
|
||||||
self.gp = import_module('gphoto2')
|
self.gp = import_module('gphoto2')
|
||||||
self.camera_current_settings = {
|
self.camera_current_settings = {
|
||||||
'capturemode' : dict(min=0, max=4, default=0, value=1), # 0: single,1: burst,2:Timer,3:2S Remote,4:Quick remote
|
'capturemode' : dict(min=0, max=4, step=1, default=0, value=1), # 0: single,1: burst,2:Timer,3:2S Remote,4:Quick remote
|
||||||
'imagesize' : dict(min=0, max=2, default=2, value=2), # 0:L, 1:M, 2: S (1936x1296)
|
'imagesize' : dict(min=0, max=2, step=1, default=2, value=2), # 0:L, 1:M, 2: S (1936x1296)
|
||||||
'imagequality' : dict(min=0, max=2, default=2, value=2), # 0 JPEG basic 1 JPEG normal 2 JPEG fine 3 raw 4 raw+jpg
|
'imagequality' : dict(min=0, max=2, step=1, default=2, value=2), # 0 JPEG basic 1 JPEG normal 2 JPEG fine 3 raw 4 raw+jpg
|
||||||
'whitebalance' : dict(min=0, max=7, default=2, value=1), # 0 Automatic 1 Daylight 2 Fluorescent 3 Tungsten 4 Flash 5 Cloudy 6 Shade 7 Preset
|
'whitebalance' : dict(min=0, max=7, step=1, default=2, value=1), # 0 Automatic 1 Daylight 2 Fluorescent 3 Tungsten 4 Flash 5 Cloudy 6 Shade 7 Preset
|
||||||
'capturetarget' : dict(min=0, max=1, default=0, value=0), # Internal memory
|
'capturetarget' : dict(min=0, max=1, step=1, default=0, value=0), # Internal memory
|
||||||
'iso' : dict(min=0, max=5, default=0, value=0), # 0:100, 5:3200
|
'iso' : dict(min=0, max=5, default=0, step=1, value=0), # 0:100, 5:3200
|
||||||
'shutterspeed' : dict(min=0, max=51, default=0, value=20), # 0 : 1/4000, 51: 30s
|
'shutterspeed' : dict(min=0, max=51, step=1, default=0, value=20), # 0 : 1/4000, 51: 30s
|
||||||
'manualfocusdrive' : dict(min=0, max=1, default=0, value=0), # Trigger autofocus # manualfocusdrive
|
'manualfocusdrive' : dict(min=0, max=1, step=1, default=0, value=0), # Trigger autofocus # manualfocusdrive
|
||||||
}
|
}
|
||||||
# Map generic config name to specific picamera setting name
|
# Map generic config name to specific picamera setting name
|
||||||
self.cam_settings_map = {
|
self.cam_settings_map = {
|
||||||
|
@ -417,6 +417,7 @@ class dslr():
|
||||||
self.onionskin_was_on = self.onionskin
|
self.onionskin_was_on = self.onionskin
|
||||||
self.liveview_only = False
|
self.liveview_only = False
|
||||||
self.lenspos = None
|
self.lenspos = None
|
||||||
|
self.cam_busy = False
|
||||||
self.cam = self.gp.check_result(self.gp.gp_camera_new())
|
self.cam = self.gp.check_result(self.gp.gp_camera_new())
|
||||||
try:
|
try:
|
||||||
self.gp.check_result(self.gp.gp_camera_init(self.cam))
|
self.gp.check_result(self.gp.gp_camera_init(self.cam))
|
||||||
|
@ -459,38 +460,46 @@ class dslr():
|
||||||
return cur_check_value
|
return cur_check_value
|
||||||
|
|
||||||
def capture_frame(self, img_path):
|
def capture_frame(self, img_path):
|
||||||
# CHECK: Should we init and close dslr for each frame ?
|
if not self.cam_busy:
|
||||||
# Check battery level
|
# CHECK: Should we init and close dslr for each frame ?
|
||||||
battery_level = int(self.check_status_value(self.camera_current_config, 'batterylevel')[:-1])
|
# Check battery level
|
||||||
if battery_level < 10:
|
battery_level = int(self.check_status_value(self.camera_current_config, 'batterylevel')[:-1])
|
||||||
print("Battery level is too low, shutter disabled.")
|
if battery_level < 10:
|
||||||
return False
|
print("Battery level is too low, shutter disabled.")
|
||||||
file_path = self.cam.capture(self.gp.GP_CAPTURE_IMAGE)
|
return False
|
||||||
print('Camera file path: {0}/{1}'.format(file_path.folder, file_path.name))
|
try:
|
||||||
# We don't want to download a jpg or raw from the dslr and save it as a false *.png.
|
self.cam_busy = True
|
||||||
img_path = self.find_file_ext(file_path.name, img_path)
|
file_path = self.cam.capture(self.gp.GP_CAPTURE_IMAGE)
|
||||||
print('Copying image to', img_path)
|
print('Camera file path: {0}/{1}'.format(file_path.folder, file_path.name))
|
||||||
try:
|
# We don't want to download a jpg or raw from the dslr and save it as a false *.png.
|
||||||
camera_file = self.cam.file_get(
|
img_path = self.find_file_ext(file_path.name, img_path)
|
||||||
file_path.folder,
|
print('Copying image to', img_path)
|
||||||
file_path.name,
|
except Exception as e:
|
||||||
self.gp.GP_FILE_TYPE_NORMAL
|
print(e)
|
||||||
)
|
try:
|
||||||
except:
|
camera_file = self.cam.file_get(
|
||||||
print("Camera error. Check Battery and try restarting the camera.")
|
file_path.folder,
|
||||||
return False
|
file_path.name,
|
||||||
try:
|
self.gp.GP_FILE_TYPE_NORMAL
|
||||||
capture_ok = camera_file.save(img_path)
|
)
|
||||||
except:
|
except:
|
||||||
print('File access error.')
|
print("Camera error. Check Battery and try restarting the camera.")
|
||||||
return False
|
return False
|
||||||
# ~ camera.exit()
|
try:
|
||||||
# Update frame
|
capture_ok = camera_file.save(img_path)
|
||||||
cam.frame = cv2.imread(img_path)
|
except:
|
||||||
# ~ frame = cv2.resize(frame, (project_settings['screen_w'], project_settings['screen_h']))
|
print('File access error.')
|
||||||
# ~ if capture_ok is None:
|
return False
|
||||||
# ~ return True
|
# ~ camera.exit()
|
||||||
return True
|
self.cam_busy = False
|
||||||
|
# Update frame
|
||||||
|
cam.frame = cv2.imread(img_path)
|
||||||
|
# ~ frame = cv2.resize(frame, (project_settings['screen_w'], project_settings['screen_h']))
|
||||||
|
# ~ if capture_ok is None:
|
||||||
|
# ~ return True
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
def apply_gphoto_setting(self, setting:str):
|
def apply_gphoto_setting(self, setting:str):
|
||||||
# Get corresponding setting name if possible
|
# Get corresponding setting name if possible
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Stopi2
|
# Stopi2
|
||||||
|
|
||||||
## Branche gphoto / réflexe nunmérique
|
## Branche gphoto / réflexe numérique
|
||||||
|
|
||||||
**Ceci est la branche qui restaure la possibilité d'utiliser des périphériques compatibles [gphoto](http://gphoto.org/doc/remote).**
|
**Ceci est la branche qui restaure la possibilité d'utiliser des périphériques compatibles [gphoto](http://gphoto.org/doc/remote).**
|
||||||
|
|
||||||
|
@ -30,8 +30,8 @@ Voici un récapitulatif des tests effectués :
|
||||||
|
|
||||||
| Machine \ Type de Caméra | Webcam | [Showmewebcam](https://github.com/showmewebcam/showmewebcam) | RPI Caméra module V1 (5MP) | [RPI Caméra module V3](https://www.raspberrypi.com/products/camera-module-3/) (12MP) | [Réflexe numérique](http://gphoto.org/doc/remote) (Nikon D3000/D40x)|
|
| Machine \ Type de Caméra | Webcam | [Showmewebcam](https://github.com/showmewebcam/showmewebcam) | RPI Caméra module V1 (5MP) | [RPI Caméra module V3](https://www.raspberrypi.com/products/camera-module-3/) (12MP) | [Réflexe numérique](http://gphoto.org/doc/remote) (Nikon D3000/D40x)|
|
||||||
| --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- |
|
||||||
| Raspberry Pi 3B+ (Debian 12) | À faire | À faire | ✓ | À faire | À faire |
|
| Raspberry Pi 3B+ (Debian 12) | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
| Raspberry Pi 4B (Debian 12) | À faire | À faire | ✓ | ✓ | À faire |
|
| Raspberry Pi 4B (Debian 12) | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
| PC Linux (Debian, Manjaro) | ✓ | ✓ | N/A | N/A | ✓ |
|
| PC Linux (Debian, Manjaro) | ✓ | ✓ | N/A | N/A | ✓ |
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
@ -42,6 +42,7 @@ La documentation est disponible [dans le wiki](/arthus/stopi2/wiki/).
|
||||||
|
|
||||||
Des fonctions supplémentaires sont prévues :
|
Des fonctions supplémentaires sont prévues :
|
||||||
|
|
||||||
|
* Mise à jour de la traduction FR
|
||||||
* Réflexe numérique: utilisation du liveview de l'appareil (si je trouve un modèle compatible pour le développement)
|
* Réflexe numérique: utilisation du liveview de l'appareil (si je trouve un modèle compatible pour le développement)
|
||||||
|
|
||||||
## Contributions
|
## Contributions
|
||||||
|
|
Loading…
Reference in New Issue