mirror of
https://github.com/k4yt3x/video2x.git
synced 2025-01-30 15:48:13 +00:00
better exception handling, soft task interruption, GUI stop button, GUI folder processing, better argument checks
This commit is contained in:
parent
134e8b7080
commit
e9c1c22788
@ -5,8 +5,8 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: 2020-05-04 19:14-0400\n"
|
||||
"PO-Revision-Date: 2020-05-04 19:16-0400\n"
|
||||
"POT-Creation-Date: 2020-05-07 15:54-0400\n"
|
||||
"PO-Revision-Date: 2020-05-07 15:55-0400\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: zh_CN\n"
|
||||
@ -17,107 +17,155 @@ msgstr ""
|
||||
"X-Generator: Poedit 2.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#: upscaler.py:85
|
||||
msgid "Extracted frames are being saved to: {}"
|
||||
msgstr "提取的帧将被保存到:{}"
|
||||
|
||||
#: upscaler.py:87
|
||||
msgid "Upscaled frames are being saved to: {}"
|
||||
msgstr "已放大的帧将被保存到:{}"
|
||||
|
||||
#: upscaler.py:97
|
||||
msgid "Cleaning up cache directory: {}"
|
||||
msgstr "清理缓存目录:{}"
|
||||
|
||||
#: upscaler.py:100
|
||||
msgid "Unable to delete: {}"
|
||||
msgstr "无法删除:{}"
|
||||
|
||||
#: upscaler.py:107
|
||||
msgid "You must specify input video file/directory path"
|
||||
msgstr "您必须指定输入视频文件/目录路径"
|
||||
|
||||
#: upscaler.py:110
|
||||
msgid "You must specify output video file/directory path"
|
||||
msgstr "您必须指定输出视频文件/目录路径"
|
||||
|
||||
#: upscaler.py:113
|
||||
msgid "Selected driver accepts only scaling ratio"
|
||||
msgstr "所选驱动程序仅接受缩放比率"
|
||||
|
||||
#: upscaler.py:116
|
||||
msgid "Scaling ratio must be 1 or 2 for waifu2x_ncnn_vulkan"
|
||||
msgstr "waifu2x_ncnn_vulkan 的缩放比必须为 1 或 2"
|
||||
|
||||
#: upscaler.py:119
|
||||
msgid "Scaling ratio must be one of 2, 3 or 4 for srmd_ncnn_vulkan"
|
||||
msgstr "srmd_ncnn_vulkan 的缩放比必须为 2、3 或 4"
|
||||
|
||||
#: upscaler.py:122
|
||||
msgid "You can only specify either scaling ratio or output width and height"
|
||||
msgstr "您只能指定缩放比或输出宽度和高度两者之一"
|
||||
|
||||
#: upscaler.py:125
|
||||
msgid "You must specify both width and height"
|
||||
msgstr "您必须同时指定宽度和高度"
|
||||
|
||||
#: upscaler.py:142
|
||||
#: progress_monitor.py:42
|
||||
msgid "Upscaling Progress"
|
||||
msgstr "放大进度"
|
||||
|
||||
#: upscaler.py:179
|
||||
#: upscaler.py:104
|
||||
msgid "Specified or default cache directory is a file/link"
|
||||
msgstr "指定或默认的缓存目录是文件/链接"
|
||||
|
||||
#: upscaler.py:110
|
||||
msgid "Creating cache directory {}"
|
||||
msgstr "创建缓存目录 {}"
|
||||
|
||||
#: upscaler.py:113
|
||||
msgid "Unable to create {}"
|
||||
msgstr "无法创建 {}"
|
||||
|
||||
#: upscaler.py:118
|
||||
msgid "Extracted frames are being saved to: {}"
|
||||
msgstr "提取的帧将被保存到:{}"
|
||||
|
||||
#: upscaler.py:120
|
||||
msgid "Upscaled frames are being saved to: {}"
|
||||
msgstr "已放大的帧将被保存到:{}"
|
||||
|
||||
#: upscaler.py:130
|
||||
msgid "Cleaning up cache directory: {}"
|
||||
msgstr "清理缓存目录:{}"
|
||||
|
||||
#: upscaler.py:133
|
||||
msgid "Unable to delete: {}"
|
||||
msgstr "无法删除:{}"
|
||||
|
||||
#: upscaler.py:140 upscaler.py:151
|
||||
msgid "Input and output path type mismatch"
|
||||
msgstr "输入和输出路径类型不匹配"
|
||||
|
||||
#: upscaler.py:141
|
||||
msgid "Input is single file but output is directory"
|
||||
msgstr "所选的输入路径是单个文件,但输出路径是目录"
|
||||
|
||||
#: upscaler.py:144
|
||||
msgid "No suffix found in output file path"
|
||||
msgstr "在输出文件路径中未找到后缀"
|
||||
|
||||
#: upscaler.py:145
|
||||
msgid "Suffix must be specified for FFmpeg"
|
||||
msgstr "必须为 FFmpeg 指定后缀"
|
||||
|
||||
#: upscaler.py:152
|
||||
msgid "Input is directory but output is existing single file"
|
||||
msgstr "输入是目录,但输出是现有的单个文件"
|
||||
|
||||
#: upscaler.py:157
|
||||
msgid "Input path is neither a file nor a directory"
|
||||
msgstr "输入路径既不是文件也不是目录"
|
||||
|
||||
#: upscaler.py:166
|
||||
msgid "FFmpeg or FFprobe cannot be found under the specified path"
|
||||
msgstr "在指定的路径下找不到 FFmpeg 或 FFprobe"
|
||||
|
||||
#: upscaler.py:167 upscaler.py:177
|
||||
msgid "Please check the configuration file settings"
|
||||
msgstr "请检查配置文件设置"
|
||||
|
||||
#: upscaler.py:176
|
||||
msgid "Specified driver executable directory doesn't exist"
|
||||
msgstr "指定驱动的可执行文件不存在"
|
||||
|
||||
#: upscaler.py:203
|
||||
msgid "Failed to parse driver argument: {}"
|
||||
msgstr "解析驱动程序参数失败:{}"
|
||||
|
||||
#: upscaler.py:218
|
||||
msgid "Unrecognized driver: {}"
|
||||
msgstr "无法识别的驱动名称:{}"
|
||||
|
||||
#: upscaler.py:258
|
||||
#: upscaler.py:290
|
||||
msgid "Starting progress monitor"
|
||||
msgstr "启动进度监视器"
|
||||
|
||||
#: upscaler.py:295
|
||||
msgid "Starting upscaled image cleaner"
|
||||
msgstr "启动已放大图像清理程序"
|
||||
|
||||
#: upscaler.py:264
|
||||
msgid "Main process waiting for subprocesses to exit"
|
||||
msgstr "主进程开始等待子进程结束"
|
||||
#: upscaler.py:304 upscaler.py:321
|
||||
msgid "Killing progress monitor"
|
||||
msgstr "终结进度监视器"
|
||||
|
||||
#: upscaler.py:266
|
||||
msgid "Subprocess {} exited with code {}"
|
||||
msgstr "子进程 {} 结束,返回码 {}"
|
||||
|
||||
#: upscaler.py:274 upscaler.py:287
|
||||
#: upscaler.py:307 upscaler.py:324
|
||||
msgid "Killing upscaled image cleaner"
|
||||
msgstr "终结已放大图像清理程序"
|
||||
|
||||
#: upscaler.py:313 upscaler.py:368
|
||||
#: upscaler.py:328
|
||||
msgid "Terminating all processes"
|
||||
msgstr "正在终止所有进程"
|
||||
|
||||
#: upscaler.py:335
|
||||
msgid "Main process waiting for subprocesses to exit"
|
||||
msgstr "主进程开始等待子进程结束"
|
||||
|
||||
#: upscaler.py:354 upscaler.py:358
|
||||
msgid "Subprocess {} exited with code {}"
|
||||
msgstr "子进程 {} 结束,返回码 {}"
|
||||
|
||||
#: upscaler.py:364
|
||||
msgid "Stop signal received"
|
||||
msgstr "收到停止信号"
|
||||
|
||||
#: upscaler.py:369
|
||||
msgid "Subprocess execution ran into an error"
|
||||
msgstr "子进程执行遇到错误"
|
||||
|
||||
#: upscaler.py:395
|
||||
msgid "Upscaling single video file: {}"
|
||||
msgstr "放大单个视频文件:{}"
|
||||
|
||||
#: upscaler.py:414 upscaler.py:477
|
||||
msgid "Starting to upscale extracted images"
|
||||
msgstr "开始对提取的帧进行放大"
|
||||
|
||||
#: upscaler.py:316 upscaler.py:370
|
||||
#: upscaler.py:423 upscaler.py:479
|
||||
msgid "Upscaling completed"
|
||||
msgstr "放大完成"
|
||||
|
||||
#: upscaler.py:324
|
||||
#: upscaler.py:432
|
||||
msgid "Reading video information"
|
||||
msgstr "读取视频信息"
|
||||
|
||||
#: upscaler.py:338
|
||||
#: upscaler.py:446
|
||||
msgid "Aborting: No video stream found"
|
||||
msgstr "程序中止:文件中未找到视频流"
|
||||
|
||||
#: upscaler.py:355
|
||||
#: upscaler.py:464
|
||||
msgid "Unsupported pixel format: {}"
|
||||
msgstr "不支持的像素格式:{}"
|
||||
|
||||
#: upscaler.py:358
|
||||
#: upscaler.py:467
|
||||
msgid "Framerate: {}"
|
||||
msgstr "帧率:{}"
|
||||
|
||||
#: upscaler.py:373
|
||||
#: upscaler.py:482
|
||||
msgid "Converting extracted frames into video"
|
||||
msgstr "将提取的帧转换为视频"
|
||||
|
||||
#: upscaler.py:377
|
||||
#: upscaler.py:487
|
||||
msgid "Conversion completed"
|
||||
msgstr "转换已完成"
|
||||
|
||||
#: upscaler.py:380
|
||||
#: upscaler.py:490
|
||||
msgid "Migrating audio tracks and subtitles to upscaled video"
|
||||
msgstr "将音轨和字幕迁移到放大后的视频"
|
||||
|
||||
@ -187,58 +235,34 @@ msgstr "缩放比"
|
||||
msgid "This file cannot be imported"
|
||||
msgstr "此文件无法被当作模块导入"
|
||||
|
||||
#: video2x.py:193
|
||||
msgid "Specified driver executable directory doesn't exist"
|
||||
msgstr "指定驱动的可执行文件不存在"
|
||||
|
||||
#: video2x.py:194
|
||||
msgid "Please check the configuration file settings"
|
||||
msgstr "请检查配置文件设置"
|
||||
|
||||
#: video2x.py:211
|
||||
msgid "Specified cache directory is a file/link"
|
||||
msgstr "指定的缓存目录是文件/链接"
|
||||
|
||||
#: video2x.py:218
|
||||
msgid "Creating cache directory {}"
|
||||
msgstr "创建缓存目录 {}"
|
||||
|
||||
#: video2x.py:224
|
||||
msgid "Unable to create {}"
|
||||
msgstr "无法创建 {}"
|
||||
|
||||
#: video2x.py:237
|
||||
msgid "Upscaling single video file: {}"
|
||||
msgstr "放大单个视频文件:{}"
|
||||
|
||||
#: video2x.py:241
|
||||
msgid "Input and output path type mismatch"
|
||||
msgstr "输入和输出路径类型不匹配"
|
||||
|
||||
#: video2x.py:242
|
||||
msgid "Input is single file but output is directory"
|
||||
msgstr "所选的输入路径是单个文件,但输出路径是目录"
|
||||
|
||||
#: video2x.py:245
|
||||
msgid "No suffix found in output file path"
|
||||
msgstr "在输出文件路径中未找到后缀"
|
||||
|
||||
#: video2x.py:246
|
||||
msgid "Suffix must be specified for FFmpeg"
|
||||
msgstr "必须为 FFmpeg 指定后缀"
|
||||
|
||||
#: video2x.py:270
|
||||
msgid "Upscaling videos in directory: {}"
|
||||
msgstr "放大该文件夹中的所有视频:{}"
|
||||
|
||||
#: video2x.py:295
|
||||
msgid "Input path is neither a file nor a directory"
|
||||
msgstr "输入路径既不是文件也不是目录"
|
||||
|
||||
#: video2x.py:298
|
||||
msgid "Program completed, taking {} seconds"
|
||||
msgstr "程序执行完毕,总计花费 {} 秒"
|
||||
|
||||
#: video2x.py:301
|
||||
#: video2x.py:227
|
||||
msgid "An exception has occurred"
|
||||
msgstr "发生了异常"
|
||||
|
||||
#~ msgid "You must specify input video file/directory path"
|
||||
#~ msgstr "您必须指定输入视频文件/目录路径"
|
||||
|
||||
#~ msgid "You must specify output video file/directory path"
|
||||
#~ msgstr "您必须指定输出视频文件/目录路径"
|
||||
|
||||
#~ msgid "Selected driver accepts only scaling ratio"
|
||||
#~ msgstr "所选驱动程序仅接受缩放比率"
|
||||
|
||||
#~ msgid "Scaling ratio must be 1 or 2 for waifu2x_ncnn_vulkan"
|
||||
#~ msgstr "waifu2x_ncnn_vulkan 的缩放比必须为 1 或 2"
|
||||
|
||||
#~ msgid "Scaling ratio must be one of 2, 3 or 4 for srmd_ncnn_vulkan"
|
||||
#~ msgstr "srmd_ncnn_vulkan 的缩放比必须为 2、3 或 4"
|
||||
|
||||
#~ msgid "You can only specify either scaling ratio or output width and height"
|
||||
#~ msgstr "您只能指定缩放比或输出宽度和高度两者之一"
|
||||
|
||||
#~ msgid "You must specify both width and height"
|
||||
#~ msgstr "您必须同时指定宽度和高度"
|
||||
|
||||
#~ msgid "Upscaling videos in directory: {}"
|
||||
#~ msgstr "放大该文件夹中的所有视频:{}"
|
||||
|
61
src/progress_monitor.py
Normal file
61
src/progress_monitor.py
Normal file
@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Name: Video2X Upscale Progress Monitor
|
||||
Author: BrianPetkovsek
|
||||
Date Created: May 7, 2020
|
||||
Last Modified: May 7, 2020
|
||||
"""
|
||||
|
||||
# built-in imports
|
||||
import contextlib
|
||||
import threading
|
||||
import time
|
||||
|
||||
# third-party imports
|
||||
from tqdm import tqdm
|
||||
|
||||
|
||||
class ProgressMonitor(threading.Thread):
|
||||
""" progress monitor
|
||||
|
||||
This class provides progress monitoring functionalities
|
||||
by keeping track of the amount of frames in the input
|
||||
directory and the output directory. This is originally
|
||||
suggested by @ArmandBernard.
|
||||
"""
|
||||
|
||||
def __init__(self, upscaler, extracted_frames_directories):
|
||||
threading.Thread.__init__(self)
|
||||
self.upscaler = upscaler
|
||||
self.extracted_frames_directories = extracted_frames_directories
|
||||
self.running = False
|
||||
|
||||
def run(self):
|
||||
self.running = True
|
||||
|
||||
# get number of extracted frames
|
||||
self.upscaler.total_frames = 0
|
||||
for directory in self.extracted_frames_directories:
|
||||
self.upscaler.total_frames += len([f for f in directory.iterdir() if str(f).lower().endswith(self.upscaler.image_format.lower())])
|
||||
|
||||
with tqdm(total=self.upscaler.total_frames, ascii=True, desc=_('Upscaling Progress')) as progress_bar:
|
||||
# tqdm update method adds the value to the progress
|
||||
# bar instead of setting the value. Therefore, a delta
|
||||
# needs to be calculated.
|
||||
previous_cycle_frames = 0
|
||||
while self.running:
|
||||
|
||||
with contextlib.suppress(FileNotFoundError):
|
||||
self.upscaler.total_frames_upscaled = len([f for f in self.upscaler.upscaled_frames.iterdir() if str(f).lower().endswith(self.upscaler.image_format.lower())])
|
||||
|
||||
# update progress bar
|
||||
delta = self.upscaler.total_frames_upscaled - previous_cycle_frames
|
||||
previous_cycle_frames = self.upscaler.total_frames_upscaled
|
||||
progress_bar.update(delta)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
self.join()
|
413
src/upscaler.py
413
src/upscaler.py
@ -4,7 +4,7 @@
|
||||
Name: Video2X Upscaler
|
||||
Author: K4YT3X
|
||||
Date Created: December 10, 2018
|
||||
Last Modified: May 6, 2020
|
||||
Last Modified: May 7, 2020
|
||||
|
||||
Description: This file contains the Upscaler class. Each
|
||||
instance of the Upscaler class is an upscaler on an image or
|
||||
@ -14,6 +14,7 @@ a folder.
|
||||
# local imports
|
||||
from exceptions import *
|
||||
from image_cleaner import ImageCleaner
|
||||
from progress_monitor import ProgressMonitor
|
||||
from wrappers.ffmpeg import Ffmpeg
|
||||
|
||||
# built-in imports
|
||||
@ -25,8 +26,10 @@ import importlib
|
||||
import locale
|
||||
import os
|
||||
import pathlib
|
||||
import queue
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
@ -35,7 +38,6 @@ import traceback
|
||||
|
||||
# third-party imports
|
||||
from avalon_framework import Avalon
|
||||
from tqdm import tqdm
|
||||
|
||||
# internationalization constants
|
||||
DOMAIN = 'video2x'
|
||||
@ -67,10 +69,10 @@ class Upscaler:
|
||||
ArgumentError -- if argument is not valid
|
||||
"""
|
||||
|
||||
def __init__(self, input_video, output_video, driver_settings, ffmpeg_settings):
|
||||
def __init__(self, input_path, output_path, driver_settings, ffmpeg_settings):
|
||||
# mandatory arguments
|
||||
self.input_video = input_video
|
||||
self.output_video = output_video
|
||||
self.input_path = input_path
|
||||
self.output_path = output_path
|
||||
self.driver_settings = driver_settings
|
||||
self.ffmpeg_settings = ffmpeg_settings
|
||||
|
||||
@ -84,14 +86,33 @@ class Upscaler:
|
||||
self.image_format = 'png'
|
||||
self.preserve_frames = False
|
||||
|
||||
# other internal members and signals
|
||||
self.stop_signal = False
|
||||
self.total_frames_upscaled = 0
|
||||
self.total_frames = 0
|
||||
|
||||
def create_temp_directories(self):
|
||||
"""create temporary directory
|
||||
"""create temporary directories
|
||||
"""
|
||||
|
||||
# create a new temp directory if the current one is not found
|
||||
if not self.video2x_cache_directory.exists():
|
||||
# if cache directory unspecified, use %TEMP%\video2x
|
||||
if self.video2x_cache_directory is None:
|
||||
self.video2x_cache_directory = pathlib.Path(tempfile.gettempdir()) / 'video2x'
|
||||
|
||||
# if specified cache path exists and isn't a directory
|
||||
if self.video2x_cache_directory.exists() and not self.video2x_cache_directory.is_dir():
|
||||
Avalon.error(_('Specified or default cache directory is a file/link'))
|
||||
raise FileExistsError('Specified or default cache directory is a file/link')
|
||||
|
||||
# if cache directory doesn't exist, try creating it
|
||||
if not self.video2x_cache_directory.exists():
|
||||
try:
|
||||
Avalon.debug_info(_('Creating cache directory {}').format(self.video2x_cache_directory))
|
||||
self.video2x_cache_directory.mkdir(parents=True, exist_ok=True)
|
||||
except Exception as exception:
|
||||
Avalon.error(_('Unable to create {}').format(self.video2x_cache_directory))
|
||||
raise exception
|
||||
|
||||
# create temp directories for extracted frames and upscaled frames
|
||||
self.extracted_frames = pathlib.Path(tempfile.mkdtemp(dir=self.video2x_cache_directory))
|
||||
Avalon.debug_info(_('Extracted frames are being saved to: {}').format(self.extracted_frames))
|
||||
@ -113,65 +134,74 @@ class Upscaler:
|
||||
traceback.print_exc()
|
||||
|
||||
def _check_arguments(self):
|
||||
# check if arguments are valid / all necessary argument
|
||||
# values are specified
|
||||
if not self.input_video:
|
||||
Avalon.error(_('You must specify input video file/directory path'))
|
||||
raise ArgumentError('input video path not specified')
|
||||
if not self.output_video:
|
||||
Avalon.error(_('You must specify output video file/directory path'))
|
||||
raise ArgumentError('output video path not specified')
|
||||
if (self.driver in ['waifu2x_converter', 'waifu2x_ncnn_vulkan', 'anime4k']) and self.scale_width and self.scale_height:
|
||||
Avalon.error(_('Selected driver accepts only scaling ratio'))
|
||||
raise ArgumentError('selected driver supports only scaling ratio')
|
||||
if self.driver == 'waifu2x_ncnn_vulkan' and self.scale_ratio is not None and (self.scale_ratio > 2 or not self.scale_ratio.is_integer()):
|
||||
Avalon.error(_('Scaling ratio must be 1 or 2 for waifu2x_ncnn_vulkan'))
|
||||
raise ArgumentError('scaling ratio must be 1 or 2 for waifu2x_ncnn_vulkan')
|
||||
if self.driver == 'srmd_ncnn_vulkan' and self.scale_ratio is not None and (self.scale_ratio not in [2, 3, 4]):
|
||||
Avalon.error(_('Scaling ratio must be one of 2, 3 or 4 for srmd_ncnn_vulkan'))
|
||||
raise ArgumentError('scaling ratio must be one of 2, 3 or 4 for srmd_ncnn_vulkan')
|
||||
if (self.scale_width or self.scale_height) and self.scale_ratio:
|
||||
Avalon.error(_('You can only specify either scaling ratio or output width and height'))
|
||||
raise ArgumentError('both scaling ration and width/height specified')
|
||||
if (self.scale_width and not self.scale_height) or (not self.scale_width and self.scale_height):
|
||||
Avalon.error(_('You must specify both width and height'))
|
||||
raise ArgumentError('only one of width or height is specified')
|
||||
# if input is a file
|
||||
if self.input_path.is_file():
|
||||
if self.output_path.is_dir():
|
||||
Avalon.error(_('Input and output path type mismatch'))
|
||||
Avalon.error(_('Input is single file but output is directory'))
|
||||
raise ArgumentError('input output path type mismatch')
|
||||
if not re.search(r'.*\..*$', str(self.output_path)):
|
||||
Avalon.error(_('No suffix found in output file path'))
|
||||
Avalon.error(_('Suffix must be specified for FFmpeg'))
|
||||
raise ArgumentError('no output video suffix specified')
|
||||
|
||||
def _progress_bar(self, extracted_frames_directories):
|
||||
""" This method prints a progress bar
|
||||
# if input is a directory
|
||||
elif self.input_path.is_dir():
|
||||
if self.output_path.is_file():
|
||||
Avalon.error(_('Input and output path type mismatch'))
|
||||
Avalon.error(_('Input is directory but output is existing single file'))
|
||||
raise ArgumentError('input output path type mismatch')
|
||||
|
||||
This method prints a progress bar by keeping track
|
||||
of the amount of frames in the input directory
|
||||
and the output directory. This is originally
|
||||
suggested by @ArmandBernard.
|
||||
"""
|
||||
# if input is neither
|
||||
else:
|
||||
Avalon.error(_('Input path is neither a file nor a directory'))
|
||||
raise FileNotFoundError(f'{self.input_path} is neither file nor directory')
|
||||
|
||||
# check Fmpeg settings
|
||||
ffmpeg_path = pathlib.Path(self.ffmpeg_settings['ffmpeg_path'])
|
||||
if not ((pathlib.Path(ffmpeg_path / 'ffmpeg.exe').is_file() and
|
||||
pathlib.Path(ffmpeg_path / 'ffprobe.exe').is_file()) or
|
||||
(pathlib.Path(ffmpeg_path / 'ffmpeg').is_file() and
|
||||
pathlib.Path(ffmpeg_path / 'ffprobe').is_file())):
|
||||
Avalon.error(_('FFmpeg or FFprobe cannot be found under the specified path'))
|
||||
Avalon.error(_('Please check the configuration file settings'))
|
||||
raise FileNotFoundError(self.ffmpeg_settings['ffmpeg_path'])
|
||||
|
||||
# get number of extracted frames
|
||||
self.total_frames = 0
|
||||
for directory in extracted_frames_directories:
|
||||
self.total_frames += len([f for f in directory.iterdir() if str(f).lower().endswith(self.image_format.lower())])
|
||||
# check if driver settings
|
||||
driver_settings = copy.deepcopy(self.driver_settings)
|
||||
driver_path = driver_settings.pop('path')
|
||||
|
||||
with tqdm(total=self.total_frames, ascii=True, desc=_('Upscaling Progress')) as progress_bar:
|
||||
# check if driver path exists
|
||||
if not (pathlib.Path(driver_path).is_file() or pathlib.Path(f'{driver_path}.exe').is_file()):
|
||||
Avalon.error(_('Specified driver executable directory doesn\'t exist'))
|
||||
Avalon.error(_('Please check the configuration file settings'))
|
||||
raise FileNotFoundError(driver_path)
|
||||
|
||||
# tqdm update method adds the value to the progress
|
||||
# bar instead of setting the value. Therefore, a delta
|
||||
# needs to be calculated.
|
||||
previous_cycle_frames = 0
|
||||
while not self.progress_bar_exit_signal:
|
||||
# parse driver arguments using driver's parser
|
||||
# the parser will throw AttributeError if argument doesn't satisfy constraints
|
||||
try:
|
||||
driver_arguments = []
|
||||
for key in driver_settings.keys():
|
||||
|
||||
with contextlib.suppress(FileNotFoundError):
|
||||
self.total_frames_upscaled = len([f for f in self.upscaled_frames.iterdir() if str(f).lower().endswith(self.image_format.lower())])
|
||||
delta = self.total_frames_upscaled - previous_cycle_frames
|
||||
previous_cycle_frames = self.total_frames_upscaled
|
||||
value = driver_settings[key]
|
||||
|
||||
# if upscaling is finished
|
||||
if self.total_frames_upscaled >= self.total_frames:
|
||||
return
|
||||
if value is None or value is False:
|
||||
continue
|
||||
|
||||
# adds the delta into the progress bar
|
||||
progress_bar.update(delta)
|
||||
else:
|
||||
if len(key) == 1:
|
||||
driver_arguments.append(f'-{key}')
|
||||
else:
|
||||
driver_arguments.append(f'--{key}')
|
||||
# true means key is an option
|
||||
if value is not True:
|
||||
driver_arguments.append(str(value))
|
||||
|
||||
time.sleep(1)
|
||||
DriverWrapperMain = getattr(importlib.import_module(f'wrappers.{self.driver}'), 'WrapperMain')
|
||||
DriverWrapperMain.parse_arguments(driver_arguments)
|
||||
except AttributeError as e:
|
||||
Avalon.error(_('Failed to parse driver argument: {}').format(e.args[0]))
|
||||
raise e
|
||||
|
||||
def _upscale_frames(self):
|
||||
""" Upscale video frames with waifu2x-caffe
|
||||
@ -183,16 +213,10 @@ class Upscaler:
|
||||
w2 {Waifu2x Object} -- initialized waifu2x object
|
||||
"""
|
||||
|
||||
# progress bar process exit signal
|
||||
self.progress_bar_exit_signal = False
|
||||
|
||||
# initialize waifu2x driver
|
||||
if self.driver not in AVAILABLE_DRIVERS:
|
||||
raise UnrecognizedDriverError(_('Unrecognized driver: {}').format(self.driver))
|
||||
|
||||
# create a container for all upscaler processes
|
||||
upscaler_processes = []
|
||||
|
||||
# list all images in the extracted frames
|
||||
frames = [(self.extracted_frames / f) for f in self.extracted_frames.iterdir() if f.is_file]
|
||||
|
||||
@ -234,7 +258,7 @@ class Upscaler:
|
||||
|
||||
# if the driver being used is waifu2x-caffe
|
||||
if self.driver == 'waifu2x_caffe':
|
||||
upscaler_processes.append(driver.upscale(process_directory,
|
||||
self.process_pool.append(driver.upscale(process_directory,
|
||||
self.upscaled_frames,
|
||||
self.scale_ratio,
|
||||
self.scale_width,
|
||||
@ -244,7 +268,7 @@ class Upscaler:
|
||||
|
||||
# if the driver being used is waifu2x-converter-cpp
|
||||
elif self.driver == 'waifu2x_converter_cpp':
|
||||
upscaler_processes.append(driver.upscale(process_directory,
|
||||
self.process_pool.append(driver.upscale(process_directory,
|
||||
self.upscaled_frames,
|
||||
self.scale_ratio,
|
||||
self.processes,
|
||||
@ -252,55 +276,99 @@ class Upscaler:
|
||||
|
||||
# if the driver being used is waifu2x-ncnn-vulkan
|
||||
elif self.driver == 'waifu2x_ncnn_vulkan':
|
||||
upscaler_processes.append(driver.upscale(process_directory,
|
||||
self.process_pool.append(driver.upscale(process_directory,
|
||||
self.upscaled_frames,
|
||||
self.scale_ratio))
|
||||
|
||||
# if the driver being used is srmd_ncnn_vulkan
|
||||
elif self.driver == 'srmd_ncnn_vulkan':
|
||||
upscaler_processes.append(driver.upscale(process_directory,
|
||||
self.process_pool.append(driver.upscale(process_directory,
|
||||
self.upscaled_frames,
|
||||
self.scale_ratio))
|
||||
|
||||
# start progress bar in a different thread
|
||||
progress_bar = threading.Thread(target=self._progress_bar, args=(process_directories,))
|
||||
progress_bar.start()
|
||||
Avalon.debug_info(_('Starting progress monitor'))
|
||||
self.progress_monitor = ProgressMonitor(self, process_directories)
|
||||
self.progress_monitor.start()
|
||||
|
||||
# create the clearer and start it
|
||||
Avalon.debug_info(_('Starting upscaled image cleaner'))
|
||||
image_cleaner = ImageCleaner(self.extracted_frames, self.upscaled_frames, len(upscaler_processes))
|
||||
image_cleaner.start()
|
||||
self.image_cleaner = ImageCleaner(self.extracted_frames, self.upscaled_frames, len(self.process_pool))
|
||||
self.image_cleaner.start()
|
||||
|
||||
# wait for all process to exit
|
||||
try:
|
||||
Avalon.debug_info(_('Main process waiting for subprocesses to exit'))
|
||||
for process in upscaler_processes:
|
||||
Avalon.debug_info(_('Subprocess {} exited with code {}').format(process.pid, process.wait()))
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
Avalon.warning('Exit signal received')
|
||||
Avalon.warning('Killing processes')
|
||||
for process in upscaler_processes:
|
||||
process.terminate()
|
||||
self._wait()
|
||||
except (Exception, KeyboardInterrupt, SystemExit) as e:
|
||||
# cleanup
|
||||
Avalon.debug_info(_('Killing progress monitor'))
|
||||
self.progress_monitor.stop()
|
||||
|
||||
# cleanup and exit with exit code 1
|
||||
Avalon.debug_info(_('Killing upscaled image cleaner'))
|
||||
image_cleaner.stop()
|
||||
self.progress_bar_exit_signal = True
|
||||
sys.exit(1)
|
||||
self.image_cleaner.stop()
|
||||
raise e
|
||||
|
||||
# if the driver is waifu2x-converter-cpp
|
||||
# images need to be renamed to be recognizable for FFmpeg
|
||||
if self.driver == 'waifu2x_converter_cpp':
|
||||
for image in [f for f in self.upscaled_frames.iterdir() if f.is_file()]:
|
||||
renamed = re.sub(f'_\\[.*\\]\\[x(\\d+(\\.\\d+)?)\\]\\.{self.image_format}', f'.{self.image_format}', str(image.name))
|
||||
renamed = re.sub(f'_\\[.*\\]\\[x(\\d+(\\.\\d+)?)\\]\\.{self.image_format}',
|
||||
f'.{self.image_format}',
|
||||
str(image.name))
|
||||
(self.upscaled_frames / image).rename(self.upscaled_frames / renamed)
|
||||
|
||||
# upscaling done, kill the clearer
|
||||
Avalon.debug_info(_('Killing upscaled image cleaner'))
|
||||
image_cleaner.stop()
|
||||
# upscaling done, kill helper threads
|
||||
Avalon.debug_info(_('Killing progress monitor'))
|
||||
self.progress_monitor.stop()
|
||||
|
||||
# pass exit signal to progress bar thread
|
||||
self.progress_bar_exit_signal = True
|
||||
Avalon.debug_info(_('Killing upscaled image cleaner'))
|
||||
self.image_cleaner.stop()
|
||||
|
||||
def _terminate_subprocesses(self):
|
||||
Avalon.warning(_('Terminating all processes'))
|
||||
for process in self.process_pool:
|
||||
process.terminate()
|
||||
|
||||
def _wait(self):
|
||||
""" wait for subprocesses in process pool to complete
|
||||
"""
|
||||
Avalon.debug_info(_('Main process waiting for subprocesses to exit'))
|
||||
|
||||
try:
|
||||
# while process pool not empty
|
||||
while self.process_pool:
|
||||
|
||||
# if stop signal received, terminate all processes
|
||||
if self.stop_signal is True:
|
||||
raise SystemExit
|
||||
|
||||
for process in self.process_pool:
|
||||
process_status = process.poll()
|
||||
|
||||
# if process finished
|
||||
if process_status is None:
|
||||
continue
|
||||
|
||||
# if return code is not 0
|
||||
elif process_status != 0:
|
||||
Avalon.error(_('Subprocess {} exited with code {}').format(process.pid, process_status))
|
||||
raise subprocess.CalledProcessError(process_status, process.args)
|
||||
|
||||
else:
|
||||
Avalon.debug_info(_('Subprocess {} exited with code {}').format(process.pid, process_status))
|
||||
self.process_pool.remove(process)
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
except (KeyboardInterrupt, SystemExit) as e:
|
||||
Avalon.warning(_('Stop signal received'))
|
||||
self._terminate_subprocesses()
|
||||
raise e
|
||||
|
||||
except (Exception, subprocess.CalledProcessError) as e:
|
||||
Avalon.error(_('Subprocess execution ran into an error'))
|
||||
self._terminate_subprocesses()
|
||||
raise e
|
||||
|
||||
def run(self):
|
||||
""" Main controller for Video2X
|
||||
@ -308,94 +376,125 @@ class Upscaler:
|
||||
This function controls the flow of video conversion
|
||||
and handles all necessary functions.
|
||||
"""
|
||||
|
||||
# external stop signal when called in a thread
|
||||
self.stop_signal = False
|
||||
|
||||
# define process pool to contain processes
|
||||
self.process_pool = []
|
||||
|
||||
# parse arguments for waifu2x
|
||||
# check argument sanity
|
||||
self._check_arguments()
|
||||
|
||||
# convert paths to absolute paths
|
||||
self.input_video = self.input_video.absolute()
|
||||
self.output_video = self.output_video.absolute()
|
||||
# define processing queue
|
||||
processing_queue = queue.Queue()
|
||||
|
||||
# drivers that have native support for video processing
|
||||
if self.driver == 'anime4kcpp':
|
||||
# append FFmpeg path to the end of PATH
|
||||
# Anime4KCPP will then use FFmpeg to migrate audio tracks
|
||||
os.environ['PATH'] += f';{self.ffmpeg_settings["ffmpeg_path"]}'
|
||||
Avalon.info(_('Starting to upscale extracted images'))
|
||||
# if input specified is single file
|
||||
if self.input_path.is_file():
|
||||
Avalon.info(_('Upscaling single video file: {}').format(self.input_path))
|
||||
processing_queue.put((self.input_path.absolute(), self.output_path.absolute()))
|
||||
|
||||
# import and initialize Anime4KCPP wrapper
|
||||
DriverWrapperMain = getattr(importlib.import_module('wrappers.anime4kcpp'), 'WrapperMain')
|
||||
driver = DriverWrapperMain(copy.deepcopy(self.driver_settings))
|
||||
# if input specified is a directory
|
||||
elif self.input_path.is_dir():
|
||||
|
||||
# run Anime4KCPP
|
||||
driver.upscale(self.input_video, self.output_video, self.scale_ratio, self.processes).wait()
|
||||
Avalon.info(_('Upscaling completed'))
|
||||
# make output directory if it doesn't exist
|
||||
self.output_path.mkdir(parents=True, exist_ok=True)
|
||||
for input_video in [f for f in self.input_path.iterdir() if f.is_file()]:
|
||||
output_video = self.output_path / input_video.name
|
||||
processing_queue.put((input_video.absolute(), output_video.absolute()))
|
||||
|
||||
else:
|
||||
self.create_temp_directories()
|
||||
while not processing_queue.empty():
|
||||
input_video, output_video = processing_queue.get()
|
||||
# drivers that have native support for video processing
|
||||
if self.driver == 'anime4kcpp':
|
||||
# append FFmpeg path to the end of PATH
|
||||
# Anime4KCPP will then use FFmpeg to migrate audio tracks
|
||||
os.environ['PATH'] += f';{self.ffmpeg_settings["ffmpeg_path"]}'
|
||||
Avalon.info(_('Starting to upscale extracted images'))
|
||||
|
||||
# initialize objects for ffmpeg and waifu2x-caffe
|
||||
fm = Ffmpeg(self.ffmpeg_settings, self.image_format)
|
||||
# import and initialize Anime4KCPP wrapper
|
||||
DriverWrapperMain = getattr(importlib.import_module('wrappers.anime4kcpp'), 'WrapperMain')
|
||||
driver = DriverWrapperMain(copy.deepcopy(self.driver_settings))
|
||||
|
||||
Avalon.info(_('Reading video information'))
|
||||
video_info = fm.get_video_info(self.input_video)
|
||||
# analyze original video with ffprobe and retrieve framerate
|
||||
# width, height = info['streams'][0]['width'], info['streams'][0]['height']
|
||||
# run Anime4KCPP
|
||||
self.process_pool.append(driver.upscale(input_video, output_video, self.scale_ratio, self.processes))
|
||||
self._wait()
|
||||
Avalon.info(_('Upscaling completed'))
|
||||
|
||||
# find index of video stream
|
||||
video_stream_index = None
|
||||
for stream in video_info['streams']:
|
||||
if stream['codec_type'] == 'video':
|
||||
video_stream_index = stream['index']
|
||||
break
|
||||
else:
|
||||
try:
|
||||
self.create_temp_directories()
|
||||
|
||||
# exit if no video stream found
|
||||
if video_stream_index is None:
|
||||
Avalon.error(_('Aborting: No video stream found'))
|
||||
raise StreamNotFoundError('no video stream found')
|
||||
# initialize objects for ffmpeg and waifu2x-caffe
|
||||
fm = Ffmpeg(self.ffmpeg_settings, self.image_format)
|
||||
|
||||
# extract frames from video
|
||||
fm.extract_frames(self.input_video, self.extracted_frames)
|
||||
Avalon.info(_('Reading video information'))
|
||||
video_info = fm.get_video_info(input_video)
|
||||
# analyze original video with ffprobe and retrieve framerate
|
||||
# width, height = info['streams'][0]['width'], info['streams'][0]['height']
|
||||
|
||||
# get average frame rate of video stream
|
||||
framerate = float(Fraction(video_info['streams'][video_stream_index]['avg_frame_rate']))
|
||||
fm.pixel_format = video_info['streams'][video_stream_index]['pix_fmt']
|
||||
# find index of video stream
|
||||
video_stream_index = None
|
||||
for stream in video_info['streams']:
|
||||
if stream['codec_type'] == 'video':
|
||||
video_stream_index = stream['index']
|
||||
break
|
||||
|
||||
# get a dict of all pixel formats and corresponding bit depth
|
||||
pixel_formats = fm.get_pixel_formats()
|
||||
# exit if no video stream found
|
||||
if video_stream_index is None:
|
||||
Avalon.error(_('Aborting: No video stream found'))
|
||||
raise StreamNotFoundError('no video stream found')
|
||||
|
||||
# try getting pixel format's corresponding bti depth
|
||||
try:
|
||||
self.bit_depth = pixel_formats[fm.pixel_format]
|
||||
except KeyError:
|
||||
Avalon.error(_('Unsupported pixel format: {}').format(fm.pixel_format))
|
||||
raise UnsupportedPixelError(f'unsupported pixel format {fm.pixel_format}')
|
||||
# extract frames from video
|
||||
self.process_pool.append((fm.extract_frames(input_video, self.extracted_frames)))
|
||||
self._wait()
|
||||
|
||||
Avalon.info(_('Framerate: {}').format(framerate))
|
||||
# get average frame rate of video stream
|
||||
framerate = float(Fraction(video_info['streams'][video_stream_index]['avg_frame_rate']))
|
||||
fm.pixel_format = video_info['streams'][video_stream_index]['pix_fmt']
|
||||
|
||||
# width/height will be coded width/height x upscale factor
|
||||
if self.scale_ratio:
|
||||
original_width = video_info['streams'][video_stream_index]['width']
|
||||
original_height = video_info['streams'][video_stream_index]['height']
|
||||
self.scale_width = int(self.scale_ratio * original_width)
|
||||
self.scale_height = int(self.scale_ratio * original_height)
|
||||
# get a dict of all pixel formats and corresponding bit depth
|
||||
pixel_formats = fm.get_pixel_formats()
|
||||
|
||||
# upscale images one by one using waifu2x
|
||||
Avalon.info(_('Starting to upscale extracted images'))
|
||||
self._upscale_frames()
|
||||
Avalon.info(_('Upscaling completed'))
|
||||
# try getting pixel format's corresponding bti depth
|
||||
try:
|
||||
self.bit_depth = pixel_formats[fm.pixel_format]
|
||||
except KeyError:
|
||||
Avalon.error(_('Unsupported pixel format: {}').format(fm.pixel_format))
|
||||
raise UnsupportedPixelError(f'unsupported pixel format {fm.pixel_format}')
|
||||
|
||||
# frames to Video
|
||||
Avalon.info(_('Converting extracted frames into video'))
|
||||
Avalon.info(_('Framerate: {}').format(framerate))
|
||||
|
||||
# use user defined output size
|
||||
fm.convert_video(framerate, f'{self.scale_width}x{self.scale_height}', self.upscaled_frames)
|
||||
Avalon.info(_('Conversion completed'))
|
||||
# width/height will be coded width/height x upscale factor
|
||||
if self.scale_ratio:
|
||||
original_width = video_info['streams'][video_stream_index]['width']
|
||||
original_height = video_info['streams'][video_stream_index]['height']
|
||||
self.scale_width = int(self.scale_ratio * original_width)
|
||||
self.scale_height = int(self.scale_ratio * original_height)
|
||||
|
||||
# migrate audio tracks and subtitles
|
||||
Avalon.info(_('Migrating audio tracks and subtitles to upscaled video'))
|
||||
fm.migrate_audio_tracks_subtitles(self.input_video, self.output_video, self.upscaled_frames)
|
||||
# upscale images one by one using waifu2x
|
||||
Avalon.info(_('Starting to upscale extracted images'))
|
||||
self._upscale_frames()
|
||||
Avalon.info(_('Upscaling completed'))
|
||||
|
||||
# destroy temp directories
|
||||
self.cleanup_temp_directories()
|
||||
# frames to Video
|
||||
Avalon.info(_('Converting extracted frames into video'))
|
||||
|
||||
# use user defined output size
|
||||
self.process_pool.append(fm.convert_video(framerate, f'{self.scale_width}x{self.scale_height}', self.upscaled_frames))
|
||||
self._wait()
|
||||
Avalon.info(_('Conversion completed'))
|
||||
|
||||
# migrate audio tracks and subtitles
|
||||
Avalon.info(_('Migrating audio tracks and subtitles to upscaled video'))
|
||||
self.process_pool.append(fm.migrate_audio_tracks_subtitles(input_video, output_video, self.upscaled_frames))
|
||||
self._wait()
|
||||
|
||||
# destroy temp directories
|
||||
self.cleanup_temp_directories()
|
||||
|
||||
except (Exception, KeyboardInterrupt, SystemExit) as e:
|
||||
with contextlib.suppress(ValueError):
|
||||
self.cleanup_temp_directories()
|
||||
raise e
|
||||
|
134
src/video2x.py
134
src/video2x.py
@ -13,7 +13,7 @@ __ __ _ _ ___ __ __
|
||||
Name: Video2X Controller
|
||||
Creator: K4YT3X
|
||||
Date Created: Feb 24, 2018
|
||||
Last Modified: May 4, 2020
|
||||
Last Modified: May 7, 2020
|
||||
|
||||
Editor: BrianPetkovsek
|
||||
Last Modified: June 17, 2019
|
||||
@ -181,20 +181,6 @@ config = read_config(video2x_args.config)
|
||||
driver_settings = config[video2x_args.driver]
|
||||
driver_settings['path'] = os.path.expandvars(driver_settings['path'])
|
||||
|
||||
# overwrite driver_settings with driver_args
|
||||
if driver_args is not None:
|
||||
driver_args_dict = vars(driver_args)
|
||||
for key in driver_args_dict:
|
||||
if driver_args_dict[key] is not None:
|
||||
driver_settings[key] = driver_args_dict[key]
|
||||
|
||||
# check if driver path exists
|
||||
if not pathlib.Path(driver_settings['path']).exists():
|
||||
if not pathlib.Path(f'{driver_settings["path"]}.exe').exists():
|
||||
Avalon.error(_('Specified driver executable directory doesn\'t exist'))
|
||||
Avalon.error(_('Please check the configuration file settings'))
|
||||
raise FileNotFoundError(driver_settings['path'])
|
||||
|
||||
# read FFmpeg configuration
|
||||
ffmpeg_settings = config['ffmpeg']
|
||||
ffmpeg_settings['ffmpeg_path'] = os.path.expandvars(ffmpeg_settings['ffmpeg_path'])
|
||||
@ -202,113 +188,41 @@ ffmpeg_settings['ffmpeg_path'] = os.path.expandvars(ffmpeg_settings['ffmpeg_path
|
||||
# load video2x settings
|
||||
image_format = config['video2x']['image_format'].lower()
|
||||
preserve_frames = config['video2x']['preserve_frames']
|
||||
video2x_cache_directory = config['video2x']['video2x_cache_directory']
|
||||
|
||||
# load cache directory
|
||||
if config['video2x']['video2x_cache_directory'] is not None:
|
||||
video2x_cache_directory = pathlib.Path(config['video2x']['video2x_cache_directory'])
|
||||
else:
|
||||
video2x_cache_directory = pathlib.Path(tempfile.gettempdir()) / 'video2x'
|
||||
|
||||
if video2x_cache_directory.exists() and not video2x_cache_directory.is_dir():
|
||||
Avalon.error(_('Specified cache directory is a file/link'))
|
||||
raise FileExistsError('Specified cache directory is a file/link')
|
||||
|
||||
# if cache directory doesn't exist
|
||||
# ask the user if it should be created
|
||||
elif not video2x_cache_directory.exists():
|
||||
try:
|
||||
Avalon.debug_info(_('Creating cache directory {}').format(video2x_cache_directory))
|
||||
video2x_cache_directory.mkdir(parents=True, exist_ok=True)
|
||||
# there can be a number of exceptions here
|
||||
# PermissionError, FileExistsError, etc.
|
||||
# therefore, we put a catch-them-all here
|
||||
except Exception as exception:
|
||||
Avalon.error(_('Unable to create {}').format(video2x_cache_directory))
|
||||
raise exception
|
||||
|
||||
# overwrite driver_settings with driver_args
|
||||
if driver_args is not None:
|
||||
driver_args_dict = vars(driver_args)
|
||||
for key in driver_args_dict:
|
||||
if driver_args_dict[key] is not None:
|
||||
driver_settings[key] = driver_args_dict[key]
|
||||
|
||||
# start execution
|
||||
try:
|
||||
# start timer
|
||||
begin_time = time.time()
|
||||
|
||||
# if input specified is a single file
|
||||
if video2x_args.input.is_file():
|
||||
# initialize upscaler object
|
||||
upscaler = Upscaler(input_path=video2x_args.input,
|
||||
output_path=video2x_args.output,
|
||||
driver_settings=driver_settings,
|
||||
ffmpeg_settings=ffmpeg_settings)
|
||||
|
||||
# upscale single video file
|
||||
Avalon.info(_('Upscaling single video file: {}').format(video2x_args.input))
|
||||
# set upscaler optional options
|
||||
upscaler.driver = video2x_args.driver
|
||||
upscaler.scale_width = video2x_args.width
|
||||
upscaler.scale_height = video2x_args.height
|
||||
upscaler.scale_ratio = video2x_args.ratio
|
||||
upscaler.processes = video2x_args.processes
|
||||
upscaler.video2x_cache_directory = video2x_cache_directory
|
||||
upscaler.image_format = image_format
|
||||
upscaler.preserve_frames = preserve_frames
|
||||
|
||||
# check for input output format mismatch
|
||||
if video2x_args.output.is_dir():
|
||||
Avalon.error(_('Input and output path type mismatch'))
|
||||
Avalon.error(_('Input is single file but output is directory'))
|
||||
raise Exception('input output path type mismatch')
|
||||
if not re.search(r'.*\..*$', str(video2x_args.output)):
|
||||
Avalon.error(_('No suffix found in output file path'))
|
||||
Avalon.error(_('Suffix must be specified for FFmpeg'))
|
||||
raise Exception('No suffix specified')
|
||||
|
||||
upscaler = Upscaler(input_video=video2x_args.input,
|
||||
output_video=video2x_args.output,
|
||||
driver_settings=driver_settings,
|
||||
ffmpeg_settings=ffmpeg_settings)
|
||||
|
||||
# set optional options
|
||||
upscaler.driver = video2x_args.driver
|
||||
upscaler.scale_width = video2x_args.width
|
||||
upscaler.scale_height = video2x_args.height
|
||||
upscaler.scale_ratio = video2x_args.ratio
|
||||
upscaler.processes = video2x_args.processes
|
||||
upscaler.video2x_cache_directory = video2x_cache_directory
|
||||
upscaler.image_format = image_format
|
||||
upscaler.preserve_frames = preserve_frames
|
||||
|
||||
# run upscaler
|
||||
upscaler.run()
|
||||
|
||||
# if input specified is a directory
|
||||
elif video2x_args.input.is_dir():
|
||||
# upscale videos in a directory
|
||||
Avalon.info(_('Upscaling videos in directory: {}').format(video2x_args.input))
|
||||
|
||||
# make output directory if it doesn't exist
|
||||
video2x_args.output.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for input_video in [f for f in video2x_args.input.iterdir() if f.is_file()]:
|
||||
output_video = video2x_args.output / input_video.name
|
||||
upscaler = Upscaler(input_video=input_video,
|
||||
output_video=output_video,
|
||||
driver_settings=driver_settings,
|
||||
ffmpeg_settings=ffmpeg_settings)
|
||||
|
||||
# set optional options
|
||||
upscaler.driver = video2x_args.driver
|
||||
upscaler.scale_width = video2x_args.width
|
||||
upscaler.scale_height = video2x_args.height
|
||||
upscaler.scale_ratio = video2x_args.ratio
|
||||
upscaler.processes = video2x_args.processes
|
||||
upscaler.video2x_cache_directory = video2x_cache_directory
|
||||
upscaler.image_format = image_format
|
||||
upscaler.preserve_frames = preserve_frames
|
||||
|
||||
# run upscaler
|
||||
upscaler.run()
|
||||
else:
|
||||
Avalon.error(_('Input path is neither a file nor a directory'))
|
||||
raise FileNotFoundError(f'{video2x_args.input} is neither file nor directory')
|
||||
# run upscaler
|
||||
upscaler.run()
|
||||
|
||||
Avalon.info(_('Program completed, taking {} seconds').format(round((time.time() - begin_time), 5)))
|
||||
|
||||
except Exception:
|
||||
Avalon.error(_('An exception has occurred'))
|
||||
traceback.print_exc()
|
||||
|
||||
# try cleaning up temp directories
|
||||
with contextlib.suppress(Exception):
|
||||
upscaler.cleanup_temp_directories()
|
||||
|
||||
finally:
|
||||
# remove Video2X cache directory
|
||||
with contextlib.suppress(FileNotFoundError):
|
||||
if not preserve_frames:
|
||||
shutil.rmtree(video2x_cache_directory)
|
||||
|
@ -15,7 +15,6 @@ import contextlib
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
@ -54,6 +53,7 @@ def resource_path(relative_path: str) -> pathlib.Path:
|
||||
class WorkerSignals(QObject):
|
||||
progress = pyqtSignal(tuple)
|
||||
error = pyqtSignal(str)
|
||||
interrupted = pyqtSignal()
|
||||
finished = pyqtSignal()
|
||||
|
||||
class ProgressBarWorker(QRunnable):
|
||||
@ -89,11 +89,13 @@ class UpscalerWorker(QRunnable):
|
||||
# Retrieve args/kwargs here; and fire processing using them
|
||||
try:
|
||||
self.fn(*self.args, **self.kwargs)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
self.signals.interrupted.emit()
|
||||
except Exception:
|
||||
error_message = traceback.format_exc()
|
||||
print(error_message, file=sys.stderr)
|
||||
self.signals.error.emit(error_message)
|
||||
finally:
|
||||
else:
|
||||
self.signals.finished.emit()
|
||||
|
||||
class Video2XMainWindow(QtWidgets.QMainWindow):
|
||||
@ -141,7 +143,7 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
|
||||
|
||||
# express settings
|
||||
self.driver_combo_box = self.findChild(QtWidgets.QComboBox, 'driverComboBox')
|
||||
self.driver_combo_box.currentTextChanged.connect(self.update_driver_constraints)
|
||||
self.driver_combo_box.currentTextChanged.connect(self.update_gui_for_driver)
|
||||
self.processes_spin_box = self.findChild(QtWidgets.QSpinBox, 'processesSpinBox')
|
||||
self.scale_ratio_double_spin_box = self.findChild(QtWidgets.QDoubleSpinBox, 'scaleRatioDoubleSpinBox')
|
||||
self.preserve_frames_check_box = self.findChild(QtWidgets.QCheckBox, 'preserveFramesCheckBox')
|
||||
@ -258,25 +260,10 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
|
||||
self.ffmpeg_settings = self.config['ffmpeg']
|
||||
self.ffmpeg_settings['ffmpeg_path'] = os.path.expandvars(self.ffmpeg_settings['ffmpeg_path'])
|
||||
|
||||
# load cache directory, create it if necessary
|
||||
if self.config['video2x']['video2x_cache_directory'] is not None:
|
||||
video2x_cache_directory = pathlib.Path(self.config['video2x']['video2x_cache_directory'])
|
||||
else:
|
||||
video2x_cache_directory = pathlib.Path(tempfile.gettempdir()) / 'video2x'
|
||||
|
||||
if video2x_cache_directory.exists() and not video2x_cache_directory.is_dir():
|
||||
self.show_error('Specified cache directory is a file/link')
|
||||
raise FileExistsError('Specified cache directory is a file/link')
|
||||
|
||||
# if cache directory doesn't exist
|
||||
# ask the user if it should be created
|
||||
elif not video2x_cache_directory.exists():
|
||||
try:
|
||||
video2x_cache_directory.mkdir(parents=True, exist_ok=True)
|
||||
except Exception as exception:
|
||||
self.show_error(f'Unable to create cache directory: {video2x_cache_directory}')
|
||||
raise exception
|
||||
self.cache_line_edit.setText(str(video2x_cache_directory.absolute()))
|
||||
# set cache directory path
|
||||
if self.config['video2x']['video2x_cache_directory'] is None:
|
||||
video2x_cache_directory = str((pathlib.Path(tempfile.gettempdir()) / 'video2x').absolute())
|
||||
self.cache_line_edit.setText(video2x_cache_directory)
|
||||
|
||||
# load preserve frames settings
|
||||
self.preserve_frames_check_box.setChecked(self.config['video2x']['preserve_frames'])
|
||||
@ -399,8 +386,10 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
|
||||
self.config['anime4kcpp']['postprocessing'] = bool(self.anime4kcpp_post_processing_check_box.checkState())
|
||||
self.config['anime4kcpp']['GPUMode'] = bool(self.anime4kcpp_gpu_mode_check_box.checkState())
|
||||
|
||||
def update_driver_constraints(self):
|
||||
def update_gui_for_driver(self):
|
||||
current_driver = AVAILABLE_DRIVERS[self.driver_combo_box.currentText()]
|
||||
|
||||
# update scale ratio constraints
|
||||
if current_driver in ['waifu2x_caffe', 'waifu2x_converter_cpp', 'anime4kcpp']:
|
||||
self.scale_ratio_double_spin_box.setMinimum(0.0)
|
||||
self.scale_ratio_double_spin_box.setMaximum(999.0)
|
||||
@ -413,14 +402,29 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
|
||||
self.scale_ratio_double_spin_box.setMinimum(2.0)
|
||||
self.scale_ratio_double_spin_box.setMaximum(4.0)
|
||||
self.scale_ratio_double_spin_box.setValue(2.0)
|
||||
|
||||
# update preferred processes/threads count
|
||||
if current_driver == 'anime4kcpp':
|
||||
self.processes_spin_box.setValue(16)
|
||||
else:
|
||||
self.processes_spin_box.setValue(1)
|
||||
|
||||
def select_file(self, *args, **kwargs) -> pathlib.Path:
|
||||
file_selected = QtWidgets.QFileDialog.getOpenFileName(self, *args, **kwargs)
|
||||
if not isinstance(file_selected, tuple) or file_selected[0] == '':
|
||||
return None
|
||||
return pathlib.Path(file_selected[0])
|
||||
|
||||
def select_folder(self, *args, **kwargs) -> pathlib.Path:
|
||||
folder_selected = QtWidgets.QFileDialog.getExistingDirectory(self, *args, **kwargs)
|
||||
if folder_selected == '':
|
||||
return None
|
||||
return pathlib.Path(folder_selected)
|
||||
|
||||
def select_input_file(self):
|
||||
input_file = QtWidgets.QFileDialog.getOpenFileName(self, 'Select Input File')
|
||||
if not isinstance(input_file, tuple) or input_file[0] == '':
|
||||
|
||||
if (input_file := self.select_file('Select Input File')) is None:
|
||||
return
|
||||
|
||||
input_file = pathlib.Path(input_file[0])
|
||||
|
||||
self.input_line_edit.setText(str(input_file.absolute()))
|
||||
|
||||
# try to set an output file name automatically
|
||||
@ -435,11 +439,9 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
|
||||
self.output_line_edit.setText(str(output_file.absolute()))
|
||||
|
||||
def select_input_folder(self):
|
||||
input_folder = QtWidgets.QFileDialog.getExistingDirectory(self, 'Select Input Folder')
|
||||
if input_folder == '':
|
||||
return
|
||||
|
||||
input_folder = pathlib.Path(input_folder)
|
||||
if (input_folder := self.select_folder('Select Input Folder')) is None:
|
||||
return
|
||||
|
||||
self.input_line_edit.setText(str(input_folder.absolute()))
|
||||
|
||||
@ -455,40 +457,30 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
|
||||
self.output_line_edit.setText(str(output_folder.absolute()))
|
||||
|
||||
def select_output_file(self):
|
||||
output_file = QtWidgets.QFileDialog.getOpenFileName(self, 'Select Output File')
|
||||
if not isinstance(output_file, tuple):
|
||||
if (output_file := self.select_file('Select Output File')) is None:
|
||||
return
|
||||
|
||||
self.output_line_edit.setText(str(pathlib.Path(output_file[0]).absolute()))
|
||||
self.output_line_edit.setText(str(output_file.absolute()))
|
||||
|
||||
def select_output_folder(self):
|
||||
output_folder = QtWidgets.QFileDialog.getSaveFileName(self, 'Select Output Folder')
|
||||
if output_folder == '':
|
||||
if (output_folder := self.select_folder('Select Output Folder')) is None:
|
||||
return
|
||||
|
||||
self.output_line_edit.setText(str(pathlib.Path(output_folder).absolute()))
|
||||
self.output_line_edit.setText(str(output_folder.absolute()))
|
||||
|
||||
def select_cache_folder(self):
|
||||
cache_folder = QtWidgets.QFileDialog.getExistingDirectory(self, 'Select Cache Folder')
|
||||
if cache_folder == '':
|
||||
if (cache_folder := self.select_folder('Select Cache Folder')) is None:
|
||||
return
|
||||
|
||||
self.cache_line_edit.setText(str(pathlib.Path(cache_folder).absolute()))
|
||||
self.cache_line_edit.setText(str(cache_folder.absolute()))
|
||||
|
||||
def select_config_file(self):
|
||||
config_file = QtWidgets.QFileDialog.getOpenFileName(self, 'Select Config File', filter='(YAML files (*.yaml))')
|
||||
if not isinstance(config_file, tuple):
|
||||
if (config_file := self.select_file('Select Config File', filter='(YAML files (*.yaml))')) is None:
|
||||
return
|
||||
|
||||
self.config_line_edit.setText(str(pathlib.Path(config_file[0]).absolute()))
|
||||
self.config_line_edit.setText(str(config_file.absolute()))
|
||||
self.load_configurations()
|
||||
|
||||
def select_driver_binary_path(self, driver_line_edit):
|
||||
driver_binary_path = QtWidgets.QFileDialog.getOpenFileName(self, 'Select Driver Binary File')
|
||||
if not isinstance(driver_binary_path, tuple) or driver_binary_path[0] == '':
|
||||
if (driver_binary_path := self.select_file('Select Driver Binary File')) is None:
|
||||
return
|
||||
|
||||
driver_line_edit.setText(str(pathlib.Path(driver_binary_path[0]).absolute()))
|
||||
driver_line_edit.setText(str(driver_binary_path.absolute()))
|
||||
|
||||
def show_error(self, message: str):
|
||||
QtWidgets.QErrorMessage(self).showMessage(message.replace('\n', '<br>'))
|
||||
@ -504,19 +496,24 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
|
||||
message_box.exec_()
|
||||
|
||||
def start_progress_bar(self, progress_callback):
|
||||
|
||||
# initialize variables early
|
||||
self.upscaler.progress_bar_exit_signal = False
|
||||
self.upscaler.total_frames_upscaled = 0
|
||||
self.upscaler.total_frames = 1
|
||||
# wait for progress monitor to come online
|
||||
while 'progress_monitor' not in self.upscaler.__dict__:
|
||||
if self.upscaler.stop_signal:
|
||||
return
|
||||
time.sleep(0.1)
|
||||
|
||||
# initialize progress bar values
|
||||
upscale_begin_time = time.time()
|
||||
progress_callback.emit((0, 0, 0, upscale_begin_time))
|
||||
|
||||
# keep querying upscaling process and feed information to callback signal
|
||||
while not self.upscaler.progress_bar_exit_signal:
|
||||
progress_callback.emit((int(100 * self.upscaler.total_frames_upscaled / self.upscaler.total_frames),
|
||||
while self.upscaler.progress_monitor.running:
|
||||
try:
|
||||
progress_percentage = int(100 * self.upscaler.total_frames_upscaled / self.upscaler.total_frames)
|
||||
except ZeroDivisionError:
|
||||
progress_percentage = 0
|
||||
|
||||
progress_callback.emit((progress_percentage,
|
||||
self.upscaler.total_frames_upscaled,
|
||||
self.upscaler.total_frames,
|
||||
upscale_begin_time))
|
||||
@ -574,109 +571,60 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
|
||||
# load driver settings for the current driver
|
||||
self.driver_settings = self.config[AVAILABLE_DRIVERS[self.driver_combo_box.currentText()]]
|
||||
|
||||
# if input specified is a single file
|
||||
if input_directory.is_file():
|
||||
self.upscaler = Upscaler(input_path=input_directory,
|
||||
output_path=output_directory,
|
||||
driver_settings=self.driver_settings,
|
||||
ffmpeg_settings=self.ffmpeg_settings)
|
||||
|
||||
# check for input output format mismatch
|
||||
if output_directory.is_dir():
|
||||
self.show_error('Input and output path type mismatch\n\
|
||||
Input is single file but output is directory')
|
||||
raise Exception('input output path type mismatch')
|
||||
if not re.search(r'.*\..*$', str(output_directory)):
|
||||
self.show_error('No suffix found in output file path\n\
|
||||
Suffix must be specified for FFmpeg')
|
||||
raise Exception('No suffix specified')
|
||||
# set optional options
|
||||
self.upscaler.driver = AVAILABLE_DRIVERS[self.driver_combo_box.currentText()]
|
||||
self.upscaler.scale_ratio = self.scale_ratio_double_spin_box.value()
|
||||
self.upscaler.processes = self.processes_spin_box.value()
|
||||
self.upscaler.video2x_cache_directory = pathlib.Path(os.path.expandvars(self.cache_line_edit.text()))
|
||||
self.upscaler.image_format = self.config['video2x']['image_format'].lower()
|
||||
self.upscaler.preserve_frames = bool(self.preserve_frames_check_box.checkState())
|
||||
|
||||
self.upscaler = Upscaler(input_video=input_directory,
|
||||
output_video=output_directory,
|
||||
driver_settings=self.driver_settings,
|
||||
ffmpeg_settings=self.ffmpeg_settings)
|
||||
# start progress bar
|
||||
if AVAILABLE_DRIVERS[self.driver_combo_box.currentText()] != 'anime4kcpp':
|
||||
progress_bar_worker = ProgressBarWorker(self.start_progress_bar)
|
||||
progress_bar_worker.signals.progress.connect(self.set_progress)
|
||||
self.threadpool.start(progress_bar_worker)
|
||||
|
||||
# set optional options
|
||||
self.upscaler.driver = AVAILABLE_DRIVERS[self.driver_combo_box.currentText()]
|
||||
self.upscaler.scale_ratio = self.scale_ratio_double_spin_box.value()
|
||||
self.upscaler.processes = self.processes_spin_box.value()
|
||||
self.upscaler.video2x_cache_directory = pathlib.Path(os.path.expandvars(self.cache_line_edit.text()))
|
||||
self.upscaler.image_format = self.config['video2x']['image_format'].lower()
|
||||
self.upscaler.preserve_frames = bool(self.preserve_frames_check_box.checkState())
|
||||
|
||||
# start progress bar
|
||||
if AVAILABLE_DRIVERS[self.driver_combo_box.currentText()] != 'anime4kcpp':
|
||||
progress_bar_worker = ProgressBarWorker(self.start_progress_bar)
|
||||
progress_bar_worker.signals.progress.connect(self.set_progress)
|
||||
self.threadpool.start(progress_bar_worker)
|
||||
|
||||
# run upscaler
|
||||
worker = UpscalerWorker(self.upscaler.run)
|
||||
worker.signals.error.connect(self.upscale_errored)
|
||||
worker.signals.finished.connect(self.upscale_completed)
|
||||
self.threadpool.start(worker)
|
||||
self.start_button.setEnabled(False)
|
||||
# self.stop_button.setEnabled(True)
|
||||
|
||||
# if input specified is a directory
|
||||
elif input_directory.is_dir():
|
||||
# upscale videos in a directory
|
||||
|
||||
# make output directory if it doesn't exist
|
||||
output_directory.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
for input_video in [f for f in input_directory.iterdir() if f.is_file()]:
|
||||
output_video = output_directory / input_video.name
|
||||
self.upscaler = Upscaler(input_video=input_video,
|
||||
output_video=output_video,
|
||||
driver_settings=self.driver_settings,
|
||||
ffmpeg_settings=self.ffmpeg_settings)
|
||||
|
||||
# set optional options
|
||||
self.upscaler.driver = AVAILABLE_DRIVERS[self.driver_combo_box.currentText()]
|
||||
self.upscaler.scale_ratio = self.scale_ratio_double_spin_box.value()
|
||||
self.upscaler.processes = self.processes_spin_box.value()
|
||||
self.upscaler.video2x_cache_directory = pathlib.Path(os.path.expandvars(self.cache_line_edit.text()))
|
||||
self.upscaler.image_format = self.config['video2x']['image_format'].lower()
|
||||
self.upscaler.preserve_frames = bool(self.preserve_frames_check_box.checkState())
|
||||
|
||||
# start progress bar
|
||||
if AVAILABLE_DRIVERS[self.driver_combo_box.currentText()] != 'anime4kcpp':
|
||||
progress_bar_worker = ProgressBarWorker(self.start_progress_bar)
|
||||
self.threadpool.start(progress_bar_worker)
|
||||
|
||||
# run upscaler
|
||||
worker = UpscalerWorker(self.upscaler.run)
|
||||
worker.signals.error.connect(self.upscale_errored)
|
||||
worker.signals.finished.connect(self.upscale_completed)
|
||||
self.threadpool.start(worker)
|
||||
self.start_button.setEnabled(False)
|
||||
# self.stop_button.setEnabled(True)
|
||||
else:
|
||||
self.show_error('Input path is neither a file nor a directory')
|
||||
raise FileNotFoundError(f'{input_directory} is neither file nor directory')
|
||||
# run upscaler
|
||||
worker = UpscalerWorker(self.upscaler.run)
|
||||
worker.signals.error.connect(self.upscale_errored)
|
||||
worker.signals.finished.connect(self.upscale_completed)
|
||||
worker.signals.interrupted.connect(self.upscale_interrupted)
|
||||
self.threadpool.start(worker)
|
||||
self.start_button.setEnabled(False)
|
||||
self.stop_button.setEnabled(True)
|
||||
|
||||
except Exception:
|
||||
self.upscale_errored(traceback.format_exc())
|
||||
self.upscale_completed()
|
||||
|
||||
def upscale_errored(self, error_message):
|
||||
self.show_error(f'Upscaler ran into an error:\n{error_message}')
|
||||
|
||||
# try cleaning up temp directories
|
||||
with contextlib.suppress(Exception):
|
||||
self.upscaler.progress_bar_exit_signal = True
|
||||
self.upscaler.cleanup_temp_directories()
|
||||
|
||||
def upscale_completed(self):
|
||||
# if all threads have finished
|
||||
if self.threadpool.activeThreadCount() == 0:
|
||||
self.show_message('Program completed, taking {} seconds'.format(round((time.time() - self.begin_time), 5)))
|
||||
# remove Video2X cache directory
|
||||
with contextlib.suppress(FileNotFoundError):
|
||||
if not bool(self.preserve_frames_check_box.checkState()):
|
||||
shutil.rmtree(pathlib.Path(os.path.expandvars(self.cache_line_edit.text())))
|
||||
self.start_button.setEnabled(True)
|
||||
self.stop_button.setEnabled(False)
|
||||
|
||||
def upscale_interrupted(self):
|
||||
self.show_message('Upscale has been interrupted')
|
||||
self.start_button.setEnabled(True)
|
||||
self.stop_button.setEnabled(False)
|
||||
|
||||
def stop(self):
|
||||
# TODO unimplemented yet
|
||||
pass
|
||||
with contextlib.suppress(AttributeError):
|
||||
self.upscaler.stop_signal = True
|
||||
|
||||
def closeEvent(self, event):
|
||||
# try cleaning up temp directories
|
||||
self.stop()
|
||||
event.accept()
|
||||
|
||||
|
||||
# this file shouldn't be imported
|
||||
|
@ -1206,6 +1206,16 @@
|
||||
<string>vp09</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>hevc</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>av01</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -13,6 +13,8 @@ for waifu2x-caffe.
|
||||
# built-in imports
|
||||
import argparse
|
||||
import os
|
||||
import pathlib
|
||||
import platform
|
||||
import shlex
|
||||
import subprocess
|
||||
import threading
|
||||
@ -32,6 +34,7 @@ class WrapperMain:
|
||||
@staticmethod
|
||||
def parse_arguments(arguments):
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False)
|
||||
parser.error = lambda message: (_ for _ in ()).throw(AttributeError(message))
|
||||
parser.add_argument('--help', action='help', help='show this help message and exit')
|
||||
# parser.add_argument('-i', '--input', type=pathlib.Path, help='File for loading')
|
||||
# parser.add_argument('-o', '--output', type=pathlib.Path, help='File for outputting')
|
||||
@ -70,6 +73,11 @@ class WrapperMain:
|
||||
self.driver_settings['output'] = output_file
|
||||
self.driver_settings['zoomFactor'] = zoom_factor
|
||||
self.driver_settings['threads'] = threads
|
||||
|
||||
# Anime4KCPP will look for Anime4KCPPKernel.cl under the current working directory
|
||||
# change the CWD to its containing directory so it will find it
|
||||
if platform.system() == 'Windows':
|
||||
os.chdir(pathlib.Path(self.driver_settings['path']).parent)
|
||||
|
||||
# list to be executed
|
||||
# initialize the list with waifu2x binary path as the first element
|
||||
|
@ -4,7 +4,7 @@
|
||||
Name: Video2X FFmpeg Controller
|
||||
Author: K4YT3X
|
||||
Date Created: Feb 24, 2018
|
||||
Last Modified: November 15, 2019
|
||||
Last Modified: May 7, 2020
|
||||
|
||||
Description: This class handles all FFmpeg related operations.
|
||||
"""
|
||||
@ -131,7 +131,7 @@ class Ffmpeg:
|
||||
extracted_frames / f'extracted_%0d.{self.image_format}'
|
||||
])
|
||||
|
||||
self._execute(execute)
|
||||
return(self._execute(execute))
|
||||
|
||||
def convert_video(self, framerate, resolution, upscaled_frames):
|
||||
"""Converts images into videos
|
||||
@ -180,7 +180,7 @@ class Ffmpeg:
|
||||
upscaled_frames / 'no_audio.mp4'
|
||||
])
|
||||
|
||||
self._execute(execute)
|
||||
return(self._execute(execute))
|
||||
|
||||
def migrate_audio_tracks_subtitles(self, input_video, output_video, upscaled_frames):
|
||||
""" Migrates audio tracks and subtitles from input video to output video
|
||||
@ -209,7 +209,7 @@ class Ffmpeg:
|
||||
output_video
|
||||
])
|
||||
|
||||
self._execute(execute)
|
||||
return(self._execute(execute))
|
||||
|
||||
def _read_configuration(self, phase, section=None):
|
||||
""" read configuration from JSON
|
||||
@ -284,4 +284,4 @@ class Ffmpeg:
|
||||
|
||||
Avalon.debug_info(f'Executing: {execute}')
|
||||
|
||||
return subprocess.run(execute, check=True).returncode
|
||||
return subprocess.Popen(execute)
|
||||
|
@ -4,7 +4,7 @@
|
||||
Name: SRMD NCNN Vulkan Driver
|
||||
Creator: K4YT3X
|
||||
Date Created: April 26, 2020
|
||||
Last Modified: May 5, 2020
|
||||
Last Modified: May 7, 2020
|
||||
|
||||
Description: This class is a high-level wrapper
|
||||
for srmd_ncnn_vulkan.
|
||||
@ -39,6 +39,7 @@ class WrapperMain:
|
||||
@staticmethod
|
||||
def parse_arguments(arguments):
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False)
|
||||
parser.error = lambda message: (_ for _ in ()).throw(AttributeError(message))
|
||||
parser.add_argument('--help', action='help', help='show this help message and exit')
|
||||
parser.add_argument('-v', action='store_true', help='verbose output')
|
||||
# parser.add_argument('-i', type=pathlib.Path, help='input image path (jpg/png) or directory')
|
||||
|
@ -4,7 +4,7 @@
|
||||
Name: Waifu2x Caffe Driver
|
||||
Author: K4YT3X
|
||||
Date Created: Feb 24, 2018
|
||||
Last Modified: May 4, 2020
|
||||
Last Modified: May 7, 2020
|
||||
|
||||
Description: This class is a high-level wrapper
|
||||
for waifu2x-caffe.
|
||||
@ -37,6 +37,7 @@ class WrapperMain:
|
||||
@staticmethod
|
||||
def parse_arguments(arguments):
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False)
|
||||
parser.error = lambda message: (_ for _ in ()).throw(AttributeError(message))
|
||||
parser.add_argument('--help', action='help', help='show this help message and exit')
|
||||
parser.add_argument('-t', '--tta', type=int, choices=range(2), help='8x slower and slightly high quality')
|
||||
parser.add_argument('--gpu', type=int, help='gpu device no')
|
||||
|
@ -4,7 +4,7 @@
|
||||
Name: Waifu2x Converter CPP Driver
|
||||
Author: K4YT3X
|
||||
Date Created: February 8, 2019
|
||||
Last Modified: May 4, 2020
|
||||
Last Modified: May 7, 2020
|
||||
|
||||
Description: This class is a high-level wrapper
|
||||
for waifu2x-converter-cpp.
|
||||
@ -38,6 +38,7 @@ class WrapperMain:
|
||||
@staticmethod
|
||||
def parse_arguments(arguments):
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False)
|
||||
parser.error = lambda message: (_ for _ in ()).throw(AttributeError(message))
|
||||
parser.add_argument('--help', action='help', help='show this help message and exit')
|
||||
parser.add_argument('--list-supported-formats', action='store_true', help='dump currently supported format list')
|
||||
parser.add_argument('--list-opencv-formats', action='store_true', help='(deprecated. Use --list-supported-formats) dump opencv supported format list')
|
||||
|
@ -4,7 +4,7 @@
|
||||
Name: Waifu2x NCNN Vulkan Driver
|
||||
Creator: SAT3LL
|
||||
Date Created: June 26, 2019
|
||||
Last Modified: May 5, 2020
|
||||
Last Modified: May 7, 2020
|
||||
|
||||
Editor: K4YT3X
|
||||
Last Modified: February 22, 2020
|
||||
@ -42,6 +42,7 @@ class WrapperMain:
|
||||
@staticmethod
|
||||
def parse_arguments(arguments):
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False)
|
||||
parser.error = lambda message: (_ for _ in ()).throw(AttributeError(message))
|
||||
parser.add_argument('--help', action='help', help='show this help message and exit')
|
||||
parser.add_argument('-v', action='store_true', help='verbose output')
|
||||
# parser.add_argument('-i', type=pathlib.Path, help='input image path (jpg/png) or directory')
|
||||
|
Loading…
Reference in New Issue
Block a user