Use Classes for cam type
This commit is contained in:
parent
8bc52eae04
commit
ab586d29ca
|
@ -8,10 +8,11 @@ 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
|
||||
|
|
732
frame_opencv.py
732
frame_opencv.py
|
@ -13,11 +13,6 @@ import time
|
|||
from timeit import default_timer as timer
|
||||
import tomllib
|
||||
import numpy as np
|
||||
import serialutils
|
||||
|
||||
# DSLR
|
||||
# ~ import gphoto2 as gp
|
||||
import dslr_helper
|
||||
|
||||
# Run from SSH
|
||||
if not os.getenv('DISPLAY'):
|
||||
|
@ -26,11 +21,9 @@ if not os.getenv('DISPLAY'):
|
|||
running_from_folder = os.path.realpath(__file__)
|
||||
alphabet = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
|
||||
|
||||
cam_lenspos = 0
|
||||
index = 0
|
||||
playhead = 0
|
||||
playback = 0
|
||||
liveview_only = 0
|
||||
camera_settings = 0
|
||||
camera_status = []
|
||||
|
||||
|
@ -53,6 +46,7 @@ project_settings_defaults = {
|
|||
'screen_h' : 1080,
|
||||
'framerate' : 16,
|
||||
'ffmpeg_path' : None,
|
||||
'v4l2-ctl_path' : None,
|
||||
'export_options' : 'scale=1920:-1,crop=1920:1080:0:102',
|
||||
}
|
||||
|
||||
|
@ -82,67 +76,462 @@ for location in config_locations:
|
|||
config_found_msg = _("Found configuration file in {}").format(os.path.expanduser(location))
|
||||
print(config_found_msg)
|
||||
|
||||
if project_settings['cam_type'] == "showmewebcam":
|
||||
camera_current_settings = {
|
||||
class webcam():
|
||||
def __init__(self):
|
||||
self.camera_current_settings = {
|
||||
'auto_exposure': dict(min=0, max=1, default=camera_settings['auto_exposure'], value=camera_settings['auto_exposure']),
|
||||
'white_balance_auto_preset': dict(min=0, max=9, default=camera_settings['white_balance_auto_preset'], value=camera_settings['white_balance_auto_preset']),
|
||||
'horizontal_flip': dict(min=0, max=1, default=camera_settings['hflip'], value=camera_settings['hflip']),
|
||||
'vertical_flip': dict(min=0, max=1, default=camera_settings['vflip'], value=camera_settings['vflip']),
|
||||
'video_bitrate': dict(min=25000000, max=25000000, default=camera_settings['video_bitrate'], value=camera_settings['video_bitrate']),
|
||||
}
|
||||
elif project_settings['cam_type'] == "picam":
|
||||
camera_current_settings = {
|
||||
self.has_liveview = True
|
||||
self.frame = None
|
||||
self.overlay = None
|
||||
self.onionskin = project_settings['onion_skin_onstartup']
|
||||
self.onionskin_was_on = self.onionskin
|
||||
self.liveview_only = False
|
||||
self.lenspos = None
|
||||
# Test first device
|
||||
if not self.test_device(0):
|
||||
print(_("No camera device found. Exiting..."))
|
||||
sys.exit()
|
||||
try:
|
||||
self.cam = cv2.VideoCapture(0)
|
||||
self.cam.set(cv2.CAP_PROP_FRAME_WIDTH, camera_settings['cam_w'])
|
||||
self.cam.set(cv2.CAP_PROP_FRAME_HEIGHT, camera_settings['cam_h'])
|
||||
except:
|
||||
sys.exit()
|
||||
|
||||
def test_device(self, 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:
|
||||
print(_("Failed to grab frame."))
|
||||
return False
|
||||
# Resize preview
|
||||
frame = cv2.resize(overlay, (project_settings['screen_w'], project_settings['screen_h']))
|
||||
if self.liveview_only:
|
||||
# Don't mix it
|
||||
self.frame = frame
|
||||
# ~ return frame
|
||||
if self.onionskin:
|
||||
# Keep original pic in memory
|
||||
self.og_frame = overlay.copy()
|
||||
# calculate and apply alpha
|
||||
alpha = project_settings['onionskin_alpha_default']
|
||||
beta = (1.0 - alpha)
|
||||
frame = cv2.addWeighted(frame, alpha, overlay, beta, 0)
|
||||
# ~ self.frame = frame
|
||||
# ~ return frame
|
||||
|
||||
def capture_frame(self, img_path):
|
||||
if project_settings['file_extension'] == 'jpg':
|
||||
capture_ok = cv2.imwrite(img_path, self.og_frame, [int(cv2.IMWRITE_JPEG_QUALITY), project_settings['jpg_quality']])
|
||||
else:
|
||||
capture_ok = cv2.imwrite(img_path, self.og_frame)
|
||||
return capture_ok
|
||||
|
||||
def increment_setting(self, setting:str):
|
||||
if self.camera_current_settings[setting]['value']+1 in range(self.camera_current_settings[setting]['min'],self.camera_current_settings[setting]['max']+1):
|
||||
self.camera_current_settings[setting]['value'] += 1
|
||||
else:
|
||||
self.camera_current_settings[setting]['value'] = self.camera_current_settings[setting]['min']
|
||||
|
||||
def build_v4l2_cmd(self, to_set:list=None):
|
||||
self.cmd = '{v4l2-ctl_path} -d /dev/video0'.format(project_settings['v4l2-ctl_path'])
|
||||
self.args = []
|
||||
for setting in self.camera_current_settings:
|
||||
if to_set is None:
|
||||
# Apply defaults
|
||||
self.camera_current_settings[setting]['value'] = self.camera_current_settings[setting]['default']
|
||||
self.cmd += ' -c {}={}'
|
||||
self.args.append(setting)
|
||||
self.args.append(self.camera_current_settings[setting]['value'])
|
||||
else:
|
||||
# Increment settings in to_set
|
||||
for setting in to_set:
|
||||
if setting in self.camera_current_settings:
|
||||
self.increment_setting(setting)
|
||||
self.cmd += ' -c {}={}'
|
||||
self.args.append(setting)
|
||||
self.args.append(self.camera_current_settings[setting]['value'])
|
||||
else:
|
||||
print(_("Unknown setting!"))
|
||||
break
|
||||
return self.cmd, self.args
|
||||
|
||||
def run_v4l2_ctl(self):
|
||||
if project_settings['v4l2-ctl_path'] is None:
|
||||
return False
|
||||
v4l2_ctl_process = subprocess.Popen(self.cmd.split(' '))
|
||||
return ffmpeg_process
|
||||
|
||||
def apply_setting(self, to_set:list=None):
|
||||
self.cmd, self.args = self.build_v4l2_cmd(to_set)
|
||||
run_v4l2_ctl(self.cmd)
|
||||
return self.camera_current_settings
|
||||
|
||||
def flip_image(self):
|
||||
self.apply_setting(['vertical_flip','horizontal_flip'])
|
||||
|
||||
def focus(self, direction:str='-'):
|
||||
pass
|
||||
|
||||
def reset_picture_settings(self):
|
||||
self.apply_setting()
|
||||
|
||||
def close(self):
|
||||
self.cam.release()
|
||||
|
||||
|
||||
class showmewebcam(webcam):
|
||||
def __init__(self):
|
||||
self.serialutils = __import__('serialutils')
|
||||
super().__init__()
|
||||
|
||||
def apply_setting(self, to_set:list=None):
|
||||
self.cmd, self.args = self.build_v4l2_cmd(to_set)
|
||||
self.serialutils.send_serial_cmd(self.serialutils.find_cam_port(), self.cmd.format(*self.args))
|
||||
return self.camera_current_settings
|
||||
|
||||
class picam():
|
||||
def __init__(self):
|
||||
self.camera_current_settings = {
|
||||
'auto_exposure': dict(min=0, max=4, default=camera_settings['auto_exposure'], value=camera_settings['auto_exposure']),
|
||||
'white_balance_auto_preset': dict(min=0, max=7, default=camera_settings['white_balance_auto_preset'], value=camera_settings['white_balance_auto_preset']),
|
||||
'horizontal_flip': dict(min=0, max=1, default=camera_settings['hflip'], value=camera_settings['hflip']),
|
||||
'vertical_flip': dict(min=0, max=1, default=camera_settings['vflip'], value=camera_settings['vflip']),
|
||||
'anti_flicker': dict(min=0, max=2, default=1, value=1),
|
||||
}
|
||||
elif project_settings['cam_type'] == "dslr":
|
||||
# Sane values for Nikon D40X
|
||||
camera_current_settings = {
|
||||
'capturemode' : 3, # use IR remote
|
||||
'imagesize' : 2, # use size S (1936x1296)
|
||||
'whitebalance' : 1, # Natural light
|
||||
'capturetarget' : 0, # Internal memory
|
||||
'nocfcardrelease' : 0, # Allow capture without sd card
|
||||
'recordingmedia' : 1, # Write to RAM
|
||||
}
|
||||
else:
|
||||
camera_current_settings = {}
|
||||
self.has_liveview = True
|
||||
self.frame = None
|
||||
self.overlay = None
|
||||
self.onionskin = project_settings['onion_skin_onstartup']
|
||||
self.onionskin_was_on = self.onionskin
|
||||
self.liveview_only = False
|
||||
# Pi Cam V3 setup
|
||||
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'])
|
||||
|
||||
def apply_cam_setting(cam_settings:dict, to_set:list=None):
|
||||
# TODO: Refactor so that we can call with whatever camera type and setting, and have the work of determining how to apply it done here
|
||||
# This version should probably be kept and renamed construct_v4l2_cmd()
|
||||
cmd = 'v4l2-ctl -d /dev/video0'
|
||||
args = []
|
||||
for setting in cam_settings:
|
||||
if to_set is None:
|
||||
# Apply defaults
|
||||
cam_settings[setting]['value'] = cam_settings[setting]['default']
|
||||
cmd += ' -c {}={}'
|
||||
args.append(setting)
|
||||
args.append(cam_settings[setting]['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)
|
||||
# Map generic config name to specific picamera setting name
|
||||
self.cam_settings_map = {
|
||||
'white_balance_auto_preset': 'AwbMode',
|
||||
'auto_exposure':'AeExposureMode',
|
||||
'anti_flicker' : 'AeFlickerMode',
|
||||
'lenspos' : 'LensPosition',
|
||||
}
|
||||
except:
|
||||
pass
|
||||
|
||||
def test_device(self, source):
|
||||
pass
|
||||
|
||||
def capture_preview(self):
|
||||
ret, overlay = self.cam.read()
|
||||
if not ret:
|
||||
print(_("Failed to grab frame."))
|
||||
return False
|
||||
# Resize preview
|
||||
self.frame = cv2.resize(overlay, (project_settings['screen_w'], project_settings['screen_h']))
|
||||
if self.liveview_only:
|
||||
# Don't mix it
|
||||
frame = self.frame
|
||||
return True
|
||||
if self.onionskin:
|
||||
# Keep original pic in memory
|
||||
self.og_frame = overlay.copy()
|
||||
# calculate and apply alpha
|
||||
alpha = project_settings['onionskin_alpha_default']
|
||||
beta = (1.0 - alpha)
|
||||
frame = cv2.addWeighted(self.frame, alpha, overlay, beta, 0)
|
||||
# ~ self.frame = frame
|
||||
# ~ return frame
|
||||
|
||||
def capture_frame(self, img_path):
|
||||
if project_settings['file_extension'] == 'jpg':
|
||||
capture_ok = cv2.imwrite(img_path, self.og_frame, [int(cv2.IMWRITE_JPEG_QUALITY), project_settings['jpg_quality']])
|
||||
else:
|
||||
# Increment settings in to_set
|
||||
capture_ok = cv2.imwrite(img_path, self.og_frame)
|
||||
return capture_ok
|
||||
|
||||
def increment_setting(self, setting:str):
|
||||
if self.camera_current_settings[setting]['value']+1 in range(self.camera_current_settings[setting]['min'],self.camera_current_settings[setting]['max']+1):
|
||||
self.camera_current_settings[setting]['value'] += 1
|
||||
else:
|
||||
self.camera_current_settings[setting]['value'] = self.camera_current_settings[setting]['min']
|
||||
# Special cases
|
||||
# Autoexposure
|
||||
if setting == 'autoexposure' and self.camera_current_settings['autoexposure']['value'] == 4:
|
||||
self.cam.set_controls({'AeEnable': 1})
|
||||
else:
|
||||
self.cam.set_controls({'AeEnable': 0})
|
||||
self.cam.set_controls({"AeExposureMode": self.camera_current_settings['auto_exposure']['value']})
|
||||
# Antiflicker
|
||||
if setting == 'anti_flicker' and self.camera_current_settings['anti_flicker']['value'] == 0:
|
||||
self.cam.set_controls({'AeFlickerMode': 0})
|
||||
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})
|
||||
|
||||
def apply_setting(self, to_set:list=None):
|
||||
for setting in to_set:
|
||||
if setting in cam_settings:
|
||||
if cam_settings[setting]['value']+1 in range(cam_settings[setting]['min'],cam_settings[setting]['max']+1):
|
||||
cam_settings[setting]['value'] += 1
|
||||
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
|
||||
else:
|
||||
cam_settings[setting]['value'] = cam_settings[setting]['min']
|
||||
cmd += ' -c {}={}'
|
||||
args.append(setting)
|
||||
args.append(cam_settings[setting]['value'])
|
||||
self.lenspos -= 0.2
|
||||
# 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()
|
||||
|
||||
|
||||
class dslr():
|
||||
def __init__(self):
|
||||
# ~ import gphoto2 as gp
|
||||
self.gp = __import__('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)
|
||||
'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
|
||||
'iso' : dict(min=0, max=5, default=0, value=0), # 0:100, 5:3200
|
||||
'shutterspeed' : dict(min=0, max=51, default=0, value=20), # 0 : 1/4000, 51: 30s
|
||||
'manualfocusdrive' : dict(min=0, max=1, default=0, value=0), # Trigger autofocus # manualfocusdrive
|
||||
}
|
||||
# Map generic config name to specific picamera setting name
|
||||
self.cam_settings_map = {
|
||||
'white_balance_auto_preset': 'whitebalance',
|
||||
'auto_exposure':'iso',
|
||||
'anti_flicker' : 'imagesize',
|
||||
'lenspos' : 'shutterspeed',
|
||||
}
|
||||
self.frame = None
|
||||
self.has_liveview = False
|
||||
# TODO: check if DSLR has live feed and set accordingly
|
||||
# ~ self.onionskin = project_settings['onion_skin_onstartup']
|
||||
self.onionskin = False
|
||||
self.onionskin_was_on = self.onionskin
|
||||
self.liveview_only = False
|
||||
self.lenspos = None
|
||||
self.cam = self.gp.check_result(self.gp.gp_camera_new())
|
||||
try:
|
||||
self.gp.check_result(self.gp.gp_camera_init(self.cam))
|
||||
# get configuration tree
|
||||
self.camera_current_config = self.gp.check_result(self.gp.gp_camera_get_config(self.cam))
|
||||
except:
|
||||
print(_("No camera found."))
|
||||
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
|
||||
pass
|
||||
|
||||
def find_file_ext(self, gp_name:str, full_path:str):
|
||||
# TODO: use re to sub png with jpg ?
|
||||
# extract dir path
|
||||
dirname = os.path.dirname(full_path)
|
||||
# extract filename from path
|
||||
new_name = os.path.basename(full_path)
|
||||
# if the path doesn't 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)
|
||||
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))
|
||||
if optimal_value is not None:
|
||||
cur_check_choice = self.gp.check_result(self.gp.gp_widget_get_choice(cur_check, optimal_value[value]))
|
||||
return [cur_check_value, cur_check_choice]
|
||||
else:
|
||||
print(_("Unknown setting!"))
|
||||
break
|
||||
if project_settings['cam_type'] == "showmewebcam":
|
||||
serialutils.send_serial_cmd(serialutils.find_cam_port(), cmd.format(*args))
|
||||
return cur_check_value
|
||||
|
||||
def capture_frame(self, img_path):
|
||||
# CHECK: Should we init and close dslr for each frame ?
|
||||
# Check battery level
|
||||
battery_level = int(self.check_status_value(self.camera_current_config, 'batterylevel')[:-1])
|
||||
if battery_level < 10:
|
||||
print("Battery level is too low, shutter disabled.")
|
||||
return False
|
||||
file_path = self.cam.capture(self.gp.GP_CAPTURE_IMAGE)
|
||||
print('Camera file path: {0}/{1}'.format(file_path.folder, file_path.name))
|
||||
# We don't want to download a jpg or raw from the dslr and save it as a false *.png.
|
||||
img_path = self.find_file_ext(file_path.name, img_path)
|
||||
print('Copying image to', img_path)
|
||||
try:
|
||||
camera_file = self.cam.file_get(
|
||||
file_path.folder,
|
||||
file_path.name,
|
||||
self.gp.GP_FILE_TYPE_NORMAL
|
||||
)
|
||||
except:
|
||||
print("Camera error. Check Battery and try restarting the camera.")
|
||||
return False
|
||||
try:
|
||||
capture_ok = camera_file.save(img_path)
|
||||
except:
|
||||
print('File access error.')
|
||||
return False
|
||||
# ~ camera.exit()
|
||||
# Update frame
|
||||
frame = cv2.imread(img_path)
|
||||
# ~ frame = cv2.resize(frame, (project_settings['screen_w'], project_settings['screen_h']))
|
||||
return capture_ok
|
||||
|
||||
def apply_gphoto_setting(self, setting:str):
|
||||
# Get corresponding setting name if possible
|
||||
if setting in self.cam_settings_map:
|
||||
setting = self.cam_settings_map[setting]
|
||||
# Try to apply setting
|
||||
if setting in self.camera_current_settings:
|
||||
print(setting)
|
||||
select_setting = self.camera_current_settings[setting]
|
||||
# find the $setting config item
|
||||
try:
|
||||
# Get widget with name $setting
|
||||
cur_setting = self.gp.check_result(self.gp.gp_widget_get_child_by_name(self.camera_current_config, setting))
|
||||
# Get a list of available choices
|
||||
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
|
||||
# ~ 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 choices.index(new_value) in range(0, self.camera_current_settings[setting]['max']+1):
|
||||
# Apply or loop value accordingly
|
||||
# ~ pass
|
||||
# If new_value exists in list, apply
|
||||
if select_setting['value'] in choices_dict:
|
||||
cur_setting_choice = self.gp.check_result(self.gp.gp_widget_get_choice(cur_setting, select_setting['value']))
|
||||
# set config value
|
||||
self.gp.check_result(self.gp.gp_widget_set_value(cur_setting, cur_setting_choice))
|
||||
except:
|
||||
print("Configuration error while setting {} to {}".format(setting, select_setting))
|
||||
|
||||
def increment_setting(self, setting:str):
|
||||
if setting in self.camera_current_settings:
|
||||
if self.camera_current_settings[setting]['value']+1 in range(self.camera_current_settings[setting]['min'],self.camera_current_settings[setting]['max']+1):
|
||||
self.camera_current_settings[setting]['value'] += 1
|
||||
else:
|
||||
# TODO: execute v4l2 directly
|
||||
print(_("Camera function not supported."))
|
||||
return cam_settings
|
||||
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
|
||||
if to_set is None:
|
||||
for setting in self.camera_current_settings:
|
||||
if inc:
|
||||
self.increment_setting(setting)
|
||||
self.apply_gphoto_setting(setting)
|
||||
else:
|
||||
# Get corresponding setting name if possible
|
||||
for setting in to_set:
|
||||
if setting in self.cam_settings_map:
|
||||
setting = self.cam_settings_map[setting]
|
||||
if inc:
|
||||
self.increment_setting(setting)
|
||||
self.apply_gphoto_setting(setting)
|
||||
# validate config
|
||||
status = self.gp.check_result(self.gp.gp_camera_set_config(self.cam, self.camera_current_config))
|
||||
# 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:
|
||||
self.camera_current_settings[setting]['value'] = self.camera_current_settings[setting]['default']
|
||||
# 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()
|
||||
|
||||
def get_cam_class(camera_type):
|
||||
if camera_type == 'showmewebcam':
|
||||
return showmewebcam()
|
||||
elif camera_type == 'picam':
|
||||
return picam()
|
||||
elif camera_type == 'dslr':
|
||||
return dslr()
|
||||
elif camera_type == 'webcam':
|
||||
# ~ return webcam()
|
||||
pass
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def generate_text_image(text:str, screen_w, screen_h, bullets=False):
|
||||
|
@ -301,6 +690,19 @@ def get_frames_list(folder:str):
|
|||
return existing_animation_files
|
||||
|
||||
|
||||
def get_frame_by_idx(folder:str, index:int):
|
||||
# Refresh file list
|
||||
existing_animation_files = get_frames_list(folder)
|
||||
# Get last file
|
||||
# Filename pattern is A.0001.JPG
|
||||
if index and index in range(len(existing_animation_files)):
|
||||
frm = cv2.imread(os.path.join(folder, existing_animation_files[index]))
|
||||
frm = cv2.resize(frm, (project_settings['screen_w'], project_settings['screen_h']))
|
||||
return frm
|
||||
else:
|
||||
return generate_text_image(_("Image not found."), project_settings['screen_w'], project_settings['screen_h'])
|
||||
|
||||
|
||||
def get_last_frame(folder:str):
|
||||
# Refresh file list
|
||||
existing_animation_files = get_frames_list(folder)
|
||||
|
@ -308,6 +710,7 @@ def get_last_frame(folder:str):
|
|||
# Filename pattern is A.0001.JPG
|
||||
return existing_animation_files[-1].split('.')
|
||||
|
||||
|
||||
def get_before_last_frame(folder:str):
|
||||
# Refresh file list
|
||||
existing_animation_files = get_frames_list(folder)
|
||||
|
@ -316,13 +719,13 @@ def get_before_last_frame(folder:str):
|
|||
return existing_animation_files[-2]
|
||||
|
||||
|
||||
def get_onionskin_frame(folder:str, index=None):
|
||||
def get_onionskin_frame(folder:str):
|
||||
prev_image = get_last_frame(folder)
|
||||
prev_image = '.'.join(prev_image)
|
||||
if os.path.exists( os.path.expanduser(os.path.join(savepath, prev_image))):
|
||||
frm = cv2.imread(os.path.join(savepath, prev_image))
|
||||
frm = cv2.resize(frm, (project_settings['screen_w'], project_settings['screen_h']))
|
||||
# Imge does not exist, load blank image
|
||||
# Img does not exist, load blank image
|
||||
else:
|
||||
frm = blank_image
|
||||
return frm
|
||||
|
@ -345,6 +748,7 @@ def update_image(img_list, img_index):
|
|||
img = blank_image
|
||||
return img
|
||||
|
||||
|
||||
def next_frame(img_index, loop=True):
|
||||
img_index = check_range(img_index+1, loop)
|
||||
return img_index, update_image(img_list, img_index)
|
||||
|
@ -381,7 +785,7 @@ def check_range(x, loop=True):
|
|||
return len(img_list)-1
|
||||
else:
|
||||
return 0
|
||||
elif x >= len(img_list)-1:
|
||||
elif x > len(img_list)-1:
|
||||
if loop:
|
||||
return 0
|
||||
else:
|
||||
|
@ -472,78 +876,42 @@ def export_animation(input_filename, export_filename):
|
|||
# Fix for Quicktime
|
||||
'-pix_fmt', 'yuv420p',
|
||||
'-vcodec', 'libx264',
|
||||
# Visually lossless export
|
||||
'-crf', '18',
|
||||
export_filename,
|
||||
])
|
||||
return ffmpeg_process
|
||||
|
||||
|
||||
cam = get_cam_class(project_settings['cam_type'])
|
||||
|
||||
if cam is None:
|
||||
print(_("Wrong camera type in configuration."))
|
||||
time.sleep(1)
|
||||
sys.exit()
|
||||
|
||||
|
||||
def main(args):
|
||||
global img_list
|
||||
|
||||
global onionskin, liveview_only, playback, loop_playback, playhead, index, img_list, first_playback, camera_current_settings
|
||||
playback = False
|
||||
first_playback = True
|
||||
playhead = 0
|
||||
loop_playback = True
|
||||
index = len(img_list)-1
|
||||
playhead = index
|
||||
|
||||
# Initialise camera
|
||||
if project_settings['cam_type'] == "showmewebcam" or project_settings['cam_type'] == "webcam" :
|
||||
if not testDevice(0):
|
||||
print(_("No camera device found. Exiting..."))
|
||||
return 1
|
||||
cam = cv2.VideoCapture(0)
|
||||
cam.set(cv2.CAP_PROP_FRAME_WIDTH, camera_settings['cam_w'])
|
||||
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, camera_settings['cam_h'])
|
||||
elif project_settings['cam_type'] == "picam":
|
||||
# Pi Cam V3 setup
|
||||
from picamera2 import Picamera2
|
||||
from libcamera import Transform
|
||||
cam = Picamera2()
|
||||
picam_config = 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'])
|
||||
picam_config["transform"] = Transform(vflip=camera_current_settings['vertical_flip']['value'],hflip=camera_current_settings['horizontal_flip']['value'])
|
||||
cam.apply_setting()
|
||||
|
||||
cam.configure(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
|
||||
cam.set_controls({'AfMode':1})
|
||||
cam.start()
|
||||
cam.autofocus_cycle()
|
||||
cam_lenspos = cam.capture_metadata()['LensPosition']
|
||||
# Set focus, wb, exp to manual
|
||||
cam.set_controls({'AfMode': 0,
|
||||
'AwbEnable': 1,
|
||||
'AwbMode': camera_current_settings['white_balance_auto_preset']['default'],
|
||||
'AeEnable': 1,
|
||||
'AeExposureMode': 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)
|
||||
})
|
||||
# ~ cam.stop()
|
||||
elif project_settings['cam_type'] == "dslr":
|
||||
print(_("Applying DSLR settings..."))
|
||||
dslr_helper.apply_dslr_settings(camera_current_settings)
|
||||
else:
|
||||
print(_("No camera type was defined in config.toml"))
|
||||
|
||||
frame = get_onionskin_frame(savepath, index)
|
||||
|
||||
if project_settings['cam_type'] == "showmewebcam":
|
||||
# Apply config camera settings
|
||||
camera_current_settings = apply_cam_setting(camera_current_settings)
|
||||
time.sleep(.5)
|
||||
|
||||
if project_settings['cam_type'] == "dslr":
|
||||
onionskin = False
|
||||
frame = get_onionskin_frame(savepath)
|
||||
|
||||
loop_delta = 0
|
||||
while True:
|
||||
start = timer()
|
||||
if playback:
|
||||
if onionskin:
|
||||
onionskin = False
|
||||
onionskin_was_on = True
|
||||
if cam.onionskin:
|
||||
cam.onionskin = False
|
||||
cam.onionskin_was_on = True
|
||||
if first_playback:
|
||||
playhead = 0
|
||||
first_playback = False
|
||||
|
@ -562,46 +930,13 @@ def main(args):
|
|||
first_playback = True
|
||||
playback = False
|
||||
# Restore onionskin
|
||||
if 'onionskin_was_on' in locals():
|
||||
onionskin = True
|
||||
if cam.onionskin_was_on:
|
||||
cam.onionskin = True
|
||||
loop_playback = False
|
||||
|
||||
# If cam is DLSR, we have no liveview/onionskin
|
||||
if project_settings['cam_type'] != "dslr":
|
||||
if liveview_only:
|
||||
# ~ onionskin = False
|
||||
if project_settings['cam_type'] == "showmewebcam" or project_settings['cam_type'] == "webcam" :
|
||||
ret, overlay = cam.read()
|
||||
if not ret:
|
||||
print(_("Failed to grab frame."))
|
||||
break
|
||||
elif project_settings['cam_type'] == "picam":
|
||||
overlay = cam.capture_array("main")
|
||||
# Resize preview
|
||||
overlay = cv2.resize(overlay, (project_settings['screen_w'], project_settings['screen_h']))
|
||||
cv2.imshow("StopiCV", overlay)
|
||||
# ~ else:
|
||||
# ~ onionskin = True
|
||||
|
||||
if project_settings['cam_type'] != "dslr":
|
||||
if onionskin:
|
||||
if project_settings['cam_type'] == "showmewebcam":
|
||||
ret, overlay = cam.read()
|
||||
if not ret:
|
||||
print(_("Failed to grab frame."))
|
||||
break
|
||||
elif project_settings['cam_type'] == "picam":
|
||||
overlay = cam.capture_array("main")
|
||||
og_frame = overlay.copy()
|
||||
# Resize preview
|
||||
overlay = cv2.resize(overlay, (project_settings['screen_w'], project_settings['screen_h']))
|
||||
# Apply onionskin
|
||||
alpha = project_settings['onionskin_alpha_default']
|
||||
beta = (1.0 - alpha)
|
||||
overlay = cv2.addWeighted(frame, alpha, overlay, beta, 0)
|
||||
cv2.imshow("StopiCV", overlay)
|
||||
|
||||
if not playback and not onionskin and not liveview_only:
|
||||
if not playback:
|
||||
if cam.has_liveview:
|
||||
frame = cam.capture_preview()
|
||||
cv2.imshow("StopiCV", frame)
|
||||
|
||||
k = cv2.waitKey(1)
|
||||
|
@ -609,42 +944,26 @@ def main(args):
|
|||
if (k%256 == 108) or (k%256 == 53) or (k%256 == 181):
|
||||
print(_("Liveview only"))
|
||||
# Toggle liveview
|
||||
liveview_only = not liveview_only
|
||||
onionskin = not onionskin
|
||||
cam.liveview_only = not cam.liveview_only
|
||||
cam.onionskin = not cam.onionskin
|
||||
# Key o / kp slash
|
||||
elif (k%256 == 111) or (k%256 == 47) or (k%256 == 175):
|
||||
print(_("Onionskin toggle"))
|
||||
# Toggle onionskin
|
||||
onionskin = not onionskin
|
||||
liveview_only = False
|
||||
cam.onionskin = not cam.onionskin
|
||||
cam.liveview_only = False
|
||||
# Key w / 7 - cycle wb
|
||||
elif (k%256 == 119) or (k%256 == 55) or (k%256 == 183):
|
||||
print(_("White balance mode"))
|
||||
camera_current_settings = apply_cam_setting(camera_current_settings, ['white_balance_auto_preset'])
|
||||
if project_settings['cam_type'] == "picam":
|
||||
cam.set_controls({'AwbMode': camera_current_settings['white_balance_auto_preset']['value']})
|
||||
cam.apply_setting(['white_balance_auto_preset'], True)
|
||||
# Key x / 1 - cycle exposure
|
||||
elif (k%256 == 120) or (k%256 == 49) or (k%256 == 177):
|
||||
print(_("Exp. mode"))
|
||||
camera_current_settings = apply_cam_setting(camera_current_settings, ['auto_exposure'])
|
||||
# TODO : Move in function
|
||||
if project_settings['cam_type'] == "picam":
|
||||
print(camera_current_settings['auto_exposure']['value'])
|
||||
if camera_current_settings['auto_exposure']['value'] == 4:
|
||||
cam.set_controls({'AeEnable': 1})
|
||||
else:
|
||||
cam.set_controls({'AeEnable': 0})
|
||||
cam.set_controls({"AeExposureMode": camera_current_settings['auto_exposure']['value']})
|
||||
cam.apply_setting(['auto_exposure'], True)
|
||||
# Key f / 3 - flip image
|
||||
elif (k%256 == 102) or (k%256 == 51) or (k%256 == 179):
|
||||
print(_("Flip image"))
|
||||
camera_current_settings = apply_cam_setting(camera_current_settings, ['vertical_flip','horizontal_flip'])
|
||||
# TODO : Move in function
|
||||
if project_settings['cam_type'] == "picam":
|
||||
cam.stop()
|
||||
picam_config["transform"] = Transform(vflip=camera_current_settings['vertical_flip']['value'],hflip=camera_current_settings['horizontal_flip']['value'])
|
||||
cam.configure(picam_config)
|
||||
cam.start()
|
||||
cam.flip_image()
|
||||
# Key up, kp 8
|
||||
elif (k%256 == 82) or (k%256 == 56) or (k%256 == 184):
|
||||
print(_("Last frame"))
|
||||
|
@ -678,19 +997,7 @@ def main(args):
|
|||
# Key r / keypad 9 - reset wb,exp
|
||||
elif (k%256 == 114) or (k%256 == 57) or (k%256 == 185) :
|
||||
print(_("Reset camera settings"))
|
||||
camera_current_settings = apply_cam_setting(camera_current_settings)
|
||||
# TODO : This should be in a function ?
|
||||
if project_settings['cam_type'] == "picam":
|
||||
if camera_current_settings['auto_exposure']['default'] == 4:
|
||||
cam.set_controls({'AeEnable': 0})
|
||||
else:
|
||||
cam.set_controls({'AeEnable': 1})
|
||||
cam.set_controls({"AeExposureMode": camera_current_settings['auto_exposure']['default']})
|
||||
cam.set_controls({'AwbMode': camera_current_settings['white_balance_auto_preset']['default']})
|
||||
cam.stop()
|
||||
picam_config["transform"] = Transform(vflip=camera_current_settings['vertical_flip']['default'],hflip=camera_current_settings['horizontal_flip']['default'])
|
||||
cam.configure(picam_config)
|
||||
cam.start()
|
||||
cam.reset_picture_settings()
|
||||
# Key e / keypad *
|
||||
elif (k%256 == 101) or (k%256 == 42) or (k%256 == 170) :
|
||||
print(_("Export"))
|
||||
|
@ -707,56 +1014,37 @@ def main(args):
|
|||
print(_("Remove frame"))
|
||||
img_list, index, frame = remove_frame(img_list, index)
|
||||
# Focus +/- with a,z
|
||||
elif (k%256 == 97) and project_settings['cam_type'] == "picam":
|
||||
cam_lenspos += 0.2
|
||||
# Set AfMode to Manual
|
||||
cam.set_controls({'AfMode': 0, 'LensPosition': cam_lenspos})
|
||||
print(_("+Lens pos: {}".format(cam_lenspos)))
|
||||
elif (k%256 == 122) and project_settings['cam_type'] == "picam":
|
||||
cam_lenspos -= 0.2
|
||||
# Set AfMode to Manual
|
||||
cam.set_controls({'AfMode': 0, 'LensPosition': cam_lenspos})
|
||||
print(_("-Lens pos: {}".format(cam_lenspos)))
|
||||
elif (k%256 == 97):
|
||||
cam.focus('+')
|
||||
print(_("+Lens pos: {}".format(cam.lenspos)))
|
||||
elif (k%256 == 122):
|
||||
cam.focus('-')
|
||||
print(_("-Lens pos: {}".format(cam.lenspos)))
|
||||
# Set anti-flicker mode with q
|
||||
elif (k%256 == 113) and project_settings['cam_type'] == "picam":
|
||||
elif (k%256 == 113):
|
||||
print(_("Anti-flicker mode"))
|
||||
camera_current_settings = apply_cam_setting(camera_current_settings, ['anti_flicker'])
|
||||
# TODO : Move this to a function ?
|
||||
if camera_current_settings['anti_flicker']['value'] == 0:
|
||||
cam.set_controls({'AeFlickerMode': 0})
|
||||
elif camera_current_settings['anti_flicker']['value'] == 1:
|
||||
cam.set_controls({'AeFlickerMode': 1, 'AeFlickerPeriod':8333})
|
||||
else:
|
||||
cam.set_controls({'AeFlickerMode': 1, 'AeFlickerPeriod':10000})
|
||||
print(camera_current_settings['anti_flicker']['value'])
|
||||
cam.apply_setting(['anti_flicker'], True)
|
||||
# SPACE or numpad 0 pressed
|
||||
elif (k%256 == 32) or (k%256 == 48) or (k%256 == 176):
|
||||
print(_("Capture frame"))
|
||||
img_name = return_next_frame_number(get_last_frame(savepath))
|
||||
img_path = os.path.join(savepath, img_name)
|
||||
if project_settings['cam_type'] == 'dslr':
|
||||
# Get file from DSLR
|
||||
capture_ok = dslr_helper.capture_and_download(img_path)
|
||||
else:
|
||||
# Cam is either webcam, showmewebcam or picam
|
||||
if project_settings['file_extension'] == 'jpg':
|
||||
capture_ok = cv2.imwrite(img_path, og_frame, [int(cv2.IMWRITE_JPEG_QUALITY), project_settings['jpg_quality']])
|
||||
else:
|
||||
capture_ok = cv2.imwrite(img_path, og_frame)
|
||||
capture_ok = cam.capture_frame(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
|
||||
else:
|
||||
index += 1
|
||||
print(index)
|
||||
# Display a message if capture was not successfull
|
||||
if not capture_ok:
|
||||
if capture_ok is not None:
|
||||
frame = generate_text_image(_("Error during capture."),
|
||||
project_settings['screen_w'], project_settings['screen_h']
|
||||
)
|
||||
else:
|
||||
frame = get_onionskin_frame(savepath, index)
|
||||
frame = get_onionskin_frame(savepath)
|
||||
# ~ frame = cam.frame
|
||||
# Quit app
|
||||
elif k%256 == 27:
|
||||
# ESC pressed
|
||||
|
@ -791,12 +1079,7 @@ def main(args):
|
|||
except:
|
||||
print(_("Terminating running process..."))
|
||||
ffmpeg_process.terminate()
|
||||
if project_settings['cam_type'] == "showmewebcam":
|
||||
cam.release()
|
||||
elif project_settings['cam_type'] == "picam":
|
||||
cam.close()
|
||||
elif project_settings['cam_type'] == "dslr":
|
||||
pass
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
cv2.namedWindow("StopiCV", cv2.WINDOW_GUI_NORMAL)
|
||||
|
@ -811,10 +1094,6 @@ img_list = []
|
|||
savepath = get_session_folder()
|
||||
onionskin = project_settings['onion_skin_onstartup']
|
||||
liveview_only = False
|
||||
playback = False
|
||||
first_playback = True
|
||||
playhead = 0
|
||||
loop_playback = True
|
||||
blank_image = generate_text_image(_("No images yet! Start shooting..."), project_settings['screen_w'], project_settings['screen_h'])
|
||||
|
||||
if len(savepath):
|
||||
|
@ -827,7 +1106,6 @@ index = len(img_list)-1
|
|||
# Export settings
|
||||
input_filename = "{folder}{sep}{letter}.%04d.{ext}".format(folder=savepath, sep=os.sep, letter=project_letter, ext=project_settings['file_extension'])
|
||||
input_options = ["image2", str(project_settings['framerate'])]
|
||||
# ~ output_filename = "{folder}{sep}{filename}.mp4".format(folder=projects_folder, sep=os.sep, filename=savepath.split(os.sep)[-1])
|
||||
output_filename = "{filename}.mp4".format(filename=project_letter)
|
||||
output_options = project_settings['export_options']
|
||||
|
||||
|
|
Loading…
Reference in New Issue