Skip to content

Instantly share code, notes, and snippets.

@parthp08
Last active May 28, 2020 01:48
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save parthp08/3605737aae5ba7fa33ce626501b26be0 to your computer and use it in GitHub Desktop.
import subprocess
from PIL import ImageFont
import random
import os
import shutil
os.getcwd()
def fit_text(string: str, frame_width, translation_font):
split_line = [x.strip() for x in string.split()]
lines = ""
w = 0
line_num = 0
line = ""
for word in split_line:
# Make a test
w, _ = translation_font.getsize(" ".join([line, word]))
# If it exceeds the frame width, add a new line
if w > (frame_width - (2 * 6)): # Leave 6px margin on each side
lines += line.strip() + "\n"
line = ""
line += word + " "
lines += line.strip() # Append leftover words
return lines.split("\n")
def lines2str(lines_list, duration_per_img, fontsize, fontcolor, fontfamily_path, text_animation):
a = ""
i = 0
if text_animation[1] == "d":
if len(lines_list) > 1:
i = -fontsize * (len(lines_list)-1)
delta_t = 0
for line in lines_list:
if text_animation == "fio":
# fade in fade out -- without background
time_display = (duration_per_img-5) # 5 -- 1 second to load, 2 to fade in and 2 to fade out
anim = f":alpha=\'if(lt(t,1),0,if(lt(t,3),(t-1)/2,if(lt(t,3+{time_display}),1,if(lt(t,5+{time_display}),(2-(t-(3+{time_display})))/2,0))))\'"
a += f"drawtext=fontsize={fontsize}" + ":fontcolor=" + fontcolor + ":fontfile=" + fontfamily_path + f":text={line}" + anim + f" :x=(w-text_w)/2:y=((h-text_h)/2)+{i},"
elif text_animation == "fiob":
## fade in fade out -- with background
time_display = (duration_per_img-5) # 5 -- 1 second to load, 2 to fade in and 2 to fade out
anim = f":alpha=\'if(lt(t,1),0,if(lt(t,3),(t-1)/2,if(lt(t,3+{time_display}),1,if(lt(t,5+{time_display}),(2-(t-(3+{time_display})))/2,0))))\'"
a += f"drawtext=fontsize={fontsize}" + " :shadowcolor=black@0.6:shadowx=0:shadowy=0 :box=1: boxcolor=black@0.4:boxborderw=1" + ":fontcolor=" + fontcolor + ":fontfile=" + fontfamily_path + f":text={line}" + anim + f" :x=(w-text_w)/2:y=((h-text_h)/2)+{i},"
elif text_animation == "ld":
# ld -- coming from left -- without background
time_display = (duration_per_img-5) # 5 -- 1 second to load, 2 to fade in and 2 to fade out
anim = f":alpha=\'if(lt(t,0),0,if(lt(t,0),(t-1)/2,if(lt(t,3+{time_display}),1,if(lt(t,5+{time_display}),(2-(t-(3+{time_display})))/2,0))))\'"
a += f"drawtext=fontsize={fontsize}" + ":fontcolor=" + fontcolor + ":fontfile=" + fontfamily_path + f":text={line}" + anim + f":x=if(lt(t\,1)\,(-w+((3*w-tw)*t/2.5))\,40) :y=h-80-(text_h)+{i}:enable='between(t,0.5,50)',"
elif text_animation == "ldb":
# ld -- coming from left -- with background
time_display = (duration_per_img-5) # 5 -- 1 second to load, 2 to fade in and 2 to fade out
anim = f":alpha=\'if(lt(t,0),0,if(lt(t,0),(t-1)/2,if(lt(t,3+{time_display}),1,if(lt(t,5+{time_display}),(2-(t-(3+{time_display})))/2,0))))\'"
a += f"drawtext=fontsize={fontsize}" + " :shadowcolor=black@0.6:shadowx=0:shadowy=0 :box=1: boxcolor=black@0.4:boxborderw=1" + ":fontcolor=" + fontcolor + ":fontfile=" + fontfamily_path + f":text={line}" + anim + f":x=if(lt(t\,1)\,(-w+((3*w-tw)*t/2.5))\,40) :y=h-80-(text_h)+{i}:enable='between(t,0.5,50)',"
elif text_animation == "lu":
# lu -- coming from left -- without background
time_display = (duration_per_img-5) # 5 -- 1 second to load, 2 to fade in and 2 to fade out
anim = f":alpha=\'if(lt(t,0),0,if(lt(t,0),(t-1)/2,if(lt(t,3+{time_display}),1,if(lt(t,5+{time_display}),(2-(t-(3+{time_display})))/2,0))))\'"
a += f"drawtext=fontsize={fontsize}" + ":fontcolor=" + fontcolor + ":fontfile=" + fontfamily_path + f":text={line}" + anim + f":x=if(lt(t\,1)\,(-w+((3*w-tw)*t/2.5))\,40) :y=40+{i}:enable='between(t,0.5,50)',"
elif text_animation == "lub":
# lu -- coming from left -- with background
time_display = (duration_per_img-5) # 5 -- 1 second to load, 2 to fade in and 2 to fade out
anim = f":alpha=\'if(lt(t,0),0,if(lt(t,0),(t-1)/2,if(lt(t,3+{time_display}),1,if(lt(t,5+{time_display}),(2-(t-(3+{time_display})))/2,0))))\'"
a += f"drawtext=fontsize={fontsize}" + " :shadowcolor=black@0.6:shadowx=0:shadowy=0 :box=1: boxcolor=black@0.4:boxborderw=1" + ":fontcolor=" + fontcolor + ":fontfile=" + fontfamily_path + f":text={line}" + anim + f":x=if(lt(t\,1)\,(-w+((3*w-tw)*t/2.5))\,40) :y=40+{i}:enable='between(t,0.5,50)',"
elif text_animation == "rd":
# rd -- coming from bottom -- without background
time_display = (duration_per_img-5) # 5 -- 1 second to load, 2 to fade in and 2 to fade out
anim = f":alpha=\'if(lt(t,0),0,if(lt(t,0),(t-1)/2,if(lt(t,3+{time_display}),1,if(lt(t,5+{time_display}),(2-(t-(3+{time_display})))/2,0))))\'"
a += f"drawtext=fontsize={fontsize}" + ":fontcolor=" + fontcolor + ":fontfile=" + fontfamily_path + f":text={line}" + anim + f":x=if(lt(t\,1)\,(2*w-((3*w+tw)*t/2.6))\,w-40-(text_w)) :y=h-80-(text_h)+{i}:enable='between(t,0.5,50)',"
elif text_animation == "rdb":
# rd -- coming from right -- with background
time_display = (duration_per_img-5) # 5 -- 1 second to load, 2 to fade in and 2 to fade out
anim = f":alpha=\'if(lt(t,0),0,if(lt(t,0),(t-1)/2,if(lt(t,3+{time_display}),1,if(lt(t,5+{time_display}),(2-(t-(3+{time_display})))/2,0))))\'"
a += f"drawtext=fontsize={fontsize}" + " :shadowcolor=black@0.6:shadowx=0:shadowy=0 :box=1: boxcolor=black@0.4:boxborderw=1" + ":fontcolor=" + fontcolor + ":fontfile=" + fontfamily_path + f":text={line}" + anim + f":x=if(lt(t\,1)\,(2*w-((3*w+tw)*t/2.6))\,w-40-(text_w)) :y=h-80-(text_h)+{i}:enable='between(t,0.5,50)',"
elif text_animation == "ru":
# ru -- coming from right -- without background
time_display = (duration_per_img-5) # 5 -- 1 second to load, 2 to fade in and 2 to fade out
anim = f":alpha=\'if(lt(t,0),0,if(lt(t,0),(t-1)/2,if(lt(t,3+{time_display}),1,if(lt(t,5+{time_display}),(2-(t-(3+{time_display})))/2,0))))\'"
a += f"drawtext=fontsize={fontsize}" + ":fontcolor=" + fontcolor + ":fontfile=" + fontfamily_path + f":text={line}" + anim + f":x=if(lt(t\,1)\,(2*w-((3*w+tw)*t/2.6))\,w-40-(text_w)) :y=40+(text_h)+{i}:enable='between(t,0.5,50)',"
elif text_animation == "rub":
# ru -- coming from right -- with background
time_display = (duration_per_img-5) # 5 -- 1 second to load, 2 to fade in and 2 to fade out
anim = f":alpha=\'if(lt(t,0),0,if(lt(t,0),(t-1)/2,if(lt(t,3+{time_display}),1,if(lt(t,5+{time_display}),(2-(t-(3+{time_display})))/2,0))))\'"
a += f"drawtext=fontsize={fontsize}" + " :shadowcolor=black@0.6:shadowx=0:shadowy=0 :box=1: boxcolor=black@0.4:boxborderw=1" + ":fontcolor=" + fontcolor + ":fontfile=" + fontfamily_path + f":text={line}" + anim + f":x=if(lt(t\,1)\,(2*w-((3*w+tw)*t/2.6))\,w-40-(text_w)) :y=40+(text_h)+{i}:enable='between(t,0.5,50)',"
elif text_animation == "blnk":
## blink
time_display = (duration_per_img-5) # 5 -- 1 second to load, 2 to fade in and 2 to fade out
anim = f":alpha=\'if(lt(t,0),0,if(lt(t,0),(t-1)/2,if(lt(t,3+{time_display}),1,if(lt(t,5+{time_display}),(2-(t-(3+{time_display})))/2,0))))\'"
a += f"drawtext=fontsize={fontsize}" + ":fontcolor=" + fontcolor + ":fontfile=" + fontfamily_path + f":text={line}" + anim + f":x=(w-text_w)/2:y=((h-text_h)/2)+{i}:enable=if(lt(t\,5)\, lt(mod(t\,1)\,0.5)\, lt(mod(t\,1)\,1)) ,"
elif text_animation == "rand":
# random location
time_display = (duration_per_img-5) # 5 -- 1 second to load, 2 to fade in and 2 to fade out
anim = f":alpha=\'if(lt(t,1),0,if(lt(t,3),(t-1)/2,if(lt(t,3+{time_display}),1,if(lt(t,5+{time_display}),(2-(t-(3+{time_display})))/2,0))))\'"
a += f"drawtext=fontsize={fontsize}" + ":fontcolor=" + fontcolor + ":fontfile=" + fontfamily_path + f":text={line}" + anim + f":x=if(eq(mod(t\,2)\,0+{i})\,rand(0\,(w-text_w))\,x):y=if(eq(mod(t\,2)\,0)\,rand(0\,(h-text_h))\,y) ,"
i += fontsize
return a[:-1]
def text2str(text_str, duration_per_img, fontsize, fontcolor, fontfamily_path, width, text_animation):
translation_font = ImageFont.truetype(fontfamily_path, size=fontsize, encoding="unic")
list_ = fit_text(text_str, width, translation_font)
str_list = lines2str(list_, duration_per_img, fontsize, fontcolor, fontfamily_path, text_animation)
return str_list
def single_img(img_path, text_str, duration_per_img, fontsize, fontcolor, fontfamily_path, text_animation, img_num):
img_num = str(img_num)
cmd1 = f"ffmpeg -y -hide_banner -loglevel panic -r 1/{duration_per_img} -i {img_path} -c:v libx264 -vf fps=25 -pix_fmt yuv420p ./temp/vid{img_num}_1.mp4"
subprocess.run(cmd1, shell=True)
x_loc_list = [1280, 1280, 1280/2, 0, 0, 1280/2, 1280/2, 0, 1280/2]
y_loc_list = [720, 0, 720/2, 720, 0, 0, 720, 720/2, 720/2]
loc = random.randint(0,len(x_loc_list)-1)
pan_list = [100, 200, 300, 400, 500, 600]
pan = random.randint(0,len(pan_list)-1)
pan = pan_list[pan]
zoom = f"zoompan=z='1+(0.1*in/{pan})':x='{x_loc_list[loc]}*in/{pan}':y='{y_loc_list[loc]}*in/{pan}'"
cmd2 = f"ffmpeg -y -hide_banner -loglevel panic -i ./temp/vid{img_num}_1.mp4 -vf \"scale=8000:-1,{zoom}:d=1\" ./temp/vid{img_num}_2.mp4"
subprocess.run(cmd2, shell=True)
if text_animation[0] == "f":
resolution = 1280/1.1
else:
resolution = 1280/1.7
text_data = text2str(text_str, duration_per_img, fontsize, fontcolor, fontfamily_path, resolution, text_animation)
cmd3 = f"ffmpeg -y -hide_banner -loglevel panic -i ./temp/vid{img_num}_2.mp4 -filter_complex \"[0:v]{text_data}\" ./temp2/vid{img_num}_3.mp4"
subprocess.run(cmd3, shell=True)
def merge(vid_folder_path):
cmd6 = "ffmpeg -y -hide_banner -loglevel panic -i \"concat:"
files_len = len(os.listdir(vid_folder_path))
for i,filename in enumerate(os.listdir(vid_folder_path)):
cmd = f"ffmpeg -y -hide_banner -loglevel panic -i {vid_folder_path}/vid{i}_3.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts {vid_folder_path}/intermediate{i}.ts"
subprocess.run(cmd, shell=True)
cmd6 += f"{vid_folder_path}/intermediate{i}.ts" + "|"
cmd6 = cmd6[:-1] + "\" -c copy -bsf:a aac_adtstoasc output.mp4"
subprocess.run(cmd6, shell=True)
def list_from_text_file(text_file_path):
pass # return text_list
def main(img_folder, text_file_path, duration_per_img, fontsize, fontcolor, fontfamily_path, audio_file_path=None, text_animation_list=None, debug=False):
# text_list = list_from_text_file(text_file_path)
text_list = ["The Irene Method is one effective way to treat visual perception disorders.0", # replace this with file
"The Irene Method is one effective way to treat visual perception disorders.1",
"The Irene Method is one effective way to treat visual perception disorders.2",
"The Irene Method is one effective way to treat visual perception disorders.3",
"The Irene Method is one effective way to treat visual perception disorders.4",
"The Irene Method is one effective way to treat visual perception disorders.5",
"The Irene Method is one effective way to treat visual perception disorders.6",
"The Irene Method is one effective way to treat visual perception disorders.7",
"The Irene Method is one effective way to treat visual perception disorders.8",
]
assert len(text_list) >= len(os.listdir(img_folder)), "make sure each image have its own text defined"
txt_anim_list = ['fio', 'fiob', 'ld', 'ldb', 'lu', 'lub', 'rd', 'rdb', 'ru', 'rub', 'blnk'] #, 'rand']
if not os.path.exists("./temp"):
os.mkdir("./temp")
if not os.path.exists("./temp2"):
os.mkdir("./temp2")
if not os.path.exists("./output"):
os.mkdir("./output")
for i, filename in enumerate(os.listdir(img_folder)):
if debug: # only run once
text_animation = "fio"
img_path = img_folder + "/" + filename
single_img(img_path, text_list[i], duration_per_img, fontsize, fontcolor, fontfamily_path, text_animation, i)
break
if text_animation_list == None: # choose random animation if not provided
if i == 0: # first img
text_animation = txt_anim_list[0]
elif i == len(os.listdir(img_folder))-1: # last image
text_animation = txt_anim_list[1]
else:
val = random.randint(0,len(txt_anim_list)-1)
text_animation = txt_anim_list[val]
img_path = img_folder + "/" + filename
single_img(img_path, text_list[i], duration_per_img, fontsize, fontcolor, fontfamily_path, text_animation, i)
else: # if custom defined animation
assert len(text_animation_list) == len(os.listdir(img_folder)), "make sure each image have its own animation defined"
img_path = img_folder + "/" + filename
single_img(img_path, text_list[i], duration_per_img, fontsize, fontcolor, fontfamily_path, text_animation_list[i], i)
merge("./temp2")
if audio_file_path:
cmd = f"ffmpeg -y -hide_banner -loglevel panic -i output.mp4 -i {audio_file_path} -c:v copy -c:a aac -shortest ./output/output.mp4"
subprocess.run(cmd, shell=True)
os.remove("output.mp4")
shutil.rmtree("./temp")
shutil.rmtree("./temp2")
if __name__ == "__main__":
img_folder_path = "./images"
text_file_path = None # --------------
fontfamily_path = "EmpireStateNF.ttf"
audio_file_path = "audio_file.mpeg"
fontsize = 30
fontcolor = "white"
duration_per_img = 10
text_animation_list = ['fio']*9
main(img_folder_path, text_file_path, duration_per_img, fontsize, fontcolor, fontfamily_path, audio_file_path, text_animation_list=None, debug=False)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment