Fix picam support
This commit is contained in:
parent
e03d5e7872
commit
4cead89259
|
@ -29,8 +29,8 @@ import sys
|
||||||
import gphoto2 as gp
|
import gphoto2 as gp
|
||||||
|
|
||||||
camera_current_settings = {
|
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
|
'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)
|
'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
|
'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
|
'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
|
# ~ '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)))
|
choices = list(gp.check_result(gp.gp_widget_get_choices(cur_setting)))
|
||||||
# Build dict with name/value equivalence
|
# Build dict with name/value equivalence
|
||||||
choices_dict = {choices.index(i):i for i in list(choices)}
|
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:
|
if inc:
|
||||||
# Get current setting value
|
# Get current setting value
|
||||||
new_value = gp.check_result(gp.gp_widget_get_value(cur_setting))
|
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(suffix)
|
||||||
print(prefix)
|
print(prefix)
|
||||||
return os.path.join(dirname, '.'.join(prefix))
|
return os.path.join(dirname, '.'.join(prefix))
|
||||||
|
|
||||||
|
|
||||||
def capture_and_download(target:str='/tmp'):
|
def capture_and_download(target:str='/tmp'):
|
||||||
camera, current_camera_config = initialize_camera()
|
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')
|
LOCALE = os.getenv('LANG', 'en_EN')
|
||||||
_ = gettext.translation('template', localedir='locales', languages=[LOCALE]).gettext
|
_ = gettext.translation('template', localedir='locales', languages=[LOCALE]).gettext
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
# defaults
|
# defaults
|
||||||
project_settings_defaults = {
|
project_settings_defaults = {
|
||||||
'cam_type': "webcam",
|
'cam_type': "webcam",
|
||||||
|
@ -88,8 +88,8 @@ class webcam():
|
||||||
}
|
}
|
||||||
self.cam_settings_map = {
|
self.cam_settings_map = {
|
||||||
'white_balance_auto_preset': 'white_balance_temperature',
|
'white_balance_auto_preset': 'white_balance_temperature',
|
||||||
'white_balance_automatic': 'white_balance_automatic',
|
'white_balance_automatic': 'white_balance_automatic',
|
||||||
'auto_exposure':'auto_exposure',
|
'auto_exposure':'auto_exposure',
|
||||||
'anti_flicker' : 'power_line_frequency',
|
'anti_flicker' : 'power_line_frequency',
|
||||||
'lenspos' : 'sharpness',
|
'lenspos' : 'sharpness',
|
||||||
}
|
}
|
||||||
|
@ -118,13 +118,13 @@ class webcam():
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
def test_device(self, source):
|
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():
|
if self.cap is None or not self.cap.isOpened():
|
||||||
print(_("Warning: unable to open video source: {}").format(source))
|
print(_("Warning: unable to open video source: {}").format(source))
|
||||||
return False
|
return False
|
||||||
self.cap.release()
|
self.cap.release()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def capture_preview(self):
|
def capture_preview(self):
|
||||||
ret, overlay = self.cam.read()
|
ret, overlay = self.cam.read()
|
||||||
if not ret:
|
if not ret:
|
||||||
|
@ -251,7 +251,7 @@ class picam():
|
||||||
# Map generic config name to specific picamera setting name
|
# Map generic config name to specific picamera setting name
|
||||||
self.cam_settings_map = {
|
self.cam_settings_map = {
|
||||||
'white_balance_auto_preset': 'AwbMode',
|
'white_balance_auto_preset': 'AwbMode',
|
||||||
'auto_exposure':'AeExposureMode',
|
'auto_exposure':'AeExposureMode',
|
||||||
'anti_flicker' : 'AeFlickerMode',
|
'anti_flicker' : 'AeFlickerMode',
|
||||||
'lenspos' : 'LensPosition',
|
'lenspos' : 'LensPosition',
|
||||||
}
|
}
|
||||||
|
@ -270,48 +270,41 @@ class picam():
|
||||||
# Pi Cam V3 setup
|
# Pi Cam V3 setup
|
||||||
self.Picamera2 = getattr(import_module('picamera2'), 'Picamera2')
|
self.Picamera2 = getattr(import_module('picamera2'), 'Picamera2')
|
||||||
self.Transform = getattr(import_module('libcamera'), 'Transform')
|
self.Transform = getattr(import_module('libcamera'), 'Transform')
|
||||||
# ~ self.Picamera2 = __import__('picamera2.Picamera2')
|
# Cam setup
|
||||||
# ~ self.Transform = __import__('libcamera.Transform')
|
self.cam = self.Picamera2()
|
||||||
# ~ from picamera2 import Picamera2
|
self.picam_config = self.cam.create_video_configuration(main={"format": 'RGB888',"size": (camera_settings['cam_w'], camera_settings['cam_h'])})
|
||||||
# ~ from libcamera import Transform
|
self.picam_config["transform"] = self.Transform(vflip=self.camera_current_settings['vertical_flip']['value'],hflip=self.camera_current_settings['horizontal_flip']['value'])
|
||||||
try:
|
|
||||||
self.cam = self.Picamera2()
|
self.cam.configure(self.picam_config)
|
||||||
self.picam_config = self.cam.create_video_configuration(main={"format": 'RGB888',"size": (camera_settings['cam_w'], camera_settings['cam_h'])})
|
# Autofocus, get lens position and switch to manual mode
|
||||||
# ~ picam_config["transform"] = Transform(hflip=camera_settings['hflip'], vflip=camera_settings['vflip'])
|
# Set Af mode to Auto then Manual (0). Default is Continuous (2), Auto is 1
|
||||||
self.picam_config["transform"] = self.Transform(vflip=camera_current_settings['vertical_flip']['value'],hflip=camera_current_settings['horizontal_flip']['value'])
|
self.cam.set_controls({'AfMode':1})
|
||||||
|
self.cam.start()
|
||||||
self.cam.configure(self.picam_config)
|
self.cam.autofocus_cycle()
|
||||||
# Autofocus, get lens position and switch to manual mode
|
self.lenspos = self.cam.capture_metadata()['LensPosition']
|
||||||
# Set Af mode to Auto then Manual (0). Default is Continuous (2), Auto is 1
|
# Set focus, wb, exp to manual
|
||||||
self.cam.set_controls({'AfMode':1})
|
self.camera_default_settings = {'AfMode': 0,
|
||||||
self.cam.start()
|
'AwbEnable': 1,
|
||||||
self.cam.autofocus_cycle()
|
'AwbMode': self.camera_current_settings['white_balance_auto_preset']['default'],
|
||||||
self.lenspos = self.cam.capture_metadata()['LensPosition']
|
'AeEnable': 1,
|
||||||
# Set focus, wb, exp to manual
|
'AeExposureMode': self.camera_current_settings['auto_exposure']['default'],
|
||||||
self.camera_default_settings = {'AfMode': 0,
|
# Enable flicker avoidance due to mains
|
||||||
'AwbEnable': 1,
|
'AeFlickerMode': 1,
|
||||||
'AwbMode': self.camera_current_settings['white_balance_auto_preset']['default'],
|
# Mains 50hz = 10000, 60hz = 8333
|
||||||
'AeEnable': 1,
|
# ~ 'AeFlickerPeriod': 8333,
|
||||||
'AeExposureMode': self.camera_current_settings['auto_exposure']['default'],
|
'AeFlickerPeriod': 10000,
|
||||||
# Enable flicker avoidance due to mains
|
# Format is (min, max, default) in ms
|
||||||
'AeFlickerMode': 1,
|
# here: (60fps, 12fps, None)
|
||||||
# Mains 50hz = 10000, 60hz = 8333
|
# ~ 'FrameDurationLimits':(16666,83333,None)
|
||||||
# ~ 'AeFlickerPeriod': 8333,
|
}
|
||||||
'AeFlickerPeriod': 10000,
|
self.cam.set_controls(self.camera_default_settings)
|
||||||
# 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
|
|
||||||
|
|
||||||
def test_device(self, source):
|
def test_device(self, source):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Same as in webcam() class
|
# Same as in webcam() class
|
||||||
def capture_preview(self):
|
def capture_preview(self):
|
||||||
overlay = cam.capture_array("main")
|
overlay = self.cam.capture_array("main")
|
||||||
# Resize preview to fit screen
|
# Resize preview to fit screen
|
||||||
overlay = cv2.resize(overlay, (project_settings['screen_w'], project_settings['screen_h']))
|
overlay = cv2.resize(overlay, (project_settings['screen_w'], project_settings['screen_h']))
|
||||||
if self.liveview_only:
|
if self.liveview_only:
|
||||||
|
@ -328,7 +321,7 @@ class picam():
|
||||||
return True
|
return True
|
||||||
self.frame = self.o_frame
|
self.frame = self.o_frame
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Same as in webcam() class
|
# Same as in webcam() class
|
||||||
def capture_frame(self, img_path):
|
def capture_frame(self, img_path):
|
||||||
if project_settings['file_extension'] == 'jpg':
|
if project_settings['file_extension'] == 'jpg':
|
||||||
|
@ -336,7 +329,7 @@ class picam():
|
||||||
else:
|
else:
|
||||||
capture_ok = cv2.imwrite(img_path, self.og_frame)
|
capture_ok = cv2.imwrite(img_path, self.og_frame)
|
||||||
return capture_ok
|
return capture_ok
|
||||||
|
|
||||||
def increment_setting(self, setting:str):
|
def increment_setting(self, setting:str):
|
||||||
if setting in self.camera_current_settings:
|
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):
|
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:
|
elif self.camera_current_settings['anti_flicker']['value'] == 1:
|
||||||
self.cam.set_controls({'AeFlickerMode': 1, 'AeFlickerPeriod':8333})
|
self.cam.set_controls({'AeFlickerMode': 1, 'AeFlickerPeriod':8333})
|
||||||
else:
|
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):
|
def apply_setting(self, to_set:list=None, inc:bool=False):
|
||||||
if to_set is not None:
|
if to_set is not None:
|
||||||
for setting in to_set:
|
for setting in to_set:
|
||||||
if inc:
|
if inc:
|
||||||
self.increment_setting(setting)
|
self.increment_setting(setting)
|
||||||
self.cam.set_controls({self.cam_settings_map[setting] : self.camera_current_settings[setting]['value']})
|
self.cam.set_controls({self.cam_settings_map[setting] : self.camera_current_settings[setting]['value']})
|
||||||
|
|
||||||
def flip_image(self):
|
def flip_image(self):
|
||||||
self.cam.stop()
|
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.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.configure(self.picam_config)
|
||||||
self.cam.start()
|
self.cam.start()
|
||||||
|
|
||||||
def focus(self, direction:str='-'):
|
def focus(self, direction:str='-'):
|
||||||
if direction == '+':
|
if direction == '+':
|
||||||
self.lenspos += 0.2
|
self.lenspos += 0.2
|
||||||
|
@ -379,11 +372,11 @@ class picam():
|
||||||
# Set AfMode to Manual
|
# Set AfMode to Manual
|
||||||
self.cam.set_controls({'AfMode': 0, 'LensPosition': self.lenspos})
|
self.cam.set_controls({'AfMode': 0, 'LensPosition': self.lenspos})
|
||||||
print(_("-Lens pos: {}".format(self.lenspos)))
|
print(_("-Lens pos: {}".format(self.lenspos)))
|
||||||
|
|
||||||
def reset_picture_settings(self):
|
def reset_picture_settings(self):
|
||||||
for setting in self.camera_default_settings:
|
for setting in self.camera_default_settings:
|
||||||
self.cam.set_controls({setting : self.camera_default_settings[setting]})
|
self.cam.set_controls({setting : self.camera_default_settings[setting]})
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
cam.close()
|
cam.close()
|
||||||
|
|
||||||
|
@ -393,8 +386,8 @@ class dslr():
|
||||||
# ~ import gphoto2 as gp
|
# ~ import gphoto2 as gp
|
||||||
self.gp = import_module('gphoto2')
|
self.gp = import_module('gphoto2')
|
||||||
self.camera_current_settings = {
|
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
|
'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)
|
'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
|
'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
|
'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
|
'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
|
# Map generic config name to specific picamera setting name
|
||||||
self.cam_settings_map = {
|
self.cam_settings_map = {
|
||||||
'white_balance_auto_preset': 'whitebalance',
|
'white_balance_auto_preset': 'whitebalance',
|
||||||
'auto_exposure':'iso',
|
'auto_exposure':'iso',
|
||||||
'anti_flicker' : 'imagesize',
|
'anti_flicker' : 'imagesize',
|
||||||
'lenspos' : 'shutterspeed',
|
'lenspos' : 'shutterspeed',
|
||||||
}
|
}
|
||||||
|
@ -434,14 +427,14 @@ class dslr():
|
||||||
self.cam.exit()
|
self.cam.exit()
|
||||||
self.camera = None
|
self.camera = None
|
||||||
self.current_camera_config = None
|
self.current_camera_config = None
|
||||||
|
|
||||||
def test_device(self, source):
|
def test_device(self, source):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def capture_preview(self):
|
def capture_preview(self):
|
||||||
# TODO : check DSLR has preview/live feed
|
# TODO : check DSLR has preview/live feed
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def find_file_ext(self, gp_name:str, full_path:str):
|
def find_file_ext(self, gp_name:str, full_path:str):
|
||||||
# TODO: use re to sub png with jpg ?
|
# TODO: use re to sub png with jpg ?
|
||||||
# extract dir path
|
# extract dir path
|
||||||
|
@ -455,7 +448,7 @@ class dslr():
|
||||||
prefix = new_name.split('.')[:-1]
|
prefix = new_name.split('.')[:-1]
|
||||||
prefix.insert(len(prefix), suffix)
|
prefix.insert(len(prefix), suffix)
|
||||||
return os.path.join(dirname, '.'.join(prefix))
|
return os.path.join(dirname, '.'.join(prefix))
|
||||||
|
|
||||||
def check_status_value(self, config, value, optimal_value=None):
|
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 = 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))
|
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]
|
return [cur_check_value, cur_check_choice]
|
||||||
else:
|
else:
|
||||||
return cur_check_value
|
return cur_check_value
|
||||||
|
|
||||||
def capture_frame(self, img_path):
|
def capture_frame(self, img_path):
|
||||||
# CHECK: Should we init and close dslr for each frame ?
|
# CHECK: Should we init and close dslr for each frame ?
|
||||||
# Check battery level
|
# Check battery level
|
||||||
|
@ -515,7 +508,7 @@ class dslr():
|
||||||
choices = list(self.gp.check_result(self.gp.gp_widget_get_choices(cur_setting)))
|
choices = list(self.gp.check_result(self.gp.gp_widget_get_choices(cur_setting)))
|
||||||
# Build dict with name/value equivalence
|
# Build dict with name/value equivalence
|
||||||
choices_dict = {choices.index(i):i for i in list(choices)}
|
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:
|
# ~ if inc:
|
||||||
# Get current setting value
|
# Get current setting value
|
||||||
# ~ new_value = gp.check_result(gp.gp_widget_get_value(cur_setting))
|
# ~ 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']
|
self.camera_current_settings[setting]['value'] += self.camera_current_settings[setting]['step']
|
||||||
else:
|
else:
|
||||||
self.camera_current_settings[setting]['value'] = self.camera_current_settings[setting]['min']
|
self.camera_current_settings[setting]['value'] = self.camera_current_settings[setting]['min']
|
||||||
|
|
||||||
def apply_setting(self, to_set:list=None, inc:bool=False):
|
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))
|
self.camera_current_config = self.gp.check_result(self.gp.gp_camera_get_config(self.cam))
|
||||||
# iterate over the settings dictionary
|
# iterate over the settings dictionary
|
||||||
|
@ -559,13 +552,13 @@ class dslr():
|
||||||
# close camera
|
# close camera
|
||||||
# ~ self.cam.exit()
|
# ~ self.cam.exit()
|
||||||
return status
|
return status
|
||||||
|
|
||||||
def flip_image(self):
|
def flip_image(self):
|
||||||
self.frame = cv2.flip(self.frame, -1)
|
self.frame = cv2.flip(self.frame, -1)
|
||||||
|
|
||||||
def focus(self, direction:str='-'):
|
def focus(self, direction:str='-'):
|
||||||
self.apply_setting(['shutterspeed'], True)
|
self.apply_setting(['shutterspeed'], True)
|
||||||
|
|
||||||
def reset_picture_settings(self):
|
def reset_picture_settings(self):
|
||||||
self.camera_current_config = self.gp.check_result(self.gp.gp_camera_get_config(self.cam))
|
self.camera_current_config = self.gp.check_result(self.gp.gp_camera_get_config(self.cam))
|
||||||
for setting in self.camera_current_settings:
|
for setting in self.camera_current_settings:
|
||||||
|
@ -573,7 +566,7 @@ class dslr():
|
||||||
# TODO: use self.apply_setting() instead
|
# TODO: use self.apply_setting() instead
|
||||||
self.apply_gphoto_setting(setting)
|
self.apply_gphoto_setting(setting)
|
||||||
status = self.gp.check_result(self.gp.gp_camera_set_config(self.cam, self.camera_current_config))
|
status = self.gp.check_result(self.gp.gp_camera_set_config(self.cam, self.camera_current_config))
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.cam.exit()
|
self.cam.exit()
|
||||||
|
|
||||||
|
@ -812,7 +805,7 @@ def update_image(img_list, img_index):
|
||||||
def next_frame(img_index, loop=True):
|
def next_frame(img_index, loop=True):
|
||||||
img_index = check_range(img_index+1, loop)
|
img_index = check_range(img_index+1, loop)
|
||||||
return img_index, update_image(img_list, img_index)
|
return img_index, update_image(img_list, img_index)
|
||||||
|
|
||||||
|
|
||||||
def previous_frame(img_index):
|
def previous_frame(img_index):
|
||||||
img_index = check_range(img_index-1)
|
img_index = check_range(img_index-1)
|
||||||
|
@ -854,7 +847,7 @@ def check_range(x, loop=True):
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
def batch_rename(folder:str):
|
def batch_rename(folder:str):
|
||||||
# initialize counter to 0
|
# initialize counter to 0
|
||||||
frame_list = get_frames_list(folder)
|
frame_list = get_frames_list(folder)
|
||||||
counter = (".%04i." % x for x in count(0))
|
counter = (".%04i." % x for x in count(0))
|
||||||
|
@ -900,7 +893,7 @@ def remove_frame(img_list, img_index):
|
||||||
|
|
||||||
|
|
||||||
def testDevice(source):
|
def testDevice(source):
|
||||||
cap = cv2.VideoCapture(source)
|
cap = cv2.VideoCapture(source)
|
||||||
if cap is None or not cap.isOpened():
|
if cap is None or not cap.isOpened():
|
||||||
print(_("Warning: unable to open video source: {}").format(source))
|
print(_("Warning: unable to open video source: {}").format(source))
|
||||||
return False
|
return False
|
||||||
|
@ -929,7 +922,7 @@ def export_animation(input_filename, export_filename):
|
||||||
project_settings['ffmpeg_path'],
|
project_settings['ffmpeg_path'],
|
||||||
'-v','quiet',
|
'-v','quiet',
|
||||||
'-y',
|
'-y',
|
||||||
'-f', input_format,
|
'-f', input_format,
|
||||||
'-r', framerate,
|
'-r', framerate,
|
||||||
'-i', input_filename,
|
'-i', input_filename,
|
||||||
'-vf', output_options,
|
'-vf', output_options,
|
||||||
|
@ -953,19 +946,19 @@ if cam is None:
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
global img_list
|
global img_list
|
||||||
|
|
||||||
playback = False
|
playback = False
|
||||||
first_playback = True
|
first_playback = True
|
||||||
playhead = 0
|
playhead = 0
|
||||||
loop_playback = True
|
loop_playback = True
|
||||||
index = len(img_list)-1
|
index = len(img_list)-1
|
||||||
playhead = index
|
playhead = index
|
||||||
|
|
||||||
cam.apply_setting()
|
cam.apply_setting()
|
||||||
|
|
||||||
cam.frame = get_onionskin_frame(savepath)
|
cam.frame = get_onionskin_frame(savepath)
|
||||||
cam.o_frame = cam.frame.copy()
|
cam.o_frame = cam.frame.copy()
|
||||||
|
|
||||||
loop_delta = 0
|
loop_delta = 0
|
||||||
while True:
|
while True:
|
||||||
start = timer()
|
start = timer()
|
||||||
|
@ -1028,7 +1021,7 @@ def main(args):
|
||||||
# Key up, kp 8
|
# Key up, kp 8
|
||||||
elif (k%256 == 82) or (k%256 == 56) or (k%256 == 184):
|
elif (k%256 == 82) or (k%256 == 56) or (k%256 == 184):
|
||||||
print(_("Last frame"))
|
print(_("Last frame"))
|
||||||
if len(img_list):
|
if len(img_list):
|
||||||
if playback:
|
if playback:
|
||||||
playback = False
|
playback = False
|
||||||
index, frame = last_frame(index)
|
index, frame = last_frame(index)
|
||||||
|
@ -1036,7 +1029,7 @@ def main(args):
|
||||||
# Key down , kp 2
|
# Key down , kp 2
|
||||||
elif (k%256 == 84) or (k%256 == 50) or (k%256 == 178):
|
elif (k%256 == 84) or (k%256 == 50) or (k%256 == 178):
|
||||||
print(_("First frame"))
|
print(_("First frame"))
|
||||||
if len(img_list):
|
if len(img_list):
|
||||||
if playback:
|
if playback:
|
||||||
playback = False
|
playback = False
|
||||||
index, frame = first_frame(index)
|
index, frame = first_frame(index)
|
||||||
|
@ -1096,7 +1089,7 @@ def main(args):
|
||||||
img_name = return_next_frame_number(get_last_frame(savepath))
|
img_name = return_next_frame_number(get_last_frame(savepath))
|
||||||
img_path = os.path.join(savepath, img_name)
|
img_path = os.path.join(savepath, img_name)
|
||||||
capture_ok = cam.capture_frame(img_path)
|
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
|
# 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'])):
|
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
|
img_list[index] = img_name
|
||||||
|
|
76
readme.md
76
readme.md
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
## Branche gphoto / réflexe nunmérique
|
## 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).
|
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).
|
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 :
|
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.
|
* 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.
|
* [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
|
## 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 :
|
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)|
|
| 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) | | | ✓ | | |
|
| Raspberry Pi 4B (Debian 12) | | | ✓ | | |
|
||||||
| PC Linux (Debian, Manjaro) | ✓ | ✓ | N/A | N/A | ✓ |
|
| PC Linux (Debian, Manjaro) | ✓ | ✓ | N/A | N/A | ✓ |
|
||||||
|
|
||||||
## Feuille de route
|
## Feuille de route
|
||||||
|
|
||||||
|
@ -39,22 +39,22 @@ Des fonctions supplémentaires sont prévues :
|
||||||
|
|
||||||
## Contributions
|
## Contributions
|
||||||
|
|
||||||
Les contributions et rapports de bugs sont les bienvenus !
|
Les contributions et rapports de bugs sont les bienvenus !
|
||||||
|
|
||||||
## Installation
|
## 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.
|
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...)
|
# 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`
|
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`
|
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`
|
- `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`.
|
- (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`
|
5. Activer l'environnement virtuel avec `source bin/activate`
|
||||||
|
@ -64,26 +64,26 @@ Dans un terminal :
|
||||||
|
|
||||||
## Fonction par touches
|
## 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 |
|
| Fonction | Boutton | Clavier |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| Capturer une image | 🟢 | touche Espace ou 0 sur le clavier numérique |
|
| 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 |
|
| Supprimer une image | 🔴 | touche Suppr, touche Backspace ou touche - sur le clavier numérique |
|
||||||
| Lecture de l'animation | Alt + 🟢 | touches Entrée |
|
| Lecture de l'animation | Alt + 🟢 | touches Entrée |
|
||||||
| Exporter l'animation | Alt + 🔴 | touche E ou * sur le clavier numérique |
|
| 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 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 |
|
| 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 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 |
|
| 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 |
|
| Activer/Désactiver onionskin | ⚫ | touche O ou / sur le clavier numérique |
|
||||||
| Quitter le logiciel | n/a | touche Échap, Alt-F4 ou Ctrl-C |
|
| Quitter le logiciel | n/a | touche Échap, Alt-F4 ou Ctrl-C |
|
||||||
| **Réglages de la caméra (compatible showmewebcam seulement)** | | |
|
| **Réglages de la caméra (compatible showmewebcam seulement)** | | |
|
||||||
| Réinitialiser la caméra | Alt + ⚫ | touche R ou 9 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 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 |
|
| 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 |
|
| 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 |
|
| Rotation de 180° de la capture vidéo | Alt + ② | touche F ou 5 sur le clavier numérique |
|
||||||
|
|
||||||
|
|
||||||
## Installation Kiosque
|
## 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.
|
2. Suivre les [étapes d'installation](#installation) ci-dessus.
|
||||||
3. Activer le login automatique de votre utilisateur au démarrage :
|
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éé:
|
# Ajout du contenu suivant dans le fichier créé:
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=
|
ExecStart=
|
||||||
|
@ -132,7 +132,7 @@ unclutter -idle 0.2 &
|
||||||
/home/$USER/stopi2.sh &
|
/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'
|
# Démarrage 'silencieux'
|
||||||
|
|
||||||
|
@ -147,13 +147,13 @@ et modifier la ligne :
|
||||||
en :
|
en :
|
||||||
`GRUB_CMDLINE_LINUX_DEFAULT="quiet splash loglevel=3`
|
`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
|
## Raspberry Pi OS
|
||||||
|
|
||||||
Avec Raspberry Pi OS, il suffit d'ajouter les options suivantes dans '/boot/firmware/cmdline.txt':
|
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`
|
`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'
|
append = b'\r'
|
||||||
con.write(str.encode(cmd) + append)
|
con.write(str.encode(cmd) + append)
|
||||||
con.close()
|
con.close()
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
cmd = "/usr/bin/v4l2-ctl --all"
|
cmd = "/usr/bin/v4l2-ctl --all"
|
||||||
send_serial_cmd(find_cam_port(), cmd)
|
send_serial_cmd(find_cam_port(), cmd)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import sys
|
import sys
|
||||||
sys.exit(main())
|
sys.exit(main())
|
||||||
|
|
Loading…
Reference in New Issue