17 Commits

Author SHA1 Message Date
k4yt3x
14f7f34ee3 build script displays v2x version when building 2020-05-09 01:11:03 -04:00
k4yt3x
11ba334f16 added more comments and ffmpeg Video2X signature 2020-05-09 01:01:12 -04:00
k4yt3x
c451b14bd7 fixed waifu2x-converter-cpp path error 2020-05-09 00:54:57 -04:00
k4yt3x
80623a6bb7 fixed waifu2x-converter-cpp constraints 2020-05-09 00:47:12 -04:00
k4yt3x
a5dd87a72c updated build script to read version from args 2020-05-08 22:35:10 -04:00
k4yt3x
9b20ef89c9 fixed Anime4KCPP execution issues 2020-05-08 22:34:53 -04:00
k4yt3x
91efe2d684 updated zh_CN translation 2020-05-08 22:12:33 -04:00
k4yt3x
0d9d5c4f43 fixed progress monitor error, enhanced GUI error display 2020-05-08 22:12:24 -04:00
k4yt3x
e0e42b11c8 update qtcreator file 2020-05-08 20:29:08 -04:00
k4yt3x
790bb54598 redesigned UI progress display 2020-05-08 20:28:46 -04:00
k4yt3x
f2943802cb upgraded input QLineEdit to QTableView 2020-05-08 17:37:16 -04:00
k4yt3x
d12f2a3888 deleted blank lines 2020-05-08 17:32:36 -04:00
k4yt3x
871d6386a8 added file drag and drop to GUI 2020-05-07 21:11:33 -04:00
k4yt3x
589a68caf7 removed some obsolete items from gitignore 2020-05-07 19:59:04 -04:00
k4yt3x
afacc48e1e added qtcreator project file 2020-05-07 19:58:51 -04:00
k4yt3x
4a3553607b deleted some empty lines with indentations in the project 2020-05-07 19:55:33 -04:00
k4yt3x
988600a769 fixed checkbox return value type error 2020-05-07 19:50:40 -04:00
12 changed files with 1129 additions and 526 deletions

6
.gitignore vendored
View File

@@ -1,9 +1,3 @@
# Runtime files
upscaled/
frames/
waifu2x-caffe/
testvid.mp4
# PyCharm # PyCharm
.idea/ .idea/

View File

@@ -12,12 +12,18 @@ To start a PowerShell session with execution policy bypass
powershell ExecutionPolicy Bypass powershell ExecutionPolicy Bypass
#> #>
if ($args.count -ne 1){
Write-Host -ForegroundColor White "Usage:`n .\build.ps1 VIDEO2X_VERSION"
Exit
}
# version number # version number
$SCRIPT_VERSION = "1.0.1" $SCRIPT_VERSION = "1.0.1"
$VIDEO2X_VERSION = "4.0.0_beta2" $VIDEO2X_VERSION = $args[0]
Write-Host -ForegroundColor White "Video2X Building Script Version $($SCRIPT_VERSION) Write-Host -ForegroundColor White "Video2X Building Script Version $($SCRIPT_VERSION)
Starting to build Video2X release packages" Starting to build Video2X release packages
Building Video2X release $($VIDEO2X_VERSION)"
# build Video2X CLI # build Video2X CLI
Write-Host -ForegroundColor White "`nBuilding Video2X CLI" Write-Host -ForegroundColor White "`nBuilding Video2X CLI"

View File

@@ -5,8 +5,8 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"POT-Creation-Date: 2020-05-07 15:54-0400\n" "POT-Creation-Date: 2020-05-08 22:10-0400\n"
"PO-Revision-Date: 2020-05-07 15:55-0400\n" "PO-Revision-Date: 2020-05-08 22:11-0400\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"Language: zh_CN\n" "Language: zh_CN\n"
@@ -21,151 +21,163 @@ msgstr ""
msgid "Upscaling Progress" msgid "Upscaling Progress"
msgstr "放大进度" msgstr "放大进度"
#: upscaler.py:104 #: upscaler.py:105
msgid "Specified or default cache directory is a file/link" msgid "Specified or default cache directory is a file/link"
msgstr "指定或默认的缓存目录是文件/链接" msgstr "指定或默认的缓存目录是文件/链接"
#: upscaler.py:110 #: upscaler.py:111
msgid "Creating cache directory {}" msgid "Creating cache directory {}"
msgstr "创建缓存目录 {}" msgstr "创建缓存目录 {}"
#: upscaler.py:113 #: upscaler.py:114
msgid "Unable to create {}" msgid "Unable to create {}"
msgstr "无法创建 {}" msgstr "无法创建 {}"
#: upscaler.py:118 #: upscaler.py:119
msgid "Extracted frames are being saved to: {}" msgid "Extracted frames are being saved to: {}"
msgstr "提取的帧将被保存到:{}" msgstr "提取的帧将被保存到:{}"
#: upscaler.py:120 #: upscaler.py:121
msgid "Upscaled frames are being saved to: {}" msgid "Upscaled frames are being saved to: {}"
msgstr "已放大的帧将被保存到:{}" msgstr "已放大的帧将被保存到:{}"
#: upscaler.py:130 #: upscaler.py:131
msgid "Cleaning up cache directory: {}" msgid "Cleaning up cache directory: {}"
msgstr "清理缓存目录:{}" msgstr "清理缓存目录:{}"
#: upscaler.py:133 #: upscaler.py:134
msgid "Unable to delete: {}" msgid "Unable to delete: {}"
msgstr "无法删除:{}" msgstr "无法删除:{}"
#: upscaler.py:140 upscaler.py:151 #: upscaler.py:140 upscaler.py:155 upscaler.py:166
msgid "Input and output path type mismatch" msgid "Input and output path type mismatch"
msgstr "输入和输出路径类型不匹配" msgstr "输入和输出路径类型不匹配"
#: upscaler.py:141 #: upscaler.py:141
msgid "Input is multiple files but output is not directory"
msgstr "输入是多个文件,但输出不是目录"
#: upscaler.py:145
msgid "Input path {} is neither a file nor a directory"
msgstr "输入路径 {} 既不是文件也不是目录"
#: upscaler.py:149 upscaler.py:171
msgid "Input directory and output directory cannot be the same"
msgstr "输入目录和输出目录不能相同"
#: upscaler.py:156
msgid "Input is single file but output is directory" msgid "Input is single file but output is directory"
msgstr "所选的输入路径是单个文件,但输出路径是目录" msgstr "所选的输入路径是单个文件,但输出路径是目录"
#: upscaler.py:144 #: upscaler.py:159
msgid "No suffix found in output file path" msgid "No suffix found in output file path"
msgstr "在输出文件路径中未找到后缀" msgstr "在输出文件路径中未找到后缀"
#: upscaler.py:145 #: upscaler.py:160
msgid "Suffix must be specified for FFmpeg" msgid "Suffix must be specified for FFmpeg"
msgstr "必须为 FFmpeg 指定后缀" msgstr "必须为 FFmpeg 指定后缀"
#: upscaler.py:152 #: upscaler.py:167
msgid "Input is directory but output is existing single file" msgid "Input is directory but output is existing single file"
msgstr "输入是目录,但输出是现有的单个文件" msgstr "输入是目录,但输出是现有的单个文件"
#: upscaler.py:157 #: upscaler.py:176
msgid "Input path is neither a file nor a directory" msgid "Input path is neither a file nor a directory"
msgstr "输入路径既不是文件也不是目录" msgstr "输入路径既不是文件也不是目录"
#: upscaler.py:166 #: upscaler.py:185
msgid "FFmpeg or FFprobe cannot be found under the specified path" msgid "FFmpeg or FFprobe cannot be found under the specified path"
msgstr "在指定的路径下找不到 FFmpeg 或 FFprobe" msgstr "在指定的路径下找不到 FFmpeg 或 FFprobe"
#: upscaler.py:167 upscaler.py:177 #: upscaler.py:186 upscaler.py:196
msgid "Please check the configuration file settings" msgid "Please check the configuration file settings"
msgstr "请检查配置文件设置" msgstr "请检查配置文件设置"
#: upscaler.py:176 #: upscaler.py:195
msgid "Specified driver executable directory doesn't exist" msgid "Specified driver executable directory doesn't exist"
msgstr "指定驱动的可执行文件不存在" msgstr "指定驱动的可执行文件不存在"
#: upscaler.py:203 #: upscaler.py:222
msgid "Failed to parse driver argument: {}" msgid "Failed to parse driver argument: {}"
msgstr "解析驱动程序参数失败:{}" msgstr "解析驱动程序参数失败:{}"
#: upscaler.py:218 #: upscaler.py:237
msgid "Unrecognized driver: {}" msgid "Unrecognized driver: {}"
msgstr "无法识别的驱动名称:{}" msgstr "无法识别的驱动名称:{}"
#: upscaler.py:290 #: upscaler.py:309
msgid "Starting progress monitor" msgid "Starting progress monitor"
msgstr "启动进度监视器" msgstr "启动进度监视器"
#: upscaler.py:295 #: upscaler.py:314
msgid "Starting upscaled image cleaner" msgid "Starting upscaled image cleaner"
msgstr "启动已放大图像清理程序" msgstr "启动已放大图像清理程序"
#: upscaler.py:304 upscaler.py:321 #: upscaler.py:323 upscaler.py:340
msgid "Killing progress monitor" msgid "Killing progress monitor"
msgstr "终结进度监视器" msgstr "终结进度监视器"
#: upscaler.py:307 upscaler.py:324 #: upscaler.py:326 upscaler.py:343
msgid "Killing upscaled image cleaner" msgid "Killing upscaled image cleaner"
msgstr "终结已放大图像清理程序" msgstr "终结已放大图像清理程序"
#: upscaler.py:328 #: upscaler.py:347
msgid "Terminating all processes" msgid "Terminating all processes"
msgstr "正在终止所有进程" msgstr "正在终止所有进程"
#: upscaler.py:335 #: upscaler.py:354
msgid "Main process waiting for subprocesses to exit" msgid "Main process waiting for subprocesses to exit"
msgstr "主进程开始等待子进程结束" msgstr "主进程开始等待子进程结束"
#: upscaler.py:354 upscaler.py:358 #: upscaler.py:373 upscaler.py:377
msgid "Subprocess {} exited with code {}" msgid "Subprocess {} exited with code {}"
msgstr "子进程 {} 结束,返回码 {}" msgstr "子进程 {} 结束,返回码 {}"
#: upscaler.py:364 #: upscaler.py:383
msgid "Stop signal received" msgid "Stop signal received"
msgstr "收到停止信号" msgstr "收到停止信号"
#: upscaler.py:369 #: upscaler.py:388
msgid "Subprocess execution ran into an error" msgid "Subprocess execution ran into an error"
msgstr "子进程执行遇到错误" msgstr "子进程执行遇到错误"
#: upscaler.py:395 #: upscaler.py:430
msgid "Upscaling single video file: {}" msgid "Upscaling single video file: {}"
msgstr "放大单个视频文件:{}" msgstr "放大单个视频文件:{}"
#: upscaler.py:414 upscaler.py:477 #: upscaler.py:452 upscaler.py:515
msgid "Starting to upscale extracted images" msgid "Starting to upscale extracted images"
msgstr "开始对提取的帧进行放大" msgstr "开始对提取的帧进行放大"
#: upscaler.py:423 upscaler.py:479 #: upscaler.py:461 upscaler.py:517
msgid "Upscaling completed" msgid "Upscaling completed"
msgstr "放大完成" msgstr "放大完成"
#: upscaler.py:432 #: upscaler.py:470
msgid "Reading video information" msgid "Reading video information"
msgstr "读取视频信息" msgstr "读取视频信息"
#: upscaler.py:446 #: upscaler.py:484
msgid "Aborting: No video stream found" msgid "Aborting: No video stream found"
msgstr "程序中止:文件中未找到视频流" msgstr "程序中止:文件中未找到视频流"
#: upscaler.py:464 #: upscaler.py:502
msgid "Unsupported pixel format: {}" msgid "Unsupported pixel format: {}"
msgstr "不支持的像素格式:{}" msgstr "不支持的像素格式:{}"
#: upscaler.py:467 #: upscaler.py:505
msgid "Framerate: {}" msgid "Framerate: {}"
msgstr "帧率:{}" msgstr "帧率:{}"
#: upscaler.py:482 #: upscaler.py:520
msgid "Converting extracted frames into video" msgid "Converting extracted frames into video"
msgstr "将提取的帧转换为视频" msgstr "将提取的帧转换为视频"
#: upscaler.py:487 #: upscaler.py:525
msgid "Conversion completed" msgid "Conversion completed"
msgstr "转换已完成" msgstr "转换已完成"
#: upscaler.py:490 #: upscaler.py:528
msgid "Migrating audio tracks and subtitles to upscaled video" msgid "Migrating audio tracks and subtitles to upscaled video"
msgstr "将音轨和字幕迁移到放大后的视频" msgstr "将音轨和字幕迁移到放大后的视频"

View File

@@ -33,7 +33,7 @@ class ProgressMonitor(threading.Thread):
def run(self): def run(self):
self.running = True self.running = True
# get number of extracted frames # get number of extracted frames
self.upscaler.total_frames = 0 self.upscaler.total_frames = 0
for directory in self.extracted_frames_directories: for directory in self.extracted_frames_directories:

View File

@@ -4,7 +4,7 @@
Name: Video2X Upscaler Name: Video2X Upscaler
Author: K4YT3X Author: K4YT3X
Date Created: December 10, 2018 Date Created: December 10, 2018
Last Modified: May 7, 2020 Last Modified: May 8, 2020
Description: This file contains the Upscaler class. Each Description: This file contains the Upscaler class. Each
instance of the Upscaler class is an upscaler on an image or instance of the Upscaler class is an upscaler on an image or
@@ -30,9 +30,7 @@ import queue
import re import re
import shutil import shutil
import subprocess import subprocess
import sys
import tempfile import tempfile
import threading
import time import time
import traceback import traceback
@@ -71,8 +69,8 @@ class Upscaler:
def __init__(self, input_path, output_path, driver_settings, ffmpeg_settings): def __init__(self, input_path, output_path, driver_settings, ffmpeg_settings):
# mandatory arguments # mandatory arguments
self.input_path = input_path self.input = input_path
self.output_path = output_path self.output = output_path
self.driver_settings = driver_settings self.driver_settings = driver_settings
self.ffmpeg_settings = ffmpeg_settings self.ffmpeg_settings = ffmpeg_settings
@@ -87,9 +85,12 @@ class Upscaler:
self.preserve_frames = False self.preserve_frames = False
# other internal members and signals # other internal members and signals
self.stop_signal = False self.running = False
self.total_frames_upscaled = 0 self.total_frames_upscaled = 0
self.total_frames = 0 self.total_frames = 0
self.total_videos = 0
self.total_processed = 0
self.current_input_video = pathlib.Path()
def create_temp_directories(self): def create_temp_directories(self):
"""create temporary directories """create temporary directories
@@ -103,7 +104,7 @@ class Upscaler:
if self.video2x_cache_directory.exists() and not self.video2x_cache_directory.is_dir(): 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')) Avalon.error(_('Specified or default cache directory is a file/link'))
raise FileExistsError('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 cache directory doesn't exist, try creating it
if not self.video2x_cache_directory.exists(): if not self.video2x_cache_directory.exists():
try: try:
@@ -134,29 +135,47 @@ class Upscaler:
traceback.print_exc() traceback.print_exc()
def _check_arguments(self): def _check_arguments(self):
if isinstance(self.input, list):
if self.output.exists() and not self.output.is_dir():
Avalon.error(_('Input and output path type mismatch'))
Avalon.error(_('Input is multiple files but output is not directory'))
raise ArgumentError('input output path type mismatch')
for input_path in self.input:
if not input_path.is_file() and not input_path.is_dir():
Avalon.error(_('Input path {} is neither a file nor a directory').format(input_path))
raise FileNotFoundError(f'{input_path} is neither file nor directory')
with contextlib.suppress(FileNotFoundError):
if input_path.samefile(self.output):
Avalon.error(_('Input directory and output directory cannot be the same'))
raise FileExistsError('input directory and output directory are the same')
# if input is a file # if input is a file
if self.input_path.is_file(): elif self.input.is_file():
if self.output_path.is_dir(): if self.output.is_dir():
Avalon.error(_('Input and output path type mismatch')) Avalon.error(_('Input and output path type mismatch'))
Avalon.error(_('Input is single file but output is directory')) Avalon.error(_('Input is single file but output is directory'))
raise ArgumentError('input output path type mismatch') raise ArgumentError('input output path type mismatch')
if not re.search(r'.*\..*$', str(self.output_path)): if not re.search(r'.*\..*$', str(self.output)):
Avalon.error(_('No suffix found in output file path')) Avalon.error(_('No suffix found in output file path'))
Avalon.error(_('Suffix must be specified for FFmpeg')) Avalon.error(_('Suffix must be specified for FFmpeg'))
raise ArgumentError('no output video suffix specified') raise ArgumentError('no output video suffix specified')
# if input is a directory # if input is a directory
elif self.input_path.is_dir(): elif self.input.is_dir():
if self.output_path.is_file(): if self.output.is_file():
Avalon.error(_('Input and output path type mismatch')) Avalon.error(_('Input and output path type mismatch'))
Avalon.error(_('Input is directory but output is existing single file')) Avalon.error(_('Input is directory but output is existing single file'))
raise ArgumentError('input output path type mismatch') raise ArgumentError('input output path type mismatch')
with contextlib.suppress(FileNotFoundError):
if self.input.samefile(self.output):
Avalon.error(_('Input directory and output directory cannot be the same'))
raise FileExistsError('input directory and output directory are the same')
# if input is neither # if input is neither
else: else:
Avalon.error(_('Input path is neither a file nor a directory')) Avalon.error(_('Input path is neither a file nor a directory'))
raise FileNotFoundError(f'{self.input_path} is neither file nor directory') raise FileNotFoundError(f'{self.input} is neither file nor directory')
# check Fmpeg settings # check Fmpeg settings
ffmpeg_path = pathlib.Path(self.ffmpeg_settings['ffmpeg_path']) ffmpeg_path = pathlib.Path(self.ffmpeg_settings['ffmpeg_path'])
if not ((pathlib.Path(ffmpeg_path / 'ffmpeg.exe').is_file() and if not ((pathlib.Path(ffmpeg_path / 'ffmpeg.exe').is_file() and
@@ -259,32 +278,32 @@ class Upscaler:
# if the driver being used is waifu2x-caffe # if the driver being used is waifu2x-caffe
if self.driver == 'waifu2x_caffe': if self.driver == 'waifu2x_caffe':
self.process_pool.append(driver.upscale(process_directory, self.process_pool.append(driver.upscale(process_directory,
self.upscaled_frames, self.upscaled_frames,
self.scale_ratio, self.scale_ratio,
self.scale_width, self.scale_width,
self.scale_height, self.scale_height,
self.image_format, self.image_format,
self.bit_depth)) self.bit_depth))
# if the driver being used is waifu2x-converter-cpp # if the driver being used is waifu2x-converter-cpp
elif self.driver == 'waifu2x_converter_cpp': elif self.driver == 'waifu2x_converter_cpp':
self.process_pool.append(driver.upscale(process_directory, self.process_pool.append(driver.upscale(process_directory,
self.upscaled_frames, self.upscaled_frames,
self.scale_ratio, self.scale_ratio,
self.processes, self.processes,
self.image_format)) self.image_format))
# if the driver being used is waifu2x-ncnn-vulkan # if the driver being used is waifu2x-ncnn-vulkan
elif self.driver == 'waifu2x_ncnn_vulkan': elif self.driver == 'waifu2x_ncnn_vulkan':
self.process_pool.append(driver.upscale(process_directory, self.process_pool.append(driver.upscale(process_directory,
self.upscaled_frames, self.upscaled_frames,
self.scale_ratio)) self.scale_ratio))
# if the driver being used is srmd_ncnn_vulkan # if the driver being used is srmd_ncnn_vulkan
elif self.driver == 'srmd_ncnn_vulkan': elif self.driver == 'srmd_ncnn_vulkan':
self.process_pool.append(driver.upscale(process_directory, self.process_pool.append(driver.upscale(process_directory,
self.upscaled_frames, self.upscaled_frames,
self.scale_ratio)) self.scale_ratio))
# start progress bar in a different thread # start progress bar in a different thread
Avalon.debug_info(_('Starting progress monitor')) Avalon.debug_info(_('Starting progress monitor'))
@@ -305,7 +324,7 @@ class Upscaler:
self.progress_monitor.stop() self.progress_monitor.stop()
Avalon.debug_info(_('Killing upscaled image cleaner')) Avalon.debug_info(_('Killing upscaled image cleaner'))
self.image_cleaner.stop() self.image_cleaner.stop()
raise e raise e
# if the driver is waifu2x-converter-cpp # if the driver is waifu2x-converter-cpp
@@ -337,9 +356,9 @@ class Upscaler:
try: try:
# while process pool not empty # while process pool not empty
while self.process_pool: while self.process_pool:
# if stop signal received, terminate all processes # if stop signal received, terminate all processes
if self.stop_signal is True: if self.running is False:
raise SystemExit raise SystemExit
for process in self.process_pool: for process in self.process_pool:
@@ -376,9 +395,9 @@ class Upscaler:
This function controls the flow of video conversion This function controls the flow of video conversion
and handles all necessary functions. and handles all necessary functions.
""" """
# external stop signal when called in a thread # external stop signal when called in a thread
self.stop_signal = False self.running = True
# define process pool to contain processes # define process pool to contain processes
self.process_pool = [] self.process_pool = []
@@ -388,24 +407,43 @@ class Upscaler:
self._check_arguments() self._check_arguments()
# define processing queue # define processing queue
processing_queue = queue.Queue() self.processing_queue = queue.Queue()
# if input is a list of files
if isinstance(self.input, list):
# make output directory if it doesn't exist
self.output.mkdir(parents=True, exist_ok=True)
for input_path in self.input:
if input_path.is_file():
output_video = self.output / input_path.name
self.processing_queue.put((input_path.absolute(), output_video.absolute()))
elif input_path.is_dir():
for input_video in [f for f in input_path.iterdir() if f.is_file()]:
output_video = self.output / input_video.name
self.processing_queue.put((input_video.absolute(), output_video.absolute()))
# if input specified is single file # if input specified is single file
if self.input_path.is_file(): elif self.input.is_file():
Avalon.info(_('Upscaling single video file: {}').format(self.input_path)) Avalon.info(_('Upscaling single video file: {}').format(self.input))
processing_queue.put((self.input_path.absolute(), self.output_path.absolute())) self.processing_queue.put((self.input.absolute(), self.output.absolute()))
# if input specified is a directory # if input specified is a directory
elif self.input_path.is_dir(): elif self.input.is_dir():
# make output directory if it doesn't exist # make output directory if it doesn't exist
self.output_path.mkdir(parents=True, exist_ok=True) self.output.mkdir(parents=True, exist_ok=True)
for input_video in [f for f in self.input_path.iterdir() if f.is_file()]: for input_video in [f for f in self.input.iterdir() if f.is_file()]:
output_video = self.output_path / input_video.name output_video = self.output / input_video.name
processing_queue.put((input_video.absolute(), output_video.absolute())) self.processing_queue.put((input_video.absolute(), output_video.absolute()))
while not processing_queue.empty(): # record video count for external calls
input_video, output_video = processing_queue.get() self.total_videos = self.processing_queue.qsize()
while not self.processing_queue.empty():
self.current_input_video, output_video = self.processing_queue.get()
# drivers that have native support for video processing # drivers that have native support for video processing
if self.driver == 'anime4kcpp': if self.driver == 'anime4kcpp':
# append FFmpeg path to the end of PATH # append FFmpeg path to the end of PATH
@@ -418,7 +456,7 @@ class Upscaler:
driver = DriverWrapperMain(copy.deepcopy(self.driver_settings)) driver = DriverWrapperMain(copy.deepcopy(self.driver_settings))
# run Anime4KCPP # run Anime4KCPP
self.process_pool.append(driver.upscale(input_video, output_video, self.scale_ratio, self.processes)) self.process_pool.append(driver.upscale(self.current_input_video, output_video, self.scale_ratio, self.processes))
self._wait() self._wait()
Avalon.info(_('Upscaling completed')) Avalon.info(_('Upscaling completed'))
@@ -430,8 +468,8 @@ class Upscaler:
fm = Ffmpeg(self.ffmpeg_settings, self.image_format) fm = Ffmpeg(self.ffmpeg_settings, self.image_format)
Avalon.info(_('Reading video information')) Avalon.info(_('Reading video information'))
video_info = fm.get_video_info(input_video) video_info = fm.get_video_info(self.current_input_video)
# analyze original video with ffprobe and retrieve framerate # analyze original video with FFprobe and retrieve framerate
# width, height = info['streams'][0]['width'], info['streams'][0]['height'] # width, height = info['streams'][0]['width'], info['streams'][0]['height']
# find index of video stream # find index of video stream
@@ -447,7 +485,7 @@ class Upscaler:
raise StreamNotFoundError('no video stream found') raise StreamNotFoundError('no video stream found')
# extract frames from video # extract frames from video
self.process_pool.append((fm.extract_frames(input_video, self.extracted_frames))) self.process_pool.append((fm.extract_frames(self.current_input_video, self.extracted_frames)))
self._wait() self._wait()
# get average frame rate of video stream # get average frame rate of video stream
@@ -488,7 +526,7 @@ class Upscaler:
# migrate audio tracks and subtitles # migrate audio tracks and subtitles
Avalon.info(_('Migrating audio tracks and subtitles to upscaled video')) 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.process_pool.append(fm.migrate_audio_tracks_subtitles(self.current_input_video, output_video, self.upscaled_frames))
self._wait() self._wait()
# destroy temp directories # destroy temp directories
@@ -498,3 +536,9 @@ class Upscaler:
with contextlib.suppress(ValueError): with contextlib.suppress(ValueError):
self.cleanup_temp_directories() self.cleanup_temp_directories()
raise e raise e
# increment total number of videos processed
self.total_processed += 1
# signal upscaling completion
self.running = False

View File

@@ -1,7 +1,7 @@
# Name: Video2X Configuration File # Name: Video2X Configuration File
# Creator: K4YT3X # Creator: K4YT3X
# Date Created: October 23, 2018 # Date Created: October 23, 2018
# Last Modified: May 7, 2020 # Last Modified: May 8, 2020
# Items commented out are parameters handled by Video2x. # Items commented out are parameters handled by Video2x.
waifu2x_caffe: waifu2x_caffe:
path: '%LOCALAPPDATA%\video2x\waifu2x-caffe\waifu2x-caffe-cui' path: '%LOCALAPPDATA%\video2x\waifu2x-caffe\waifu2x-caffe-cui'
@@ -31,13 +31,13 @@ waifu2x_converter_cpp:
#list-processor # dump processor list #list-processor # dump processor list
output-format: null # The format used when running in recursive/folder mode output-format: null # The format used when running in recursive/folder mode
png-compression: 5 # Set PNG compression level (0-9), 9 = Max compression (slowest & smallest) png-compression: 5 # Set PNG compression level (0-9), 9 = Max compression (slowest & smallest)
image-quality: -1 # JPEG & WebP Compression quality (0-101, 0 being smallest size and lowest quality), use 101 for lossless WebP image-quality: -1 # JPEG & WebP Compression quality (-1-101, 0 being smallest size and lowest quality, -1 being default), use 101 for lossless WebP
block-size: 0 # block size block-size: 0 # block size
disable-gpu: false # disable GPU disable-gpu: false # disable GPU
force-OpenCL: false # force to use OpenCL on Intel Platform force-OpenCL: false # force to use OpenCL on Intel Platform
processor: -1 # set target processor processor: -1 # set target processor (-1 uses default device)
jobs: 0 # number of threads launching at the same time jobs: 0 # number of threads launching at the same time
model-dir: null # path to custom model directory (don't append last / ) default: models_rgb model-dir: null # path to custom model directory (don't append last / ) default: models_rgb
#scale-ratio: 2.0 # custom scale ratio #scale-ratio: 2.0 # custom scale ratio
noise-level: 1 # <0|1|2|3> noise reduction level noise-level: 1 # <0|1|2|3> noise reduction level
mode: noise-scale # <noise|scale|noise-scale> image processing mode mode: noise-scale # <noise|scale|noise-scale> image processing mode
@@ -97,12 +97,16 @@ anime4kcpp:
codec: mp4v # Specify the codec for encoding from mp4v(recommended in Windows), dxva(for Windows), avc1(H264, recommended in Linux), vp09(very slow), hevc(not support in Windowds), av01(not support in Windowds) (string [=mp4v]) codec: mp4v # Specify the codec for encoding from mp4v(recommended in Windows), dxva(for Windows), avc1(H264, recommended in Linux), vp09(very slow), hevc(not support in Windowds), av01(not support in Windowds) (string [=mp4v])
ffmpeg: ffmpeg:
ffmpeg_path: '%LOCALAPPDATA%\video2x\ffmpeg-latest-win64-static\bin' ffmpeg_path: '%LOCALAPPDATA%\video2x\ffmpeg-latest-win64-static\bin'
# step 1: extract all frames from original video
# into temporary directory
video_to_frames: video_to_frames:
output_options: output_options:
'-qscale:v': null '-qscale:v': null
'-pix_fmt': rgba64be '-pix_fmt': rgba64be
'-hwaccel': auto '-hwaccel': auto
'-y': true '-y': true
# step 2: stitch all frames back into a video
# with only a video track
frames_to_video: frames_to_video:
input_options: input_options:
'-qscale:v': null '-qscale:v': null
@@ -115,6 +119,8 @@ ffmpeg:
'-pix_fmt': null '-pix_fmt': null
'-hwaccel': auto '-hwaccel': auto
'-y': true '-y': true
# step 3: migrate audio and subtitle tracks from original
# video into the upscaled video
migrating_tracks: migrating_tracks:
output_options: output_options:
'-map': '-map':
@@ -125,6 +131,7 @@ ffmpeg:
- '1:t?' - '1:t?'
'-c': copy '-c': copy
'-pix_fmt': null '-pix_fmt': null
'-metadata': 'comment=Upscaled by Video2X'
'-hwaccel': auto '-hwaccel': auto
'-y': true '-y': true
video2x: video2x:

View File

@@ -1,10 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Creator: Video2X QT Creator: Video2X GUI
Author: K4YT3X Author: K4YT3X
Date Created: May 5, 2020 Date Created: May 5, 2020
Last Modified: May 6, 2020 Last Modified: May 8, 2020
""" """
# local imports # local imports
@@ -14,7 +14,6 @@ from upscaler import Upscaler
import contextlib import contextlib
import os import os
import pathlib import pathlib
import re
import sys import sys
import tempfile import tempfile
import time import time
@@ -22,9 +21,10 @@ import traceback
import yaml import yaml
# third-party imports # third-party imports
from PyQt5 import QtWidgets, QtGui from PyQt5 import QtGui, uic
from PyQt5 import uic from PyQt5.QtCore import *
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, QRunnable, QThreadPool from PyQt5.QtWidgets import *
# QObject, pyqtSlot, pyqtSignal, QRunnable, QThreadPool, QAbstractTableModel, Qt
VERSION = '2.0.0' VERSION = '2.0.0'
@@ -42,6 +42,7 @@ AVAILABLE_DRIVERS = {
'Anime4KCPP': 'anime4kcpp' 'Anime4KCPP': 'anime4kcpp'
} }
def resource_path(relative_path: str) -> pathlib.Path: def resource_path(relative_path: str) -> pathlib.Path:
try: try:
base_path = pathlib.Path(sys._MEIPASS) base_path = pathlib.Path(sys._MEIPASS)
@@ -52,13 +53,14 @@ def resource_path(relative_path: str) -> pathlib.Path:
class WorkerSignals(QObject): class WorkerSignals(QObject):
progress = pyqtSignal(tuple) progress = pyqtSignal(tuple)
error = pyqtSignal(str) error = pyqtSignal(Exception)
interrupted = pyqtSignal() interrupted = pyqtSignal()
finished = pyqtSignal() finished = pyqtSignal()
class ProgressBarWorker(QRunnable):
class ProgressMonitorWorkder(QRunnable):
def __init__(self, fn, *args, **kwargs): def __init__(self, fn, *args, **kwargs):
super(ProgressBarWorker, self).__init__() super(ProgressMonitorWorkder, self).__init__()
self.fn = fn self.fn = fn
self.args = args self.args = args
self.kwargs = kwargs self.kwargs = kwargs
@@ -72,6 +74,7 @@ class ProgressBarWorker(QRunnable):
except Exception: except Exception:
pass pass
class UpscalerWorker(QRunnable): class UpscalerWorker(QRunnable):
def __init__(self, fn, *args, **kwargs): def __init__(self, fn, *args, **kwargs):
@@ -91,144 +94,241 @@ class UpscalerWorker(QRunnable):
self.fn(*self.args, **self.kwargs) self.fn(*self.args, **self.kwargs)
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
self.signals.interrupted.emit() self.signals.interrupted.emit()
except Exception: except Exception as e:
error_message = traceback.format_exc() traceback.print_exc()
print(error_message, file=sys.stderr) self.signals.error.emit(e)
self.signals.error.emit(error_message)
else: else:
self.signals.finished.emit() self.signals.finished.emit()
class Video2XMainWindow(QtWidgets.QMainWindow):
class InputTableModel(QAbstractTableModel):
def __init__(self, data):
super(InputTableModel, self).__init__()
self._data = data
def data(self, index, role):
if role == Qt.DisplayRole:
if index.column() == 0:
return str(self._data[index.row()].absolute())
else:
if self._data[index.row()].is_file():
return 'File'
elif self._data[index.row()].is_dir():
return 'Folder'
else:
return 'Unknown'
def rowCount(self, index):
return len(self._data)
def columnCount(self, index):
return 2
def removeRow(self, index):
self._data.pop(index)
def headerData(self, section, orientation, role):
# section is the index of the column/row.
if role != Qt.DisplayRole:
return None
horizontal_headers = ['File Path', 'Type']
# return the correspondign header
if orientation == Qt.Horizontal:
return horizontal_headers[section]
# simply return the line number
if orientation == Qt.Vertical:
return str(section)
class Video2XMainWindow(QMainWindow):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
uic.loadUi(str(resource_path('video2x_gui.ui')), self) uic.loadUi(str(resource_path('video2x_gui.ui')), self)
# create thread pool for upscaler workers
self.threadpool = QThreadPool()
# set window title and icon
self.video2x_icon_path = str(resource_path('images/video2x.png')) self.video2x_icon_path = str(resource_path('images/video2x.png'))
self.setWindowTitle(f'Video2X GUI {VERSION}') self.setWindowTitle(f'Video2X GUI {VERSION}')
self.setWindowIcon(QtGui.QIcon(self.video2x_icon_path)) self.setWindowIcon(QtGui.QIcon(self.video2x_icon_path))
# menu bar # menu bar
self.action_exit = self.findChild(QtWidgets.QAction, 'actionExit') self.action_exit = self.findChild(QAction, 'actionExit')
self.action_exit.triggered.connect(sys.exit) self.action_exit.triggered.connect(sys.exit)
self.action_about = self.findChild(QtWidgets.QAction, 'actionAbout') self.action_about = self.findChild(QAction, 'actionAbout')
self.action_about.triggered.connect(lambda: self.show_message(LEGAL_INFO, custom_icon=QtGui.QPixmap(self.video2x_icon_path))) self.action_about.triggered.connect(self.show_about)
# main tab # main tab
# select input file/folder # select input file/folder
self.input_line_edit = self.findChild(QtWidgets.QLineEdit, 'inputLineEdit') self.input_table_view = self.findChild(QTableView, 'inputTableView')
self.input_select_file_button = self.findChild(QtWidgets.QPushButton, 'inputSelectFileButton') self.input_table_view.dragEnterEvent = self.dragEnterEvent
self.input_select_file_button.clicked.connect(self.select_input_file) self.input_table_view.dropEvent = self.dropEvent
self.input_select_folder_button = self.findChild(QtWidgets.QPushButton, 'inputSelectFolderButton')
self.input_select_folder_button.clicked.connect(self.select_input_folder)
# initialize data in table
self.input_table_data = []
self.input_table_model = InputTableModel(self.input_table_data)
self.input_table_view.setModel(self.input_table_model)
# stretch file path and fill columns horizontally
self.input_table_view.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
# input table buttons
self.input_select_file_button = self.findChild(QPushButton, 'inputSelectFileButton')
self.input_select_file_button.clicked.connect(self.select_input_file)
self.input_select_folder_button = self.findChild(QPushButton, 'inputSelectFolderButton')
self.input_select_folder_button.clicked.connect(self.select_input_folder)
self.input_delete_selected_button = self.findChild(QPushButton, 'inputDeleteSelectedButton')
self.input_delete_selected_button.clicked.connect(self.input_table_delete_selected)
self.input_clear_all_button = self.findChild(QPushButton, 'inputClearAllButton')
self.input_clear_all_button.clicked.connect(self.input_table_clear_all)
# other paths selection
# select output file/folder # select output file/folder
self.output_line_edit = self.findChild(QtWidgets.QLineEdit, 'outputLineEdit') self.output_line_edit = self.findChild(QLineEdit, 'outputLineEdit')
self.output_select_file_button = self.findChild(QtWidgets.QPushButton, 'outputSelectFileButton') self.enable_line_edit_file_drop(self.output_line_edit)
self.output_line_edit.setText(str((pathlib.Path().cwd() / 'output').absolute()))
self.output_select_file_button = self.findChild(QPushButton, 'outputSelectFileButton')
self.output_select_file_button.clicked.connect(self.select_output_file) self.output_select_file_button.clicked.connect(self.select_output_file)
self.output_select_folder_button = self.findChild(QtWidgets.QPushButton, 'outputSelectFolderButton') self.output_select_folder_button = self.findChild(QPushButton, 'outputSelectFolderButton')
self.output_select_folder_button.clicked.connect(self.select_output_folder) self.output_select_folder_button.clicked.connect(self.select_output_folder)
# config file # config file
self.config_line_edit = self.findChild(QtWidgets.QLineEdit, 'configLineEdit') self.config_line_edit = self.findChild(QLineEdit, 'configLineEdit')
self.enable_line_edit_file_drop(self.config_line_edit)
self.config_line_edit.setText(str((pathlib.Path(__file__).parent / 'video2x.yaml').absolute())) self.config_line_edit.setText(str((pathlib.Path(__file__).parent / 'video2x.yaml').absolute()))
self.config_select_file_button = self.findChild(QtWidgets.QPushButton, 'configSelectButton') self.config_select_file_button = self.findChild(QPushButton, 'configSelectButton')
self.config_select_file_button.clicked.connect(self.select_config_file) self.config_select_file_button.clicked.connect(self.select_config_file)
# cache directory # cache directory
self.cache_line_edit = self.findChild(QtWidgets.QLineEdit, 'cacheLineEdit') self.cache_line_edit = self.findChild(QLineEdit, 'cacheLineEdit')
self.cache_select_folder_button = self.findChild(QtWidgets.QPushButton, 'cacheSelectFolderButton') self.enable_line_edit_file_drop(self.cache_line_edit)
self.cache_select_folder_button = self.findChild(QPushButton, 'cacheSelectFolderButton')
self.cache_select_folder_button.clicked.connect(self.select_cache_folder) self.cache_select_folder_button.clicked.connect(self.select_cache_folder)
# express settings # express settings
self.driver_combo_box = self.findChild(QtWidgets.QComboBox, 'driverComboBox') self.driver_combo_box = self.findChild(QComboBox, 'driverComboBox')
self.driver_combo_box.currentTextChanged.connect(self.update_gui_for_driver) self.driver_combo_box.currentTextChanged.connect(self.update_gui_for_driver)
self.processes_spin_box = self.findChild(QtWidgets.QSpinBox, 'processesSpinBox') self.processes_spin_box = self.findChild(QSpinBox, 'processesSpinBox')
self.scale_ratio_double_spin_box = self.findChild(QtWidgets.QDoubleSpinBox, 'scaleRatioDoubleSpinBox') self.scale_ratio_double_spin_box = self.findChild(QDoubleSpinBox, 'scaleRatioDoubleSpinBox')
self.preserve_frames_check_box = self.findChild(QtWidgets.QCheckBox, 'preserveFramesCheckBox') self.preserve_frames_check_box = self.findChild(QCheckBox, 'preserveFramesCheckBox')
# progress bar and start/stop controls # currently processing
self.progress_bar = self.findChild(QtWidgets.QProgressBar, 'progressBar') self.currently_processing_label = self.findChild(QLabel, 'currentlyProcessingLabel')
self.time_elapsed_label = self.findChild(QtWidgets.QLabel, 'timeElapsedLabel') self.current_progress_bar = self.findChild(QProgressBar, 'currentProgressBar')
self.time_remaining_label = self.findChild(QtWidgets.QLabel, 'timeRemainingLabel') self.time_elapsed_label = self.findChild(QLabel, 'timeElapsedLabel')
self.rate_label = self.findChild(QtWidgets.QLabel, 'rateLabel') self.time_remaining_label = self.findChild(QLabel, 'timeRemainingLabel')
self.start_button = self.findChild(QtWidgets.QPushButton, 'startButton') self.rate_label = self.findChild(QLabel, 'rateLabel')
self.frames_label = self.findChild(QLabel, 'framesLabel')
# overall progress
self.overall_progress_bar = self.findChild(QProgressBar, 'overallProgressBar')
self.overall_progress_label = self.findChild(QLabel, 'overallProgressLabel')
self.start_button = self.findChild(QPushButton, 'startButton')
self.start_button.clicked.connect(self.start) self.start_button.clicked.connect(self.start)
self.stop_button = self.findChild(QtWidgets.QPushButton, 'stopButton') self.stop_button = self.findChild(QPushButton, 'stopButton')
self.stop_button.clicked.connect(self.stop) self.stop_button.clicked.connect(self.stop)
# driver settings # driver settings
# waifu2x-caffe # waifu2x-caffe
self.waifu2x_caffe_path_line_edit = self.findChild(QtWidgets.QLineEdit, 'waifu2xCaffePathLineEdit') self.waifu2x_caffe_path_line_edit = self.findChild(QLineEdit, 'waifu2xCaffePathLineEdit')
self.waifu2x_caffe_path_select_button = self.findChild(QtWidgets.QPushButton, 'waifu2xCaffePathSelectButton') self.enable_line_edit_file_drop(self.waifu2x_caffe_path_line_edit)
self.waifu2x_caffe_path_select_button = self.findChild(QPushButton, 'waifu2xCaffePathSelectButton')
self.waifu2x_caffe_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.waifu2x_caffe_path_line_edit)) self.waifu2x_caffe_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.waifu2x_caffe_path_line_edit))
self.waifu2x_caffe_mode_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xCaffeModeComboBox') self.waifu2x_caffe_mode_combo_box = self.findChild(QComboBox, 'waifu2xCaffeModeComboBox')
self.waifu2x_caffe_noise_level_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeNoiseLevelSpinBox') self.waifu2x_caffe_noise_level_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeNoiseLevelSpinBox')
self.waifu2x_caffe_process_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xCaffeProcessComboBox') self.waifu2x_caffe_process_combo_box = self.findChild(QComboBox, 'waifu2xCaffeProcessComboBox')
self.waifu2x_caffe_model_combobox = self.findChild(QtWidgets.QComboBox, 'waifu2xCaffeModelComboBox') self.waifu2x_caffe_model_combobox = self.findChild(QComboBox, 'waifu2xCaffeModelComboBox')
self.waifu2x_caffe_crop_size_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeCropSizeSpinBox') self.waifu2x_caffe_crop_size_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeCropSizeSpinBox')
self.waifu2x_caffe_output_quality_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeOutputQualitySpinBox') self.waifu2x_caffe_output_quality_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeOutputQualitySpinBox')
self.waifu2x_caffe_output_depth_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeOutputDepthSpinBox') self.waifu2x_caffe_output_depth_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeOutputDepthSpinBox')
self.waifu2x_caffe_batch_size_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeBatchSizeSpinBox') self.waifu2x_caffe_batch_size_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeBatchSizeSpinBox')
self.waifu2x_caffe_gpu_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xCaffeGpuSpinBox') self.waifu2x_caffe_gpu_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeGpuSpinBox')
self.waifu2x_caffe_tta_check_box = self.findChild(QtWidgets.QCheckBox, 'waifu2xCaffeTtaCheckBox') self.waifu2x_caffe_tta_check_box = self.findChild(QCheckBox, 'waifu2xCaffeTtaCheckBox')
# waifu2x-converter-cpp # waifu2x-converter-cpp
self.waifu2x_converter_cpp_path_line_edit = self.findChild(QtWidgets.QLineEdit, 'waifu2xConverterCppPathLineEdit') self.waifu2x_converter_cpp_path_line_edit = self.findChild(QLineEdit, 'waifu2xConverterCppPathLineEdit')
self.waifu2x_converter_cpp_path_edit_button = self.findChild(QtWidgets.QPushButton, 'waifu2xConverterCppPathSelectButton') self.enable_line_edit_file_drop(self.waifu2x_converter_cpp_path_line_edit)
self.waifu2x_converter_cpp_path_edit_button = self.findChild(QPushButton, 'waifu2xConverterCppPathSelectButton')
self.waifu2x_converter_cpp_path_edit_button.clicked.connect(lambda: self.select_driver_binary_path(self.waifu2x_converter_cpp_path_line_edit)) self.waifu2x_converter_cpp_path_edit_button.clicked.connect(lambda: self.select_driver_binary_path(self.waifu2x_converter_cpp_path_line_edit))
self.waifu2x_converter_cpp_png_compression_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xConverterCppPngCompressionSpinBox') self.waifu2x_converter_cpp_png_compression_spin_box = self.findChild(QSpinBox, 'waifu2xConverterCppPngCompressionSpinBox')
self.waifu2x_converter_cpp_processor_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xConverterCppProcessorSpinBox') self.waifu2x_converter_cpp_processor_spin_box = self.findChild(QSpinBox, 'waifu2xConverterCppProcessorSpinBox')
self.waifu2x_converter_cpp_model_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xConverterCppModelComboBox') self.waifu2x_converter_cpp_model_combo_box = self.findChild(QComboBox, 'waifu2xConverterCppModelComboBox')
self.waifu2x_converter_cpp_mode_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xConverterCppModeComboBox') self.waifu2x_converter_cpp_mode_combo_box = self.findChild(QComboBox, 'waifu2xConverterCppModeComboBox')
self.waifu2x_converter_cpp_disable_gpu_check_box = self.findChild(QtWidgets.QCheckBox, 'disableGpuCheckBox') self.waifu2x_converter_cpp_disable_gpu_check_box = self.findChild(QCheckBox, 'disableGpuCheckBox')
self.waifu2x_converter_cpp_tta_check_box = self.findChild(QtWidgets.QCheckBox, 'ttaCheckBox') self.waifu2x_converter_cpp_tta_check_box = self.findChild(QCheckBox, 'ttaCheckBox')
# waifu2x-ncnn-vulkan # waifu2x-ncnn-vulkan
self.waifu2x_ncnn_vulkan_path_line_edit = self.findChild(QtWidgets.QLineEdit, 'waifu2xNcnnVulkanPathLineEdit') self.waifu2x_ncnn_vulkan_path_line_edit = self.findChild(QLineEdit, 'waifu2xNcnnVulkanPathLineEdit')
self.waifu2x_ncnn_vulkan_path_select_button = self.findChild(QtWidgets.QPushButton, 'waifu2xNcnnVulkanPathSelectButton') self.enable_line_edit_file_drop(self.waifu2x_ncnn_vulkan_path_line_edit)
self.waifu2x_ncnn_vulkan_path_select_button = self.findChild(QPushButton, 'waifu2xNcnnVulkanPathSelectButton')
self.waifu2x_ncnn_vulkan_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.waifu2x_ncnn_vulkan_path_line_edit)) self.waifu2x_ncnn_vulkan_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.waifu2x_ncnn_vulkan_path_line_edit))
self.waifu2x_ncnn_vulkan_noise_level_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xNcnnVulkanNoiseLevelSpinBox') self.waifu2x_ncnn_vulkan_noise_level_spin_box = self.findChild(QSpinBox, 'waifu2xNcnnVulkanNoiseLevelSpinBox')
self.waifu2x_ncnn_vulkan_tile_size_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xNcnnVulkanTileSizeSpinBox') self.waifu2x_ncnn_vulkan_tile_size_spin_box = self.findChild(QSpinBox, 'waifu2xNcnnVulkanTileSizeSpinBox')
self.waifu2x_ncnn_vulkan_model_combo_box = self.findChild(QtWidgets.QComboBox, 'waifu2xNcnnVulkanModelComboBox') self.waifu2x_ncnn_vulkan_model_combo_box = self.findChild(QComboBox, 'waifu2xNcnnVulkanModelComboBox')
self.waifu2x_ncnn_vulkan_gpu_id_spin_box = self.findChild(QtWidgets.QSpinBox, 'waifu2xNcnnVulkanGpuIdSpinBox') self.waifu2x_ncnn_vulkan_gpu_id_spin_box = self.findChild(QSpinBox, 'waifu2xNcnnVulkanGpuIdSpinBox')
self.waifu2x_ncnn_vulkan_jobs_line_edit = self.findChild(QtWidgets.QLineEdit, 'waifu2xNcnnVulkanJobsLineEdit') self.waifu2x_ncnn_vulkan_jobs_line_edit = self.findChild(QLineEdit, 'waifu2xNcnnVulkanJobsLineEdit')
self.waifu2x_ncnn_vulkan_tta_check_box = self.findChild(QtWidgets.QCheckBox, 'waifu2xNcnnVulkanTtaCheckBox') self.waifu2x_ncnn_vulkan_tta_check_box = self.findChild(QCheckBox, 'waifu2xNcnnVulkanTtaCheckBox')
# srmd-ncnn-vulkan # srmd-ncnn-vulkan
self.srmd_ncnn_vulkan_path_line_edit = self.findChild(QtWidgets.QLineEdit, 'srmdNcnnVulkanPathLineEdit') self.srmd_ncnn_vulkan_path_line_edit = self.findChild(QLineEdit, 'srmdNcnnVulkanPathLineEdit')
self.srmd_ncnn_vulkan_path_select_button = self.findChild(QtWidgets.QPushButton, 'srmdNcnnVulkanPathSelectButton') self.enable_line_edit_file_drop(self.srmd_ncnn_vulkan_path_line_edit)
self.srmd_ncnn_vulkan_path_select_button = self.findChild(QPushButton, 'srmdNcnnVulkanPathSelectButton')
self.srmd_ncnn_vulkan_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.srmd_ncnn_vulkan_path_line_edit)) self.srmd_ncnn_vulkan_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.srmd_ncnn_vulkan_path_line_edit))
self.srmd_ncnn_vulkan_noise_level_spin_box = self.findChild(QtWidgets.QSpinBox, 'srmdNcnnVulkanNoiseLevelSpinBox') self.srmd_ncnn_vulkan_noise_level_spin_box = self.findChild(QSpinBox, 'srmdNcnnVulkanNoiseLevelSpinBox')
self.srmd_ncnn_vulkan_tile_size_spin_box = self.findChild(QtWidgets.QSpinBox, 'srmdNcnnVulkanTileSizeSpinBox') self.srmd_ncnn_vulkan_tile_size_spin_box = self.findChild(QSpinBox, 'srmdNcnnVulkanTileSizeSpinBox')
self.srmd_ncnn_vulkan_model_combo_box = self.findChild(QtWidgets.QComboBox, 'srmdNcnnVulkanModelComboBox') self.srmd_ncnn_vulkan_model_combo_box = self.findChild(QComboBox, 'srmdNcnnVulkanModelComboBox')
self.srmd_ncnn_vulkan_gpu_id_spin_box = self.findChild(QtWidgets.QSpinBox, 'srmdNcnnVulkanGpuIdSpinBox') self.srmd_ncnn_vulkan_gpu_id_spin_box = self.findChild(QSpinBox, 'srmdNcnnVulkanGpuIdSpinBox')
self.srmd_ncnn_vulkan_jobs_line_edit = self.findChild(QtWidgets.QLineEdit, 'srmdNcnnVulkanJobsLineEdit') self.srmd_ncnn_vulkan_jobs_line_edit = self.findChild(QLineEdit, 'srmdNcnnVulkanJobsLineEdit')
self.srmd_ncnn_vulkan_tta_check_box = self.findChild(QtWidgets.QCheckBox, 'srmdNcnnVulkanTtaCheckBox') self.srmd_ncnn_vulkan_tta_check_box = self.findChild(QCheckBox, 'srmdNcnnVulkanTtaCheckBox')
# anime4k # anime4k
self.anime4kcpp_path_line_edit = self.findChild(QtWidgets.QLineEdit, 'anime4kCppPathLineEdit') self.anime4kcpp_path_line_edit = self.findChild(QLineEdit, 'anime4kCppPathLineEdit')
self.anime4kcpp_path_select_button = self.findChild(QtWidgets.QPushButton, 'anime4kCppPathSelectButton') self.enable_line_edit_file_drop(self.anime4kcpp_path_line_edit)
self.anime4kcpp_path_select_button = self.findChild(QPushButton, 'anime4kCppPathSelectButton')
self.anime4kcpp_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.anime4kcpp_path_line_edit)) self.anime4kcpp_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.anime4kcpp_path_line_edit))
self.anime4kcpp_passes_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPassesSpinBox') self.anime4kcpp_passes_spin_box = self.findChild(QSpinBox, 'anime4kCppPassesSpinBox')
self.anime4kcpp_push_color_count_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPushColorCountSpinBox') self.anime4kcpp_push_color_count_spin_box = self.findChild(QSpinBox, 'anime4kCppPushColorCountSpinBox')
self.anime4kcpp_strength_color_spin_box = self.findChild(QtWidgets.QDoubleSpinBox, 'anime4kCppStrengthColorSpinBox') self.anime4kcpp_strength_color_spin_box = self.findChild(QDoubleSpinBox, 'anime4kCppStrengthColorSpinBox')
self.anime4kcpp_strength_gradient_spin_box = self.findChild(QtWidgets.QDoubleSpinBox, 'anime4kCppStrengthGradientSpinBox') self.anime4kcpp_strength_gradient_spin_box = self.findChild(QDoubleSpinBox, 'anime4kCppStrengthGradientSpinBox')
self.anime4kcpp_threads_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppThreadsSpinBox') self.anime4kcpp_threads_spin_box = self.findChild(QSpinBox, 'anime4kCppThreadsSpinBox')
self.anime4kcpp_pre_filters_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPreFiltersSpinBox') self.anime4kcpp_pre_filters_spin_box = self.findChild(QSpinBox, 'anime4kCppPreFiltersSpinBox')
self.anime4kcpp_post_filters_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPostFiltersSpinBox') self.anime4kcpp_post_filters_spin_box = self.findChild(QSpinBox, 'anime4kCppPostFiltersSpinBox')
self.anime4kcpp_platform_id_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppPlatformIdSpinBox') self.anime4kcpp_platform_id_spin_box = self.findChild(QSpinBox, 'anime4kCppPlatformIdSpinBox')
self.anime4kcpp_device_id_spin_box = self.findChild(QtWidgets.QSpinBox, 'anime4kCppDeviceIdSpinBox') self.anime4kcpp_device_id_spin_box = self.findChild(QSpinBox, 'anime4kCppDeviceIdSpinBox')
self.anime4kcpp_codec_combo_box = self.findChild(QtWidgets.QComboBox, 'anime4kCppCodecComboBox') self.anime4kcpp_codec_combo_box = self.findChild(QComboBox, 'anime4kCppCodecComboBox')
self.anime4kcpp_fast_mode_check_box = self.findChild(QtWidgets.QCheckBox, 'anime4kCppFastModeCheckBox') self.anime4kcpp_fast_mode_check_box = self.findChild(QCheckBox, 'anime4kCppFastModeCheckBox')
self.anime4kcpp_pre_processing_check_box = self.findChild(QtWidgets.QCheckBox, 'anime4kCppPreProcessingCheckBox') self.anime4kcpp_pre_processing_check_box = self.findChild(QCheckBox, 'anime4kCppPreProcessingCheckBox')
self.anime4kcpp_post_processing_check_box = self.findChild(QtWidgets.QCheckBox, 'anime4kCppPostProcessingCheckBox') self.anime4kcpp_post_processing_check_box = self.findChild(QCheckBox, 'anime4kCppPostProcessingCheckBox')
self.anime4kcpp_gpu_mode_check_box = self.findChild(QtWidgets.QCheckBox, 'anime4kCppGpuModeCheckBox') self.anime4kcpp_gpu_mode_check_box = self.findChild(QCheckBox, 'anime4kCppGpuModeCheckBox')
# load configurations # load configurations
self.load_configurations() self.load_configurations()
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.accept()
else:
event.ignore()
def dropEvent(self, event):
input_paths = [pathlib.Path(u.toLocalFile()) for u in event.mimeData().urls()]
for path in input_paths:
if (path.is_file() or path.is_dir()) and not self.input_table_path_exists(path):
self.input_table_data.append(path)
self.update_input_table()
self.update_output_path()
def enable_line_edit_file_drop(self, line_edit: QLineEdit):
line_edit.dragEnterEvent = self.dragEnterEvent
line_edit.dropEvent = lambda event: line_edit.setText(str(pathlib.Path(event.mimeData().urls()[0].toLocalFile()).absolute()))
@staticmethod @staticmethod
def read_config(config_file: pathlib.Path) -> dict: def read_config(config_file: pathlib.Path) -> dict:
""" read video2x configurations from config file """ read video2x configurations from config file
@@ -250,7 +350,7 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
# if file doesn't exist, return # if file doesn't exist, return
if not config_file_path.is_file(): if not config_file_path.is_file():
QtWidgets.QErrorMessage(self).showMessage('Video2X configuration file not found, please specify manually.') QErrorMessage(self).showMessage('Video2X configuration file not found, please specify manually.')
return return
# read configuration dict from config file # read configuration dict from config file
@@ -328,7 +428,7 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
self.anime4kcpp_gpu_mode_check_box.setChecked(settings['GPUMode']) self.anime4kcpp_gpu_mode_check_box.setChecked(settings['GPUMode'])
def resolve_driver_settings(self): def resolve_driver_settings(self):
# waifu2x-caffe # waifu2x-caffe
self.config['waifu2x_caffe']['path'] = os.path.expandvars(self.waifu2x_caffe_path_line_edit.text()) self.config['waifu2x_caffe']['path'] = os.path.expandvars(self.waifu2x_caffe_path_line_edit.text())
self.config['waifu2x_caffe']['mode'] = self.waifu2x_caffe_mode_combo_box.currentText() self.config['waifu2x_caffe']['mode'] = self.waifu2x_caffe_mode_combo_box.currentText()
@@ -340,7 +440,7 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
self.config['waifu2x_caffe']['output_depth'] = self.waifu2x_caffe_output_depth_spin_box.value() self.config['waifu2x_caffe']['output_depth'] = self.waifu2x_caffe_output_depth_spin_box.value()
self.config['waifu2x_caffe']['batch_size'] = self.waifu2x_caffe_batch_size_spin_box.value() self.config['waifu2x_caffe']['batch_size'] = self.waifu2x_caffe_batch_size_spin_box.value()
self.config['waifu2x_caffe']['gpu'] = self.waifu2x_caffe_gpu_spin_box.value() self.config['waifu2x_caffe']['gpu'] = self.waifu2x_caffe_gpu_spin_box.value()
self.config['waifu2x_caffe']['tta'] = int(self.waifu2x_caffe_tta_check_box.checkState()) self.config['waifu2x_caffe']['tta'] = int(self.waifu2x_caffe_tta_check_box.isChecked())
# waifu2x-converter-cpp # waifu2x-converter-cpp
self.config['waifu2x_converter_cpp']['path'] = os.path.expandvars(self.waifu2x_converter_cpp_path_line_edit.text()) self.config['waifu2x_converter_cpp']['path'] = os.path.expandvars(self.waifu2x_converter_cpp_path_line_edit.text())
@@ -348,8 +448,8 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
self.config['waifu2x_converter_cpp']['processor'] = self.waifu2x_converter_cpp_processor_spin_box.value() self.config['waifu2x_converter_cpp']['processor'] = self.waifu2x_converter_cpp_processor_spin_box.value()
self.config['waifu2x_converter_cpp']['model-dir'] = str((pathlib.Path(self.config['waifu2x_converter_cpp']['path']).parent / self.waifu2x_converter_cpp_model_combo_box.currentText()).absolute()) self.config['waifu2x_converter_cpp']['model-dir'] = str((pathlib.Path(self.config['waifu2x_converter_cpp']['path']).parent / self.waifu2x_converter_cpp_model_combo_box.currentText()).absolute())
self.config['waifu2x_converter_cpp']['mode'] = self.waifu2x_converter_cpp_mode_combo_box.currentText() self.config['waifu2x_converter_cpp']['mode'] = self.waifu2x_converter_cpp_mode_combo_box.currentText()
self.config['waifu2x_converter_cpp']['disable-gpu'] = bool(self.waifu2x_converter_cpp_disable_gpu_check_box.checkState()) self.config['waifu2x_converter_cpp']['disable-gpu'] = bool(self.waifu2x_converter_cpp_disable_gpu_check_box.isChecked())
self.config['waifu2x_converter_cpp']['tta'] = int(self.waifu2x_converter_cpp_tta_check_box.checkState()) self.config['waifu2x_converter_cpp']['tta'] = int(self.waifu2x_converter_cpp_tta_check_box.isChecked())
# waifu2x-ncnn-vulkan # waifu2x-ncnn-vulkan
self.config['waifu2x_ncnn_vulkan']['path'] = os.path.expandvars(self.waifu2x_ncnn_vulkan_path_line_edit.text()) self.config['waifu2x_ncnn_vulkan']['path'] = os.path.expandvars(self.waifu2x_ncnn_vulkan_path_line_edit.text())
@@ -358,7 +458,7 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
self.config['waifu2x_ncnn_vulkan']['m'] = str((pathlib.Path(self.config['waifu2x_ncnn_vulkan']['path']).parent / self.waifu2x_ncnn_vulkan_model_combo_box.currentText()).absolute()) self.config['waifu2x_ncnn_vulkan']['m'] = str((pathlib.Path(self.config['waifu2x_ncnn_vulkan']['path']).parent / self.waifu2x_ncnn_vulkan_model_combo_box.currentText()).absolute())
self.config['waifu2x_ncnn_vulkan']['g'] = self.waifu2x_ncnn_vulkan_gpu_id_spin_box.value() self.config['waifu2x_ncnn_vulkan']['g'] = self.waifu2x_ncnn_vulkan_gpu_id_spin_box.value()
self.config['waifu2x_ncnn_vulkan']['j'] = self.waifu2x_ncnn_vulkan_jobs_line_edit.text() self.config['waifu2x_ncnn_vulkan']['j'] = self.waifu2x_ncnn_vulkan_jobs_line_edit.text()
self.config['waifu2x_ncnn_vulkan']['x'] = self.waifu2x_ncnn_vulkan_tta_check_box.checkState() self.config['waifu2x_ncnn_vulkan']['x'] = self.waifu2x_ncnn_vulkan_tta_check_box.isChecked()
# srmd-ncnn-vulkan # srmd-ncnn-vulkan
self.config['srmd_ncnn_vulkan']['path'] = os.path.expandvars(self.srmd_ncnn_vulkan_path_line_edit.text()) self.config['srmd_ncnn_vulkan']['path'] = os.path.expandvars(self.srmd_ncnn_vulkan_path_line_edit.text())
@@ -367,7 +467,7 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
self.config['srmd_ncnn_vulkan']['m'] = str((pathlib.Path(self.config['srmd_ncnn_vulkan']['path']).parent / self.srmd_ncnn_vulkan_model_combo_box.currentText()).absolute()) self.config['srmd_ncnn_vulkan']['m'] = str((pathlib.Path(self.config['srmd_ncnn_vulkan']['path']).parent / self.srmd_ncnn_vulkan_model_combo_box.currentText()).absolute())
self.config['srmd_ncnn_vulkan']['g'] = self.srmd_ncnn_vulkan_gpu_id_spin_box.value() self.config['srmd_ncnn_vulkan']['g'] = self.srmd_ncnn_vulkan_gpu_id_spin_box.value()
self.config['srmd_ncnn_vulkan']['j'] = self.srmd_ncnn_vulkan_jobs_line_edit.text() self.config['srmd_ncnn_vulkan']['j'] = self.srmd_ncnn_vulkan_jobs_line_edit.text()
self.config['srmd_ncnn_vulkan']['x'] = self.srmd_ncnn_vulkan_tta_check_box.checkState() self.config['srmd_ncnn_vulkan']['x'] = self.srmd_ncnn_vulkan_tta_check_box.isChecked()
# anime4k # anime4k
self.config['anime4kcpp']['path'] = os.path.expandvars(self.anime4kcpp_path_line_edit.text()) self.config['anime4kcpp']['path'] = os.path.expandvars(self.anime4kcpp_path_line_edit.text())
@@ -381,14 +481,14 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
self.config['anime4kcpp']['platformID'] = self.anime4kcpp_platform_id_spin_box.value() self.config['anime4kcpp']['platformID'] = self.anime4kcpp_platform_id_spin_box.value()
self.config['anime4kcpp']['deviceID'] = self.anime4kcpp_device_id_spin_box.value() self.config['anime4kcpp']['deviceID'] = self.anime4kcpp_device_id_spin_box.value()
self.config['anime4kcpp']['codec'] = self.anime4kcpp_codec_combo_box.currentText() self.config['anime4kcpp']['codec'] = self.anime4kcpp_codec_combo_box.currentText()
self.config['anime4kcpp']['fastMode'] = bool(self.anime4kcpp_fast_mode_check_box.checkState()) self.config['anime4kcpp']['fastMode'] = bool(self.anime4kcpp_fast_mode_check_box.isChecked())
self.config['anime4kcpp']['preprocessing'] = bool(self.anime4kcpp_pre_processing_check_box.checkState()) self.config['anime4kcpp']['preprocessing'] = bool(self.anime4kcpp_pre_processing_check_box.isChecked())
self.config['anime4kcpp']['postprocessing'] = bool(self.anime4kcpp_post_processing_check_box.checkState()) self.config['anime4kcpp']['postprocessing'] = bool(self.anime4kcpp_post_processing_check_box.isChecked())
self.config['anime4kcpp']['GPUMode'] = bool(self.anime4kcpp_gpu_mode_check_box.checkState()) self.config['anime4kcpp']['GPUMode'] = bool(self.anime4kcpp_gpu_mode_check_box.isChecked())
def update_gui_for_driver(self): def update_gui_for_driver(self):
current_driver = AVAILABLE_DRIVERS[self.driver_combo_box.currentText()] current_driver = AVAILABLE_DRIVERS[self.driver_combo_box.currentText()]
# update scale ratio constraints # update scale ratio constraints
if current_driver in ['waifu2x_caffe', 'waifu2x_converter_cpp', 'anime4kcpp']: 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.setMinimum(0.0)
@@ -402,59 +502,93 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
self.scale_ratio_double_spin_box.setMinimum(2.0) self.scale_ratio_double_spin_box.setMinimum(2.0)
self.scale_ratio_double_spin_box.setMaximum(4.0) self.scale_ratio_double_spin_box.setMaximum(4.0)
self.scale_ratio_double_spin_box.setValue(2.0) self.scale_ratio_double_spin_box.setValue(2.0)
# update preferred processes/threads count # update preferred processes/threads count
if current_driver == 'anime4kcpp': if current_driver == 'anime4kcpp':
self.processes_spin_box.setValue(16) self.processes_spin_box.setValue(16)
else: else:
self.processes_spin_box.setValue(1) self.processes_spin_box.setValue(1)
def update_input_table(self):
# HACK: use insertRow, removeRow and signals
del self.input_table_model
self.input_table_model = InputTableModel(self.input_table_data)
self.input_table_view.setModel(self.input_table_model)
def input_table_delete_selected(self):
items_to_delete = []
for index in [i.row() for i in self.input_table_view.selectedIndexes()]:
items_to_delete.append(self.input_table_data[index])
for item in items_to_delete:
self.input_table_data.remove(item)
self.update_input_table()
def input_table_clear_all(self):
self.input_table_data = []
self.update_input_table()
def input_table_path_exists(self, input_path):
for path in self.input_table_data:
# not using Path.samefile since file may not exist
if str(path.absolute()) == str(input_path.absolute()):
return True
return False
def select_file(self, *args, **kwargs) -> pathlib.Path: def select_file(self, *args, **kwargs) -> pathlib.Path:
file_selected = QtWidgets.QFileDialog.getOpenFileName(self, *args, **kwargs) file_selected = QFileDialog.getOpenFileName(self, *args, **kwargs)
if not isinstance(file_selected, tuple) or file_selected[0] == '': if not isinstance(file_selected, tuple) or file_selected[0] == '':
return None return None
return pathlib.Path(file_selected[0]) return pathlib.Path(file_selected[0])
def select_folder(self, *args, **kwargs) -> pathlib.Path: def select_folder(self, *args, **kwargs) -> pathlib.Path:
folder_selected = QtWidgets.QFileDialog.getExistingDirectory(self, *args, **kwargs) folder_selected = QFileDialog.getExistingDirectory(self, *args, **kwargs)
if folder_selected == '': if folder_selected == '':
return None return None
return pathlib.Path(folder_selected) return pathlib.Path(folder_selected)
def select_input_file(self): def update_output_path(self):
# if there is more than one input
if (input_file := self.select_file('Select Input File')) is None: if len(self.input_table_data) != 1:
return return
self.input_line_edit.setText(str(input_file.absolute()))
# try to set an output file name automatically input_path = self.input_table_data[0]
output_file = input_file.parent / f'{input_file.stem}_output.mp4' # give up if input path doesn't exist or isn't a file or a directory
if not input_path.exists() or not (input_path.is_file() or input_path.is_dir()):
return
output_file_id = 0 if input_path.is_file():
while output_file.is_file() and output_file_id <= 10: output_path = input_path.parent / f'{input_path.stem}_output.mp4'
output_file = input_file.parent / pathlib.Path(f'{input_file.stem}_output_{output_file_id}.mp4') elif input_path.is_dir():
output_file_id += 1 output_path = input_path.parent / f'{input_path.stem}_output'
if not output_file.exists(): # try up to 1000 times
self.output_line_edit.setText(str(output_file.absolute())) output_path_id = 0
while output_path.exists() and output_path_id <= 1000:
if input_path.is_file():
output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}.mp4')
elif input_path.is_dir():
output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}')
output_path_id += 1
if not output_path.exists():
self.output_line_edit.setText(str(output_path.absolute()))
def select_input_file(self):
if ((input_file := self.select_file('Select Input File')) is None or
self.input_table_path_exists(input_file)):
return
self.input_table_data.append(input_file)
self.update_output_path()
self.update_input_table()
def select_input_folder(self): def select_input_folder(self):
if ((input_folder := self.select_folder('Select Input Folder')) is None or
if (input_folder := self.select_folder('Select Input Folder')) is None: self.input_table_path_exists(input_folder)):
return return
self.input_table_data.append(input_folder)
self.input_line_edit.setText(str(input_folder.absolute())) self.update_output_path()
self.update_input_table()
# try to set an output file name automatically
output_folder = input_folder.parent / f'{input_folder.stem}_output'
output_file_id = 0
while output_folder.is_dir() and output_file_id <= 10:
output_folder = input_folder.parent / pathlib.Path(f'{input_folder.stem}_output_{output_file_id}')
output_file_id += 1
if not output_folder.exists():
self.output_line_edit.setText(str(output_folder.absolute()))
def select_output_file(self): def select_output_file(self):
if (output_file := self.select_file('Select Output File')) is None: if (output_file := self.select_file('Select Output File')) is None:
@@ -476,58 +610,79 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
return return
self.config_line_edit.setText(str(config_file.absolute())) self.config_line_edit.setText(str(config_file.absolute()))
self.load_configurations() self.load_configurations()
def select_driver_binary_path(self, driver_line_edit): def select_driver_binary_path(self, driver_line_edit):
if (driver_binary_path := self.select_file('Select Driver Binary File')) is None: if (driver_binary_path := self.select_file('Select Driver Binary File')) is None:
return return
driver_line_edit.setText(str(driver_binary_path.absolute())) driver_line_edit.setText(str(driver_binary_path.absolute()))
def show_error(self, message: str): def show_about(self, message: str):
QtWidgets.QErrorMessage(self).showMessage(message.replace('\n', '<br>')) message_box = QMessageBox(self)
message_box.setWindowTitle('About Video2X')
message_box.setIconPixmap(QtGui.QPixmap(self.video2x_icon_path).scaled(64, 64))
message_box.setText(LEGAL_INFO)
message_box.exec_()
def show_message(self, message: str, custom_icon=None): def show_information(self, message: str):
message_box = QtWidgets.QMessageBox() message_box = QMessageBox(self)
message_box.setWindowTitle('Message') message_box.setWindowTitle('Information')
if custom_icon: message_box.setIcon(QMessageBox.Information)
message_box.setIconPixmap(custom_icon.scaled(64, 64))
else:
message_box.setIcon(QtWidgets.QMessageBox.Information)
message_box.setText(message) message_box.setText(message)
message_box.exec_() message_box.exec_()
def start_progress_bar(self, progress_callback): def show_warning(self, message: str):
# wait for progress monitor to come online message_box = QMessageBox(self)
while 'progress_monitor' not in self.upscaler.__dict__: message_box.setWindowTitle('Warning')
if self.upscaler.stop_signal: message_box.setIcon(QMessageBox.Warning)
return message_box.setText(message)
time.sleep(0.1) message_box.exec_()
def show_error(self, exception: Exception):
# QErrorMessage(self).showMessage(message.replace('\n', '<br>'))
message_box = QMessageBox(self)
message_box.setWindowTitle('Error')
message_box.setIcon(QMessageBox.Critical)
error_message = '''Upscaler ran into an error:
{}
Check the console output for details.
When reporting an error, please include console output.'''
try:
message_box.setText(error_message.format(exception.args[0]))
except (AttributeError, IndexError):
message_box.setText(error_message.format(exception))
message_box.exec_()
def progress_monitor(self, progress_callback):
# initialize progress bar values # initialize progress bar values
upscale_begin_time = time.time() upscale_begin_time = time.time()
progress_callback.emit((0, 0, 0, upscale_begin_time)) progress_callback.emit((upscale_begin_time, 0, 0, 0, 0, pathlib.Path()))
# keep querying upscaling process and feed information to callback signal # keep querying upscaling process and feed information to callback signal
while self.upscaler.progress_monitor.running: while self.upscaler.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, progress_callback.emit((upscale_begin_time,
self.upscaler.total_frames_upscaled, self.upscaler.total_frames_upscaled,
self.upscaler.total_frames, self.upscaler.total_frames,
upscale_begin_time)) self.upscaler.total_processed,
self.upscaler.total_videos,
self.upscaler.current_input_video))
time.sleep(1) time.sleep(1)
# upscale process will stop at 99% # upscale process will stop at 99%
# so it's set to 100 manually when all is done # so it's set to 100 manually when all is done
progress_callback.emit((100, 0, 0, upscale_begin_time)) progress_callback.emit((upscale_begin_time, 0, 0, 0, 0, pathlib.Path()))
def set_progress(self, progress_information: tuple): def set_progress(self, progress_information: tuple):
progress_percentage = progress_information[0] upscale_begin_time = progress_information[0]
total_frames_upscaled = progress_information[1] total_frames_upscaled = progress_information[1]
total_frames = progress_information[2] total_frames = progress_information[2]
upscale_begin_time = progress_information[3] total_processed = progress_information[3]
total_videos = progress_information[4]
current_input_video = progress_information[5]
# calculate fields based on frames and time elapsed # calculate fields based on frames and time elapsed
time_elapsed = time.time() - upscale_begin_time time_elapsed = time.time() - upscale_begin_time
@@ -539,10 +694,29 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
time_remaining = 0.0 time_remaining = 0.0
# set calculated values in GUI # set calculated values in GUI
self.progress_bar.setValue(progress_percentage) self.current_progress_bar.setMaximum(total_frames)
self.current_progress_bar.setValue(total_frames_upscaled)
self.frames_label.setText('Frames: {}/{}'.format(total_frames_upscaled, total_frames))
self.time_elapsed_label.setText('Time Elapsed: {}'.format(time.strftime("%H:%M:%S", time.gmtime(time_elapsed)))) self.time_elapsed_label.setText('Time Elapsed: {}'.format(time.strftime("%H:%M:%S", time.gmtime(time_elapsed))))
self.time_remaining_label.setText('Time Remaining: {}'.format(time.strftime("%H:%M:%S", time.gmtime(time_remaining)))) self.time_remaining_label.setText('Time Remaining: {}'.format(time.strftime("%H:%M:%S", time.gmtime(time_remaining))))
self.rate_label.setText('Rate (FPS): {}'.format(round(rate, 2))) self.rate_label.setText('Rate (FPS): {}'.format(round(rate, 2)))
self.overall_progress_label.setText('Overall Progress: {}/{}'.format(total_processed, total_videos))
self.overall_progress_bar.setMaximum(total_videos)
self.overall_progress_bar.setValue(total_processed)
self.currently_processing_label.setText('Currently Processing: {}'.format(str(current_input_video.name)))
def reset_progress_display(self):
# reset progress display UI elements
self.current_progress_bar.setMaximum(100)
self.current_progress_bar.setValue(0)
self.frames_label.setText('Frames: {}/{}'.format(0, 0))
self.time_elapsed_label.setText('Time Elapsed: {}'.format(time.strftime("%H:%M:%S", time.gmtime(0))))
self.time_remaining_label.setText('Time Remaining: {}'.format(time.strftime("%H:%M:%S", time.gmtime(0))))
self.rate_label.setText('Rate (FPS): {}'.format(0.0))
self.overall_progress_label.setText('Overall Progress: {}/{}'.format(0, 0))
self.overall_progress_bar.setMaximum(100)
self.overall_progress_bar.setValue(0)
self.currently_processing_label.setText('Currently Processing:')
def start(self): def start(self):
@@ -552,18 +726,20 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
self.begin_time = time.time() self.begin_time = time.time()
# resolve input and output directories from GUI # resolve input and output directories from GUI
if self.input_line_edit.text().strip() == '': if len(self.input_table_data) == 0:
self.show_error('Input path not specified') self.show_warning('Input path unspecified', standard_icon=QMessageBox.Warning)
return return
if self.output_line_edit.text().strip() == '': if self.output_line_edit.text().strip() == '':
self.show_error('Output path not specified') self.show_warning('Output path unspecified', standard_icon=QMessageBox.Warning)
return return
input_directory = pathlib.Path(os.path.expandvars(self.input_line_edit.text())) if len(self.input_table_data) == 1:
output_directory = pathlib.Path(os.path.expandvars(self.output_line_edit.text())) input_directory = self.input_table_data[0]
else:
input_directory = self.input_table_data
# create thread pool for upscaler workers # resolve output directory
self.threadpool = QThreadPool() output_directory = pathlib.Path(os.path.expandvars(self.output_line_edit.text()))
# load driver settings from GUI # load driver settings from GUI
self.resolve_driver_settings() self.resolve_driver_settings()
@@ -582,44 +758,52 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
self.upscaler.processes = self.processes_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.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.image_format = self.config['video2x']['image_format'].lower()
self.upscaler.preserve_frames = bool(self.preserve_frames_check_box.checkState()) self.upscaler.preserve_frames = bool(self.preserve_frames_check_box.isChecked())
# 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 # run upscaler
worker = UpscalerWorker(self.upscaler.run) worker = UpscalerWorker(self.upscaler.run)
worker.signals.error.connect(self.upscale_errored) worker.signals.error.connect(self.upscale_errored)
worker.signals.finished.connect(self.upscale_completed)
worker.signals.interrupted.connect(self.upscale_interrupted) worker.signals.interrupted.connect(self.upscale_interrupted)
worker.signals.finished.connect(self.upscale_successful)
self.threadpool.start(worker) self.threadpool.start(worker)
# start progress monitoring
progress_bar_worker = ProgressMonitorWorkder(self.progress_monitor)
progress_bar_worker.signals.progress.connect(self.set_progress)
self.threadpool.start(progress_bar_worker)
self.start_button.setEnabled(False) self.start_button.setEnabled(False)
self.stop_button.setEnabled(True) self.stop_button.setEnabled(True)
except Exception: except Exception as e:
self.upscale_errored(traceback.format_exc()) traceback.print_exc()
self.upscale_errored(e)
def upscale_errored(self, error_message):
self.show_error(f'Upscaler ran into an error:\n{error_message}')
def upscale_completed(self): def upscale_errored(self, exception: Exception):
# if all threads have finished self.show_error(exception)
if self.threadpool.activeThreadCount() == 0: self.threadpool.waitForDone(5)
self.show_message('Program completed, taking {} seconds'.format(round((time.time() - self.begin_time), 5)))
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.start_button.setEnabled(True)
self.stop_button.setEnabled(False) self.stop_button.setEnabled(False)
self.reset_progress_display()
def upscale_interrupted(self):
self.show_information('Upscale has been interrupted')
self.threadpool.waitForDone(5)
self.start_button.setEnabled(True)
self.stop_button.setEnabled(False)
self.reset_progress_display()
def upscale_successful(self):
# if all threads have finished
self.threadpool.waitForDone(5)
self.show_information('Upscale finished successfully, taking {} seconds'.format(round((time.time() - self.begin_time), 5)))
self.start_button.setEnabled(True)
self.stop_button.setEnabled(False)
self.reset_progress_display()
def stop(self): def stop(self):
with contextlib.suppress(AttributeError): with contextlib.suppress(AttributeError):
self.upscaler.stop_signal = True self.upscaler.running = False
def closeEvent(self, event): def closeEvent(self, event):
# try cleaning up temp directories # try cleaning up temp directories
@@ -629,7 +813,14 @@ class Video2XMainWindow(QtWidgets.QMainWindow):
# this file shouldn't be imported # this file shouldn't be imported
if __name__ == '__main__': if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv) try:
window = Video2XMainWindow() app = QApplication(sys.argv)
window.show() window = Video2XMainWindow()
app.exec_() window.show()
app.exec_()
# on GUI exception, print error message in console
# and hold window open using input()
except Exception:
traceback.print_exc()
input('Press enter to close')

View File

@@ -0,0 +1,190 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.12.0, 2020-05-08T20:28:53. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{25c1e4a4-6b4a-4828-883b-6f32c3f7eba0}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey">
<value type="QString">-fno-delayed-template-parsing</value>
</valuelist>
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
<value type="QString" key="ClangCodeModel.WarningConfigId">Builtin.Questionable</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">8</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop (x86-windows-msvc2019-pe-64bit)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop (x86-windows-msvc2019-pe-64bit)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{5450e32e-f498-4c15-a0a5-7456f3375388}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">-1</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
<value type="QString">cpu-cycles</value>
</valuelist>
<valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
<value type="int" key="Analyzer.Perf.Frequency">250</value>
<valuelist type="QVariantList" key="Analyzer.Perf.RecordArguments">
<value type="QString">-e</value>
<value type="QString">cpu-cycles</value>
<value type="QString">--call-graph</value>
<value type="QString">dwarf,4096</value>
<value type="QString">-F</value>
<value type="QString">250</value>
</valuelist>
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Perf.StackSize">4096</value>
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">video2x_gui</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration.C:/Users/k4yt3x/Documents/Projects/video2x/src/video2x_gui.py</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">C:/Users/k4yt3x/Documents/Projects/video2x/src/video2x_gui.py</value>
<value type="bool" key="PythonEditor.RunConfiguation.Buffered">false</value>
<value type="QString" key="PythonEditor.RunConfiguation.Interpreter">{ffc39ed5-fbe0-4af8-8b16-9a60c393bdb8}</value>
<value type="QString" key="PythonEditor.RunConfiguation.Script">C:\Users\k4yt3x\Documents\Projects\video2x\src\video2x_gui.py</value>
<value type="QString" key="RunConfiguration.Arguments"></value>
<value type="bool" key="RunConfiguration.Arguments.multi">false</value>
<value type="QString" key="RunConfiguration.OverrideDebuggerStartup"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default"></value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@@ -1,19 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>Video2xQt</class> <class>Video2xGui</class>
<widget class="QMainWindow" name="Video2xQt"> <widget class="QMainWindow" name="Video2xGui">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>691</width> <width>718</width>
<height>503</height> <height>740</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="acceptDrops">
<string>Video2xQt</string> <bool>true</bool>
</property> </property>
<widget class="QWidget" name="centralwidget"> <property name="windowTitle">
<string>Video2X GUI</string>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin"> <property name="leftMargin">
<number>5</number> <number>5</number>
@@ -38,154 +41,198 @@
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_8"> <layout class="QVBoxLayout" name="verticalLayout_8">
<item> <item>
<layout class="QVBoxLayout" name="fileSelectionVerticalLayout"> <widget class="QLabel" name="dragDropLabelTop">
<item> <property name="maximumSize">
<layout class="QHBoxLayout" name="inputHorizontalLayout"> <size>
<item> <width>16777215</width>
<widget class="QLabel" name="inputLabel"> <height>15</height>
<property name="minimumSize"> </size>
<size> </property>
<width>63</width> <property name="frameShape">
<height>0</height> <enum>QFrame::NoFrame</enum>
</size> </property>
</property> <property name="text">
<property name="lineWidth"> <string>**Drag and drop file or folder anywhere in this window to select input file/folder.**</string>
<number>1</number> </property>
</property> <property name="textFormat">
<property name="text"> <enum>Qt::MarkdownText</enum>
<string>Input</string> </property>
</property> <property name="wordWrap">
<property name="wordWrap"> <bool>false</bool>
<bool>false</bool> </property>
</property> </widget>
<property name="margin"> </item>
<number>0</number> <item>
</property> <widget class="QLabel" name="dragDropLabelBottom">
</widget> <property name="maximumSize">
</item> <size>
<item> <width>16777215</width>
<widget class="QLineEdit" name="inputLineEdit"/> <height>15</height>
</item> </size>
<item> </property>
<widget class="QPushButton" name="inputSelectFileButton"> <property name="text">
<property name="enabled"> <string>**Drop item on a specific input box to set the value of that box.**</string>
<bool>true</bool> </property>
</property> <property name="textFormat">
<property name="text"> <enum>Qt::MarkdownText</enum>
<string>Select File</string> </property>
</property> </widget>
</widget> </item>
</item> <item>
<item> <widget class="QGroupBox" name="inputSelectionGroupBox">
<widget class="QPushButton" name="inputSelectFolderButton"> <property name="title">
<property name="text"> <string>Input Selection</string>
<string>Select Folder</string> </property>
</property> <layout class="QVBoxLayout" name="verticalLayout_5">
</widget> <item>
</item> <widget class="QTableView" name="inputTableView"/>
</layout> </item>
</item> <item>
<item> <layout class="QHBoxLayout" name="inputButtonsHorizontalLayout">
<layout class="QHBoxLayout" name="outputHorizontalLayout"> <item>
<item> <widget class="QPushButton" name="inputSelectFileButton">
<widget class="QLabel" name="outputLabel"> <property name="enabled">
<property name="sizePolicy"> <bool>true</bool>
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> </property>
<horstretch>0</horstretch> <property name="text">
<verstretch>0</verstretch> <string>Select File</string>
</sizepolicy> </property>
</property> </widget>
<property name="minimumSize"> </item>
<size> <item>
<width>63</width> <widget class="QPushButton" name="inputSelectFolderButton">
<height>0</height> <property name="text">
</size> <string>Select Folder</string>
</property> </property>
<property name="text"> </widget>
<string>Output</string> </item>
</property> <item>
</widget> <widget class="QPushButton" name="inputDeleteSelectedButton">
</item> <property name="text">
<item> <string>Delete Selected</string>
<widget class="QLineEdit" name="outputLineEdit"/> </property>
</item> </widget>
<item> </item>
<widget class="QPushButton" name="outputSelectFileButton"> <item>
<property name="text"> <widget class="QPushButton" name="inputClearAllButton">
<string>Select File</string> <property name="text">
</property> <string>Clear All</string>
</widget> </property>
</item> </widget>
<item> </item>
<widget class="QPushButton" name="outputSelectFolderButton"> </layout>
<property name="text"> </item>
<string>Select Folder</string> </layout>
</property> </widget>
</widget> </item>
</item> <item>
</layout> <widget class="QGroupBox" name="otherPathsSelectionGroupBox">
</item> <property name="title">
<item> <string>Other Paths Selection</string>
<layout class="QHBoxLayout" name="configHorizontalLayout"> </property>
<item> <layout class="QVBoxLayout" name="verticalLayout_9">
<widget class="QLabel" name="configLabel"> <item>
<property name="sizePolicy"> <layout class="QHBoxLayout" name="outputHorizontalLayout">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <item>
<horstretch>0</horstretch> <widget class="QLabel" name="outputLabel">
<verstretch>0</verstretch> <property name="sizePolicy">
</sizepolicy> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
</property> <horstretch>0</horstretch>
<property name="minimumSize"> <verstretch>0</verstretch>
<size> </sizepolicy>
<width>63</width> </property>
<height>0</height> <property name="minimumSize">
</size> <size>
</property> <width>63</width>
<property name="text"> <height>0</height>
<string>Config</string> </size>
</property> </property>
</widget> <property name="text">
</item> <string>Output</string>
<item> </property>
<widget class="QLineEdit" name="configLineEdit"/> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="configSelectButton"> <widget class="QLineEdit" name="outputLineEdit"/>
<property name="text"> </item>
<string>Select</string> <item>
</property> <widget class="QPushButton" name="outputSelectFileButton">
</widget> <property name="text">
</item> <string>Select File</string>
</layout> </property>
</item> </widget>
<item> </item>
<layout class="QHBoxLayout" name="cacheHorizontalLayout"> <item>
<item> <widget class="QPushButton" name="outputSelectFolderButton">
<widget class="QLabel" name="cacheLabel"> <property name="text">
<property name="minimumSize"> <string>Select Folder</string>
<size> </property>
<width>63</width> </widget>
<height>0</height> </item>
</size> </layout>
</property> </item>
<property name="text"> <item>
<string>Cache Folder</string> <layout class="QHBoxLayout" name="configHorizontalLayout">
</property> <item>
</widget> <widget class="QLabel" name="configLabel">
</item> <property name="sizePolicy">
<item> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<widget class="QLineEdit" name="cacheLineEdit"/> <horstretch>0</horstretch>
</item> <verstretch>0</verstretch>
<item> </sizepolicy>
<widget class="QPushButton" name="cacheSelectFolderButton"> </property>
<property name="text"> <property name="minimumSize">
<string>Select Folder</string> <size>
</property> <width>63</width>
</widget> <height>0</height>
</item> </size>
</layout> </property>
</item> <property name="text">
</layout> <string>Config</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="configLineEdit"/>
</item>
<item>
<widget class="QPushButton" name="configSelectButton">
<property name="text">
<string>Select</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="cacheHorizontalLayout">
<item>
<widget class="QLabel" name="cacheLabel">
<property name="minimumSize">
<size>
<width>63</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Cache Folder</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="cacheLineEdit"/>
</item>
<item>
<widget class="QPushButton" name="cacheSelectFolderButton">
<property name="text">
<string>Select Folder</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item> </item>
<item> <item>
<widget class="QGroupBox" name="expressSettingsGroupBox"> <widget class="QGroupBox" name="expressSettingsGroupBox">
@@ -1229,72 +1276,184 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QVBoxLayout" name="controlsVerticalLayout"> <widget class="QGroupBox" name="currentVideoProcessingProgressGroupBox">
<item> <property name="title">
<widget class="QProgressBar" name="progressBar"> <string>Current Video Processing Progress</string>
<property name="value"> </property>
<number>0</number> <layout class="QVBoxLayout" name="verticalLayout_11">
</property> <item>
</widget> <widget class="QLabel" name="currentlyProcessingLabel">
</item> <property name="minimumSize">
<item> <size>
<layout class="QHBoxLayout" name="timeControlsHorizontalLayout"> <width>0</width>
<item> <height>20</height>
<widget class="QLabel" name="timeElapsedLabel"> </size>
<property name="acceptDrops"> </property>
<bool>false</bool> <property name="text">
</property> <string>Currently Processing:</string>
<property name="frameShape"> </property>
<enum>QFrame::StyledPanel</enum> </widget>
</property> </item>
<property name="text"> <item>
<string>Time Elapsed: 00:00:00</string> <widget class="QProgressBar" name="currentProgressBar">
</property> <property name="value">
</widget> <number>0</number>
</item> </property>
<item> </widget>
<widget class="QLabel" name="timeRemainingLabel"> </item>
<property name="frameShape"> <item>
<enum>QFrame::StyledPanel</enum> <layout class="QHBoxLayout" name="currentlyProcessingInfoHorizontalLayout">
</property> <item>
<property name="text"> <widget class="QLabel" name="timeElapsedLabel">
<string>Time Remaining: 00:00:00</string> <property name="minimumSize">
</property> <size>
</widget> <width>0</width>
</item> <height>20</height>
<item> </size>
<widget class="QLabel" name="rateLabel"> </property>
<property name="frameShape"> <property name="acceptDrops">
<enum>QFrame::StyledPanel</enum> <bool>false</bool>
</property> </property>
<property name="text"> <property name="frameShape">
<string>Rate (FPS): 0.0</string> <enum>QFrame::StyledPanel</enum>
</property> </property>
</widget> <property name="text">
</item> <string>Time Elapsed: 00:00:00</string>
<item> </property>
<widget class="QPushButton" name="startButton"> </widget>
<property name="enabled"> </item>
<bool>false</bool> <item>
</property> <widget class="QLabel" name="timeRemainingLabel">
<property name="text"> <property name="minimumSize">
<string>Start</string> <size>
</property> <width>0</width>
</widget> <height>20</height>
</item> </size>
<item> </property>
<widget class="QPushButton" name="stopButton"> <property name="frameShape">
<property name="enabled"> <enum>QFrame::StyledPanel</enum>
<bool>false</bool> </property>
</property> <property name="text">
<property name="text"> <string>Time Remaining: 00:00:00</string>
<string>Stop</string> </property>
</property> </widget>
</widget> </item>
</item> <item>
</layout> <widget class="QLabel" name="rateLabel">
</item> <property name="minimumSize">
</layout> <size>
<width>0</width>
<height>20</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="text">
<string>Rate (FPS): 0.0</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="framesLabel">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="text">
<string>Frames: 0/0</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="overallVideoProcessingProgressGroupBox">
<property name="title">
<string>Overall Video Processing Progress</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13">
<item>
<widget class="QProgressBar" name="overallProgressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="overallProcessingHorizontalLayout">
<item>
<widget class="QLabel" name="overallProgressLabel">
<property name="minimumSize">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="text">
<string>Overall Progress: 0/0</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="startButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>130</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="stopButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimumSize">
<size>
<width>130</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Stop</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>
@@ -1303,7 +1462,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>691</width> <width>718</width>
<height>21</height> <height>21</height>
</rect> </rect>
</property> </property>

View File

@@ -4,7 +4,7 @@
Name: Video2X Setup Script Name: Video2X Setup Script
Creator: K4YT3X Creator: K4YT3X
Date Created: November 28, 2018 Date Created: November 28, 2018
Last Modified: May 7, 2020 Last Modified: May 8, 2020
Editor: BrianPetkovsek Editor: BrianPetkovsek
Editor: SAT3LL Editor: SAT3LL
@@ -261,7 +261,7 @@ class Video2xSetup:
# configure only the specified drivers # configure only the specified drivers
if self.driver == 'all': if self.driver == 'all':
template_dict['waifu2x_caffe']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-caffe' / 'waifu2x-caffe-cui') template_dict['waifu2x_caffe']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-caffe' / 'waifu2x-caffe-cui')
template_dict['waifu2x_converter_cpp']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-converter-cpp') template_dict['waifu2x_converter_cpp']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-converter-cpp' / 'waifu2x-converter-cpp')
template_dict['waifu2x_ncnn_vulkan']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-ncnn-vulkan' / 'waifu2x-ncnn-vulkan') template_dict['waifu2x_ncnn_vulkan']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-ncnn-vulkan' / 'waifu2x-ncnn-vulkan')
template_dict['srmd_ncnn_vulkan']['path'] = str(LOCALAPPDATA / 'video2x' / 'srmd-ncnn-vulkan' / 'srmd-ncnn-vulkan') template_dict['srmd_ncnn_vulkan']['path'] = str(LOCALAPPDATA / 'video2x' / 'srmd-ncnn-vulkan' / 'srmd-ncnn-vulkan')
template_dict['anime4kcpp']['path'] = str(LOCALAPPDATA / 'video2x' / 'anime4kcpp' / 'CLI' / 'Anime4KCPP_CLI' / 'Anime4KCPP_CLI') template_dict['anime4kcpp']['path'] = str(LOCALAPPDATA / 'video2x' / 'anime4kcpp' / 'CLI' / 'Anime4KCPP_CLI' / 'Anime4KCPP_CLI')

View File

@@ -45,7 +45,7 @@ class WrapperMain:
parser.add_argument('-z', '--zoomFactor', type=float, help='zoom factor for resizing') parser.add_argument('-z', '--zoomFactor', type=float, help='zoom factor for resizing')
parser.add_argument('-t', '--threads', type=int, help='Threads count for video processing') parser.add_argument('-t', '--threads', type=int, help='Threads count for video processing')
parser.add_argument('-f', '--fastMode', action='store_true', help='Faster but maybe low quality') parser.add_argument('-f', '--fastMode', action='store_true', help='Faster but maybe low quality')
# parser.add_argument('-v', '--videoMode', action='store_true', help='Video process') parser.add_argument('-v', '--videoMode', action='store_true', help='Video process')
parser.add_argument('-s', '--preview', action='store_true', help='Preview image') parser.add_argument('-s', '--preview', action='store_true', help='Preview image')
parser.add_argument('-b', '--preprocessing', action='store_true', help='Enable pre processing') parser.add_argument('-b', '--preprocessing', action='store_true', help='Enable pre processing')
parser.add_argument('-a', '--postprocessing', action='store_true', help='Enable post processing') parser.add_argument('-a', '--postprocessing', action='store_true', help='Enable post processing')
@@ -73,7 +73,7 @@ class WrapperMain:
self.driver_settings['output'] = output_file self.driver_settings['output'] = output_file
self.driver_settings['zoomFactor'] = zoom_factor self.driver_settings['zoomFactor'] = zoom_factor
self.driver_settings['threads'] = threads self.driver_settings['threads'] = threads
# Anime4KCPP will look for Anime4KCPPKernel.cl under the current working directory # Anime4KCPP will look for Anime4KCPPKernel.cl under the current working directory
# change the CWD to its containing directory so it will find it # change the CWD to its containing directory so it will find it
if platform.system() == 'Windows': if platform.system() == 'Windows':

View File

@@ -45,7 +45,7 @@ class WrapperMain:
parser.add_argument('-l', '--list-processor', action='store_true', help='dump processor list') parser.add_argument('-l', '--list-processor', action='store_true', help='dump processor list')
parser.add_argument('-f', '--output-format', choices=['png', 'jpg'], help='The format used when running in recursive/folder mode\nSee --list-supported-formats for a list of supported formats/extensions.') parser.add_argument('-f', '--output-format', choices=['png', 'jpg'], help='The format used when running in recursive/folder mode\nSee --list-supported-formats for a list of supported formats/extensions.')
parser.add_argument('-c', '--png-compression', type=int, choices=range(10), help='Set PNG compression level (0-9), 9 = Max compression (slowest & smallest)') parser.add_argument('-c', '--png-compression', type=int, choices=range(10), help='Set PNG compression level (0-9), 9 = Max compression (slowest & smallest)')
parser.add_argument('-q', '--image-quality', type=int, choices=range(100), help='JPEG & WebP Compression quality (0-101, 0 being smallest size and lowest quality), use 101 for lossless WebP') parser.add_argument('-q', '--image-quality', type=int, choices=range(-1, 102), help='JPEG & WebP Compression quality (0-101, 0 being smallest size and lowest quality), use 101 for lossless WebP')
parser.add_argument('--block-size', type=int, help='block size') parser.add_argument('--block-size', type=int, help='block size')
parser.add_argument('--disable-gpu', action='store_true', help='disable GPU') parser.add_argument('--disable-gpu', action='store_true', help='disable GPU')
parser.add_argument('--force-OpenCL', action='store_true', help='force to use OpenCL on Intel Platform') parser.add_argument('--force-OpenCL', action='store_true', help='force to use OpenCL on Intel Platform')