235 lines
7.8 KiB
Python
235 lines
7.8 KiB
Python
import os
|
|
import re
|
|
import subprocess
|
|
import glob
|
|
import shutil
|
|
import time
|
|
import cv2
|
|
import numpy as np
|
|
|
|
|
|
def clamp(n, smallest, largest):
|
|
return sorted([smallest, n, largest])[1]
|
|
|
|
|
|
def create_movie_from_frames( dir, start, end, number_of_digits, fps, output_path, export_type):
|
|
def get_export_str(export_type):
|
|
if export_type == "mp4":
|
|
return " -vcodec libx264 -pix_fmt yuv420p "
|
|
elif export_type == "webm":
|
|
# return " -vcodec vp9 -crf 10 -b:v 0 "
|
|
return " -crf 40 -b:v 0 -threads 4 "
|
|
elif export_type == "gif":
|
|
return " "
|
|
elif export_type == "rawvideo":
|
|
return " -vcodec rawvideo -pix_fmt bgr24 "
|
|
|
|
vframes = end - start + 1
|
|
path = os.path.join(dir , '%0' + str(number_of_digits) + 'd.png')
|
|
|
|
# ffmpeg -r 10 -start_number n -i snapshot_%03d.png -vframes 50 example.gif
|
|
subprocess.call("ffmpeg -framerate " + str(fps) + " -r " + str(fps) +
|
|
" -start_number " + str(start) +
|
|
" -i " + path +
|
|
" -vframes " + str( vframes ) +
|
|
get_export_str(export_type) +
|
|
output_path, shell=True)
|
|
|
|
|
|
def search_out_dirs(proj_dir, blend_rate):
|
|
### create out_dirs
|
|
p = re.compile(r'.*[\\\/]out\-([0-9]+)[\\\/]')
|
|
|
|
number_of_digits = -1
|
|
|
|
out_dirs=[]
|
|
for d in glob.glob( os.path.join(proj_dir ,"out-*/"), recursive=False):
|
|
m = p.fullmatch(d)
|
|
if m:
|
|
if number_of_digits == -1:
|
|
number_of_digits = len(m.group(1))
|
|
out_dirs.append({ 'keyframe':int(m.group(1)), 'path':d })
|
|
|
|
out_dirs = sorted(out_dirs, key=lambda x: x['keyframe'], reverse=True)
|
|
|
|
print(number_of_digits)
|
|
|
|
prev_key = -1
|
|
for out_d in out_dirs:
|
|
out_d['next_keyframe'] = prev_key
|
|
prev_key = out_d['keyframe']
|
|
|
|
out_dirs = sorted(out_dirs, key=lambda x: x['keyframe'])
|
|
|
|
|
|
### search start/end frame
|
|
prev_key = 0
|
|
for out_d in out_dirs:
|
|
imgs = sorted(glob.glob( os.path.join( out_d['path'], '[0-9]'*number_of_digits + '.png') ))
|
|
|
|
first_img = imgs[0]
|
|
print(first_img)
|
|
basename_without_ext = os.path.splitext(os.path.basename(first_img))[0]
|
|
blend_timing = (prev_key - out_d['keyframe'])*blend_rate + out_d['keyframe']
|
|
blend_timing = round(blend_timing)
|
|
start_frame = max( blend_timing, int(basename_without_ext) )
|
|
out_d['startframe'] = start_frame
|
|
|
|
last_img = imgs[-1]
|
|
print(last_img)
|
|
basename_without_ext = os.path.splitext(os.path.basename(last_img))[0]
|
|
end_frame = min( out_d['next_keyframe'], int(basename_without_ext) )
|
|
if end_frame == -1:
|
|
end_frame = int(basename_without_ext)
|
|
out_d['endframe'] = end_frame
|
|
prev_key = out_d['keyframe']
|
|
|
|
return number_of_digits, out_dirs
|
|
|
|
def get_ext(export_type):
|
|
if export_type in ("mp4","webm","gif"):
|
|
return "." + export_type
|
|
else:
|
|
return ".avi"
|
|
|
|
def trying_to_add_audio(original_movie_path, no_snd_movie_path, output_path, tmp_dir ):
|
|
if os.path.isfile(original_movie_path):
|
|
sound_path = os.path.join(tmp_dir , 'sound.mp4')
|
|
subprocess.call("ffmpeg -i " + original_movie_path + " -vn -acodec copy " + sound_path, shell=True)
|
|
|
|
if os.path.isfile(sound_path):
|
|
# ffmpeg -i video.mp4 -i audio.wav -c:v copy -c:a aac -map 0:v:0 -map 1:a:0 output.mp4
|
|
|
|
subprocess.call("ffmpeg -i " + no_snd_movie_path + " -i " + sound_path + " -c:v copy -c:a aac -map 0:v:0 -map 1:a:0 " + output_path, shell=True)
|
|
return True
|
|
|
|
return False
|
|
|
|
def ebsynth_utility_stage7(dbg, project_args, blend_rate,export_type,is_invert_mask):
|
|
dbg.print("stage7")
|
|
dbg.print("")
|
|
|
|
project_dir, original_movie_path, _, _, _, _, _ = project_args
|
|
|
|
fps = 30
|
|
clip = cv2.VideoCapture(original_movie_path)
|
|
if clip:
|
|
fps = clip.get(cv2.CAP_PROP_FPS)
|
|
clip.release()
|
|
|
|
blend_rate = clamp(blend_rate, 0.0, 1.0)
|
|
|
|
dbg.print("blend_rate: {}".format(blend_rate))
|
|
dbg.print("export_type: {}".format(export_type))
|
|
dbg.print("fps: {}".format(fps))
|
|
|
|
if is_invert_mask:
|
|
project_dir = os.path.join( project_dir , "inv")
|
|
|
|
tmp_dir = os.path.join( project_dir , "crossfade_tmp")
|
|
|
|
|
|
if os.path.isdir(tmp_dir):
|
|
shutil.rmtree(tmp_dir)
|
|
os.mkdir(tmp_dir)
|
|
|
|
number_of_digits, out_dirs = search_out_dirs( project_dir, blend_rate )
|
|
|
|
if number_of_digits == -1:
|
|
dbg.print('no out dir')
|
|
return
|
|
|
|
### create frame imgs
|
|
|
|
start = out_dirs[0]['startframe']
|
|
end = out_dirs[-1]['endframe']
|
|
|
|
cur_clip = 0
|
|
next_clip = cur_clip+1 if len(out_dirs) > cur_clip+1 else -1
|
|
|
|
current_frame = 0
|
|
|
|
print(str(start) + " -> " + str(end+1))
|
|
|
|
black_img = np.zeros_like( cv2.imread( os.path.join(out_dirs[cur_clip]['path'], str(start).zfill(number_of_digits) + ".png") ) )
|
|
|
|
for i in range(start, end+1):
|
|
|
|
print(str(i) + " / " + str(end))
|
|
|
|
if next_clip == -1:
|
|
break
|
|
|
|
if i in range( out_dirs[cur_clip]['startframe'], out_dirs[cur_clip]['endframe'] +1):
|
|
pass
|
|
elif i in range( out_dirs[next_clip]['startframe'], out_dirs[next_clip]['endframe'] +1):
|
|
cur_clip = next_clip
|
|
next_clip = cur_clip+1 if len(out_dirs) > cur_clip+1 else -1
|
|
if next_clip == -1:
|
|
break
|
|
else:
|
|
### black
|
|
# front ... none
|
|
# back ... none
|
|
cv2.imwrite( os.path.join(tmp_dir, filename) , black_img)
|
|
current_frame = i
|
|
continue
|
|
|
|
filename = str(i).zfill(number_of_digits) + ".png"
|
|
|
|
# front ... cur_clip
|
|
# back ... next_clip or none
|
|
|
|
if i in range( out_dirs[next_clip]['startframe'], out_dirs[next_clip]['endframe'] +1):
|
|
# front ... cur_clip
|
|
# back ... next_clip
|
|
img_f = cv2.imread( os.path.join(out_dirs[cur_clip]['path'] , filename) )
|
|
img_b = cv2.imread( os.path.join(out_dirs[next_clip]['path'] , filename) )
|
|
|
|
back_rate = (i - out_dirs[next_clip]['startframe'])/ max( 1 , (out_dirs[cur_clip]['endframe'] - out_dirs[next_clip]['startframe']) )
|
|
|
|
img = cv2.addWeighted(img_f, 1.0 - back_rate, img_b, back_rate, 0)
|
|
|
|
cv2.imwrite( os.path.join(tmp_dir , filename) , img)
|
|
else:
|
|
# front ... cur_clip
|
|
# back ... none
|
|
filename = str(i).zfill(number_of_digits) + ".png"
|
|
shutil.copy( os.path.join(out_dirs[cur_clip]['path'] , filename) , os.path.join(tmp_dir , filename) )
|
|
|
|
current_frame = i
|
|
|
|
|
|
start2 = current_frame+1
|
|
|
|
print(str(start2) + " -> " + str(end+1))
|
|
|
|
for i in range(start2, end+1):
|
|
filename = str(i).zfill(number_of_digits) + ".png"
|
|
shutil.copy( os.path.join(out_dirs[cur_clip]['path'] , filename) , os.path.join(tmp_dir , filename) )
|
|
|
|
### create movie
|
|
movie_base_name = time.strftime("%Y%m%d-%H%M%S")
|
|
if is_invert_mask:
|
|
movie_base_name = "inv_" + movie_base_name
|
|
|
|
nosnd_path = os.path.join(project_dir , movie_base_name + get_ext(export_type))
|
|
|
|
start = out_dirs[0]['startframe']
|
|
end = out_dirs[-1]['endframe']
|
|
|
|
create_movie_from_frames( tmp_dir, start, end, number_of_digits, fps, nosnd_path, export_type)
|
|
|
|
dbg.print("exported : " + nosnd_path)
|
|
|
|
if export_type == "mp4":
|
|
|
|
with_snd_path = os.path.join(project_dir , movie_base_name + '_with_snd.mp4')
|
|
|
|
if trying_to_add_audio(original_movie_path, nosnd_path, with_snd_path, tmp_dir):
|
|
dbg.print("exported : " + with_snd_path)
|
|
|
|
dbg.print("")
|
|
dbg.print("completed.")
|
|
|