# -*- coding: utf-8 -*-
##------------------------------------------
## My Library Video Tool Ver 0.01
## 「matplotlib」を利用した動画編集
##
## 2024.06.14 Masahiro Izutsu
##------------------------------------------
## my_videotool.py
# Color Escape Code
GREEN = '\033[1;32m'
RED = '\033[1;31m'
NOCOLOR = '\033[0m'
YELLOW = '\033[1;33m'
import imageio.v2 as imageio
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from skimage.transform import resize
import ffmpeg
import tempfile
from shutil import copyfileobj
# 静止画のリサイズ *
def image_resize(image, size = (256, 256)):
image = resize(image, size)[..., :3]
return image
# 動画のリサイズ *
def video_resize(video, size = (256, 256)):
video = [resize(frame, size)[..., :3] for frame in video]
return video
# 静止画 と 動画 2枚 の連結
def img_movie3x1(image, video, video2 = None, interval = 30, deley = 0, pixel_size = (256,256), dpi = 64):
# ピクセル → インチ変換
x_inch = pixel_size[0] / dpi
y_inch = pixel_size[1] / dpi
# リサイズ
image = image_resize(image, pixel_size)
video = video_resize(video, pixel_size)
if video2 is not None:
video2 = video_resize(video2, pixel_size)
fig = plt.figure(figsize = (x_inch * 2 + x_inch * (video2 is not None), y_inch), dpi = dpi)
fig.subplots_adjust(left=0, right=1, bottom=0, top=1)
ims = []
for i in range(len(video)):
cols = [image]
cols.append(video[i])
if video2 is not None:
cols.append(video2[i])
im = plt.imshow(np.concatenate(cols, axis=1), animated = True)
plt.axis('off')
ims.append([im])
ani = animation.ArtistAnimation(fig, ims, interval=interval + 3.4) # フレームレート補正
return ani
# 3枚の動画フレームを連結
def merge3x1_ex(listpath, interval = 30, deley = 0, pixel_size = (256,256), dpi = 64):
return merge3x1(read_listfile(listpath))
def merge3x1(source, interval = 30, deley = 0, pixel_size = (256,256), dpi = 64):
# ピクセル → インチ変換
x_inch = pixel_size[0] / dpi
y_inch = pixel_size[1] / dpi
result = []
for file_path in source:
video = imageio.mimread(file_path, memtest = False)
result.append(video_resize(video, pixel_size))
fig = plt.figure(figsize = (x_inch * 3, y_inch), dpi = 64)
fig.subplots_adjust(left=0, right=1, bottom=0, top=1)
ims =[]
for i in range(len(result[0])):
x = np.concatenate([result[0][i], result[1][i], result[2][i]],1)
z = np.concatenate([x])
im = plt.imshow(z, animated=True)
plt.axis('off')
ims.append([im])
ani = animation.ArtistAnimation(fig, ims, interval=interval + 3.36) # フレームレート補正
return ani
# 4枚の動画フレームを連結
def merge2x2_ex(listpath, interval = 30, deley = 0, pixel_size = (256,256), dpi = 64):
return merge2x2(read_listfile(listpath))
def merge2x2(source, interval = 30, deley = 0, pixel_size = (256,256), dpi = 64):
# ピクセル → インチ変換
x_inch = pixel_size[0] / dpi
y_inch = pixel_size[1] / dpi
result = []
for file_path in source:
video = imageio.mimread(file_path, memtest = False)
result.append(video_resize(video, pixel_size))
fig = plt.figure(figsize = (x_inch * 2, y_inch * 2), dpi = 64)
fig.subplots_adjust(left=0, right=1, bottom=0, top=1)
ims =[]
for i in range(len(result[0])):
x = np.concatenate([result[0][i], result[1][i]],1)
y = np.concatenate([result[2][i], result[3][i]],1)
z = np.concatenate([x, y])
im = plt.imshow(z, animated=True)
plt.axis('off')
ims.append([im])
ani = animation.ArtistAnimation(fig, ims, interval=interval + 3.36) # フレームレート補正
return ani
# 6枚の動画フレームを連結
def merge3x2_ex(listpath, interval = 30, deley = 0, pixel_size = (256,256), dpi = 64):
return merge3x2(read_listfile(listpath))
def merge3x2(source, interval = 30, deley = 0, pixel_size = (256,256), dpi = 64):
# ピクセル → インチ変換
x_inch = pixel_size[0] / dpi
y_inch = pixel_size[1] / dpi
result = []
for file_path in source:
video = imageio.mimread(file_path, memtest = False)
result.append(video_resize(video, pixel_size))
fig = plt.figure(figsize = (x_inch * 3, y_inch * 2), dpi = 64)
fig.subplots_adjust(left=0, right=1, bottom=0, top=1)
ims =[]
for i in range(len(result[0])):
x = np.concatenate([result[0][i], result[1][i], result[2][i]],1)
y = np.concatenate([result[3][i], result[4][i], result[5][i]],1)
z = np.concatenate([x, y])
im = plt.imshow(z, animated=True)
plt.axis('off')
ims.append([im])
ani = animation.ArtistAnimation(fig, ims, interval=interval + 3.36) # フレームレート補正
return ani
# 結果動画の表示と保存(終了処理)
def save_video(ani, save_path, dispf = False):
if len(save_path) > 0:
ani.save(save_path)
if dispf:
plt.show()
plt.close()
return
# 動画ファイルを読み出す
# 戻り値: video... 動画配列
# fps 速度(fps)
def read_video(filepath):
reader = imageio.get_reader(filepath)
fps = reader.get_meta_data()['fps']
video = []
try:
for im in reader:
video.append(im)
except RuntimeError:
pass
reader.close()
return video, fps
# 音声トラックを追加
def add_audio(source_path, save_path):
try:
with tempfile.TemporaryDirectory() as str_temp_dir:
tmpfile = f"{str_temp_dir}/temp.mp4"
ffmpeg.output(ffmpeg.input(save_path).video, ffmpeg.input(source_path).audio, tmpfile, c='copy').run(quiet=True)
with open(save_path, 'wb') as result:
with open(tmpfile, 'rb') as output:
copyfileobj(output, result)
except ffmpeg.Error:
print(RED + f"Failed to copy audio: no audio track or the audio format is invalid." + NOCOLOR)
# リストファイルの読み込み
# 戻り値: filelist... ファイルリスト
def read_listfile(filepath):
try:
f = open(filepath, 'r')
list = f.readlines()
filelist = [item[:-1] for item in list]
except Exception as e:
print(RED + f'{e}' + NOCOLOR)
filelist = []
return filelist
#-----Test routine-----
# $ python my_videotool.py <option:0/1/2/3/4/5>
# $ python my_videotool.py <option:10/20/30/40> <listfile> <savefile>
#
if __name__ == "__main__":
import os, sys
args = sys.argv
sel = '0' if len(args) <= 1 else args[1]
if sel == '0': # 静止画 と 動画 2枚 の連結
source_list = read_listfile('./mylib_test/filelist.txt')
source_image = imageio.imread(source_list[0])
driving_video = imageio.mimread(source_list[1], memtest = False)
generated_video = imageio.mimread(source_list[2], memtest = False)
ani = img_movie3x1(source_image, driving_video, generated_video)
save_video(ani, "test_anim.mp4", True)
save_video(ani, "test_anim.gif")
elif sel == '1': # 3枚の動画フレームを連結(規定値)
ani = merge3x2_ex('./mylib_test/filelist_1.txt')
save_video(ani, "test1_anim.mp4", True)
save_video(ani, "test1_anim.gif")
elif sel == '2': # 4枚の動画フレームを連結(規定値)
ani = merge2x2_ex('./mylib_test/filelist_2.txt')
save_video(ani, "test2_anim.mp4", True)
save_video(ani, "test2_anim.gif")
elif sel == '3': # 3枚の動画フレームを連結(規定値)
ani = merge3x1_ex('./mylib_test/filelist_3.txt')
save_video(ani, "test3_anim.mp4", True)
save_video(ani, "test3_anim.gif")
elif sel == '4':
add_audio('./mylib_test/01_video_256.mp4', './mylib_test/h1_13_out.mp4')
elif sel == '5':
files = read_listfile('./mylib_test/filelist_1.txt')
print(files)
else:
if len(args) == 4:
source_path = args[2]
save_path = args[3]
print(f" Source file → '{source_path}'")
print(f" Saving file → '{save_path}'")
if sel == '10': # 6枚の動画フレームを連結
ani = merge3x2_ex(source_path)
save_video(ani, save_path, True)
elif sel == '20': # 4枚の動画フレームを連結
ani = merge2x2_ex(source_path)
save_video(ani, save_path, True)
elif sel == '30': # 3枚の動画フレームを連結
ani = merge3x1_ex(source_path)
save_video(ani, save_path, True)
elif sel == '40': # 音声トラックを追加
add_audio(source_path, save_path)