# -*- coding: utf-8 -*-
##------------------------------------------
## Result Viewer Ver 0.01
##
## 2024.11.20 Masahiro Izutsu
##------------------------------------------
## result_viewer.py
# Color Escape Code
GREEN = '\033[1;32m'
RED = '\033[1;31m'
NOCOLOR = '\033[0m'
YELLOW = '\033[1;33m'
CYAN = '\033[1;36m'
BLUE = '\033[1;34m'
# インポート&初期設定
import os
import sys
from os.path import expanduser
import argparse
import numpy as np
import cv2
import PySimpleGUI as sg
import imageio.v2 as imageio
import my_imagetool
import my_videotool
import my_movieplay
import my_thumbnail
import my_logging
# タイトル
title = 'Result Viewer Ver. 0.01'
sub_title = ''
# 定数定義
DEF_THEME = 'Green'
SOURCE_IMAGE_DIR = ''
DRIVING_IMAGE_DIR = ''
RESULT_PATH = ''
DEF_SOURCE_IMAGE = './images/05.png'
DEF_DRIVING_IMAGE = './videos/01.mp4'
DEF_RESULT_VIDEO = './results/ff.mp4'
CANVAS_SIZE = 256
KEY_OK = '-Ok-'
KEY_CANCEL = '-Cancel-'
KEY_MAKE = '-Make-'
# Parses arguments for the application
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument("--source_image", default = DEF_SOURCE_IMAGE, help="path to source image")
parser.add_argument("--driving_video", default = DEF_DRIVING_IMAGE, help="path to driving video")
parser.add_argument("--result_video", default = DEF_RESULT_VIDEO, help="path to output")
parser.add_argument("--audio", dest="audio", action="store_false", help="copy audio to output from the driving video" )
parser.add_argument('--log', metavar = 'LOG', default = '3', help = 'Log level(-1/0/1/2/3/4/5) Default value is \'3\'')
return parser
# 基本情報の表示
def display_info(opt, title):
if title != '':
print(f'\n{GREEN}{title}: Starting application...{NOCOLOR}')
print(f'\n - {YELLOW}source_image : {NOCOLOR}{opt.source_image}')
print(f' - {YELLOW}driving_video : {NOCOLOR}{opt.driving_video}')
print(f' - {YELLOW}result_video : {NOCOLOR}{opt.result_video}')
print(f' - {YELLOW}audio : {NOCOLOR}{opt.audio}')
print(f' - {YELLOW}log : {NOCOLOR}{opt.log}')
print(f' ')
# 出力ファイル名を得る
# in: source_image 静止画ファイルパス
# driving_video 元動画ファイルパス
# result_video 処理結果画像ファイルパス
# out: out_path1 処理結果画像ファイルパス
# out_path2 静止画/元動画/処理結果画像ファイルパス
# ext 拡張子
#
def get_results_path(source_image, driving_video, result_video):
base_dir_pair = os.path.split(source_image)
s_name, ext = os.path.splitext(base_dir_pair[1])
base_dir_pair = os.path.split(driving_video)
d_name, ext = os.path.splitext(base_dir_pair[1])
name, ext = os.path.splitext(result_video)
out_path1 = name + '~' + s_name + '~' + d_name + ext
out_path2 = name + '~' + s_name + '~' + d_name + '_a' + ext
return out_path1, out_path2, ext
# ** main関数 **
def main(opt, logger):
sub_title = os.path.basename(os.getcwd()) # 一つ上のディレクトリ名
s_title = f'[{sub_title}] {title}'
# フォント取得
from my_puttext import get_font, cv2_putText
font_face = get_font()
# ウィンドウのテーマ
sg.theme(DEF_THEME)
canvas_0 = sg.Image(size = (CANVAS_SIZE, CANVAS_SIZE), key='CANVAS_0')
canvas_1 = sg.Image(size = (CANVAS_SIZE, CANVAS_SIZE), key='CANVAS_1')
canvas_2 = sg.Image(size = (CANVAS_SIZE, CANVAS_SIZE), key='CANVAS_2')
# ウィンドウのレイアウト
col_left = [
[canvas_0, canvas_1, canvas_2],
]
col_btn = [
[sg.Button('Make', size=(8, 1), key=KEY_MAKE),
sg.Button('Video', size=(8, 1), key=KEY_OK),
sg.Text('', size=(1, 1)),
sg.Button('Cancel', size=(8, 1), key=KEY_CANCEL),
sg.Text("", size=(1, 1)) ]
]
layout = [
[sg.Text("", size=(2, 1)), sg.Text(s_title, size=(40, 1), justification='left', font='Helvetica 20')],
[sg.Column(col_left, vertical_alignment='top')],
[sg.Column(col_btn, justification='r') ],
]
# ウィンドウオブジェクトの作成
window = sg.Window(title, layout, finalize=True, return_keyboard_events=True)
# ユーザーイベントの定義
canvas_0.bind('<ButtonPress>', '_click_on')
canvas_1.bind('<ButtonPress>', '_click_on')
canvas_2.bind('<ButtonPress>', '_click_on')
# キャンバス初期化
frame = np.zeros((CANVAS_SIZE, CANVAS_SIZE, 3), np.uint8)
def clear_canvas(key, frame, msg, color):
frame[:,:,] = 0xf0
img = cv2_putText(img = frame, text = msg, org = (CANVAS_SIZE//2, CANVAS_SIZE//2), fontFace = font_face, fontScale = 16, color = color, mode = 2)
img = cv2.imencode('.png', frame)[1].tobytes()
window[key].update(img)
clear_canvas('CANVAS_0', frame, 'Source Image', (0,0,0))
clear_canvas('CANVAS_1', frame, 'Driving Video', (240,0,0))
clear_canvas('CANVAS_2', frame, 'Processing Video', (0,138,0))
window[KEY_MAKE].update(disabled = True)
file_parh0 = ''
file_parh1 = ''
file_parh2 = ''
file_path3 = ''
cap2 = None
cap1_open = False
cap2_open = False
new_make_f = False
video_play_f = False
#----------------------
def check_process_image(file_parh0, file_parh1, file_parh2, cap2, cap2_open):
video_play_f = False
out_parh1, out_parh2, ext = get_results_path(file_parh0, file_parh1, opt.result_video)
if out_parh1 != file_parh2 or not os.path.isfile(file_parh2):
file_parh2 = ''
if cap2_open:
cap2.release()
cap2_open = False
frame = np.zeros((CANVAS_SIZE, CANVAS_SIZE, 3), np.uint8)
clear_canvas('CANVAS_2', frame, 'Processing Video', (0,138,0))
if os.path.isfile(out_parh1):
file_parh2 = out_parh1
video_play_f = True
elif file_parh2 == '' and os.path.isfile(out_parh1):
file_parh2 = out_parh1
video_play_f = True
elif out_parh1 == file_parh2 and os.path.isfile(file_parh2):
video_play_f = True
# 元画像を表示する
if os.path.isfile(file_parh0):
frame = cv2.imread(file_parh0)
frame = my_imagetool.frame_square2(frame) # 正方形にする
frame = cv2.resize(frame, dsize = (CANVAS_SIZE, CANVAS_SIZE))
img = cv2.imencode('.png', frame)[1].tobytes()
window['CANVAS_0'].update(img)
return video_play_f, cap2_open, file_parh2
#----------------------
# イベントのループ
while True:
event, values = window.read(timeout = 30)
# 処理動画作成
if new_make_f:
out_parh1, out_parh2, ext = get_results_path(file_parh0, file_parh1, opt.result_video)
source_image = imageio.imread(file_parh0)
driving_video = imageio.mimread(file_parh1, memtest = False)
generated_video = imageio.mimread(out_parh1, memtest = False)
ani = my_videotool.img_movie3x1(source_image, driving_video, generated_video)
my_videotool.save_video(ani, out_parh2)
my_videotool.add_audio(file_parh1, out_parh2, False)
new_make_f = False
# 動画表示
if video_play_f:
if os.path.isfile(file_parh1):
if cap1_open:
cap1.release()
cap1 = cv2.VideoCapture(file_parh1)
cap1_open = cap1.isOpened()
if os.path.isfile(file_parh2):
if cap2_open:
cap2.release()
cap2 = cv2.VideoCapture(file_parh2)
cap2_open = cap2.isOpened()
out_parh1, out_parh2, ext = get_results_path(file_parh0, file_parh1, opt.result_video)
window[KEY_MAKE].update(disabled = not cap1_open or not cap2_open or os.path.isfile(out_parh2))
video_play_f = False
# 終了
if event == KEY_CANCEL or event == sg.WIN_CLOSED:
break
# CANVAS_0
if event == 'CANVAS_0_click_on':
logger.debug(f'{event}')
fpath = my_thumbnail.image_dialog(file_path=SOURCE_IMAGE_DIR, title=SOURCE_IMAGE_DIR, theme=DEF_THEME, xn=10, yn=4, thumb_size=128, gap=4, logger=logger)
if len(fpath) > 0:
file_parh0 = fpath
# 処理画像チェック
video_play_f, cap2_open, file_parh2 = check_process_image(file_parh0, file_parh1, file_parh2, cap2, cap2_open)
# CANVAS_1
if event == 'CANVAS_1_click_on':
logger.debug(f'{event}')
fpath = my_thumbnail.movie_dialog(file_path=DRIVING_IMAGE_DIR, title=DRIVING_IMAGE_DIR, theme=DEF_THEME, xn=10, yn=4, thumb_size=128, gap=4, audio_f=opt.audio, logger=logger)
if len(fpath) > 0:
file_parh1 = fpath
if cap1_open:
cap1.release()
cap1 = cv2.VideoCapture(file_parh1)
cap1_open = cap1.isOpened()
# 処理画像チェック
video_play_f, cap2_open, file_parh2 = check_process_image(file_parh0, file_parh1, file_parh2, cap2, cap2_open)
if event == 'CANVAS_2_click_on':
logger.debug(f'{event}')
out_path1, out_path2, ext = get_results_path(file_parh0, file_parh1, opt.result_video)
file_parh2 = out_path1
if os.path.isfile(out_path1):
video_play_f = True
else:
# First Order Motion Model 処理プロセス
clear_canvas('CANVAS_2', frame, 'No video ...', (0,0,240))
# Video ボタン
if event == KEY_OK:
logger.debug(f'{event}')
fpath = my_thumbnail.movie_dialog(file_path=RESULT_PATH, title=RESULT_PATH, theme=DEF_THEME, xn=10, yn=4, thumb_size=128, gap=4, audio_f=opt.audio, logger=logger)
if os.path.isfile(fpath):
logger.debug(f'{fpath}')
target1 = '~'
idx = fpath.find(target1)
cat = fpath[0:idx]
str0 = fpath[idx + 1:]
idx = str0.find(target1)
sourse = str0[0:idx]
terget = str0[idx + 1:]
base_dir_pair = os.path.split(DEF_SOURCE_IMAGE)
src = base_dir_pair[0]
base_dir_pair = os.path.split(DEF_DRIVING_IMAGE)
tgt = base_dir_pair[0]
if terget[-4:] == '.gif':
terget = terget[0:-4] + '.mp4'
opt.audio = False
if terget[-6:] == '_a.mp4':
terget = terget[:-6] + '.mp4'
path0 = src + '/' + sourse + '.png'
path1 = src + '/' + sourse + '.jpg'
source_image = path0 if os.path.isfile(path0) else path1 if os.path.isfile(path1) else ''
target_video = tgt + '/' + terget
logger.debug(f'source_image = {source_image}')
logger.debug(f'target_video = {target_video}')
file_parh0 = source_image
file_parh1 = target_video
opt.source_image = file_parh0
opt.driving_video = file_parh1
if cap1_open:
cap1.release()
cap1_open = False
if cap2_open:
cap2.release()
cap2_open = False
video_play_f, cap2_open, file_parh2 = check_process_image(file_parh0, file_parh1, file_parh2, cap2, cap2_open)
# Make ボタン
if event == KEY_MAKE:
logger.debug(f'{event}')
out_parh1, out_parh2, ext = get_results_path(file_parh0, file_parh1, opt.result_video)
window[KEY_MAKE].update(disabled = True)
if not os.path.isfile(out_parh2):
new_make_f = True
if cap1_open:
ret, frame = cap1.read()
if frame is None:
#最初のフレームに戻る
cap1.set(cv2.CAP_PROP_POS_FRAMES, 0)
ret, frame = cap1.read()
frame = my_imagetool.frame_square2(frame) # 正方形にする
frame = cv2.resize(frame, dsize = (CANVAS_SIZE, CANVAS_SIZE))
img = cv2.imencode('.png', frame)[1].tobytes()
window['CANVAS_1'].update(img)
if cap2_open:
ret, frame = cap2.read()
if frame is None:
#最初のフレームに戻る
cap2.set(cv2.CAP_PROP_POS_FRAMES, 0)
ret, frame = cap2.read()
frame = my_imagetool.frame_square2(frame) # 正方形にする
frame = cv2.resize(frame, dsize = (CANVAS_SIZE, CANVAS_SIZE))
img = cv2.imencode('.png', frame)[1].tobytes()
window['CANVAS_2'].update(img)
# ウィンドウ終了処理
if cap1_open:
cap1.release()
if cap2_open:
cap2.release()
# ウィンドウ終了処理
window.close()
# main関数エントリーポイント(実行開始)
if __name__ == '__main__':
parser = parse_args()
opt = parser.parse_args()
# アプリケーション・ログ設定
module = os.path.basename(__file__)
module_name = os.path.splitext(module)[0]
logger = my_logging.get_module_logger_sel(module_name, int(opt.log))
# ディレクトリ変数
SOURCE_IMAGE_DIR = os.path.split(opt.source_image)[0]
DRIVING_IMAGE_DIR = os.path.split(opt.driving_video)[0]
RESULT_PATH = os.path.split(opt.result_video)[0]
display_info(opt, title)
main(opt, logger)
msg = '処理結果一覧: ' + os.getcwd() + RESULT_PATH[1:]
my_thumbnail.file_dialog(file_path=RESULT_PATH, title=msg, theme=DEF_THEME, xn=10, yn=4, thumb_size=128, gap=4, ret='終了', audio_f=opt.audio, logger=logger)
logger.info('\nFinished.\n')