From 4cead89259f6bdc4c56c380296a1ab3f619714a7 Mon Sep 17 00:00:00 2001 From: ABelliqueux Date: Sat, 15 Feb 2025 10:41:26 +0100 Subject: [PATCH] Fix picam support --- dslr_helper.py | 8 +-- frame_opencv.py | 145 +++++++++++++++++++++++------------------------- readme.md | 76 ++++++++++++------------- serialutils.py | 4 +- 4 files changed, 113 insertions(+), 120 deletions(-) diff --git a/dslr_helper.py b/dslr_helper.py index 08e421e..54c9431 100644 --- a/dslr_helper.py +++ b/dslr_helper.py @@ -29,8 +29,8 @@ 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) + '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 @@ -61,7 +61,7 @@ def apply_gphoto_setting(config, setting, new_value, inc=False): 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 + # 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)) @@ -124,7 +124,7 @@ def find_file_ext(gp_name:str, full_path:str): print(suffix) print(prefix) return os.path.join(dirname, '.'.join(prefix)) - + def capture_and_download(target:str='/tmp'): camera, current_camera_config = initialize_camera() diff --git a/frame_opencv.py b/frame_opencv.py index a7dc528..bcf7465 100644 --- a/frame_opencv.py +++ b/frame_opencv.py @@ -33,7 +33,7 @@ camera_status = [] LOCALE = os.getenv('LANG', 'en_EN') _ = gettext.translation('template', localedir='locales', languages=[LOCALE]).gettext -# Config +# Config # defaults project_settings_defaults = { 'cam_type': "webcam", @@ -88,8 +88,8 @@ class webcam(): } self.cam_settings_map = { 'white_balance_auto_preset': 'white_balance_temperature', - 'white_balance_automatic': 'white_balance_automatic', - 'auto_exposure':'auto_exposure', + 'white_balance_automatic': 'white_balance_automatic', + 'auto_exposure':'auto_exposure', 'anti_flicker' : 'power_line_frequency', 'lenspos' : 'sharpness', } @@ -118,13 +118,13 @@ class webcam(): sys.exit() def test_device(self, source): - self.cap = cv2.VideoCapture(source) + self.cap = cv2.VideoCapture(source) if self.cap is None or not self.cap.isOpened(): print(_("Warning: unable to open video source: {}").format(source)) return False self.cap.release() return True - + def capture_preview(self): ret, overlay = self.cam.read() if not ret: @@ -251,7 +251,7 @@ class picam(): # Map generic config name to specific picamera setting name self.cam_settings_map = { 'white_balance_auto_preset': 'AwbMode', - 'auto_exposure':'AeExposureMode', + 'auto_exposure':'AeExposureMode', 'anti_flicker' : 'AeFlickerMode', 'lenspos' : 'LensPosition', } @@ -270,48 +270,41 @@ class picam(): # Pi Cam V3 setup 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: - self.cam = self.Picamera2() - self.picam_config = self.cam.create_video_configuration(main={"format": 'RGB888',"size": (camera_settings['cam_w'], camera_settings['cam_h'])}) - # ~ picam_config["transform"] = Transform(hflip=camera_settings['hflip'], vflip=camera_settings['vflip']) - self.picam_config["transform"] = self.Transform(vflip=camera_current_settings['vertical_flip']['value'],hflip=camera_current_settings['horizontal_flip']['value']) - - self.cam.configure(self.picam_config) - # Autofocus, get lens position and switch to manual mode - # Set Af mode to Auto then Manual (0). Default is Continuous (2), Auto is 1 - self.cam.set_controls({'AfMode':1}) - self.cam.start() - self.cam.autofocus_cycle() - self.lenspos = self.cam.capture_metadata()['LensPosition'] - # Set focus, wb, exp to manual - self.camera_default_settings = {'AfMode': 0, - 'AwbEnable': 1, - 'AwbMode': self.camera_current_settings['white_balance_auto_preset']['default'], - 'AeEnable': 1, - 'AeExposureMode': self.camera_current_settings['auto_exposure']['default'], - # Enable flicker avoidance due to mains - 'AeFlickerMode': 1, - # Mains 50hz = 10000, 60hz = 8333 - # ~ 'AeFlickerPeriod': 8333, - 'AeFlickerPeriod': 10000, - # Format is (min, max, default) in ms - # here: (60fps, 12fps, None) - # ~ 'FrameDurationLimits':(16666,83333,None) - } - self.cam.set_controls(self.camera_default_settings) - except: - pass - + # Cam setup + self.cam = self.Picamera2() + self.picam_config = self.cam.create_video_configuration(main={"format": 'RGB888',"size": (camera_settings['cam_w'], camera_settings['cam_h'])}) + self.picam_config["transform"] = self.Transform(vflip=self.camera_current_settings['vertical_flip']['value'],hflip=self.camera_current_settings['horizontal_flip']['value']) + + self.cam.configure(self.picam_config) + # Autofocus, get lens position and switch to manual mode + # Set Af mode to Auto then Manual (0). Default is Continuous (2), Auto is 1 + self.cam.set_controls({'AfMode':1}) + self.cam.start() + self.cam.autofocus_cycle() + self.lenspos = self.cam.capture_metadata()['LensPosition'] + # Set focus, wb, exp to manual + self.camera_default_settings = {'AfMode': 0, + 'AwbEnable': 1, + 'AwbMode': self.camera_current_settings['white_balance_auto_preset']['default'], + 'AeEnable': 1, + 'AeExposureMode': self.camera_current_settings['auto_exposure']['default'], + # Enable flicker avoidance due to mains + 'AeFlickerMode': 1, + # Mains 50hz = 10000, 60hz = 8333 + # ~ 'AeFlickerPeriod': 8333, + 'AeFlickerPeriod': 10000, + # Format is (min, max, default) in ms + # here: (60fps, 12fps, None) + # ~ 'FrameDurationLimits':(16666,83333,None) + } + self.cam.set_controls(self.camera_default_settings) + def test_device(self, source): pass # Same as in webcam() class def capture_preview(self): - overlay = cam.capture_array("main") + overlay = self.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: @@ -328,7 +321,7 @@ class picam(): return True self.frame = self.o_frame return True - + # Same as in webcam() class def capture_frame(self, img_path): if project_settings['file_extension'] == 'jpg': @@ -336,7 +329,7 @@ class picam(): else: capture_ok = cv2.imwrite(img_path, self.og_frame) return capture_ok - + def increment_setting(self, setting:str): if setting in self.camera_current_settings: if self.camera_current_settings[setting]['value'] + self.camera_current_settings[setting]['step'] in range(self.camera_current_settings[setting]['min'],self.camera_current_settings[setting]['max']+1): @@ -356,21 +349,21 @@ class picam(): elif self.camera_current_settings['anti_flicker']['value'] == 1: self.cam.set_controls({'AeFlickerMode': 1, 'AeFlickerPeriod':8333}) else: - self.cam.set_controls({'AeFlickerMode': 1, 'AeFlickerPeriod':10000}) - + self.cam.set_controls({'AeFlickerMode': 1, 'AeFlickerPeriod':10000}) + def apply_setting(self, to_set:list=None, inc:bool=False): 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() self.picam_config["transform"] = self.Transform(vflip=self.camera_current_settings['vertical_flip']['value'],hflip=self.camera_current_settings['horizontal_flip']['value']) self.cam.configure(self.picam_config) self.cam.start() - + def focus(self, direction:str='-'): if direction == '+': self.lenspos += 0.2 @@ -379,11 +372,11 @@ class picam(): # Set AfMode to Manual self.cam.set_controls({'AfMode': 0, 'LensPosition': self.lenspos}) print(_("-Lens pos: {}".format(self.lenspos))) - + def reset_picture_settings(self): for setting in self.camera_default_settings: self.cam.set_controls({setting : self.camera_default_settings[setting]}) - + def close(self): cam.close() @@ -393,8 +386,8 @@ 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) + '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 @@ -405,7 +398,7 @@ class dslr(): # Map generic config name to specific picamera setting name self.cam_settings_map = { 'white_balance_auto_preset': 'whitebalance', - 'auto_exposure':'iso', + 'auto_exposure':'iso', 'anti_flicker' : 'imagesize', 'lenspos' : 'shutterspeed', } @@ -434,14 +427,14 @@ class dslr(): self.cam.exit() self.camera = None self.current_camera_config = None - + def test_device(self, source): pass def capture_preview(self): - # TODO : check DSLR has preview/live feed + # TODO : check DSLR has preview/live feed pass - + def find_file_ext(self, gp_name:str, full_path:str): # TODO: use re to sub png with jpg ? # extract dir path @@ -455,7 +448,7 @@ class dslr(): prefix = new_name.split('.')[:-1] prefix.insert(len(prefix), suffix) return os.path.join(dirname, '.'.join(prefix)) - + def check_status_value(self, config, value, optimal_value=None): cur_check = self.gp.check_result(self.gp.gp_widget_get_child_by_name(config, value)) cur_check_value = self.gp.check_result(self.gp.gp_widget_get_value(cur_check)) @@ -464,7 +457,7 @@ class dslr(): return [cur_check_value, cur_check_choice] else: return cur_check_value - + def capture_frame(self, img_path): # CHECK: Should we init and close dslr for each frame ? # Check battery level @@ -515,7 +508,7 @@ class dslr(): choices = list(self.gp.check_result(self.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 + # 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)) @@ -537,7 +530,7 @@ class dslr(): self.camera_current_settings[setting]['value'] += self.camera_current_settings[setting]['step'] else: self.camera_current_settings[setting]['value'] = self.camera_current_settings[setting]['min'] - + def apply_setting(self, to_set:list=None, inc:bool=False): self.camera_current_config = self.gp.check_result(self.gp.gp_camera_get_config(self.cam)) # iterate over the settings dictionary @@ -559,13 +552,13 @@ class dslr(): # close camera # ~ self.cam.exit() return status - + def flip_image(self): self.frame = cv2.flip(self.frame, -1) - + def focus(self, direction:str='-'): self.apply_setting(['shutterspeed'], True) - + def reset_picture_settings(self): self.camera_current_config = self.gp.check_result(self.gp.gp_camera_get_config(self.cam)) for setting in self.camera_current_settings: @@ -573,7 +566,7 @@ class dslr(): # TODO: use self.apply_setting() instead self.apply_gphoto_setting(setting) status = self.gp.check_result(self.gp.gp_camera_set_config(self.cam, self.camera_current_config)) - + def close(self): self.cam.exit() @@ -812,7 +805,7 @@ def update_image(img_list, img_index): def next_frame(img_index, loop=True): img_index = check_range(img_index+1, loop) return img_index, update_image(img_list, img_index) - + def previous_frame(img_index): img_index = check_range(img_index-1) @@ -854,7 +847,7 @@ def check_range(x, loop=True): return x -def batch_rename(folder:str): +def batch_rename(folder:str): # initialize counter to 0 frame_list = get_frames_list(folder) counter = (".%04i." % x for x in count(0)) @@ -900,7 +893,7 @@ def remove_frame(img_list, img_index): def testDevice(source): - cap = cv2.VideoCapture(source) + cap = cv2.VideoCapture(source) if cap is None or not cap.isOpened(): print(_("Warning: unable to open video source: {}").format(source)) return False @@ -929,7 +922,7 @@ def export_animation(input_filename, export_filename): project_settings['ffmpeg_path'], '-v','quiet', '-y', - '-f', input_format, + '-f', input_format, '-r', framerate, '-i', input_filename, '-vf', output_options, @@ -953,19 +946,19 @@ if cam is None: def main(args): global img_list - + playback = False first_playback = True playhead = 0 loop_playback = True index = len(img_list)-1 playhead = index - + cam.apply_setting() - + cam.frame = get_onionskin_frame(savepath) cam.o_frame = cam.frame.copy() - + loop_delta = 0 while True: start = timer() @@ -1028,7 +1021,7 @@ def main(args): # Key up, kp 8 elif (k%256 == 82) or (k%256 == 56) or (k%256 == 184): print(_("Last frame")) - if len(img_list): + if len(img_list): if playback: playback = False index, frame = last_frame(index) @@ -1036,7 +1029,7 @@ def main(args): # Key down , kp 2 elif (k%256 == 84) or (k%256 == 50) or (k%256 == 178): print(_("First frame")) - if len(img_list): + if len(img_list): if playback: playback = False index, frame = first_frame(index) @@ -1096,7 +1089,7 @@ def main(args): img_name = return_next_frame_number(get_last_frame(savepath)) img_path = os.path.join(savepath, img_name) capture_ok = cam.capture_frame(img_path) - print(_("File {} written.").format(img_path)) + print(_("File {} written.").format(img_path)) # Special case when we've no frame yet if len(img_list) and (img_list[index] == '{letter}.-001.{ext}'.format(letter=project_letter, ext=project_settings['file_extension'])): img_list[index] = img_name diff --git a/readme.md b/readme.md index a6d262f..2d6d367 100644 --- a/readme.md +++ b/readme.md @@ -2,14 +2,14 @@ ## Branche gphoto / réflexe nunmé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).** - + -Seconde version du script python [stopi](https://git.arthus.net/arthus/stopi) destiné à être utilisé avec une télécommande [picote](/arthus/picote/src/branch/picamera). - -Cette version utilise opencv et libcamera.Elle fonctionne avec une webcam ou un module vidéo Picamera (v1,v2 ou v3). -Encore une fois, l'objectif est de créer un logiciel simple et minimaliste dans son interface, dont les caractéristiques sont les suivantes : +Seconde version du script python [stopi](https://git.arthus.net/arthus/stopi) destiné à être utilisé avec une télécommande [picote](/arthus/picote/src/branch/picamera). + +Cette version utilise opencv et libcamera.Elle fonctionne avec une webcam ou un module vidéo Picamera (v1,v2 ou v3). +Encore une fois, l'objectif est de créer un logiciel simple et minimaliste dans son interface, dont les caractéristiques sont les suivantes : * Affichage des images en plein écran sans interface : toutes les fonctions utilisent quelques touches du clavier. * [Pelure d'oignon](https://fr.wikipedia.org/wiki/Pelure_d'oignon#Sciences_et_techniques) entre la dernière image et le flux vidéo. @@ -22,14 +22,14 @@ 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/). +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 | ✓ | +| 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 @@ -39,22 +39,22 @@ Des fonctions supplémentaires sont prévues : ## Contributions -Les contributions et rapports de bugs sont les bienvenus ! +Les contributions et rapports de bugs sont les bienvenus ! ## Installation -Dans un terminal : +Dans un terminal : 0. (Utilisateurs Windows) Activer le [sous système Linux **version 2** (WSL2)](https://learn.microsoft.com/fr-fr/windows/wsl/install) et installer Debian. - 1. Installer les dépendances suivantes : + 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 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 + 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` + - (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 ./` + 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` @@ -64,26 +64,26 @@ Dans un terminal : ## Fonction par touches -L'idéal est d'utiliser une télécommande [picote](/arthus/picote) mais le logiciel est aussi pilotable via un clavier/clavier numérique. +L'idéal est d'utiliser une télécommande [picote](/arthus/picote) mais le logiciel est aussi pilotable via un clavier/clavier numérique. | Fonction | Boutton | Clavier | | --- | --- | --- | -| Capturer une image | 🟢 | touche Espace ou 0 sur le clavier numérique | -| Supprimer une image | 🔴 | touche Suppr, touche Backspace ou touche - sur le clavier numérique | -| Lecture de l'animation | Alt + 🟢 | touches Entrée | -| Exporter l'animation | Alt + 🔴 | touche E ou * sur le clavier numérique | -| Image suivante | 🔵 | touche flèche droite ou 6 sur le clavier numérique | -| Image précédente | 🟡 | touche flèche gauche ou 4 sur le clavier numérique | -| Aller à la dernière image | Alt + 🔵| touche flèche bas ou 2 sur le clavier numérique | -| Aller à la première image | Alt + 🟡 | touche flèche haut ou 8 sur le clavier numérique | -| Activer/Désactiver onionskin | ⚫ | touche O ou / sur le clavier numérique | -| Quitter le logiciel | n/a | touche Échap, Alt-F4 ou Ctrl-C | +| Capturer une image | 🟢 | touche Espace ou 0 sur le clavier numérique | +| Supprimer une image | 🔴 | touche Suppr, touche Backspace ou touche - sur le clavier numérique | +| Lecture de l'animation | Alt + 🟢 | touches Entrée | +| Exporter l'animation | Alt + 🔴 | touche E ou * sur le clavier numérique | +| Image suivante | 🔵 | touche flèche droite ou 6 sur le clavier numérique | +| Image précédente | 🟡 | touche flèche gauche ou 4 sur le clavier numérique | +| Aller à la dernière image | Alt + 🔵| touche flèche bas ou 2 sur le clavier numérique | +| Aller à la première image | Alt + 🟡 | touche flèche haut ou 8 sur le clavier numérique | +| Activer/Désactiver onionskin | ⚫ | touche O ou / sur le clavier numérique | +| Quitter le logiciel | n/a | touche Échap, Alt-F4 ou Ctrl-C | | **Réglages de la caméra (compatible showmewebcam seulement)** | | | -| Réinitialiser la caméra | Alt + ⚫ | touche R ou 9 sur le clavier numérique | -| Changer le mode de balance des blancs | ① | touche W ou 7 sur le clavier numérique | -| Changer le mode d'exposition | Alt + ① | touche X ou 1 sur le clavier numérique | -| Afficher seulement le flux vidéo | ② | touche L ou 3 sur le clavier numérique | -| Rotation de 180° de la capture vidéo | Alt + ② | touche F ou 5 sur le clavier numérique | +| Réinitialiser la caméra | Alt + ⚫ | touche R ou 9 sur le clavier numérique | +| Changer le mode de balance des blancs | ① | touche W ou 7 sur le clavier numérique | +| Changer le mode d'exposition | Alt + ① | touche X ou 1 sur le clavier numérique | +| Afficher seulement le flux vidéo | ② | touche L ou 3 sur le clavier numérique | +| Rotation de 180° de la capture vidéo | Alt + ② | touche F ou 5 sur le clavier numérique | ## Installation Kiosque @@ -94,7 +94,7 @@ Pour créer un kiosque d'animation à partir d'une installation minimale de Debi 2. Suivre les [étapes d'installation](#installation) ci-dessus. 3. Activer le login automatique de votre utilisateur au démarrage : ``` -sudo systemctl edit getty@tty1.service +sudo systemctl edit getty@tty1.service # Ajout du contenu suivant dans le fichier créé: [Service] ExecStart= @@ -132,7 +132,7 @@ unclutter -idle 0.2 & /home/$USER/stopi2.sh & ``` -Au redémarrage, la session graphique devrait démarrer automatiquement. +Au redémarrage, la session graphique devrait démarrer automatiquement. # Démarrage 'silencieux' @@ -147,13 +147,13 @@ et modifier la ligne : en : `GRUB_CMDLINE_LINUX_DEFAULT="quiet splash loglevel=3` -Puis configurer plymouth : `sudo plymouth-set-default-theme` +Puis configurer plymouth : `sudo plymouth-set-default-theme` -Appliquer les modifs avec `sudo update-grub`. +Appliquer les modifs avec `sudo update-grub`. ## Raspberry Pi OS Avec Raspberry Pi OS, il suffit d'ajouter les options suivantes dans '/boot/firmware/cmdline.txt': `loglevel=3 vt.global_cursor_default=0 logo.nologo consoleblank=3 quiet` -`` \ No newline at end of file +`` diff --git a/serialutils.py b/serialutils.py index 8e0daa4..d6f963c 100644 --- a/serialutils.py +++ b/serialutils.py @@ -16,11 +16,11 @@ def send_serial_cmd(cam_port, cmd:str, clear=True): append = b'\r' con.write(str.encode(cmd) + append) 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 + sys.exit(main())