diff --git a/dslr_helper.py b/dslr_helper.py deleted file mode 100644 index 54c9431..0000000 --- a/dslr_helper.py +++ /dev/null @@ -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 -# . - -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()) diff --git a/frame_opencv.py b/frame_opencv.py index 8e5ba13..3a46052 100644 --- a/frame_opencv.py +++ b/frame_opencv.py @@ -386,14 +386,14 @@ class dslr(): # ~ import gphoto2 as gp self.gp = import_module('gphoto2') 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 - 'imagesize' : dict(min=0, max=2, 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 - '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 - 'capturetarget' : dict(min=0, max=1, default=0, value=0), # Internal memory - 'iso' : dict(min=0, max=5, default=0, value=0), # 0:100, 5:3200 - 'shutterspeed' : dict(min=0, max=51, default=0, value=20), # 0 : 1/4000, 51: 30s - 'manualfocusdrive' : dict(min=0, max=1, default=0, value=0), # Trigger autofocus # manualfocusdrive + '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, step=1, default=2, value=2), # 0:L, 1:M, 2: S (1936x1296) + '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, 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, step=1, default=0, value=0), # Internal memory + 'iso' : dict(min=0, max=5, default=0, step=1, value=0), # 0:100, 5:3200 + 'shutterspeed' : dict(min=0, max=51, step=1, default=0, value=20), # 0 : 1/4000, 51: 30s + 'manualfocusdrive' : dict(min=0, max=1, step=1, default=0, value=0), # Trigger autofocus # manualfocusdrive } # Map generic config name to specific picamera setting name self.cam_settings_map = { @@ -417,6 +417,7 @@ class dslr(): self.onionskin_was_on = self.onionskin self.liveview_only = False self.lenspos = None + self.cam_busy = False self.cam = self.gp.check_result(self.gp.gp_camera_new()) try: self.gp.check_result(self.gp.gp_camera_init(self.cam)) @@ -459,38 +460,46 @@ class dslr(): return cur_check_value def capture_frame(self, img_path): - # CHECK: Should we init and close dslr for each frame ? - # Check battery level - battery_level = int(self.check_status_value(self.camera_current_config, 'batterylevel')[:-1]) - if battery_level < 10: - print("Battery level is too low, shutter disabled.") - return False - file_path = self.cam.capture(self.gp.GP_CAPTURE_IMAGE) - print('Camera file path: {0}/{1}'.format(file_path.folder, file_path.name)) - # We don't want to download a jpg or raw from the dslr and save it as a false *.png. - img_path = self.find_file_ext(file_path.name, img_path) - print('Copying image to', img_path) - try: - camera_file = self.cam.file_get( - file_path.folder, - file_path.name, - self.gp.GP_FILE_TYPE_NORMAL - ) - except: - print("Camera error. Check Battery and try restarting the camera.") - return False - try: - capture_ok = camera_file.save(img_path) - except: - print('File access error.') - return False - # ~ camera.exit() - # 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 + if not self.cam_busy: + # CHECK: Should we init and close dslr for each frame ? + # Check battery level + battery_level = int(self.check_status_value(self.camera_current_config, 'batterylevel')[:-1]) + if battery_level < 10: + print("Battery level is too low, shutter disabled.") + return False + try: + self.cam_busy = True + file_path = self.cam.capture(self.gp.GP_CAPTURE_IMAGE) + print('Camera file path: {0}/{1}'.format(file_path.folder, file_path.name)) + # We don't want to download a jpg or raw from the dslr and save it as a false *.png. + img_path = self.find_file_ext(file_path.name, img_path) + print('Copying image to', img_path) + except Exception as e: + print(e) + try: + camera_file = self.cam.file_get( + file_path.folder, + file_path.name, + self.gp.GP_FILE_TYPE_NORMAL + ) + except: + print("Camera error. Check Battery and try restarting the camera.") + return False + try: + capture_ok = camera_file.save(img_path) + except: + print('File access error.') + return False + # ~ camera.exit() + 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): # Get corresponding setting name if possible diff --git a/readme.md b/readme.md index ad94882..4ca7c7d 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # 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).** @@ -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)| | --- | --- | --- | --- | --- | --- | -| Raspberry Pi 3B+ (Debian 12) | À faire | À faire | ✓ | À faire | À faire | -| Raspberry Pi 4B (Debian 12) | À faire | À faire | ✓ | ✓ | À faire | +| Raspberry Pi 3B+ (Debian 12) | ✓ | ✓ | ✓ | ✓ | ✓ | +| Raspberry Pi 4B (Debian 12) | ✓ | ✓ | ✓ | ✓ | ✓ | | PC Linux (Debian, Manjaro) | ✓ | ✓ | N/A | N/A | ✓ | ## Documentation @@ -42,6 +42,7 @@ La documentation est disponible [dans le wiki](/arthus/stopi2/wiki/). 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) ## Contributions