30 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
k4yt3x
36aa3bf1d4 GUI resolve driver paths into absolute paths 2020-05-07 16:31:05 -04:00
k4yt3x
9dde3c66f1 build script 1.0.1: building into directory with name of version 2020-05-07 16:30:36 -04:00
k4yt3x
e9c1c22788 better exception handling, soft task interruption, GUI stop button, GUI folder processing, better argument checks 2020-05-07 15:58:22 -04:00
k4yt3x
134e8b7080 uncommented some keys unmanaged by v2x 2020-05-07 10:03:42 -04:00
k4yt3x
a295b4a54f finished adding comments for all drivers 2020-05-07 09:58:25 -04:00
k4yt3x
c198082190 updated new Anime4KCPP parameters 2020-05-07 09:38:13 -04:00
k4yt3x
8f2dc43af3 updated UI for newest Anime4KCPP 2020-05-07 09:33:37 -04:00
k4yt3x
b0ce8f3ff9 added environment variable expansion support, updated for newest Anime4KCPP 2020-05-07 09:33:12 -04:00
k4yt3x
0b1f7b8422 replaced absolute paths with environment variables, updated for newest Anime4KCPP 2020-05-07 09:32:51 -04:00
k4yt3x
def20650e2 added environment variable expansion support 2020-05-07 09:32:15 -04:00
k4yt3x
7489376404 fixed srmd-ncnn-vulkan path not added issue in the setup script 2020-05-07 09:02:42 -04:00
k4yt3x
826279ce09 fixing image captions 2020-05-06 20:48:19 -04:00
k4yt3x
88d2cd9e14 updated README screenshots and added image captions 2020-05-06 20:40:35 -04:00
18 changed files with 1789 additions and 1057 deletions

6
.gitignore vendored
View File

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

View File

@@ -72,6 +72,8 @@ Watch for the sharper edges in this screenshot around the shadows:
![preview](https://user-images.githubusercontent.com/21986859/49412428-65083280-f73a-11e8-8237-bb34158a545e.png) ![preview](https://user-images.githubusercontent.com/21986859/49412428-65083280-f73a-11e8-8237-bb34158a545e.png)
*Upscale Comparison Demonstration*
**You can also watch the YouTube video Demo: https://www.youtube.com/watch?v=PG94iPoeoZk** **You can also watch the YouTube video Demo: https://www.youtube.com/watch?v=PG94iPoeoZk**
Clip is from trailer of animated movie "千と千尋の神隠し". Copyright belongs to "株式会社スタジオジブリ (STUDIO GHIBLI INC.)". Will delete immediately if use of clip is in violation of copyright. Clip is from trailer of animated movie "千と千尋の神隠し". Copyright belongs to "株式会社スタジオジブリ (STUDIO GHIBLI INC.)". Will delete immediately if use of clip is in violation of copyright.
@@ -80,12 +82,20 @@ Clip is from trailer of animated movie "千と千尋の神隠し". Copyright bel
### Video2X GUI ### Video2X GUI
![Video2X GUI Screenshot](https://user-images.githubusercontent.com/21986859/81157039-19346b00-8f76-11ea-965f-4d7edf6a818e.png) ![Video2X GUI Main Tab Screenshot](https://user-images.githubusercontent.com/21986859/81241806-dcf71e00-8ffa-11ea-88ed-50b062d9bb5e.png)
*Video2X GUI Main Tab Screenshot*
![Video2X GUI Driver Settings Screenshot](https://user-images.githubusercontent.com/21986859/81241837-f9935600-8ffa-11ea-8b89-5ae098b63de4.png)
*Video2X GUI Driver Settings Screenshot*
### Video2X CLI ### Video2X CLI
![Video2X CLI Screenshot](https://user-images.githubusercontent.com/21986859/81039711-4fe88380-8e99-11ea-9846-175f72100a76.png) ![Video2X CLI Screenshot](https://user-images.githubusercontent.com/21986859/81039711-4fe88380-8e99-11ea-9846-175f72100a76.png)
*Video2X CLI Screenshot*
--- ---
## Documentations ## Documentations
@@ -158,6 +168,8 @@ If you can't find a video clip to begin with, or if you want to see a before-aft
![sample_video](https://user-images.githubusercontent.com/21986859/52905766-d5512b00-3236-11e9-9aea-077636539679.png) ![sample_video](https://user-images.githubusercontent.com/21986859/52905766-d5512b00-3236-11e9-9aea-077636539679.png)
*Sample Upscale Videos*
- [Sample Video Original (240P) 1.7MB](https://files.k4yt3x.com/Resources/Videos/sample_input.mp4) - [Sample Video Original (240P) 1.7MB](https://files.k4yt3x.com/Resources/Videos/sample_input.mp4)
- [Sample Video Upscaled (1080P) 4.8MB](https://files.k4yt3x.com/Resources/Videos/sample_output.mp4) - [Sample Video Upscaled (1080P) 4.8MB](https://files.k4yt3x.com/Resources/Videos/sample_output.mp4)

View File

@@ -2,7 +2,7 @@
Name: Video2X Build Script Name: Video2X Build Script
Creator: K4YT3X Creator: K4YT3X
Date Created: May 6, 2020 Date Created: May 6, 2020
Last Modified: May 6, 2020 Last Modified: May 7, 2020
Description: A PowerShell script that will build Video2X Description: A PowerShell script that will build Video2X
executable (PE) releases automatically using PyInstaller. executable (PE) releases automatically using PyInstaller.
@@ -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.0" $SCRIPT_VERSION = "1.0.1"
$VIDEO2X_VERSION = "4.0.0" $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"
@@ -46,33 +52,33 @@ pyinstaller --noconfirm --log-level=WARN `
video2x_setup.py video2x_setup.py
# remove old builds if found # remove old builds if found
if (Test-Path "video2x-builds" -PathType any) { if (Test-Path "$($VIDEO2X_VERSION)" -PathType any) {
Remove-Item -path "video2x-builds" -recurse Remove-Item -path "$($VIDEO2X_VERSION)" -recurse
} }
# create build directory # create build directory
New-Item "video2x-builds" -ItemType Directory New-Item "$($VIDEO2X_VERSION)" -ItemType Directory
# copy files into corresponding builds # copy files into corresponding builds
# full edition # full edition
Write-Host -ForegroundColor White "`nCreating full package" Write-Host -ForegroundColor White "`nCreating full package"
New-Item "video2x-builds\video2x-$($VIDEO2X_VERSION)-win32-full" -ItemType Directory New-Item "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-full" -ItemType Directory
Copy-Item "dist\video2x.exe" -Destination "video2x-builds\video2x-$($VIDEO2X_VERSION)-win32-full\" Copy-Item "dist\video2x.exe" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-full\"
Copy-Item "dist\video2x_gui.exe" -Destination "video2x-builds\video2x-$($VIDEO2X_VERSION)-win32-full\" Copy-Item "dist\video2x_gui.exe" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-full\"
Copy-Item -Path "$env:LOCALAPPDATA\video2x" -Destination "video2x-builds\video2x-$($VIDEO2X_VERSION)-win32-full\dependencies" -Recurse Copy-Item -Path "$env:LOCALAPPDATA\video2x" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-full\dependencies" -Recurse
# overwrite paths to relative paths # overwrite paths to relative paths
(Get-Content "video2x.yaml").replace("C:\Users\K4YT3X\AppData\Local\video2x\", "dependencies\") | Set-Content "video2x.yaml.relative" (Get-Content "video2x.yaml").replace("%LOCALAPPDATA%\video2x", "dependencies") | Set-Content "video2x.yaml.relative"
Move-Item "video2x.yaml.relative" -Destination "video2x-builds\video2x-$($VIDEO2X_VERSION)-win32-full\video2x.yaml" Move-Item "video2x.yaml.relative" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-full\video2x.yaml"
# light edition # light edition
Write-Host -ForegroundColor White "`nCreating light package" Write-Host -ForegroundColor White "`nCreating light package"
New-Item "video2x-builds\video2x-$($VIDEO2X_VERSION)-win32-light" -ItemType Directory New-Item "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-light" -ItemType Directory
Copy-Item "dist\video2x.exe" -Destination "video2x-builds\video2x-$($VIDEO2X_VERSION)-win32-light\" Copy-Item "dist\video2x.exe" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-light\"
Copy-Item "dist\video2x_gui.exe" -Destination "video2x-builds\video2x-$($VIDEO2X_VERSION)-win32-light\" Copy-Item "dist\video2x_gui.exe" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-light\"
Copy-Item "dist\video2x_setup.exe" -Destination "video2x-builds\video2x-$($VIDEO2X_VERSION)-win32-light\" Copy-Item "dist\video2x_setup.exe" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-light\"
Copy-Item "video2x.yaml" -Destination "video2x-builds\video2x-$($VIDEO2X_VERSION)-win32-light\" Copy-Item "video2x.yaml" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-light\"
Copy-Item "requirements.txt" -Destination "video2x-builds\video2x-$($VIDEO2X_VERSION)-win32-light\" Copy-Item "requirements.txt" -Destination "$($VIDEO2X_VERSION)\video2x-$($VIDEO2X_VERSION)-win32-light\"
# clean up temporary files # clean up temporary files
Write-Host -ForegroundColor White "`nDeleting temporary files" Write-Host -ForegroundColor White "`nDeleting temporary files"

View File

@@ -5,8 +5,8 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"POT-Creation-Date: 2020-05-04 19:14-0400\n" "POT-Creation-Date: 2020-05-08 22:10-0400\n"
"PO-Revision-Date: 2020-05-04 19:16-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"
@@ -17,107 +17,167 @@ msgstr ""
"X-Generator: Poedit 2.3\n" "X-Generator: Poedit 2.3\n"
"Plural-Forms: nplurals=1; plural=0;\n" "Plural-Forms: nplurals=1; plural=0;\n"
#: upscaler.py:85 #: progress_monitor.py:42
msgid "Extracted frames are being saved to: {}"
msgstr "提取的帧将被保存到:{}"
#: upscaler.py:87
msgid "Upscaled frames are being saved to: {}"
msgstr "已放大的帧将被保存到:{}"
#: upscaler.py:97
msgid "Cleaning up cache directory: {}"
msgstr "清理缓存目录:{}"
#: upscaler.py:100
msgid "Unable to delete: {}"
msgstr "无法删除:{}"
#: upscaler.py:107
msgid "You must specify input video file/directory path"
msgstr "您必须指定输入视频文件/目录路径"
#: upscaler.py:110
msgid "You must specify output video file/directory path"
msgstr "您必须指定输出视频文件/目录路径"
#: upscaler.py:113
msgid "Selected driver accepts only scaling ratio"
msgstr "所选驱动程序仅接受缩放比率"
#: upscaler.py:116
msgid "Scaling ratio must be 1 or 2 for waifu2x_ncnn_vulkan"
msgstr "waifu2x_ncnn_vulkan 的缩放比必须为 1 或 2"
#: upscaler.py:119
msgid "Scaling ratio must be one of 2, 3 or 4 for srmd_ncnn_vulkan"
msgstr "srmd_ncnn_vulkan 的缩放比必须为 2、3 或 4"
#: upscaler.py:122
msgid "You can only specify either scaling ratio or output width and height"
msgstr "您只能指定缩放比或输出宽度和高度两者之一"
#: upscaler.py:125
msgid "You must specify both width and height"
msgstr "您必须同时指定宽度和高度"
#: upscaler.py:142
msgid "Upscaling Progress" msgid "Upscaling Progress"
msgstr "放大进度" msgstr "放大进度"
#: upscaler.py:179 #: upscaler.py:105
msgid "Specified or default cache directory is a file/link"
msgstr "指定或默认的缓存目录是文件/链接"
#: upscaler.py:111
msgid "Creating cache directory {}"
msgstr "创建缓存目录 {}"
#: upscaler.py:114
msgid "Unable to create {}"
msgstr "无法创建 {}"
#: upscaler.py:119
msgid "Extracted frames are being saved to: {}"
msgstr "提取的帧将被保存到:{}"
#: upscaler.py:121
msgid "Upscaled frames are being saved to: {}"
msgstr "已放大的帧将被保存到:{}"
#: upscaler.py:131
msgid "Cleaning up cache directory: {}"
msgstr "清理缓存目录:{}"
#: upscaler.py:134
msgid "Unable to delete: {}"
msgstr "无法删除:{}"
#: upscaler.py:140 upscaler.py:155 upscaler.py:166
msgid "Input and output path type mismatch"
msgstr "输入和输出路径类型不匹配"
#: 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"
msgstr "所选的输入路径是单个文件,但输出路径是目录"
#: upscaler.py:159
msgid "No suffix found in output file path"
msgstr "在输出文件路径中未找到后缀"
#: upscaler.py:160
msgid "Suffix must be specified for FFmpeg"
msgstr "必须为 FFmpeg 指定后缀"
#: upscaler.py:167
msgid "Input is directory but output is existing single file"
msgstr "输入是目录,但输出是现有的单个文件"
#: upscaler.py:176
msgid "Input path is neither a file nor a directory"
msgstr "输入路径既不是文件也不是目录"
#: upscaler.py:185
msgid "FFmpeg or FFprobe cannot be found under the specified path"
msgstr "在指定的路径下找不到 FFmpeg 或 FFprobe"
#: upscaler.py:186 upscaler.py:196
msgid "Please check the configuration file settings"
msgstr "请检查配置文件设置"
#: upscaler.py:195
msgid "Specified driver executable directory doesn't exist"
msgstr "指定驱动的可执行文件不存在"
#: upscaler.py:222
msgid "Failed to parse driver argument: {}"
msgstr "解析驱动程序参数失败:{}"
#: upscaler.py:237
msgid "Unrecognized driver: {}" msgid "Unrecognized driver: {}"
msgstr "无法识别的驱动名称:{}" msgstr "无法识别的驱动名称:{}"
#: upscaler.py:258 #: upscaler.py:309
msgid "Starting progress monitor"
msgstr "启动进度监视器"
#: upscaler.py:314
msgid "Starting upscaled image cleaner" msgid "Starting upscaled image cleaner"
msgstr "启动已放大图像清理程序" msgstr "启动已放大图像清理程序"
#: upscaler.py:264 #: upscaler.py:323 upscaler.py:340
msgid "Main process waiting for subprocesses to exit" msgid "Killing progress monitor"
msgstr "主进程开始等待子进程结束" msgstr "终结进度监视器"
#: upscaler.py:266 #: upscaler.py:326 upscaler.py:343
msgid "Subprocess {} exited with code {}"
msgstr "子进程 {} 结束,返回码 {}"
#: upscaler.py:274 upscaler.py:287
msgid "Killing upscaled image cleaner" msgid "Killing upscaled image cleaner"
msgstr "终结已放大图像清理程序" msgstr "终结已放大图像清理程序"
#: upscaler.py:313 upscaler.py:368 #: upscaler.py:347
msgid "Terminating all processes"
msgstr "正在终止所有进程"
#: upscaler.py:354
msgid "Main process waiting for subprocesses to exit"
msgstr "主进程开始等待子进程结束"
#: upscaler.py:373 upscaler.py:377
msgid "Subprocess {} exited with code {}"
msgstr "子进程 {} 结束,返回码 {}"
#: upscaler.py:383
msgid "Stop signal received"
msgstr "收到停止信号"
#: upscaler.py:388
msgid "Subprocess execution ran into an error"
msgstr "子进程执行遇到错误"
#: upscaler.py:430
msgid "Upscaling single video file: {}"
msgstr "放大单个视频文件:{}"
#: upscaler.py:452 upscaler.py:515
msgid "Starting to upscale extracted images" msgid "Starting to upscale extracted images"
msgstr "开始对提取的帧进行放大" msgstr "开始对提取的帧进行放大"
#: upscaler.py:316 upscaler.py:370 #: upscaler.py:461 upscaler.py:517
msgid "Upscaling completed" msgid "Upscaling completed"
msgstr "放大完成" msgstr "放大完成"
#: upscaler.py:324 #: upscaler.py:470
msgid "Reading video information" msgid "Reading video information"
msgstr "读取视频信息" msgstr "读取视频信息"
#: upscaler.py:338 #: upscaler.py:484
msgid "Aborting: No video stream found" msgid "Aborting: No video stream found"
msgstr "程序中止:文件中未找到视频流" msgstr "程序中止:文件中未找到视频流"
#: upscaler.py:355 #: upscaler.py:502
msgid "Unsupported pixel format: {}" msgid "Unsupported pixel format: {}"
msgstr "不支持的像素格式:{}" msgstr "不支持的像素格式:{}"
#: upscaler.py:358 #: upscaler.py:505
msgid "Framerate: {}" msgid "Framerate: {}"
msgstr "帧率:{}" msgstr "帧率:{}"
#: upscaler.py:373 #: upscaler.py:520
msgid "Converting extracted frames into video" msgid "Converting extracted frames into video"
msgstr "将提取的帧转换为视频" msgstr "将提取的帧转换为视频"
#: upscaler.py:377 #: upscaler.py:525
msgid "Conversion completed" msgid "Conversion completed"
msgstr "转换已完成" msgstr "转换已完成"
#: upscaler.py:380 #: upscaler.py:528
msgid "Migrating audio tracks and subtitles to upscaled video" msgid "Migrating audio tracks and subtitles to upscaled video"
msgstr "将音轨和字幕迁移到放大后的视频" msgstr "将音轨和字幕迁移到放大后的视频"
@@ -187,58 +247,34 @@ msgstr "缩放比"
msgid "This file cannot be imported" msgid "This file cannot be imported"
msgstr "此文件无法被当作模块导入" msgstr "此文件无法被当作模块导入"
#: video2x.py:193
msgid "Specified driver executable directory doesn't exist"
msgstr "指定驱动的可执行文件不存在"
#: video2x.py:194
msgid "Please check the configuration file settings"
msgstr "请检查配置文件设置"
#: video2x.py:211
msgid "Specified cache directory is a file/link"
msgstr "指定的缓存目录是文件/链接"
#: video2x.py:218
msgid "Creating cache directory {}"
msgstr "创建缓存目录 {}"
#: video2x.py:224 #: video2x.py:224
msgid "Unable to create {}"
msgstr "无法创建 {}"
#: video2x.py:237
msgid "Upscaling single video file: {}"
msgstr "放大单个视频文件:{}"
#: video2x.py:241
msgid "Input and output path type mismatch"
msgstr "输入和输出路径类型不匹配"
#: video2x.py:242
msgid "Input is single file but output is directory"
msgstr "所选的输入路径是单个文件,但输出路径是目录"
#: video2x.py:245
msgid "No suffix found in output file path"
msgstr "在输出文件路径中未找到后缀"
#: video2x.py:246
msgid "Suffix must be specified for FFmpeg"
msgstr "必须为 FFmpeg 指定后缀"
#: video2x.py:270
msgid "Upscaling videos in directory: {}"
msgstr "放大该文件夹中的所有视频:{}"
#: video2x.py:295
msgid "Input path is neither a file nor a directory"
msgstr "输入路径既不是文件也不是目录"
#: video2x.py:298
msgid "Program completed, taking {} seconds" msgid "Program completed, taking {} seconds"
msgstr "程序执行完毕,总计花费 {} 秒" msgstr "程序执行完毕,总计花费 {} 秒"
#: video2x.py:301 #: video2x.py:227
msgid "An exception has occurred" msgid "An exception has occurred"
msgstr "发生了异常" msgstr "发生了异常"
#~ msgid "You must specify input video file/directory path"
#~ msgstr "您必须指定输入视频文件/目录路径"
#~ msgid "You must specify output video file/directory path"
#~ msgstr "您必须指定输出视频文件/目录路径"
#~ msgid "Selected driver accepts only scaling ratio"
#~ msgstr "所选驱动程序仅接受缩放比率"
#~ msgid "Scaling ratio must be 1 or 2 for waifu2x_ncnn_vulkan"
#~ msgstr "waifu2x_ncnn_vulkan 的缩放比必须为 1 或 2"
#~ msgid "Scaling ratio must be one of 2, 3 or 4 for srmd_ncnn_vulkan"
#~ msgstr "srmd_ncnn_vulkan 的缩放比必须为 2、3 或 4"
#~ msgid "You can only specify either scaling ratio or output width and height"
#~ msgstr "您只能指定缩放比或输出宽度和高度两者之一"
#~ msgid "You must specify both width and height"
#~ msgstr "您必须同时指定宽度和高度"
#~ msgid "Upscaling videos in directory: {}"
#~ msgstr "放大该文件夹中的所有视频:{}"

61
src/progress_monitor.py Normal file
View File

@@ -0,0 +1,61 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Video2X Upscale Progress Monitor
Author: BrianPetkovsek
Date Created: May 7, 2020
Last Modified: May 7, 2020
"""
# built-in imports
import contextlib
import threading
import time
# third-party imports
from tqdm import tqdm
class ProgressMonitor(threading.Thread):
""" progress monitor
This class provides progress monitoring functionalities
by keeping track of the amount of frames in the input
directory and the output directory. This is originally
suggested by @ArmandBernard.
"""
def __init__(self, upscaler, extracted_frames_directories):
threading.Thread.__init__(self)
self.upscaler = upscaler
self.extracted_frames_directories = extracted_frames_directories
self.running = False
def run(self):
self.running = True
# get number of extracted frames
self.upscaler.total_frames = 0
for directory in self.extracted_frames_directories:
self.upscaler.total_frames += len([f for f in directory.iterdir() if str(f).lower().endswith(self.upscaler.image_format.lower())])
with tqdm(total=self.upscaler.total_frames, ascii=True, desc=_('Upscaling Progress')) as progress_bar:
# tqdm update method adds the value to the progress
# bar instead of setting the value. Therefore, a delta
# needs to be calculated.
previous_cycle_frames = 0
while self.running:
with contextlib.suppress(FileNotFoundError):
self.upscaler.total_frames_upscaled = len([f for f in self.upscaler.upscaled_frames.iterdir() if str(f).lower().endswith(self.upscaler.image_format.lower())])
# update progress bar
delta = self.upscaler.total_frames_upscaled - previous_cycle_frames
previous_cycle_frames = self.upscaler.total_frames_upscaled
progress_bar.update(delta)
time.sleep(1)
def stop(self):
self.running = False
self.join()

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 6, 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
@@ -14,6 +14,7 @@ a folder.
# local imports # local imports
from exceptions import * from exceptions import *
from image_cleaner import ImageCleaner from image_cleaner import ImageCleaner
from progress_monitor import ProgressMonitor
from wrappers.ffmpeg import Ffmpeg from wrappers.ffmpeg import Ffmpeg
# built-in imports # built-in imports
@@ -25,17 +26,16 @@ import importlib
import locale import locale
import os import os
import pathlib import pathlib
import queue
import re import re
import shutil import shutil
import sys import subprocess
import tempfile import tempfile
import threading
import time import time
import traceback import traceback
# third-party imports # third-party imports
from avalon_framework import Avalon from avalon_framework import Avalon
from tqdm import tqdm
# internationalization constants # internationalization constants
DOMAIN = 'video2x' DOMAIN = 'video2x'
@@ -67,10 +67,10 @@ class Upscaler:
ArgumentError -- if argument is not valid ArgumentError -- if argument is not valid
""" """
def __init__(self, input_video, output_video, driver_settings, ffmpeg_settings): def __init__(self, input_path, output_path, driver_settings, ffmpeg_settings):
# mandatory arguments # mandatory arguments
self.input_video = input_video self.input = input_path
self.output_video = output_video self.output = output_path
self.driver_settings = driver_settings self.driver_settings = driver_settings
self.ffmpeg_settings = ffmpeg_settings self.ffmpeg_settings = ffmpeg_settings
@@ -84,14 +84,36 @@ class Upscaler:
self.image_format = 'png' self.image_format = 'png'
self.preserve_frames = False self.preserve_frames = False
# other internal members and signals
self.running = False
self.total_frames_upscaled = 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 directory """create temporary directories
""" """
# create a new temp directory if the current one is not found # if cache directory unspecified, use %TEMP%\video2x
if not self.video2x_cache_directory.exists(): if self.video2x_cache_directory is None:
self.video2x_cache_directory = pathlib.Path(tempfile.gettempdir()) / 'video2x' self.video2x_cache_directory = pathlib.Path(tempfile.gettempdir()) / 'video2x'
# if specified cache path exists and isn't a directory
if self.video2x_cache_directory.exists() and not self.video2x_cache_directory.is_dir():
Avalon.error(_('Specified or default cache directory is a file/link'))
raise FileExistsError('Specified or default cache directory is a file/link')
# if cache directory doesn't exist, try creating it
if not self.video2x_cache_directory.exists():
try:
Avalon.debug_info(_('Creating cache directory {}').format(self.video2x_cache_directory))
self.video2x_cache_directory.mkdir(parents=True, exist_ok=True)
except Exception as exception:
Avalon.error(_('Unable to create {}').format(self.video2x_cache_directory))
raise exception
# create temp directories for extracted frames and upscaled frames # create temp directories for extracted frames and upscaled frames
self.extracted_frames = pathlib.Path(tempfile.mkdtemp(dir=self.video2x_cache_directory)) self.extracted_frames = pathlib.Path(tempfile.mkdtemp(dir=self.video2x_cache_directory))
Avalon.debug_info(_('Extracted frames are being saved to: {}').format(self.extracted_frames)) Avalon.debug_info(_('Extracted frames are being saved to: {}').format(self.extracted_frames))
@@ -113,65 +135,92 @@ class Upscaler:
traceback.print_exc() traceback.print_exc()
def _check_arguments(self): def _check_arguments(self):
# check if arguments are valid / all necessary argument if isinstance(self.input, list):
# values are specified if self.output.exists() and not self.output.is_dir():
if not self.input_video: Avalon.error(_('Input and output path type mismatch'))
Avalon.error(_('You must specify input video file/directory path')) Avalon.error(_('Input is multiple files but output is not directory'))
raise ArgumentError('input video path not specified') raise ArgumentError('input output path type mismatch')
if not self.output_video: for input_path in self.input:
Avalon.error(_('You must specify output video file/directory path')) if not input_path.is_file() and not input_path.is_dir():
raise ArgumentError('output video path not specified') Avalon.error(_('Input path {} is neither a file nor a directory').format(input_path))
if (self.driver in ['waifu2x_converter', 'waifu2x_ncnn_vulkan', 'anime4k']) and self.scale_width and self.scale_height: raise FileNotFoundError(f'{input_path} is neither file nor directory')
Avalon.error(_('Selected driver accepts only scaling ratio'))
raise ArgumentError('selected driver supports only scaling ratio')
if self.driver == 'waifu2x_ncnn_vulkan' and self.scale_ratio is not None and (self.scale_ratio > 2 or not self.scale_ratio.is_integer()):
Avalon.error(_('Scaling ratio must be 1 or 2 for waifu2x_ncnn_vulkan'))
raise ArgumentError('scaling ratio must be 1 or 2 for waifu2x_ncnn_vulkan')
if self.driver == 'srmd_ncnn_vulkan' and self.scale_ratio is not None and (self.scale_ratio not in [2, 3, 4]):
Avalon.error(_('Scaling ratio must be one of 2, 3 or 4 for srmd_ncnn_vulkan'))
raise ArgumentError('scaling ratio must be one of 2, 3 or 4 for srmd_ncnn_vulkan')
if (self.scale_width or self.scale_height) and self.scale_ratio:
Avalon.error(_('You can only specify either scaling ratio or output width and height'))
raise ArgumentError('both scaling ration and width/height specified')
if (self.scale_width and not self.scale_height) or (not self.scale_width and self.scale_height):
Avalon.error(_('You must specify both width and height'))
raise ArgumentError('only one of width or height is specified')
def _progress_bar(self, extracted_frames_directories):
""" This method prints a progress bar
This method prints a progress bar by keeping track
of the amount of frames in the input directory
and the output directory. This is originally
suggested by @ArmandBernard.
"""
# get number of extracted frames
self.total_frames = 0
for directory in extracted_frames_directories:
self.total_frames += len([f for f in directory.iterdir() if str(f).lower().endswith(self.image_format.lower())])
with tqdm(total=self.total_frames, ascii=True, desc=_('Upscaling Progress')) as progress_bar:
# tqdm update method adds the value to the progress
# bar instead of setting the value. Therefore, a delta
# needs to be calculated.
previous_cycle_frames = 0
while not self.progress_bar_exit_signal:
with contextlib.suppress(FileNotFoundError): with contextlib.suppress(FileNotFoundError):
self.total_frames_upscaled = len([f for f in self.upscaled_frames.iterdir() if str(f).lower().endswith(self.image_format.lower())]) if input_path.samefile(self.output):
delta = self.total_frames_upscaled - previous_cycle_frames Avalon.error(_('Input directory and output directory cannot be the same'))
previous_cycle_frames = self.total_frames_upscaled raise FileExistsError('input directory and output directory are the same')
# if upscaling is finished # if input is a file
if self.total_frames_upscaled >= self.total_frames: elif self.input.is_file():
return if self.output.is_dir():
Avalon.error(_('Input and output path type mismatch'))
Avalon.error(_('Input is single file but output is directory'))
raise ArgumentError('input output path type mismatch')
if not re.search(r'.*\..*$', str(self.output)):
Avalon.error(_('No suffix found in output file path'))
Avalon.error(_('Suffix must be specified for FFmpeg'))
raise ArgumentError('no output video suffix specified')
# adds the delta into the progress bar # if input is a directory
progress_bar.update(delta) elif self.input.is_dir():
if self.output.is_file():
Avalon.error(_('Input and output path type mismatch'))
Avalon.error(_('Input is directory but output is existing single file'))
raise ArgumentError('input output path type mismatch')
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')
time.sleep(1) # if input is neither
else:
Avalon.error(_('Input path is neither a file nor a directory'))
raise FileNotFoundError(f'{self.input} is neither file nor directory')
# check Fmpeg settings
ffmpeg_path = pathlib.Path(self.ffmpeg_settings['ffmpeg_path'])
if not ((pathlib.Path(ffmpeg_path / 'ffmpeg.exe').is_file() and
pathlib.Path(ffmpeg_path / 'ffprobe.exe').is_file()) or
(pathlib.Path(ffmpeg_path / 'ffmpeg').is_file() and
pathlib.Path(ffmpeg_path / 'ffprobe').is_file())):
Avalon.error(_('FFmpeg or FFprobe cannot be found under the specified path'))
Avalon.error(_('Please check the configuration file settings'))
raise FileNotFoundError(self.ffmpeg_settings['ffmpeg_path'])
# check if driver settings
driver_settings = copy.deepcopy(self.driver_settings)
driver_path = driver_settings.pop('path')
# check if driver path exists
if not (pathlib.Path(driver_path).is_file() or pathlib.Path(f'{driver_path}.exe').is_file()):
Avalon.error(_('Specified driver executable directory doesn\'t exist'))
Avalon.error(_('Please check the configuration file settings'))
raise FileNotFoundError(driver_path)
# parse driver arguments using driver's parser
# the parser will throw AttributeError if argument doesn't satisfy constraints
try:
driver_arguments = []
for key in driver_settings.keys():
value = driver_settings[key]
if value is None or value is False:
continue
else:
if len(key) == 1:
driver_arguments.append(f'-{key}')
else:
driver_arguments.append(f'--{key}')
# true means key is an option
if value is not True:
driver_arguments.append(str(value))
DriverWrapperMain = getattr(importlib.import_module(f'wrappers.{self.driver}'), 'WrapperMain')
DriverWrapperMain.parse_arguments(driver_arguments)
except AttributeError as e:
Avalon.error(_('Failed to parse driver argument: {}').format(e.args[0]))
raise e
def _upscale_frames(self): def _upscale_frames(self):
""" Upscale video frames with waifu2x-caffe """ Upscale video frames with waifu2x-caffe
@@ -183,16 +232,10 @@ class Upscaler:
w2 {Waifu2x Object} -- initialized waifu2x object w2 {Waifu2x Object} -- initialized waifu2x object
""" """
# progress bar process exit signal
self.progress_bar_exit_signal = False
# initialize waifu2x driver # initialize waifu2x driver
if self.driver not in AVAILABLE_DRIVERS: if self.driver not in AVAILABLE_DRIVERS:
raise UnrecognizedDriverError(_('Unrecognized driver: {}').format(self.driver)) raise UnrecognizedDriverError(_('Unrecognized driver: {}').format(self.driver))
# create a container for all upscaler processes
upscaler_processes = []
# list all images in the extracted frames # list all images in the extracted frames
frames = [(self.extracted_frames / f) for f in self.extracted_frames.iterdir() if f.is_file] frames = [(self.extracted_frames / f) for f in self.extracted_frames.iterdir() if f.is_file]
@@ -234,73 +277,117 @@ 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':
upscaler_processes.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':
upscaler_processes.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':
upscaler_processes.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':
upscaler_processes.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
progress_bar = threading.Thread(target=self._progress_bar, args=(process_directories,)) Avalon.debug_info(_('Starting progress monitor'))
progress_bar.start() self.progress_monitor = ProgressMonitor(self, process_directories)
self.progress_monitor.start()
# create the clearer and start it # create the clearer and start it
Avalon.debug_info(_('Starting upscaled image cleaner')) Avalon.debug_info(_('Starting upscaled image cleaner'))
image_cleaner = ImageCleaner(self.extracted_frames, self.upscaled_frames, len(upscaler_processes)) self.image_cleaner = ImageCleaner(self.extracted_frames, self.upscaled_frames, len(self.process_pool))
image_cleaner.start() self.image_cleaner.start()
# wait for all process to exit # wait for all process to exit
try: try:
Avalon.debug_info(_('Main process waiting for subprocesses to exit')) self._wait()
for process in upscaler_processes: except (Exception, KeyboardInterrupt, SystemExit) as e:
Avalon.debug_info(_('Subprocess {} exited with code {}').format(process.pid, process.wait())) # cleanup
except (KeyboardInterrupt, SystemExit): Avalon.debug_info(_('Killing progress monitor'))
Avalon.warning('Exit signal received') self.progress_monitor.stop()
Avalon.warning('Killing processes')
for process in upscaler_processes:
process.terminate()
# cleanup and exit with exit code 1
Avalon.debug_info(_('Killing upscaled image cleaner')) Avalon.debug_info(_('Killing upscaled image cleaner'))
image_cleaner.stop() self.image_cleaner.stop()
self.progress_bar_exit_signal = True raise e
sys.exit(1)
# if the driver is waifu2x-converter-cpp # if the driver is waifu2x-converter-cpp
# images need to be renamed to be recognizable for FFmpeg # images need to be renamed to be recognizable for FFmpeg
if self.driver == 'waifu2x_converter_cpp': if self.driver == 'waifu2x_converter_cpp':
for image in [f for f in self.upscaled_frames.iterdir() if f.is_file()]: for image in [f for f in self.upscaled_frames.iterdir() if f.is_file()]:
renamed = re.sub(f'_\\[.*\\]\\[x(\\d+(\\.\\d+)?)\\]\\.{self.image_format}', f'.{self.image_format}', str(image.name)) renamed = re.sub(f'_\\[.*\\]\\[x(\\d+(\\.\\d+)?)\\]\\.{self.image_format}',
f'.{self.image_format}',
str(image.name))
(self.upscaled_frames / image).rename(self.upscaled_frames / renamed) (self.upscaled_frames / image).rename(self.upscaled_frames / renamed)
# upscaling done, kill the clearer # upscaling done, kill helper threads
Avalon.debug_info(_('Killing upscaled image cleaner')) Avalon.debug_info(_('Killing progress monitor'))
image_cleaner.stop() self.progress_monitor.stop()
# pass exit signal to progress bar thread Avalon.debug_info(_('Killing upscaled image cleaner'))
self.progress_bar_exit_signal = True self.image_cleaner.stop()
def _terminate_subprocesses(self):
Avalon.warning(_('Terminating all processes'))
for process in self.process_pool:
process.terminate()
def _wait(self):
""" wait for subprocesses in process pool to complete
"""
Avalon.debug_info(_('Main process waiting for subprocesses to exit'))
try:
# while process pool not empty
while self.process_pool:
# if stop signal received, terminate all processes
if self.running is False:
raise SystemExit
for process in self.process_pool:
process_status = process.poll()
# if process finished
if process_status is None:
continue
# if return code is not 0
elif process_status != 0:
Avalon.error(_('Subprocess {} exited with code {}').format(process.pid, process_status))
raise subprocess.CalledProcessError(process_status, process.args)
else:
Avalon.debug_info(_('Subprocess {} exited with code {}').format(process.pid, process_status))
self.process_pool.remove(process)
time.sleep(0.1)
except (KeyboardInterrupt, SystemExit) as e:
Avalon.warning(_('Stop signal received'))
self._terminate_subprocesses()
raise e
except (Exception, subprocess.CalledProcessError) as e:
Avalon.error(_('Subprocess execution ran into an error'))
self._terminate_subprocesses()
raise e
def run(self): def run(self):
""" Main controller for Video2X """ Main controller for Video2X
@@ -309,93 +396,149 @@ class Upscaler:
and handles all necessary functions. and handles all necessary functions.
""" """
# external stop signal when called in a thread
self.running = True
# define process pool to contain processes
self.process_pool = []
# parse arguments for waifu2x # parse arguments for waifu2x
# check argument sanity # check argument sanity
self._check_arguments() self._check_arguments()
# convert paths to absolute paths # define processing queue
self.input_video = self.input_video.absolute() self.processing_queue = queue.Queue()
self.output_video = self.output_video.absolute()
# drivers that have native support for video processing # if input is a list of files
if self.driver == 'anime4kcpp': if isinstance(self.input, list):
# append FFmpeg path to the end of PATH # make output directory if it doesn't exist
# Anime4KCPP will then use FFmpeg to migrate audio tracks self.output.mkdir(parents=True, exist_ok=True)
os.environ['PATH'] += f';{self.ffmpeg_settings["ffmpeg_path"]}'
Avalon.info(_('Starting to upscale extracted images'))
# import and initialize Anime4KCPP wrapper for input_path in self.input:
DriverWrapperMain = getattr(importlib.import_module('wrappers.anime4kcpp'), 'WrapperMain')
driver = DriverWrapperMain(copy.deepcopy(self.driver_settings))
# run Anime4KCPP if input_path.is_file():
driver.upscale(self.input_video, self.output_video, self.scale_ratio, self.processes).wait() output_video = self.output / input_path.name
Avalon.info(_('Upscaling completed')) self.processing_queue.put((input_path.absolute(), output_video.absolute()))
else: elif input_path.is_dir():
self.create_temp_directories() 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()))
# initialize objects for ffmpeg and waifu2x-caffe # if input specified is single file
fm = Ffmpeg(self.ffmpeg_settings, self.image_format) elif self.input.is_file():
Avalon.info(_('Upscaling single video file: {}').format(self.input))
self.processing_queue.put((self.input.absolute(), self.output.absolute()))
Avalon.info(_('Reading video information')) # if input specified is a directory
video_info = fm.get_video_info(self.input_video) elif self.input.is_dir():
# analyze original video with ffprobe and retrieve framerate
# width, height = info['streams'][0]['width'], info['streams'][0]['height']
# find index of video stream # make output directory if it doesn't exist
video_stream_index = None self.output.mkdir(parents=True, exist_ok=True)
for stream in video_info['streams']: for input_video in [f for f in self.input.iterdir() if f.is_file()]:
if stream['codec_type'] == 'video': output_video = self.output / input_video.name
video_stream_index = stream['index'] self.processing_queue.put((input_video.absolute(), output_video.absolute()))
break
# exit if no video stream found # record video count for external calls
if video_stream_index is None: self.total_videos = self.processing_queue.qsize()
Avalon.error(_('Aborting: No video stream found'))
raise StreamNotFoundError('no video stream found')
# extract frames from video while not self.processing_queue.empty():
fm.extract_frames(self.input_video, self.extracted_frames) self.current_input_video, output_video = self.processing_queue.get()
# drivers that have native support for video processing
if self.driver == 'anime4kcpp':
# append FFmpeg path to the end of PATH
# Anime4KCPP will then use FFmpeg to migrate audio tracks
os.environ['PATH'] += f';{self.ffmpeg_settings["ffmpeg_path"]}'
Avalon.info(_('Starting to upscale extracted images'))
# get average frame rate of video stream # import and initialize Anime4KCPP wrapper
framerate = float(Fraction(video_info['streams'][video_stream_index]['avg_frame_rate'])) DriverWrapperMain = getattr(importlib.import_module('wrappers.anime4kcpp'), 'WrapperMain')
fm.pixel_format = video_info['streams'][video_stream_index]['pix_fmt'] driver = DriverWrapperMain(copy.deepcopy(self.driver_settings))
# get a dict of all pixel formats and corresponding bit depth # run Anime4KCPP
pixel_formats = fm.get_pixel_formats() self.process_pool.append(driver.upscale(self.current_input_video, output_video, self.scale_ratio, self.processes))
self._wait()
Avalon.info(_('Upscaling completed'))
# try getting pixel format's corresponding bti depth else:
try: try:
self.bit_depth = pixel_formats[fm.pixel_format] self.create_temp_directories()
except KeyError:
Avalon.error(_('Unsupported pixel format: {}').format(fm.pixel_format))
raise UnsupportedPixelError(f'unsupported pixel format {fm.pixel_format}')
Avalon.info(_('Framerate: {}').format(framerate)) # initialize objects for ffmpeg and waifu2x-caffe
fm = Ffmpeg(self.ffmpeg_settings, self.image_format)
# width/height will be coded width/height x upscale factor Avalon.info(_('Reading video information'))
if self.scale_ratio: video_info = fm.get_video_info(self.current_input_video)
original_width = video_info['streams'][video_stream_index]['width'] # analyze original video with FFprobe and retrieve framerate
original_height = video_info['streams'][video_stream_index]['height'] # width, height = info['streams'][0]['width'], info['streams'][0]['height']
self.scale_width = int(self.scale_ratio * original_width)
self.scale_height = int(self.scale_ratio * original_height)
# upscale images one by one using waifu2x # find index of video stream
Avalon.info(_('Starting to upscale extracted images')) video_stream_index = None
self._upscale_frames() for stream in video_info['streams']:
Avalon.info(_('Upscaling completed')) if stream['codec_type'] == 'video':
video_stream_index = stream['index']
break
# frames to Video # exit if no video stream found
Avalon.info(_('Converting extracted frames into video')) if video_stream_index is None:
Avalon.error(_('Aborting: No video stream found'))
raise StreamNotFoundError('no video stream found')
# use user defined output size # extract frames from video
fm.convert_video(framerate, f'{self.scale_width}x{self.scale_height}', self.upscaled_frames) self.process_pool.append((fm.extract_frames(self.current_input_video, self.extracted_frames)))
Avalon.info(_('Conversion completed')) self._wait()
# migrate audio tracks and subtitles # get average frame rate of video stream
Avalon.info(_('Migrating audio tracks and subtitles to upscaled video')) framerate = float(Fraction(video_info['streams'][video_stream_index]['avg_frame_rate']))
fm.migrate_audio_tracks_subtitles(self.input_video, self.output_video, self.upscaled_frames) fm.pixel_format = video_info['streams'][video_stream_index]['pix_fmt']
# destroy temp directories # get a dict of all pixel formats and corresponding bit depth
self.cleanup_temp_directories() pixel_formats = fm.get_pixel_formats()
# try getting pixel format's corresponding bti depth
try:
self.bit_depth = pixel_formats[fm.pixel_format]
except KeyError:
Avalon.error(_('Unsupported pixel format: {}').format(fm.pixel_format))
raise UnsupportedPixelError(f'unsupported pixel format {fm.pixel_format}')
Avalon.info(_('Framerate: {}').format(framerate))
# width/height will be coded width/height x upscale factor
if self.scale_ratio:
original_width = video_info['streams'][video_stream_index]['width']
original_height = video_info['streams'][video_stream_index]['height']
self.scale_width = int(self.scale_ratio * original_width)
self.scale_height = int(self.scale_ratio * original_height)
# upscale images one by one using waifu2x
Avalon.info(_('Starting to upscale extracted images'))
self._upscale_frames()
Avalon.info(_('Upscaling completed'))
# frames to Video
Avalon.info(_('Converting extracted frames into video'))
# use user defined output size
self.process_pool.append(fm.convert_video(framerate, f'{self.scale_width}x{self.scale_height}', self.upscaled_frames))
self._wait()
Avalon.info(_('Conversion completed'))
# migrate audio tracks and subtitles
Avalon.info(_('Migrating audio tracks and subtitles to upscaled video'))
self.process_pool.append(fm.migrate_audio_tracks_subtitles(self.current_input_video, output_video, self.upscaled_frames))
self._wait()
# destroy temp directories
self.cleanup_temp_directories()
except (Exception, KeyboardInterrupt, SystemExit) as e:
with contextlib.suppress(ValueError):
self.cleanup_temp_directories()
raise e
# increment total number of videos processed
self.total_processed += 1
# signal upscaling completion
self.running = False

View File

@@ -13,7 +13,7 @@ __ __ _ _ ___ __ __
Name: Video2X Controller Name: Video2X Controller
Creator: K4YT3X Creator: K4YT3X
Date Created: Feb 24, 2018 Date Created: Feb 24, 2018
Last Modified: May 4, 2020 Last Modified: May 7, 2020
Editor: BrianPetkovsek Editor: BrianPetkovsek
Last Modified: June 17, 2019 Last Modified: June 17, 2019
@@ -58,6 +58,7 @@ import contextlib
import gettext import gettext
import importlib import importlib
import locale import locale
import os
import pathlib import pathlib
import re import re
import shutil import shutil
@@ -178,6 +179,16 @@ config = read_config(video2x_args.config)
# load waifu2x configuration # load waifu2x configuration
driver_settings = config[video2x_args.driver] driver_settings = config[video2x_args.driver]
driver_settings['path'] = os.path.expandvars(driver_settings['path'])
# read FFmpeg configuration
ffmpeg_settings = config['ffmpeg']
ffmpeg_settings['ffmpeg_path'] = os.path.expandvars(ffmpeg_settings['ffmpeg_path'])
# load video2x settings
image_format = config['video2x']['image_format'].lower()
preserve_frames = config['video2x']['preserve_frames']
video2x_cache_directory = config['video2x']['video2x_cache_directory']
# overwrite driver_settings with driver_args # overwrite driver_settings with driver_args
if driver_args is not None: if driver_args is not None:
@@ -186,126 +197,32 @@ if driver_args is not None:
if driver_args_dict[key] is not None: if driver_args_dict[key] is not None:
driver_settings[key] = driver_args_dict[key] driver_settings[key] = driver_args_dict[key]
# check if driver path exists
if not pathlib.Path(driver_settings['path']).exists():
if not pathlib.Path(f'{driver_settings["path"]}.exe').exists():
Avalon.error(_('Specified driver executable directory doesn\'t exist'))
Avalon.error(_('Please check the configuration file settings'))
raise FileNotFoundError(driver_settings['path'])
# read FFmpeg configuration
ffmpeg_settings = config['ffmpeg']
# load video2x settings
image_format = config['video2x']['image_format'].lower()
preserve_frames = config['video2x']['preserve_frames']
# load cache directory
if config['video2x']['video2x_cache_directory'] is not None:
video2x_cache_directory = pathlib.Path(config['video2x']['video2x_cache_directory'])
else:
video2x_cache_directory = pathlib.Path(tempfile.gettempdir()) / 'video2x'
if video2x_cache_directory.exists() and not video2x_cache_directory.is_dir():
Avalon.error(_('Specified cache directory is a file/link'))
raise FileExistsError('Specified cache directory is a file/link')
# if cache directory doesn't exist
# ask the user if it should be created
elif not video2x_cache_directory.exists():
try:
Avalon.debug_info(_('Creating cache directory {}').format(video2x_cache_directory))
video2x_cache_directory.mkdir(parents=True, exist_ok=True)
# there can be a number of exceptions here
# PermissionError, FileExistsError, etc.
# therefore, we put a catch-them-all here
except Exception as exception:
Avalon.error(_('Unable to create {}').format(video2x_cache_directory))
raise exception
# start execution # start execution
try: try:
# start timer # start timer
begin_time = time.time() begin_time = time.time()
# if input specified is a single file # initialize upscaler object
if video2x_args.input.is_file(): upscaler = Upscaler(input_path=video2x_args.input,
output_path=video2x_args.output,
driver_settings=driver_settings,
ffmpeg_settings=ffmpeg_settings)
# upscale single video file # set upscaler optional options
Avalon.info(_('Upscaling single video file: {}').format(video2x_args.input)) upscaler.driver = video2x_args.driver
upscaler.scale_width = video2x_args.width
upscaler.scale_height = video2x_args.height
upscaler.scale_ratio = video2x_args.ratio
upscaler.processes = video2x_args.processes
upscaler.video2x_cache_directory = video2x_cache_directory
upscaler.image_format = image_format
upscaler.preserve_frames = preserve_frames
# check for input output format mismatch # run upscaler
if video2x_args.output.is_dir(): upscaler.run()
Avalon.error(_('Input and output path type mismatch'))
Avalon.error(_('Input is single file but output is directory'))
raise Exception('input output path type mismatch')
if not re.search(r'.*\..*$', str(video2x_args.output)):
Avalon.error(_('No suffix found in output file path'))
Avalon.error(_('Suffix must be specified for FFmpeg'))
raise Exception('No suffix specified')
upscaler = Upscaler(input_video=video2x_args.input,
output_video=video2x_args.output,
driver_settings=driver_settings,
ffmpeg_settings=ffmpeg_settings)
# set optional options
upscaler.driver = video2x_args.driver
upscaler.scale_width = video2x_args.width
upscaler.scale_height = video2x_args.height
upscaler.scale_ratio = video2x_args.ratio
upscaler.processes = video2x_args.processes
upscaler.video2x_cache_directory = video2x_cache_directory
upscaler.image_format = image_format
upscaler.preserve_frames = preserve_frames
# run upscaler
upscaler.run()
# if input specified is a directory
elif video2x_args.input.is_dir():
# upscale videos in a directory
Avalon.info(_('Upscaling videos in directory: {}').format(video2x_args.input))
# make output directory if it doesn't exist
video2x_args.output.mkdir(parents=True, exist_ok=True)
for input_video in [f for f in video2x_args.input.iterdir() if f.is_file()]:
output_video = video2x_args.output / input_video.name
upscaler = Upscaler(input_video=input_video,
output_video=output_video,
driver_settings=driver_settings,
ffmpeg_settings=ffmpeg_settings)
# set optional options
upscaler.driver = video2x_args.driver
upscaler.scale_width = video2x_args.width
upscaler.scale_height = video2x_args.height
upscaler.scale_ratio = video2x_args.ratio
upscaler.processes = video2x_args.processes
upscaler.video2x_cache_directory = video2x_cache_directory
upscaler.image_format = image_format
upscaler.preserve_frames = preserve_frames
# run upscaler
upscaler.run()
else:
Avalon.error(_('Input path is neither a file nor a directory'))
raise FileNotFoundError(f'{video2x_args.input} is neither file nor directory')
Avalon.info(_('Program completed, taking {} seconds').format(round((time.time() - begin_time), 5))) Avalon.info(_('Program completed, taking {} seconds').format(round((time.time() - begin_time), 5)))
except Exception: except Exception:
Avalon.error(_('An exception has occurred')) Avalon.error(_('An exception has occurred'))
traceback.print_exc() traceback.print_exc()
# try cleaning up temp directories
with contextlib.suppress(Exception):
upscaler.cleanup_temp_directories()
finally:
# remove Video2X cache directory
with contextlib.suppress(FileNotFoundError):
if not preserve_frames:
shutil.rmtree(video2x_cache_directory)

View File

@@ -1,53 +1,59 @@
# Name: Video2X Configuration File
# Creator: K4YT3X
# Date Created: October 23, 2018
# Last Modified: May 8, 2020
# Items commented out are parameters handled by Video2x.
waifu2x_caffe: waifu2x_caffe:
path: 'C:\Users\K4YT3X\AppData\Local\video2x\waifu2x-caffe\waifu2x-caffe-cui' path: '%LOCALAPPDATA%\video2x\waifu2x-caffe\waifu2x-caffe-cui'
input_extention_list: null tta: 0 # <0|1> 8x slower and slightly high quality
output_extention: null gpu: 0 # gpu device no
mode: noise_scale batch_size: 1 # input batch size
scale_ratio: null crop_h: null # input image split size(height)
scale_width: null crop_w: null # input image split size(width)
scale_height: null crop_size: 128 # input image split size
noise_level: 3 output_depth: 8 # output image chaneel depth bit
process: gpu output_quality: -1 # output image quality
crop_size: 128 process: gpu # <cpu|gpu|cudnn> process mode
output_quality: -1 model_dir: null # path to custom model directory (don't append last / )
output_depth: 8 #scale_height: null # custom scale height
batch_size: 1 #scale_width: null # custom scale width
gpu: 0 #scale_ratio: null # custom scale ratio
tta: 0 noise_level: 3 # <0|1|2|3> noise reduction level
input_path: null mode: noise_scale # <noise|scale|noise_scale|auto_scale> image processing mode
output_path: null output_extention: null # extention to output image file when output_path is (auto) or input_path is folder
model_dir: null input_extention_list: null # extention to input image file when input_path is folder
crop_w: null #output_path: null # path to output image file (when input_path is folder, output_path must be folder)
crop_h: null #input_path: null # (required) path to input image file
waifu2x_converter_cpp: waifu2x_converter_cpp:
path: 'C:\Users\K4YT3X\AppData\Local\video2x\waifu2x-converter-cpp\waifu2x-converter-cpp' path: '%LOCALAPPDATA%\video2x\waifu2x-converter-cpp\waifu2x-converter-cpp'
# list-supported-formats: null #list-supported-formats: null # dump currently supported format list
# list-opencv-formats: null #list-opencv-formats: null # (deprecated. Use --list-supported-formats) dump opencv supported format list
# list-processor #list-processor # dump processor list
output-format: progress_bar_exit_signal output-format: null # The format used when running in recursive/folder mode
png-compression: 5 png-compression: 5 # Set PNG compression level (0-9), 9 = Max compression (slowest & smallest)
image-quality: -1 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: 0 # block size
disable-gpu: false disable-gpu: false # disable GPU
force-OpenCL: false force-OpenCL: false # force to use OpenCL on Intel Platform
processor: -1 processor: -1 # set target processor (-1 uses default device)
jobs: 0 jobs: 0 # number of threads launching at the same time
model-dir: null # models_rgb model-dir: null # path to custom model directory (don't append last / ) default: models_rgb
scale-ratio: 2.0 #scale-ratio: 2.0 # custom scale ratio
noise-level: 1 noise-level: 1 # <0|1|2|3> noise reduction level
mode: noise-scale mode: noise-scale # <noise|scale|noise-scale> image processing mode
log-level: 1 log-level: 1 # <0|1|2|3|4> Set log level
silent: null silent: true # Enable silent mode. (same as --log-level 1)
tta: 0 tta: 0 # Enable Test-Time Augmentation mode. (0 or 1)
# generate-subdir: 0 #generate-subdir: 0 # Generate sub folder when recursive directory is enabled.
# recursive-directory: 0 #auto-naming: 0 # Add postfix to output name when output path is not specified.
output: null #recursive-directory: 0 # Search recursively through directories to find more images to process.
input: null #output: null # path to output image file or directory (you should use the full path)
#input: null # (required) path to input image file or directory (you should use the full path)
waifu2x_ncnn_vulkan: waifu2x_ncnn_vulkan:
path: 'C:\Users\K4YT3X\AppData\Local\video2x\waifu2x-ncnn-vulkan\waifu2x-ncnn-vulkan' path: '%LOCALAPPDATA%\video2x\waifu2x-ncnn-vulkan\waifu2x-ncnn-vulkan'
v: null # verbose output v: null # verbose output
i: null # input-path: input image path (jpg/png) or directory #i: null # input-path: input image path (jpg/png) or directory
o: null # output-path: output image path (png) or directory #o: null # output-path: output image path (png) or directory
'n': 2 # noise-level: denoise level (-1/0/1/2/3, default=0) 'n': 2 # noise-level: denoise level (-1/0/1/2/3, default=0)
s: 2 # scale: upscale ratio (1/2, default=2) s: 2 # scale: upscale ratio (1/2, default=2)
t: 400 # tile-size: tile size (>=32, default=400) t: 400 # tile-size: tile size (>=32, default=400)
@@ -56,10 +62,10 @@ waifu2x_ncnn_vulkan:
j: '1:2:2' # thread count for load/proc/save (default=1:2:2) j: '1:2:2' # thread count for load/proc/save (default=1:2:2)
x: false # enable tta mode x: false # enable tta mode
srmd_ncnn_vulkan: srmd_ncnn_vulkan:
path: 'C:\Users\K4YT3X\AppData\Local\video2x\srmd-ncnn-vulkan\srmd-ncnn-vulkan' path: '%LOCALAPPDATA%\video2x\srmd-ncnn-vulkan\srmd-ncnn-vulkan'
v: null # verbose output v: null # verbose output
i: null # input-path: input image path (jpg/png) or directory #i: null # input-path: input image path (jpg/png) or directory
o: null # output-path: output image path (png) or directory #o: null # output-path: output image path (png) or directory
'n': 3 # noise-level: denoise level (-1/0/1/2/3/4/5/6/7/8/9/10, default=3) 'n': 3 # noise-level: denoise level (-1/0/1/2/3/4/5/6/7/8/9/10, default=3)
s: 2 # upscale ratio (2/3/4, default=2) s: 2 # upscale ratio (2/3/4, default=2)
t: 400 # tile-size: tile size (>=32, default=400) t: 400 # tile-size: tile size (>=32, default=400)
@@ -68,9 +74,9 @@ srmd_ncnn_vulkan:
j: '1:2:2' # thread count for load/proc/save (default=1:2:2) j: '1:2:2' # thread count for load/proc/save (default=1:2:2)
x: false # enable tta mode x: false # enable tta mode
anime4kcpp: anime4kcpp:
path: 'C:\Users\K4YT3X\AppData\Local\video2x\anime4kcpp\CLI\Anime4KCPP_CLI\Anime4KCPP_CLI' path: '%LOCALAPPDATA%\video2x\anime4kcpp\CLI\Anime4KCPP_CLI\Anime4KCPP_CLI'
input: null # File for loading (string [=./pic/p1.png]) #input: null # File for loading (string [=./pic/p1.png])
output: null # File for outputting (string [=output.png]) #output: null # File for outputting (string [=output.png])
passes: 2 # Passes for processing (int [=2]) passes: 2 # Passes for processing (int [=2])
pushColorCount: 2 # Limit the number of color pushes (int [=2]) pushColorCount: 2 # Limit the number of color pushes (int [=2])
strengthColor: 0.3 # Strength for pushing color,range 0 to 1,higher for thinner (double [=0.3]) strengthColor: 0.3 # Strength for pushing color,range 0 to 1,higher for thinner (double [=0.3])
@@ -80,22 +86,27 @@ anime4kcpp:
fastMode: false # Faster but maybe low quality fastMode: false # Faster but maybe low quality
videoMode: true # Video process videoMode: true # Video process
preview: null # Preview image preview: null # Preview image
preProcessing: False # Enable pre processing preprocessing: False # Enable pre processing
postProcessing: False # Enable post processing postprocessing: False # Enable post processing
preFilters: 4 # Enhancement filter, only working when preProcessing is true,there are 5 options by binary:Median blur=0000001, Mean blur=0000010, CAS Sharpening=0000100, Gaussian blur weak=0001000, Gaussian blur=0010000, Bilateral filter=0100000, Bilateral filter faster=1000000, you can freely combine them, eg: Gaussian blur weak + Bilateral filter = 0001000 | 0100000 = 0101000 = 40(D) (unsigned int [=4]) preFilters: 4 # Enhancement filter, only working when preProcessing is true,there are 5 options by binary:Median blur=0000001, Mean blur=0000010, CAS Sharpening=0000100, Gaussian blur weak=0001000, Gaussian blur=0010000, Bilateral filter=0100000, Bilateral filter faster=1000000, you can freely combine them, eg: Gaussian blur weak + Bilateral filter = 0001000 | 0100000 = 0101000 = 40(D) (unsigned int [=4])
postFilters: 40 # Enhancement filter, only working when postProcessing is true,there are 5 options by binary:Median blur=0000001, Mean blur=0000010, CAS Sharpening=0000100, Gaussian blur weak=0001000, Gaussian blur=0010000, Bilateral filter=0100000, Bilateral filter faster=1000000, you can freely combine them, eg: Gaussian blur weak + Bilateral filter = 0001000 | 0100000 = 0101000 = 40(D), so you can put 40 to enable Gaussian blur weak and Bilateral filter, which also is what I recommend for image that < 1080P, 48 for image that >= 1080P, and for performance I recommend to use 72 for video that < 1080P, 80 for video that >=1080P (unsigned int [=40]) postFilters: 40 # Enhancement filter, only working when postProcessing is true,there are 5 options by binary:Median blur=0000001, Mean blur=0000010, CAS Sharpening=0000100, Gaussian blur weak=0001000, Gaussian blur=0010000, Bilateral filter=0100000, Bilateral filter faster=1000000, you can freely combine them, eg: Gaussian blur weak + Bilateral filter = 0001000 | 0100000 = 0101000 = 40(D), so you can put 40 to enable Gaussian blur weak and Bilateral filter, which also is what I recommend for image that < 1080P, 48 for image that >= 1080P, and for performance I recommend to use 72 for video that < 1080P, 80 for video that >=1080P (unsigned int [=40])
GPUMode: False # Enable GPU acceleration GPUMode: False # Enable GPU acceleration
listGPUs: null # list GPUs listGPUs: null # list GPUs
platformID: 0 # Specify the platform ID (unsigned int [=0]) platformID: 0 # Specify the platform ID (unsigned int [=0])
deviceID: 0 # Specify the device ID (unsigned int [=0]) deviceID: 0 # Specify the device ID (unsigned int [=0])
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: 'C:\Users\K4YT3X\AppData\Local\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
@@ -108,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':
@@ -118,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:

File diff suppressed because it is too large Load Diff

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">
@@ -1175,6 +1222,51 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="7" column="1">
<layout class="QHBoxLayout" name="anime4kCppCodecHorizontalLayout">
<item>
<widget class="QLabel" name="anime4kCppCodecLabel">
<property name="text">
<string>Codec</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="anime4kCppCodecComboBox">
<item>
<property name="text">
<string>mp4v</string>
</property>
</item>
<item>
<property name="text">
<string>dxva</string>
</property>
</item>
<item>
<property name="text">
<string>avc1</string>
</property>
</item>
<item>
<property name="text">
<string>vp09</string>
</property>
</item>
<item>
<property name="text">
<string>hevc</string>
</property>
</item>
<item>
<property name="text">
<string>av01</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
</widget> </widget>
@@ -1184,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>
@@ -1258,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 5, 2020 Last Modified: May 8, 2020
Editor: BrianPetkovsek Editor: BrianPetkovsek
Editor: SAT3LL Editor: SAT3LL
@@ -260,16 +260,19 @@ 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.exe') 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.exe') 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['anime4kcpp']['path'] = str(LOCALAPPDATA / 'video2x' / 'anime4kcpp' / 'CLI' / 'Anime4KCPP_CLI' / 'Anime4KCPP_CLI') template_dict['anime4kcpp']['path'] = str(LOCALAPPDATA / 'video2x' / 'anime4kcpp' / 'CLI' / 'Anime4KCPP_CLI' / 'Anime4KCPP_CLI')
elif self.driver == 'waifu2x_caffe': elif self.driver == 'waifu2x_caffe':
template_dict['waifu2x_caffe']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-caffe' / 'waifu2x-caffe-cui.exe') template_dict['waifu2x_caffe']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-caffe' / 'waifu2x-caffe-cui')
elif self.driver == 'waifu2x_converter_cpp': elif self.driver == 'waifu2x_converter_cpp':
template_dict['waifu2x_converter_cpp']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-converter-cpp' / 'waifu2x-converter-cpp') template_dict['waifu2x_converter_cpp']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-converter-cpp' / 'waifu2x-converter-cpp')
elif self.driver == 'waifu2x_ncnn_vulkan': elif self.driver == 'waifu2x_ncnn_vulkan':
template_dict['waifu2x_ncnn_vulkan']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-ncnn-vulkan' / 'waifu2x-ncnn-vulkan.exe') template_dict['waifu2x_ncnn_vulkan']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-ncnn-vulkan' / 'waifu2x-ncnn-vulkan')
elif self.driver == 'srmd_ncnn_vulkan':
template_dict['srmd_ncnn_vulkan']['path'] = str(LOCALAPPDATA / 'video2x' / 'srmd-ncnn-vulkan' / 'srmd-ncnn-vulkan')
elif self.driver == 'anime4kcpp': elif self.driver == 'anime4kcpp':
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

@@ -4,7 +4,7 @@
Name: Waifu2x Caffe Driver Name: Waifu2x Caffe Driver
Author: K4YT3X Author: K4YT3X
Date Created: May 3, 2020 Date Created: May 3, 2020
Last Modified: May 4, 2020 Last Modified: May 7, 2020
Description: This class is a high-level wrapper Description: This class is a high-level wrapper
for waifu2x-caffe. for waifu2x-caffe.
@@ -13,6 +13,8 @@ for waifu2x-caffe.
# built-in imports # built-in imports
import argparse import argparse
import os import os
import pathlib
import platform
import shlex import shlex
import subprocess import subprocess
import threading import threading
@@ -32,6 +34,7 @@ class WrapperMain:
@staticmethod @staticmethod
def parse_arguments(arguments): def parse_arguments(arguments):
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False) parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False)
parser.error = lambda message: (_ for _ in ()).throw(AttributeError(message))
parser.add_argument('--help', action='help', help='show this help message and exit') parser.add_argument('--help', action='help', help='show this help message and exit')
# parser.add_argument('-i', '--input', type=pathlib.Path, help='File for loading') # parser.add_argument('-i', '--input', type=pathlib.Path, help='File for loading')
# parser.add_argument('-o', '--output', type=pathlib.Path, help='File for outputting') # parser.add_argument('-o', '--output', type=pathlib.Path, help='File for outputting')
@@ -42,16 +45,17 @@ 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')
parser.add_argument('-r', '--preFilters', type=int, help='Enhancement filter, only working when preProcessing is true,there are 5 options by binary:Median blur=0000001, Mean blur=0000010, CAS Sharpening=0000100, Gaussian blur weak=0001000, Gaussian blur=0010000, Bilateral filter=0100000, Bilateral filter faster=1000000, you can freely combine them, eg: Gaussian blur weak + Bilateral filter = 0001000 | 0100000 = 0101000 = 40(D)') parser.add_argument('-r', '--preFilters', type=int, help='Enhancement filter, only working when preProcessing is true,there are 5 options by binary:Median blur=0000001, Mean blur=0000010, CAS Sharpening=0000100, Gaussian blur weak=0001000, Gaussian blur=0010000, Bilateral filter=0100000, Bilateral filter faster=1000000, you can freely combine them, eg: Gaussian blur weak + Bilateral filter = 0001000 | 0100000 = 0101000 = 40(D)')
parser.add_argument('-e', '--postFilters', type=int, help='Enhancement filter, only working when postProcessing is true,there are 5 options by binary:Median blur=0000001, Mean blur=0000010, CAS Sharpening=0000100, Gaussian blur weak=0001000, Gaussian blur=0010000, Bilateral filter=0100000, Bilateral filter faster=1000000, you can freely combine them, eg: Gaussian blur weak + Bilateral filter = 0001000 | 0100000 = 0101000 = 40(D), so you can put 40 to enable Gaussian blur weak and Bilateral filter, which also is what I recommend for image that < 1080P, 48 for image that >= 1080P, and for performance I recommend to use 72 for video that < 1080P, 80 for video that >=1080P') parser.add_argument('-e', '--postFilters', type=int, help='Enhancement filter, only working when postProcessing is true,there are 5 options by binary:Median blur=0000001, Mean blur=0000010, CAS Sharpening=0000100, Gaussian blur weak=0001000, Gaussian blur=0010000, Bilateral filter=0100000, Bilateral filter faster=1000000, you can freely combine them, eg: Gaussian blur weak + Bilateral filter = 0001000 | 0100000 = 0101000 = 40(D), so you can put 40 to enable Gaussian blur weak and Bilateral filter, which also is what I recommend for image that < 1080P, 48 for image that >= 1080P, and for performance I recommend to use 72 for video that < 1080P, 80 for video that >=1080P')
parser.add_argument('-q', '--GPUMode', action='store_true', help='Enable GPU acceleration') parser.add_argument('-q', '--GPUMode', action='store_true', help='Enable GPU acceleration')
parser.add_argument('-l', '--listGPUs', action='store_true', help='list GPUs') parser.add_argument('-l', '--listGPUs', action='store_true', help='list GPUs')
parser.add_argument('-h', '--platformID', type=int, help='Specify the platform ID') parser.add_argument('-h', '--platformID', type=int, help='Specify the platform ID')
parser.add_argument('-d', '--deviceID', type=int, help='Specify the device ID') parser.add_argument('-d', '--deviceID', type=int, help='Specify the device ID')
parser.add_argument('-C', '--codec', type=str, help='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])')
return parser.parse_args(arguments) return parser.parse_args(arguments)
def upscale(self, input_file, output_file, zoom_factor, threads): def upscale(self, input_file, output_file, zoom_factor, threads):
@@ -70,6 +74,11 @@ class WrapperMain:
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
# change the CWD to its containing directory so it will find it
if platform.system() == 'Windows':
os.chdir(pathlib.Path(self.driver_settings['path']).parent)
# list to be executed # list to be executed
# initialize the list with waifu2x binary path as the first element # initialize the list with waifu2x binary path as the first element
execute = [self.driver_settings.pop('path')] execute = [self.driver_settings.pop('path')]

View File

@@ -4,7 +4,7 @@
Name: Video2X FFmpeg Controller Name: Video2X FFmpeg Controller
Author: K4YT3X Author: K4YT3X
Date Created: Feb 24, 2018 Date Created: Feb 24, 2018
Last Modified: November 15, 2019 Last Modified: May 7, 2020
Description: This class handles all FFmpeg related operations. Description: This class handles all FFmpeg related operations.
""" """
@@ -131,7 +131,7 @@ class Ffmpeg:
extracted_frames / f'extracted_%0d.{self.image_format}' extracted_frames / f'extracted_%0d.{self.image_format}'
]) ])
self._execute(execute) return(self._execute(execute))
def convert_video(self, framerate, resolution, upscaled_frames): def convert_video(self, framerate, resolution, upscaled_frames):
"""Converts images into videos """Converts images into videos
@@ -180,7 +180,7 @@ class Ffmpeg:
upscaled_frames / 'no_audio.mp4' upscaled_frames / 'no_audio.mp4'
]) ])
self._execute(execute) return(self._execute(execute))
def migrate_audio_tracks_subtitles(self, input_video, output_video, upscaled_frames): def migrate_audio_tracks_subtitles(self, input_video, output_video, upscaled_frames):
""" Migrates audio tracks and subtitles from input video to output video """ Migrates audio tracks and subtitles from input video to output video
@@ -209,7 +209,7 @@ class Ffmpeg:
output_video output_video
]) ])
self._execute(execute) return(self._execute(execute))
def _read_configuration(self, phase, section=None): def _read_configuration(self, phase, section=None):
""" read configuration from JSON """ read configuration from JSON
@@ -284,4 +284,4 @@ class Ffmpeg:
Avalon.debug_info(f'Executing: {execute}') Avalon.debug_info(f'Executing: {execute}')
return subprocess.run(execute, check=True).returncode return subprocess.Popen(execute)

View File

@@ -4,7 +4,7 @@
Name: SRMD NCNN Vulkan Driver Name: SRMD NCNN Vulkan Driver
Creator: K4YT3X Creator: K4YT3X
Date Created: April 26, 2020 Date Created: April 26, 2020
Last Modified: May 5, 2020 Last Modified: May 7, 2020
Description: This class is a high-level wrapper Description: This class is a high-level wrapper
for srmd_ncnn_vulkan. for srmd_ncnn_vulkan.
@@ -39,6 +39,7 @@ class WrapperMain:
@staticmethod @staticmethod
def parse_arguments(arguments): def parse_arguments(arguments):
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False) parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False)
parser.error = lambda message: (_ for _ in ()).throw(AttributeError(message))
parser.add_argument('--help', action='help', help='show this help message and exit') parser.add_argument('--help', action='help', help='show this help message and exit')
parser.add_argument('-v', action='store_true', help='verbose output') parser.add_argument('-v', action='store_true', help='verbose output')
# parser.add_argument('-i', type=pathlib.Path, help='input image path (jpg/png) or directory') # parser.add_argument('-i', type=pathlib.Path, help='input image path (jpg/png) or directory')

View File

@@ -4,7 +4,7 @@
Name: Waifu2x Caffe Driver Name: Waifu2x Caffe Driver
Author: K4YT3X Author: K4YT3X
Date Created: Feb 24, 2018 Date Created: Feb 24, 2018
Last Modified: May 4, 2020 Last Modified: May 7, 2020
Description: This class is a high-level wrapper Description: This class is a high-level wrapper
for waifu2x-caffe. for waifu2x-caffe.
@@ -37,6 +37,7 @@ class WrapperMain:
@staticmethod @staticmethod
def parse_arguments(arguments): def parse_arguments(arguments):
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False) parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False)
parser.error = lambda message: (_ for _ in ()).throw(AttributeError(message))
parser.add_argument('--help', action='help', help='show this help message and exit') parser.add_argument('--help', action='help', help='show this help message and exit')
parser.add_argument('-t', '--tta', type=int, choices=range(2), help='8x slower and slightly high quality') parser.add_argument('-t', '--tta', type=int, choices=range(2), help='8x slower and slightly high quality')
parser.add_argument('--gpu', type=int, help='gpu device no') parser.add_argument('--gpu', type=int, help='gpu device no')

View File

@@ -4,7 +4,7 @@
Name: Waifu2x Converter CPP Driver Name: Waifu2x Converter CPP Driver
Author: K4YT3X Author: K4YT3X
Date Created: February 8, 2019 Date Created: February 8, 2019
Last Modified: May 4, 2020 Last Modified: May 7, 2020
Description: This class is a high-level wrapper Description: This class is a high-level wrapper
for waifu2x-converter-cpp. for waifu2x-converter-cpp.
@@ -38,13 +38,14 @@ class WrapperMain:
@staticmethod @staticmethod
def parse_arguments(arguments): def parse_arguments(arguments):
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False) parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False)
parser.error = lambda message: (_ for _ in ()).throw(AttributeError(message))
parser.add_argument('--help', action='help', help='show this help message and exit') parser.add_argument('--help', action='help', help='show this help message and exit')
parser.add_argument('--list-supported-formats', action='store_true', help='dump currently supported format list') parser.add_argument('--list-supported-formats', action='store_true', help='dump currently supported format list')
parser.add_argument('--list-opencv-formats', action='store_true', help='(deprecated. Use --list-supported-formats) dump opencv supported format list') parser.add_argument('--list-opencv-formats', action='store_true', help='(deprecated. Use --list-supported-formats) dump opencv supported format list')
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')

View File

@@ -4,7 +4,7 @@
Name: Waifu2x NCNN Vulkan Driver Name: Waifu2x NCNN Vulkan Driver
Creator: SAT3LL Creator: SAT3LL
Date Created: June 26, 2019 Date Created: June 26, 2019
Last Modified: May 5, 2020 Last Modified: May 7, 2020
Editor: K4YT3X Editor: K4YT3X
Last Modified: February 22, 2020 Last Modified: February 22, 2020
@@ -42,6 +42,7 @@ class WrapperMain:
@staticmethod @staticmethod
def parse_arguments(arguments): def parse_arguments(arguments):
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False) parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, add_help=False)
parser.error = lambda message: (_ for _ in ()).throw(AttributeError(message))
parser.add_argument('--help', action='help', help='show this help message and exit') parser.add_argument('--help', action='help', help='show this help message and exit')
parser.add_argument('-v', action='store_true', help='verbose output') parser.add_argument('-v', action='store_true', help='verbose output')
# parser.add_argument('-i', type=pathlib.Path, help='input image path (jpg/png) or directory') # parser.add_argument('-i', type=pathlib.Path, help='input image path (jpg/png) or directory')