Compare commits

...

9 Commits

6 changed files with 845 additions and 263 deletions

View File

@ -1,6 +1,6 @@
[DEFAULT]
cam_is_picam = true
cam_is_showmewebcam = false
# Camera type - can be : showmewebcam, picam, webcam, dslr
cam_type = 'webcam'
use_date_for_folder = false
file_extension = 'jpg'
jpg_quality = 88
@ -8,14 +8,15 @@ projects_folder = ''
onion_skin_onstartup = true
onionskin_alpha_default = 0.5
fullscreen_bool = true
screen_w = 1440
screen_h = 900
screen_w = 1920
screen_h = 1080
framerate = 16
ffmpeg_path = '/usr/bin/ffmpeg'
v4l2-ctl_path = '/usr/bin/v4l2-ctl'
export_options = 'scale=1920:-1,crop=1920:1080'
[CAMERA]
cam_w = 1920
cam_h = 1080
cam_w = 1280
cam_h = 960
vflip = 0
hflip = 0
auto_exposure = 1

162
dslr_helper.py Normal file
View File

@ -0,0 +1,162 @@
#!/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())

File diff suppressed because it is too large Load Diff

View File

@ -1,72 +1,89 @@
# 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).**
<a style="max-height: 300px;display: inline-block;" href="./stopi2/raw/branch/master/stopi_station.jpg"><img src="./stopi_station.jpg"/><a/>
<a style="max-height: 300px;display: inline-block;" href="./stopi2/raw/branch/master/stopi_station.jpg"><img src="./stopi_station.jpg"/><a/>
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).
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 :
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.
* 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.)
## 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/).
Les contributions et rapports de bugs sont les bienvenus !
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) | | | &check; | | |
| PC Linux (Debian, Manjaro) | &check; | &check; | N/A | N/A | &check; |
## 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
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-tk 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
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`
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`
## 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
@ -77,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=
@ -115,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'
@ -130,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`
``
``

View File

@ -3,3 +3,4 @@ numpy
pyserial
pillow
opencv-python
gphoto2

View File

@ -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()
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())