Init
This commit is contained in:
commit
bb5a8ceb15
|
@ -0,0 +1,30 @@
|
||||||
|
[DEFAULT]
|
||||||
|
# gphoto2 = 0, picam = 1, webcam = 2
|
||||||
|
camera_type = 2
|
||||||
|
file_extension = 'png'
|
||||||
|
trigger_mode = 'key'
|
||||||
|
projects_folder = 'testcv/'
|
||||||
|
onion_skin_onstartup = true
|
||||||
|
onionskin_alpha_default = 0.5
|
||||||
|
onionskin_fx = false
|
||||||
|
fullscreen_bool = true
|
||||||
|
screen_w = 1920
|
||||||
|
screen_h = 1080
|
||||||
|
framerate = 16
|
||||||
|
vflip = false
|
||||||
|
hflip = false
|
||||||
|
export_options = 'scale=1920:-1,crop=1920:1080:0:102'
|
||||||
|
cache_images = false
|
||||||
|
[CAMERA]
|
||||||
|
# Nikon D40x
|
||||||
|
# Add meter mode to center, focus mode to fixed selection
|
||||||
|
# /main/capturesettings/autofocusarea to 0
|
||||||
|
# /main/capturesettings/focusmetermode to 1
|
||||||
|
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
|
||||||
|
[CHECK]
|
||||||
|
acpower = 0 # we d'rather have this set to 0 which means we're running on AC
|
|
@ -0,0 +1,475 @@
|
||||||
|
import collections
|
||||||
|
import cv2
|
||||||
|
import gettext
|
||||||
|
from itertools import count
|
||||||
|
import os
|
||||||
|
from send2trash import send2trash
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import tomllib
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import filedialog, messagebox
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
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']
|
||||||
|
next_letter = 'A'
|
||||||
|
|
||||||
|
# l10n
|
||||||
|
LOCALE = os.getenv('LANG', 'en_EN')
|
||||||
|
_ = gettext.translation('template', localedir='locales', languages=[LOCALE]).gettext
|
||||||
|
|
||||||
|
# Config
|
||||||
|
# defaults
|
||||||
|
project_settings_defaults = {
|
||||||
|
# gphoto2 = 0, picam = 1, webcam = 2
|
||||||
|
'camera_type': 0,
|
||||||
|
'file_extension':'JPG',
|
||||||
|
'trigger_mode': 'key',
|
||||||
|
'projects_folder': '',
|
||||||
|
# ~ 'project_letter': 'A'
|
||||||
|
'onion_skin_onstartup' : False,
|
||||||
|
'onionskin_alpha_default' : 0.4,
|
||||||
|
'onionskin_fx' : False,
|
||||||
|
'fullscreen_bool' : True,
|
||||||
|
'screen_w' : 1920,
|
||||||
|
'screen_h' : 1080,
|
||||||
|
'framerate' : 16,
|
||||||
|
'vflip' : False,
|
||||||
|
'hflip' : False,
|
||||||
|
'export_options' : 'scale=1920:-1,crop=1920:1080:0:102',
|
||||||
|
'cache_images' : False,
|
||||||
|
'liveview' : False,
|
||||||
|
}
|
||||||
|
# Load from file
|
||||||
|
config_locations = ["./", "~/.", "~/.config/"]
|
||||||
|
config_found_msg = _("No configuration file found, using defaults.")
|
||||||
|
project_settings = project_settings_defaults
|
||||||
|
for location in config_locations:
|
||||||
|
# Optional config files, ~ is expanded to $HOME on *nix, %USERPROFILE% on windows
|
||||||
|
if os.path.exists( os.path.expanduser(os.path.join(location, 'config.toml'))):
|
||||||
|
with open(os.path.expanduser(location + 'config.toml'), 'rb') as config_file:
|
||||||
|
project_settings = tomllib.load(config_file)
|
||||||
|
if 'CHECK' in project_settings:
|
||||||
|
camera_status = project_settings['CHECK']
|
||||||
|
if 'CAMERA' in project_settings:
|
||||||
|
camera_settings = project_settings['CAMERA']
|
||||||
|
if 'DEFAULT' in project_settings:
|
||||||
|
project_settings = project_settings['DEFAULT']
|
||||||
|
config_found_msg = _("Found configuration file in {}").format(os.path.expanduser(location))
|
||||||
|
print(config_found_msg)
|
||||||
|
|
||||||
|
# Create blank image with 0s
|
||||||
|
blank_image = np.zeros((project_settings['screen_h'], project_settings['screen_w'],3), np.uint8)
|
||||||
|
# Set all pixels to light grey
|
||||||
|
blank_image[:,0:] = 200
|
||||||
|
font = cv2.FONT_HERSHEY_SIMPLEX
|
||||||
|
cv2.putText(blank_image,_("Pas d'image"),(int(project_settings['screen_w']/3),int(project_settings['screen_h']/2)), font, 4, (100, 100, 100), 8, cv2.LINE_AA)
|
||||||
|
|
||||||
|
def find_letter_after(letter:str):
|
||||||
|
if letter in alphabet and alphabet.index(letter) < len(alphabet) - 1:
|
||||||
|
return alphabet[alphabet.index(letter) + 1]
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_projects_folder():
|
||||||
|
if len(projects_folder):
|
||||||
|
project_folder = projects_folder
|
||||||
|
else:
|
||||||
|
# Get user folder
|
||||||
|
project_folder = os.path.expanduser('~')
|
||||||
|
# If a project folder is defined in settings, use it
|
||||||
|
if project_settings['projects_folder'] != '':
|
||||||
|
subfolder = project_settings['projects_folder']
|
||||||
|
else:
|
||||||
|
# If it doesn't exist, use a default name
|
||||||
|
subfolder = 'Stopmotion Projects'
|
||||||
|
project_folder = os.path.join(project_folder, subfolder)
|
||||||
|
# Create folder if it doesn't exist
|
||||||
|
if os.path.exists(project_folder) == False:
|
||||||
|
os.mkdir(project_folder)
|
||||||
|
else:
|
||||||
|
if not os.path.isdir(project_folder):
|
||||||
|
# If file exists but is not a folder, can't create it, abort
|
||||||
|
return False
|
||||||
|
return project_folder
|
||||||
|
|
||||||
|
|
||||||
|
def get_session_folder():
|
||||||
|
project_folder = get_projects_folder()
|
||||||
|
if project_folder:
|
||||||
|
sessions_list = []
|
||||||
|
dir_list = os.listdir(project_folder)
|
||||||
|
# Filter folders with name only one char long
|
||||||
|
for dir in dir_list:
|
||||||
|
if len(dir) == 1 and dir in alphabet:
|
||||||
|
sessions_list.append(dir)
|
||||||
|
# If folders exist, find last folder in alphabetical order
|
||||||
|
if len(sessions_list):
|
||||||
|
sessions_list.sort()
|
||||||
|
last_letter = sessions_list[-1]
|
||||||
|
# By default, find next letter for a new session
|
||||||
|
next_letter = find_letter_after(last_letter)
|
||||||
|
if next_letter is False:
|
||||||
|
return False
|
||||||
|
# A previous session folder was found; ask the user if they wish to resume session
|
||||||
|
resume_session = tk.messagebox.askyesno(_("Resume session?"), _("A previous session was found in {}, resume shooting ?").format(os.path.join(project_folder, last_letter)))
|
||||||
|
if resume_session:
|
||||||
|
next_letter = last_letter
|
||||||
|
else:
|
||||||
|
next_letter = 'A'
|
||||||
|
if os.path.exists(os.path.join(project_folder, next_letter)) is False:
|
||||||
|
os.mkdir(os.path.join(project_folder, next_letter))
|
||||||
|
print(_("Using {} as session folder.").format(os.path.join(project_folder, next_letter)))
|
||||||
|
return os.path.join(project_folder, next_letter)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_frames_list(folder:str):
|
||||||
|
# Get JPG files list in current directory
|
||||||
|
# ~ existing_animation_files = []
|
||||||
|
# ~ if not len(img_list):
|
||||||
|
existing_animation_files = img_list
|
||||||
|
# ~ existing_animation_files =
|
||||||
|
file_list = os.listdir(folder)
|
||||||
|
for file in file_list:
|
||||||
|
if (file.startswith(project_letter) and file.endswith(project_settings['file_extension'])):
|
||||||
|
if file not in existing_animation_files:
|
||||||
|
existing_animation_files.append(file)
|
||||||
|
# ~ existing_animation_files[file] = None
|
||||||
|
if len(existing_animation_files) == 0:
|
||||||
|
# If no images were found, return fake name set to -001 to init file count to 000
|
||||||
|
return ["{}.{:04d}.{}".format(next_letter, -1, project_settings['file_extension'])]
|
||||||
|
# ~ return {"{}.{:04d}.{}".format(project_letter, -1, project_settings['file_extension']):None}
|
||||||
|
# ~ else:
|
||||||
|
# Remove fake file name as soon as we have real pics
|
||||||
|
# ~ if 'A.-001.JPG' in existing_animation_files:
|
||||||
|
# ~ existing_animation_files.pop('A.-001.JPG')
|
||||||
|
existing_animation_files.sort()
|
||||||
|
# ~ existing_animation_files = collections.OrderedDict(sorted(existing_animation_files.items()))
|
||||||
|
return existing_animation_files
|
||||||
|
|
||||||
|
|
||||||
|
def get_last_frame(folder:str):
|
||||||
|
# Refresh file list
|
||||||
|
existing_animation_files = get_frames_list(folder)
|
||||||
|
# Get last file
|
||||||
|
# Filename pattern is A.0001.JPG
|
||||||
|
return existing_animation_files[-1].split('.')
|
||||||
|
# ~ print(next(reversed(existing_animation_files.keys())))
|
||||||
|
# ~ return next(reversed(existing_animation_files.keys())).split('.')
|
||||||
|
|
||||||
|
|
||||||
|
def get_onionskin_frame(folder:str, index=None):
|
||||||
|
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
|
||||||
|
else:
|
||||||
|
frm = blank_image
|
||||||
|
return frm
|
||||||
|
|
||||||
|
|
||||||
|
def return_next_frame_number(last_frame_name):
|
||||||
|
prefix, filecount, ext = last_frame_name
|
||||||
|
filename = '.{:04d}.'.format(int(filecount)+1)
|
||||||
|
# ~ filename = (".%04i." % x for x in count(int(filecount) + 1))
|
||||||
|
# ~ return prefix + next(filename) + ext
|
||||||
|
return prefix + filename + ext
|
||||||
|
|
||||||
|
|
||||||
|
def update_image(img_list, img_index):
|
||||||
|
if len(img_list) == 0:
|
||||||
|
return 0
|
||||||
|
img_filename = img_list[img_index]
|
||||||
|
if os.path.exists( os.path.expanduser(os.path.join(savepath, img_filename))):
|
||||||
|
img = cv2.imread(os.path.join(savepath, img_filename))
|
||||||
|
img = cv2.resize(img, (project_settings['screen_w'], project_settings['screen_h']))
|
||||||
|
else:
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
def previous_frame(img_index):
|
||||||
|
img_index = check_range(img_index-1)
|
||||||
|
return img_index, update_image(img_list, img_index)
|
||||||
|
|
||||||
|
|
||||||
|
def clean_img_list(folder_path):
|
||||||
|
# Check file in dict exists, else remove it
|
||||||
|
file_list = os.listdir(folder_path)
|
||||||
|
# Iterate over copy of dict to avoid OOR error
|
||||||
|
img_list_copy = img_list
|
||||||
|
for file in img_list_copy:
|
||||||
|
if file not in file_list:
|
||||||
|
img_list.remove(file)
|
||||||
|
|
||||||
|
|
||||||
|
def check_range(x, loop=True):
|
||||||
|
if x < 0:
|
||||||
|
if loop:
|
||||||
|
return len(img_list)-1
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
elif x >= len(img_list)-1:
|
||||||
|
if loop:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return len(img_list)-1
|
||||||
|
else:
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def batch_rename(folder:str):
|
||||||
|
# initialize counter to 0
|
||||||
|
frame_list = get_frames_list(folder)
|
||||||
|
counter = (".%04i." % x for x in count(0))
|
||||||
|
# ~ for i in range(len(frame_list)):
|
||||||
|
for i in frame_list:
|
||||||
|
# ~ if os.path.exists(os.path.realpath(frame_list[i])):
|
||||||
|
if os.path.exists(os.path.join(folder, i)):
|
||||||
|
# ~ os.rename(os.path.realpath(frame_list[i]), os.path.realpath("{}{}{}".format(project_settings['project_letter'], next(counter), project_settings['file_extension'])))
|
||||||
|
os.rename(os.path.join(folder, i), os.path.join(folder, "{}{}{}".format(project_letter, next(counter), project_settings['file_extension'])))
|
||||||
|
# ~ print(os.path.join(folder, "{}{}{}".format(project_letter, next(counter), project_settings['file_extension'])))
|
||||||
|
else:
|
||||||
|
print(_("{} does not exist").format(str(i)))
|
||||||
|
return get_frames_list(folder)
|
||||||
|
|
||||||
|
|
||||||
|
def offset_dictvalues(from_index=0):
|
||||||
|
dict_copy = dict(img_list)
|
||||||
|
for i in range(from_index, len(dict_copy)):
|
||||||
|
if i < len(img_list)-1:
|
||||||
|
img_list[list(img_list.keys())[i]] = list(img_list.values())[i+1]
|
||||||
|
else:
|
||||||
|
img_list[list(img_list.keys())[i]] = None
|
||||||
|
|
||||||
|
|
||||||
|
def remove_frame(img_list, img_index):
|
||||||
|
if len(img_list):
|
||||||
|
folder_path = os.path.realpath(savepath)
|
||||||
|
frame_name = img_list[img_index]
|
||||||
|
# ~ frame_path = os.path.realpath(frame_name)
|
||||||
|
frame_path = os.path.join(folder_path, frame_name)
|
||||||
|
if not os.path.exists(frame_path):
|
||||||
|
return img_index, blank_image
|
||||||
|
print(_("Removing {}").format(frame_path))
|
||||||
|
# trash file
|
||||||
|
send2trash(frame_path)
|
||||||
|
# remove entry from dict
|
||||||
|
img_list.remove(frame_name)
|
||||||
|
# offset cached images
|
||||||
|
# ~ offset_dictvalues(img_index)
|
||||||
|
# rename files and get new list
|
||||||
|
img_list = batch_rename(folder_path)
|
||||||
|
clean_img_list(folder_path)
|
||||||
|
# update index if possible
|
||||||
|
img_index = check_range(img_index, False)
|
||||||
|
# update display
|
||||||
|
return img_index, update_image(img_list, img_index)
|
||||||
|
else:
|
||||||
|
return 0, blank_image
|
||||||
|
|
||||||
|
def playback_animation(img_list, img_index):
|
||||||
|
# save onionskin state
|
||||||
|
if onionskin:
|
||||||
|
onionskin = False
|
||||||
|
onionskin_was_on = True
|
||||||
|
|
||||||
|
# ~ index = 0
|
||||||
|
# Play all frames
|
||||||
|
# ~ while index < len(img_list):
|
||||||
|
# ~ print(img_list[index])
|
||||||
|
# ~ img = update_image(img_list, index)
|
||||||
|
# ~ cv2.imshow("StopiCV", img)
|
||||||
|
# ~ time.sleep(.5)
|
||||||
|
# ~ index += 1
|
||||||
|
for file in img_list:
|
||||||
|
img = update_image(img_list, img_list.index(file))
|
||||||
|
print(str(img_list.index(file)) + " : " + file )
|
||||||
|
cv2.imshow("StopiCV", img)
|
||||||
|
time.sleep(.5)
|
||||||
|
# Restore previous frame
|
||||||
|
print(img_index)
|
||||||
|
frame_before_playback = update_image(img_list, img_index)
|
||||||
|
# ~ cv2.imshow("StopiCV", img)
|
||||||
|
# Restore onionskin
|
||||||
|
if 'onionskin_was_on' in locals():
|
||||||
|
onionskin = True
|
||||||
|
# Restore index
|
||||||
|
return img_index, frame_before_playback
|
||||||
|
|
||||||
|
def testDevice(source):
|
||||||
|
cap = cv2.VideoCapture(source)
|
||||||
|
if cap is None or not cap.isOpened():
|
||||||
|
print(_("Warning: unable to open video source: {}").format(source))
|
||||||
|
return False
|
||||||
|
cap.release()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def signal_handler(sig, frame):
|
||||||
|
global ctrlc_pressed
|
||||||
|
ctrlc_pressed = True
|
||||||
|
|
||||||
|
|
||||||
|
def main(args):
|
||||||
|
|
||||||
|
global onionskin, playback, loop_playback, playhead, index
|
||||||
|
|
||||||
|
if not testDevice(0):
|
||||||
|
print(_("No camera device found. Exiting..."))
|
||||||
|
return 1
|
||||||
|
cam = cv2.VideoCapture(0)
|
||||||
|
cam.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
|
||||||
|
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
|
||||||
|
|
||||||
|
cv2.namedWindow("StopiCV", cv2.WINDOW_GUI_NORMAL)
|
||||||
|
cv2.setWindowProperty("StopiCV", cv2.WND_PROP_OPENGL, cv2.WINDOW_OPENGL)
|
||||||
|
cv2.setWindowProperty("StopiCV", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
|
||||||
|
cv2.setWindowProperty("StopiCV", cv2.WND_PROP_ASPECT_RATIO, cv2.WINDOW_KEEPRATIO)
|
||||||
|
|
||||||
|
frame = get_onionskin_frame(savepath, index)
|
||||||
|
while True:
|
||||||
|
if playback:
|
||||||
|
if onionskin:
|
||||||
|
onionskin = False
|
||||||
|
onionskin_was_on = True
|
||||||
|
# Play all frames
|
||||||
|
if playhead < len(img_list)-1:
|
||||||
|
playhead, img = next_frame(playhead, loop_playback)
|
||||||
|
cv2.imshow("StopiCV", img)
|
||||||
|
time.sleep(1.0/project_settings['framerate'])
|
||||||
|
else:
|
||||||
|
playhead = index
|
||||||
|
img = update_image(img_list, index)
|
||||||
|
playback = False
|
||||||
|
# Restore onionskin
|
||||||
|
if 'onionskin_was_on' in locals():
|
||||||
|
onionskin = True
|
||||||
|
loop_playback = False
|
||||||
|
|
||||||
|
if onionskin:
|
||||||
|
ret, overlay = cam.read()
|
||||||
|
og_frame = overlay
|
||||||
|
# Resize preview
|
||||||
|
overlay = cv2.resize(overlay, (project_settings['screen_w'], project_settings['screen_h']))
|
||||||
|
# ~ if overlay is not None:
|
||||||
|
overlay = cv2.addWeighted(frame, 1.0, overlay, project_settings['onionskin_alpha_default'], 0)
|
||||||
|
# Flip preview (0 = vert; 1 = hor)
|
||||||
|
if project_settings['vflip'] or project_settings['hflip']:
|
||||||
|
if project_settings['vflip'] and project_settings['hflip']:
|
||||||
|
flip_dir = -1
|
||||||
|
elif project_settings['vflip']:
|
||||||
|
flip_dir = 0
|
||||||
|
elif project_settings['hflip']:
|
||||||
|
flip_dir = 1
|
||||||
|
frame = cv2.flip(overlay, flip_dir)
|
||||||
|
if not ret:
|
||||||
|
print(_("Failed to grab frame."))
|
||||||
|
break
|
||||||
|
cv2.imshow("StopiCV", overlay)
|
||||||
|
|
||||||
|
if not playback and not onionskin:
|
||||||
|
cv2.imshow("StopiCV", frame)
|
||||||
|
|
||||||
|
k = cv2.waitKey(1)
|
||||||
|
# Key o
|
||||||
|
if (k%256 == 111) or (k%256 == 47):
|
||||||
|
# Toggle onionskin
|
||||||
|
onionskin = not onionskin
|
||||||
|
# Key left
|
||||||
|
elif (k%256 == 81) or (k%256 == 52):
|
||||||
|
# Displau previous frame
|
||||||
|
print("Left")
|
||||||
|
if len(img_list):
|
||||||
|
if playback:
|
||||||
|
playback = False
|
||||||
|
index, frame = previous_frame(index)
|
||||||
|
print("L: {}".format(index))
|
||||||
|
elif (k%256 == 83) or (k%256 == 54):
|
||||||
|
# Displau next frame
|
||||||
|
print("Right")
|
||||||
|
if len(img_list):
|
||||||
|
if playback:
|
||||||
|
playback = False
|
||||||
|
index, frame = next_frame(index)
|
||||||
|
print("R: {}".format(index))
|
||||||
|
# Key e / keypad *
|
||||||
|
elif (k%256 == 101) or (k%256 == 42):
|
||||||
|
# TODO : Export
|
||||||
|
print("Export")
|
||||||
|
# Key Return
|
||||||
|
elif (k%256 == 13):
|
||||||
|
# TODO : Playback
|
||||||
|
print("Playback")
|
||||||
|
playhead = index
|
||||||
|
loop_playback = True
|
||||||
|
playback = not playback
|
||||||
|
# Key remove frame - backspace, del, numpad_minus
|
||||||
|
elif (k%256 == 8) or (k%256 == 45) or (k == 255):
|
||||||
|
# Remove frame
|
||||||
|
print("Remove frame")
|
||||||
|
index, frame = remove_frame(img_list, index)
|
||||||
|
# Quit app
|
||||||
|
elif k%256 == 27:
|
||||||
|
# ESC pressed
|
||||||
|
print(_("Escape hit, exiting..."))
|
||||||
|
break
|
||||||
|
elif ctrlc_pressed:
|
||||||
|
print(_("Ctrl-C hit, exiting..."))
|
||||||
|
break
|
||||||
|
elif cv2.getWindowProperty("StopiCV", cv2.WND_PROP_VISIBLE) < 1:
|
||||||
|
print(_("Window was closed, exiting..."))
|
||||||
|
break
|
||||||
|
# Take pic
|
||||||
|
elif (k%256 == 32) or (k%256 == 48):
|
||||||
|
# SPACE pressed
|
||||||
|
img_name = return_next_frame_number(get_last_frame(savepath))
|
||||||
|
img_path = os.path.join(savepath, img_name)
|
||||||
|
cv2.imwrite(img_path, og_frame)
|
||||||
|
print(_("File {} written.").format(img_path))
|
||||||
|
if len(img_list) and (img_list[index] == 'A.-001.png'):
|
||||||
|
img_list[index] = img_name
|
||||||
|
else:
|
||||||
|
index += 1
|
||||||
|
frame = get_onionskin_frame(savepath, index)
|
||||||
|
# TODO : go back to last frame ?
|
||||||
|
# REMOVE : Debug print keycode
|
||||||
|
elif k==-1: # normally -1 returned,so don't print it
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
print(k) # else print its value
|
||||||
|
cam.release()
|
||||||
|
cv2.destroyAllWindows()
|
||||||
|
|
||||||
|
|
||||||
|
ctrlc_pressed = False
|
||||||
|
projects_folder = project_settings['projects_folder']
|
||||||
|
img_list = []
|
||||||
|
savepath = get_session_folder()
|
||||||
|
onionskin = project_settings['onion_skin_onstartup']
|
||||||
|
playback = False
|
||||||
|
playhead = 0
|
||||||
|
loop_playback = True
|
||||||
|
|
||||||
|
if len(savepath):
|
||||||
|
project_letter = savepath.split(os.sep)[-1]
|
||||||
|
else:
|
||||||
|
project_letter = 'A'
|
||||||
|
img_list = get_frames_list(savepath)
|
||||||
|
index = len(img_list)-1
|
||||||
|
print(index)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
sys.exit(main(sys.argv[1:]))
|
Binary file not shown.
|
@ -0,0 +1,79 @@
|
||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR ORGANIZATION
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"POT-Creation-Date: 2024-02-18 11:30+0100\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Generated-By: pygettext.py 1.5\n"
|
||||||
|
|
||||||
|
|
||||||
|
#: ../main_c.py:87
|
||||||
|
msgid "Found configuration file in {}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:144
|
||||||
|
msgid "Warning: Some settings are not set to the recommended value!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:146
|
||||||
|
msgid "Camera not found or busy."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:256
|
||||||
|
msgid "A previous session was found in {}, resume shooting ?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:256
|
||||||
|
msgid "Resume session?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:263
|
||||||
|
msgid "Using {} as session folder."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:281
|
||||||
|
msgid "Image is being saved to {}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:336
|
||||||
|
msgid "{} does not exist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:361
|
||||||
|
msgid "Removing {}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:583
|
||||||
|
msgid "speed too high"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:596
|
||||||
|
msgid "Speed too low."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:624
|
||||||
|
msgid "Saving {}{}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:626
|
||||||
|
msgid "Getting file {}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:641
|
||||||
|
msgid "Ending thread"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:670
|
||||||
|
msgid "Exporting to {}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Mp4 files"
|
||||||
|
msgstr "Fichier Mp4"
|
Binary file not shown.
|
@ -0,0 +1,88 @@
|
||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR ORGANIZATION
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"POT-Creation-Date: 2024-02-18 11:30+0100\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Generated-By: pygettext.py 1.5\n"
|
||||||
|
|
||||||
|
#: ../main_c.py:96
|
||||||
|
msgid "No configuration file found, using defaults."
|
||||||
|
msgstr "Aucun fichier de configuration trouvé, utilisation des valeurs par défaut."
|
||||||
|
|
||||||
|
#: ../main_c.py:87
|
||||||
|
msgid "Found configuration file in {}"
|
||||||
|
msgstr "Fichier de configuration trouvé dans {}"
|
||||||
|
|
||||||
|
#: ../main_c.py:156
|
||||||
|
msgid "No images yet! Start shooting..."
|
||||||
|
msgstr "Aucune image! Commencez à tourner..."
|
||||||
|
|
||||||
|
#: ../main_c.py:144
|
||||||
|
msgid "Warning: Some settings are not set to the recommended value!"
|
||||||
|
msgstr "Attention: certains paramètres ne sont pas optimaux!"
|
||||||
|
|
||||||
|
#: ../main_c.py:146
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
"Camera not found or busy."
|
||||||
|
msgstr "\nCaméra introuvable ou occupée."
|
||||||
|
|
||||||
|
#: ../main_c.py:256
|
||||||
|
msgid "A previous session was found in {}, resume shooting ?"
|
||||||
|
msgstr "Une session précédente à été trouvée dans {}, reprendre la session ?"
|
||||||
|
|
||||||
|
#: ../main_c.py:256
|
||||||
|
msgid "Resume session?"
|
||||||
|
msgstr "Reprendre la session ?"
|
||||||
|
|
||||||
|
#: ../main_c.py:263
|
||||||
|
msgid "Using {} as session folder."
|
||||||
|
msgstr "Utilisation de {} comme dossier de session."
|
||||||
|
|
||||||
|
#: ../main_c.py:281
|
||||||
|
msgid "Image is being saved to {}"
|
||||||
|
msgstr "Image sauvegardée dans {}"
|
||||||
|
|
||||||
|
#: ../main_c.py:320
|
||||||
|
msgid "{} does not exist"
|
||||||
|
msgstr "{} n'existe pas."
|
||||||
|
|
||||||
|
#: ../main_c.py:345
|
||||||
|
msgid "Removing {}"
|
||||||
|
msgstr "Suppression de {}"
|
||||||
|
|
||||||
|
#: ../main_c.py:563
|
||||||
|
msgid "speed too high"
|
||||||
|
msgstr "Vitesse trop élevée."
|
||||||
|
|
||||||
|
#: ../main_c.py:576
|
||||||
|
msgid "Speed too low."
|
||||||
|
msgstr "Vitesse trop basse."
|
||||||
|
|
||||||
|
#: ../main_c.py:604
|
||||||
|
msgid "Saving {}{}"
|
||||||
|
msgstr "Enregistrement de {}{}"
|
||||||
|
|
||||||
|
#: ../main_c.py:606
|
||||||
|
msgid "Getting file {}"
|
||||||
|
msgstr "Récupération du fichier {}"
|
||||||
|
|
||||||
|
#: ../main_c.py:621
|
||||||
|
msgid "Ending thread"
|
||||||
|
msgstr "Terminaison du processus."
|
||||||
|
|
||||||
|
#: ../main_c.py:650
|
||||||
|
msgid "Exporting to {}"
|
||||||
|
msgstr "Exportation dans {}"
|
||||||
|
|
||||||
|
msgid "Mp4 files"
|
||||||
|
msgstr "Fichier Mp4"
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# /usr/lib/python3.11/Tools/i18n/pygettext.py -o template.pot ../app.py
|
||||||
|
#
|
||||||
|
# Change to script dir
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
for locale in */LC_MESSAGES/template.pot; do
|
||||||
|
/usr/bin/msgfmt -o ${locale%.*}.mo $locale
|
||||||
|
done
|
|
@ -0,0 +1,87 @@
|
||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR ORGANIZATION
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"POT-Creation-Date: 2024-02-19 18:47+0100\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Generated-By: pygettext.py 1.5\n"
|
||||||
|
|
||||||
|
|
||||||
|
#: ../main_c.py:96
|
||||||
|
msgid "No configuration file found, using defaults."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:109
|
||||||
|
msgid "Found configuration file in {}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:156
|
||||||
|
msgid "No images yet! Start shooting..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:165
|
||||||
|
msgid "Warning: Some settings are not set to the recommended value!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:167
|
||||||
|
msgid ""
|
||||||
|
"\n"
|
||||||
|
"Camera not found or busy."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:281
|
||||||
|
msgid "A previous session was found in {}, resume shooting ?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:281
|
||||||
|
msgid "Resume session?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:288
|
||||||
|
msgid "Using {} as session folder."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:306
|
||||||
|
msgid "Image is being saved to {}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:362
|
||||||
|
msgid "{} does not exist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:387
|
||||||
|
msgid "Removing {}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:607
|
||||||
|
msgid "speed too high"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:620
|
||||||
|
msgid "Speed too low."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:648
|
||||||
|
msgid "Saving {}{}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:650
|
||||||
|
msgid "Getting file {}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:665
|
||||||
|
msgid "Ending thread"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../main_c.py:694
|
||||||
|
msgid "Exporting to {}"
|
||||||
|
msgstr ""
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
gphoto2
|
||||||
|
pillow
|
||||||
|
python-ffmpeg
|
||||||
|
Send2Trash
|
||||||
|
opencv-python
|
||||||
|
numpy
|
Loading…
Reference in New Issue