Video export
This commit is contained in:
parent
bb5a8ceb15
commit
2582da088b
20
config.toml
20
config.toml
|
@ -1,30 +1,16 @@
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
# gphoto2 = 0, picam = 1, webcam = 2
|
#camera_type = 2
|
||||||
camera_type = 2
|
|
||||||
file_extension = 'png'
|
file_extension = 'png'
|
||||||
trigger_mode = 'key'
|
|
||||||
projects_folder = 'testcv/'
|
projects_folder = 'testcv/'
|
||||||
onion_skin_onstartup = true
|
onion_skin_onstartup = true
|
||||||
onionskin_alpha_default = 0.5
|
onionskin_alpha_default = 0.5
|
||||||
onionskin_fx = false
|
|
||||||
fullscreen_bool = true
|
fullscreen_bool = true
|
||||||
screen_w = 1920
|
screen_w = 1920
|
||||||
screen_h = 1080
|
screen_h = 1080
|
||||||
framerate = 16
|
framerate = 16
|
||||||
vflip = false
|
vflip = false
|
||||||
hflip = false
|
hflip = false
|
||||||
|
ffmpeg_path = '/usr/bin/ffmpeg'
|
||||||
export_options = 'scale=1920:-1,crop=1920:1080:0:102'
|
export_options = 'scale=1920:-1,crop=1920:1080:0:102'
|
||||||
cache_images = false
|
|
||||||
[CAMERA]
|
[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
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import collections
|
|
||||||
import cv2
|
import cv2
|
||||||
import gettext
|
import gettext
|
||||||
from itertools import count
|
from itertools import count
|
||||||
|
@ -6,8 +5,10 @@ import os
|
||||||
from send2trash import send2trash
|
from send2trash import send2trash
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
|
import subprocess
|
||||||
import time
|
import time
|
||||||
import tomllib
|
import tomllib
|
||||||
|
# TODO : remove tkinter dep
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import filedialog, messagebox
|
from tkinter import filedialog, messagebox
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -25,7 +26,7 @@ _ = gettext.translation('template', localedir='locales', languages=[LOCALE]).get
|
||||||
# defaults
|
# defaults
|
||||||
project_settings_defaults = {
|
project_settings_defaults = {
|
||||||
# gphoto2 = 0, picam = 1, webcam = 2
|
# gphoto2 = 0, picam = 1, webcam = 2
|
||||||
'camera_type': 0,
|
'camera_type': 2,
|
||||||
'file_extension':'JPG',
|
'file_extension':'JPG',
|
||||||
'trigger_mode': 'key',
|
'trigger_mode': 'key',
|
||||||
'projects_folder': '',
|
'projects_folder': '',
|
||||||
|
@ -39,6 +40,7 @@ project_settings_defaults = {
|
||||||
'framerate' : 16,
|
'framerate' : 16,
|
||||||
'vflip' : False,
|
'vflip' : False,
|
||||||
'hflip' : False,
|
'hflip' : False,
|
||||||
|
'ffmpeg_path' : None,
|
||||||
'export_options' : 'scale=1920:-1,crop=1920:1080:0:102',
|
'export_options' : 'scale=1920:-1,crop=1920:1080:0:102',
|
||||||
'cache_images' : False,
|
'cache_images' : False,
|
||||||
'liveview' : 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'))):
|
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:
|
with open(os.path.expanduser(location + 'config.toml'), 'rb') as config_file:
|
||||||
project_settings = tomllib.load(config_file)
|
project_settings = tomllib.load(config_file)
|
||||||
if 'CHECK' in project_settings:
|
|
||||||
camera_status = project_settings['CHECK']
|
|
||||||
if 'CAMERA' in project_settings:
|
if 'CAMERA' in project_settings:
|
||||||
camera_settings = project_settings['CAMERA']
|
camera_settings = project_settings['CAMERA']
|
||||||
if 'DEFAULT' in project_settings:
|
if 'DEFAULT' in project_settings:
|
||||||
|
@ -130,26 +130,16 @@ def get_session_folder():
|
||||||
|
|
||||||
def get_frames_list(folder:str):
|
def get_frames_list(folder:str):
|
||||||
# Get JPG files list in current directory
|
# Get JPG files list in current directory
|
||||||
# ~ existing_animation_files = []
|
|
||||||
# ~ if not len(img_list):
|
|
||||||
existing_animation_files = img_list
|
existing_animation_files = img_list
|
||||||
# ~ existing_animation_files =
|
|
||||||
file_list = os.listdir(folder)
|
file_list = os.listdir(folder)
|
||||||
for file in file_list:
|
for file in file_list:
|
||||||
if (file.startswith(project_letter) and file.endswith(project_settings['file_extension'])):
|
if (file.startswith(project_letter) and file.endswith(project_settings['file_extension'])):
|
||||||
if file not in existing_animation_files:
|
if file not in existing_animation_files:
|
||||||
existing_animation_files.append(file)
|
existing_animation_files.append(file)
|
||||||
# ~ existing_animation_files[file] = None
|
|
||||||
if len(existing_animation_files) == 0:
|
if len(existing_animation_files) == 0:
|
||||||
# If no images were found, return fake name set to -001 to init file count to 000
|
# 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(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.sort()
|
||||||
# ~ existing_animation_files = collections.OrderedDict(sorted(existing_animation_files.items()))
|
|
||||||
return existing_animation_files
|
return existing_animation_files
|
||||||
|
|
||||||
|
|
||||||
|
@ -159,8 +149,6 @@ def get_last_frame(folder:str):
|
||||||
# Get last file
|
# Get last file
|
||||||
# Filename pattern is A.0001.JPG
|
# Filename pattern is A.0001.JPG
|
||||||
return existing_animation_files[-1].split('.')
|
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):
|
def get_onionskin_frame(folder:str, index=None):
|
||||||
|
@ -322,6 +310,31 @@ def signal_handler(sig, frame):
|
||||||
ctrlc_pressed = True
|
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):
|
def main(args):
|
||||||
|
|
||||||
global onionskin, playback, loop_playback, playhead, index
|
global onionskin, playback, loop_playback, playhead, index
|
||||||
|
@ -363,7 +376,7 @@ def main(args):
|
||||||
og_frame = overlay
|
og_frame = overlay
|
||||||
# Resize preview
|
# Resize preview
|
||||||
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 overlay is not None:
|
# Apply onionskin
|
||||||
overlay = cv2.addWeighted(frame, 1.0, overlay, project_settings['onionskin_alpha_default'], 0)
|
overlay = cv2.addWeighted(frame, 1.0, overlay, project_settings['onionskin_alpha_default'], 0)
|
||||||
# Flip preview (0 = vert; 1 = hor)
|
# Flip preview (0 = vert; 1 = hor)
|
||||||
if project_settings['vflip'] or project_settings['hflip']:
|
if project_settings['vflip'] or project_settings['hflip']:
|
||||||
|
@ -373,7 +386,7 @@ def main(args):
|
||||||
flip_dir = 0
|
flip_dir = 0
|
||||||
elif project_settings['hflip']:
|
elif project_settings['hflip']:
|
||||||
flip_dir = 1
|
flip_dir = 1
|
||||||
frame = cv2.flip(overlay, flip_dir)
|
overlay = cv2.flip(overlay, flip_dir)
|
||||||
if not ret:
|
if not ret:
|
||||||
print(_("Failed to grab frame."))
|
print(_("Failed to grab frame."))
|
||||||
break
|
break
|
||||||
|
@ -406,11 +419,10 @@ def main(args):
|
||||||
print("R: {}".format(index))
|
print("R: {}".format(index))
|
||||||
# Key e / keypad *
|
# Key e / keypad *
|
||||||
elif (k%256 == 101) or (k%256 == 42):
|
elif (k%256 == 101) or (k%256 == 42):
|
||||||
# TODO : Export
|
|
||||||
print("Export")
|
print("Export")
|
||||||
|
ffmpeg_process = export_animation(input_filename, export_filename)
|
||||||
# Key Return
|
# Key Return
|
||||||
elif (k%256 == 13):
|
elif (k%256 == 13):
|
||||||
# TODO : Playback
|
|
||||||
print("Playback")
|
print("Playback")
|
||||||
playhead = index
|
playhead = index
|
||||||
loop_playback = True
|
loop_playback = True
|
||||||
|
@ -449,10 +461,17 @@ def main(args):
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
print(k) # else print its value
|
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()
|
cam.release()
|
||||||
cv2.destroyAllWindows()
|
cv2.destroyAllWindows()
|
||||||
|
|
||||||
|
|
||||||
ctrlc_pressed = False
|
ctrlc_pressed = False
|
||||||
projects_folder = project_settings['projects_folder']
|
projects_folder = project_settings['projects_folder']
|
||||||
img_list = []
|
img_list = []
|
||||||
|
@ -468,7 +487,17 @@ else:
|
||||||
project_letter = 'A'
|
project_letter = 'A'
|
||||||
img_list = get_frames_list(savepath)
|
img_list = get_frames_list(savepath)
|
||||||
index = len(img_list)-1
|
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__':
|
if __name__ == '__main__':
|
||||||
signal.signal(signal.SIGINT, signal_handler)
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
gphoto2
|
|
||||||
pillow
|
|
||||||
python-ffmpeg
|
|
||||||
Send2Trash
|
Send2Trash
|
||||||
opencv-python
|
opencv-python
|
||||||
numpy
|
numpy
|
||||||
|
tkinter
|
||||||
|
|
Loading…
Reference in New Issue