154 lines
4.5 KiB
Python
Executable File
154 lines
4.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
"""Video Thumbnail Generator
|
|
|
|
Usage:
|
|
./generator <video> <interval> <width> <height> <columns> <output>
|
|
./generator (-h | --help)
|
|
./generator --version
|
|
|
|
Options:
|
|
-h --help Show this screen.
|
|
--version Show version.
|
|
<video> Video filepath.
|
|
<interval> Interval em seconds between frames.
|
|
<width> Width of each thumbnail.
|
|
<height> Height of each thumbnail.
|
|
<columns> Total number of thumbnails per line.
|
|
<output> Output.
|
|
"""
|
|
|
|
from docopt import docopt
|
|
from moviepy.editor import VideoFileClip
|
|
from PIL import Image
|
|
from click import progressbar
|
|
from collections import namedtuple
|
|
import glob
|
|
import os
|
|
import random
|
|
import shutil
|
|
import math
|
|
import tempfile
|
|
import sys
|
|
|
|
|
|
TMP_FRAMES_PATH = tempfile.mkdtemp()
|
|
|
|
|
|
def generate_video_thumbnails(args):
|
|
input_path = args['<video>']
|
|
interval = int(args['<interval>'])
|
|
size = (int(args['<width>']), int(args['<height>']))
|
|
output_prefix = get_output_prefix()
|
|
columns = int(args['<columns>'])
|
|
output_path = args['<output>']
|
|
|
|
file_paths = set()
|
|
|
|
if os.path.isdir(input_path):
|
|
# Ensure output path is also directory
|
|
if not os.path.isdir(output_path):
|
|
print(
|
|
"If input path is directory then "
|
|
"output path must be directory"
|
|
)
|
|
sys.exit(1)
|
|
|
|
# Strip seperator so contructing output is uniform
|
|
output_path = output_path.strip(os.sep)
|
|
|
|
# Add all files in directory for processing
|
|
for file_name in os.listdir(input_path):
|
|
file_path = os.path.join(input_path, file_name)
|
|
if os.path.isfile(file_path):
|
|
# Construct output path for thumbnail using
|
|
# the video files filename
|
|
single_output_path = \
|
|
"{output_path}{seperator}{file_name}.png". \
|
|
format(
|
|
output_path=output_path,
|
|
seperator=os.sep,
|
|
file_name=os.path.basename(file_path)
|
|
)
|
|
file_paths.add((file_path, single_output_path,))
|
|
else:
|
|
file_paths.add((input_path, output_path,))
|
|
|
|
# Process all files sequentially
|
|
for file_path in file_paths:
|
|
video_file_clip = VideoFileClip(file_path[0])
|
|
generate_frames(video_file_clip, interval, output_prefix, size)
|
|
generate_sprite_from_frames(output_prefix, columns, size, file_path[1])
|
|
|
|
|
|
def generate_frames(video_file_clip, interval, output_prefix, size):
|
|
duration = video_file_clip.duration
|
|
print("Extracting", int(duration / interval), "frames")
|
|
frame_count = 0
|
|
with progressbar(range(0, int(duration), interval)) as items:
|
|
for i in items:
|
|
extract_frame(video_file_clip, i, output_prefix, size, frame_count)
|
|
frame_count += 1
|
|
print("Frames extracted.")
|
|
|
|
|
|
def extract_frame(video_file_clip, moment, output_prefix, size, frame_count):
|
|
output = output_prefix + ("%05d.png" % frame_count)
|
|
video_file_clip.save_frame(output, t=int(moment))
|
|
resize_frame(output, size)
|
|
|
|
|
|
def resize_frame(filename, size):
|
|
image = Image.open(filename)
|
|
image = image.resize(size, Image.ANTIALIAS)
|
|
image.save(filename)
|
|
|
|
|
|
def generate_sprite_from_frames(frames_path, columns, size, output):
|
|
frames_map = sorted(glob.glob(frames_path + "*.png"))
|
|
|
|
master_width = size[0] * columns
|
|
master_height = size[1] * int(math.ceil(float(len(frames_map)) / columns))
|
|
|
|
line, column, mode = 0, 0, 'RGBA'
|
|
|
|
try:
|
|
final_image = Image.new(
|
|
mode=mode,
|
|
size=(master_width, master_height),
|
|
color=(0, 0, 0, 0)
|
|
)
|
|
final_image.save(output)
|
|
except IOError:
|
|
mode = 'RGB'
|
|
final_image = Image.new(mode=mode, size=(master_width, master_height))
|
|
|
|
for filename in frames_map:
|
|
with Image.open(filename) as image:
|
|
|
|
location_x = size[0] * column
|
|
location_y = size[1] * line
|
|
|
|
final_image.paste(image, (location_x, location_y))
|
|
|
|
column += 1
|
|
|
|
if column == columns:
|
|
line += 1
|
|
column = 0
|
|
|
|
final_image.save(output)
|
|
shutil.rmtree(TMP_FRAMES_PATH, ignore_errors=True)
|
|
print("Saved!")
|
|
|
|
|
|
def get_output_prefix():
|
|
if not os.path.exists(TMP_FRAMES_PATH):
|
|
os.makedirs(TMP_FRAMES_PATH)
|
|
return TMP_FRAMES_PATH + os.sep + ("%032x_" % random.getrandbits(128))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
arguments = docopt(__doc__, version='0.0.2')
|
|
generate_video_thumbnails(arguments)
|