Fix picam support
This commit is contained in:
parent
e03d5e7872
commit
4cead89259
|
@ -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()
|
||||
|
|
145
frame_opencv.py
145
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
|
||||
|
|
76
readme.md
76
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).**
|
||||
|
||||
<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).
|
||||
|
||||
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`
|
||||
|
||||
``
|
||||
``
|
||||
|
|
|
@ -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())
|
||||
sys.exit(main())
|
||||
|
|
Loading…
Reference in New Issue