diff --git a/config.toml b/config.toml index db49cda..e7d2d08 100644 --- a/config.toml +++ b/config.toml @@ -1,30 +1,16 @@ [DEFAULT] -# gphoto2 = 0, picam = 1, webcam = 2 -camera_type = 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 +ffmpeg_path = '/usr/bin/ffmpeg' 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 \ No newline at end of file + diff --git a/frame_opencv.py b/frame_opencv.py index 1a6b414..5e91072 100644 --- a/frame_opencv.py +++ b/frame_opencv.py @@ -1,4 +1,3 @@ -import collections import cv2 import gettext from itertools import count @@ -6,8 +5,10 @@ import os from send2trash import send2trash import signal import sys +import subprocess import time import tomllib +# TODO : remove tkinter dep import tkinter as tk from tkinter import filedialog, messagebox import numpy as np @@ -25,7 +26,7 @@ _ = gettext.translation('template', localedir='locales', languages=[LOCALE]).get # defaults project_settings_defaults = { # gphoto2 = 0, picam = 1, webcam = 2 - 'camera_type': 0, + 'camera_type': 2, 'file_extension':'JPG', 'trigger_mode': 'key', 'projects_folder': '', @@ -39,6 +40,7 @@ project_settings_defaults = { 'framerate' : 16, 'vflip' : False, 'hflip' : False, + 'ffmpeg_path' : None, 'export_options' : 'scale=1920:-1,crop=1920:1080:0:102', 'cache_images' : False, 'liveview' : False, @@ -52,8 +54,6 @@ for location in config_locations: 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: @@ -130,26 +130,16 @@ def get_session_folder(): 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 @@ -159,8 +149,6 @@ def get_last_frame(folder:str): # 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): @@ -322,6 +310,31 @@ def signal_handler(sig, frame): ctrlc_pressed = True +def parse_export_options(options:str, vflip:bool=False, hflip:bool=False): + if vflip: + options += ',vflip' + if hflip: + options += ',hflip' + return options + + +def export_animation(input_filename, export_filename): + input_format, framerate = input_options + if project_settings['ffmpeg_path'] is None: + return False + ffmpeg_process = subprocess.Popen([ + project_settings['ffmpeg_path'], + '-v','quiet', + '-y', + '-f', input_format, + '-r', framerate, + '-i', input_filename, + '-vf', output_options, + export_filename, + ]) + return ffmpeg_process + + def main(args): global onionskin, playback, loop_playback, playhead, index @@ -363,7 +376,7 @@ def main(args): og_frame = overlay # Resize preview overlay = cv2.resize(overlay, (project_settings['screen_w'], project_settings['screen_h'])) - # ~ if overlay is not None: + # Apply onionskin 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']: @@ -373,7 +386,7 @@ def main(args): flip_dir = 0 elif project_settings['hflip']: flip_dir = 1 - frame = cv2.flip(overlay, flip_dir) + overlay = cv2.flip(overlay, flip_dir) if not ret: print(_("Failed to grab frame.")) break @@ -406,11 +419,10 @@ def main(args): print("R: {}".format(index)) # Key e / keypad * elif (k%256 == 101) or (k%256 == 42): - # TODO : Export print("Export") + ffmpeg_process = export_animation(input_filename, export_filename) # Key Return elif (k%256 == 13): - # TODO : Playback print("Playback") playhead = index loop_playback = True @@ -449,10 +461,17 @@ def main(args): continue else: print(k) # else print its value + if 'ffmpeg_process' in locals(): + if ffmpeg_process.poll() is None: + print("Ffmpeg is still running. Waiting for task to complete.") + try: + ffmpeg_process.wait(timeout=1) + except: + print("Terminating running process...") + ffmpeg_process.terminate() cam.release() cv2.destroyAllWindows() - ctrlc_pressed = False projects_folder = project_settings['projects_folder'] img_list = [] @@ -468,7 +487,17 @@ else: project_letter = 'A' img_list = get_frames_list(savepath) index = len(img_list)-1 -print(index) +print(project_letter) + +# Export settings +input_filename = "{folder}{letter}{sep}{letter}.%04d.{ext}".format(folder=projects_folder, sep=os.sep, letter=project_letter, ext=project_settings['file_extension']) +print(input_filename) +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 = parse_export_options(project_settings['export_options'], project_settings['vflip'], project_settings['hflip'] ) + +export_filename = os.path.join(savepath, output_filename) if __name__ == '__main__': signal.signal(signal.SIGINT, signal_handler) diff --git a/requirements.txt b/requirements.txt index 165ca57..71e4ffe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,4 @@ -gphoto2 -pillow -python-ffmpeg Send2Trash opencv-python numpy +tkinter