diff --git a/dslr_helper.py b/dslr_helper.py index b6238b9..08e421e 100644 --- a/dslr_helper.py +++ b/dslr_helper.py @@ -29,12 +29,12 @@ import sys import gphoto2 as gp camera_current_settings = { - 'capturemode' : 3, # use IR remote - 'imagesize' : 2, # use size S (1936x1296) - 'whitebalance' : 1, # Natural light - 'capturetarget' : 0, # Internal memory - 'nocfcardrelease' : 0, # Allow capture without sd card - 'recordingmedia' : 1, # Write to RAM + '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(): @@ -50,14 +50,30 @@ def initialize_camera(): return camera, current_camera_config -def apply_gphoto_setting(config, setting, new_value): +# 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)) - # find corresponding choice - cur_setting_choice = gp.check_result(gp.gp_widget_get_choice(cur_setting, new_value)) - # set config value - gp.check_result(gp.gp_widget_set_value(cur_setting, cur_setting_choice)) + # 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)) @@ -66,15 +82,13 @@ def apply_dslr_settings(camera_settings): camera, config = initialize_camera() # iterate over the settings dictionary for setting in camera_settings: - print(setting) - print(camera_settings[setting]) 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)) diff --git a/frame_opencv.py b/frame_opencv.py index fd4491d..a7dc528 100644 --- a/frame_opencv.py +++ b/frame_opencv.py @@ -2,6 +2,7 @@ import cv2 import gettext from itertools import count +from importlib import import_module import os # Needed for utf-8 text from PIL import ImageFont, ImageDraw, Image @@ -222,7 +223,7 @@ class webcam(): class showmewebcam(webcam): def __init__(self): - self.serialutils = __import__('serialutils') + self.serialutils = import_module('serialutils') super().__init__() self.camera_current_settings = { 'auto_exposure': dict(min=0, max=1, step=1, default=camera_settings['auto_exposure'], value=camera_settings['auto_exposure']), @@ -267,8 +268,10 @@ class picam(): self.onionskin_was_on = self.onionskin self.liveview_only = False # Pi Cam V3 setup - self.Picamera2 = __import__('picamera2.Picamera2') - self.Transform = __import__('libcamera.Transform') + self.Picamera2 = getattr(import_module('picamera2'), 'Picamera2') + self.Transform = getattr(import_module('libcamera'), 'Transform') + # ~ self.Picamera2 = __import__('picamera2.Picamera2') + # ~ self.Transform = __import__('libcamera.Transform') # ~ from picamera2 import Picamera2 # ~ from libcamera import Transform try: @@ -308,10 +311,7 @@ class picam(): # Same as in webcam() class def capture_preview(self): - ret, overlay = self.cam.read() - if not ret: - print(_("Failed to grab frame.")) - return False + overlay = cam.capture_array("main") # Resize preview to fit screen overlay = cv2.resize(overlay, (project_settings['screen_w'], project_settings['screen_h'])) if self.liveview_only: @@ -359,10 +359,11 @@ class picam(): self.cam.set_controls({'AeFlickerMode': 1, 'AeFlickerPeriod':10000}) def apply_setting(self, to_set:list=None, inc:bool=False): - for setting in to_set: - if inc: - self.increment_setting(setting) - self.cam.set_controls({self.cam_settings_map[setting] : self.camera_current_settings[setting]['value']}) + if to_set is not None: + for setting in to_set: + if inc: + self.increment_setting(setting) + self.cam.set_controls({self.cam_settings_map[setting] : self.camera_current_settings[setting]['value']}) def flip_image(self): self.cam.stop() @@ -390,7 +391,7 @@ class picam(): class dslr(): def __init__(self): # ~ import gphoto2 as gp - self.gp = __import__('gphoto2') + 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) diff --git a/readme.md b/readme.md index b16f246..a6d262f 100644 --- a/readme.md +++ b/readme.md @@ -1,9 +1,8 @@ # Stopi2 -## Branche libcamera +## Branche gphoto / réflexe nunmérique -**Ceci est la branche qui restaure la possibilité d'utiliser des périphériques compatibles rpi-libcamera (Modules Raspicam v1,v2 et v3).** -**En utilisant la [branche correspondante pour la télécommande picote](/arthus/picote/src/branch/picamera), vous pouvez régler la mise au point du module caméra avec un [codeur rotatif](https://fr.wikipedia.org/wiki/Codeur_rotatif).** +**Ceci est la branche qui restaure la possibilité d'utiliser des périphériques compatibles [gphoto](http://gphoto.org/doc/remote).** @@ -16,6 +15,7 @@ Encore une fois, l'objectif est de créer un logiciel simple et minimaliste dans * [Pelure d'oignon](https://fr.wikipedia.org/wiki/Pelure_d'oignon#Sciences_et_techniques) entre la dernière image et le flux vidéo. * Un seul fichier de configuration permet de régler les options (résolution, images/secondes, mirroir vertical/horizontal, ) * Auto configuration de la camera au démarrage (exposition et balance des blancs) + * Pilotage de certains réglages de la caméra depuis la télécommande/clavier (balance des blancs, rotation de l'image, mise au point (picam V3), exposition...) * Prévisualisation de l'animation * Exportation vidéo avec [ffmpeg](https://ffmpeg.org/) * Interface localisée (anglais et français disponible pour le moment.) @@ -23,6 +23,22 @@ Encore une fois, l'objectif est de créer un logiciel simple et minimaliste dans ## Banc de test Ce script a été testé avec une webcam compatible V4L2, une ["showmewebcam"](https://github.com/showmewebcam/showmewebcam) à base de rpi 0 et d'un module caméra v2 (8Mp), et un ordinateur classique sous [Debian](https://debian.org) et un [RPI 4B](https://www.raspberrypi.com/products/raspberry-pi-4-model-b/) munis d'un module [Picamera V3](https://www.raspberrypi.com/products/camera-module-3/). + +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 4B (Debian 12) | | | ✓ | | | +| PC Linux (Debian, Manjaro) | ✓ | ✓ | N/A | N/A | ✓ | + +## Feuille de route + +Des fonctions supplémentaires sont prévues : + + * Réflexe numérique: utilisation du liveview de l'appareil (si je trouve un modèle compatible pour le développement) + +## Contributions + Les contributions et rapports de bugs sont les bienvenus ! ## Installation @@ -33,15 +49,16 @@ Dans un terminal : 1. Installer les dépendances suivantes : ``` # Avec une distribution basée sur Debian (Ubuntu, Mint...) - sudo apt install --no-install-recommends --no-install-suggests git ffmpeg python3-pip python3-venv libtiff5-dev libtopenjp2 libopenjp2-7-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev libharfbuzz-dev libfribidi-dev libxcb1-dev python3-tk python3-dev libopenblas-dev libatlas-base-dev libhdf5-dev libhdf5-serial-dev libatlas-base-dev libjasper-dev libqtgui4 libqt4-test v4l-utils + sudo apt install --no-install-recommends --no-install-suggests git ffmpeg python3-pip python3-venv libtiff5-dev libopenjp2-7 libopenjp2-7-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev libharfbuzz-dev libfribidi-dev libxcb1-dev python3-tk python3-dev libopenblas-dev libatlas-base-dev libhdf5-dev libhdf5-serial-dev libatlas-base-dev libjasper-dev libqtgui4 libqt4-test v4l-utils ``` - (Optionnel) Pour installer un environnement graphique minimal sur une [installation console](https://debian-facile.org/doc:install:installation-minimale) : `sudo apt install --no-install-recommends --no-install-suggests openbox xserver-xorg xinit pcmanfm gmrun lxterminal hsetroot unclutter plymouth plymouth-themes` 2. Cloner le dépôt dans le dossier de votre choix : `git clone https://git.arthus.net/arthus/stopi2.git` 3. Aller dans répertoire du projet : `cd stopi2` 4. Créer un environnement virtuel (venv) Python : `python -m venv ./` + - `pip install -vvv --upgrade pip setuptools wheel` - (Optionnel) Dans le cas de l'utilisation d'une "raspicam", il faudra ajouter le paramètre `--system-site-packages` pour avoir accès au module picamera2, et installer la librairie correspondante `python3-picamera2`. 5. Activer l'environnement virtuel avec `source bin/activate` - 6. Installer les dépendances python (~150Mo) : `pip install -r requirements.txt` + 6. Installer les dépendances python (~150Mo) : `MAKEFLAGS="-j$(nproc)" pip install -r requirements.txt` 7. Activer l'éxécution du script : `chmod +x stopi2.sh` 8. Lancer le script : `./stopi2.sh` diff --git a/serialutils.py b/serialutils.py index f51d4a5..8e0daa4 100644 --- a/serialutils.py +++ b/serialutils.py @@ -15,4 +15,12 @@ def send_serial_cmd(cam_port, cmd:str, clear=True): else: append = b'\r' con.write(str.encode(cmd) + append) - con.close() \ No newline at end of file + con.close() + +def main(): + cmd = "/usr/bin/v4l2-ctl --all" + send_serial_cmd(find_cam_port(), cmd) + +if __name__ == '__main__': + import sys + sys.exit(main()) \ No newline at end of file