70 Commits
2.9.0 ... 3.0.0

Author SHA1 Message Date
k4yt3x
5db2cfd2a3 added anime4k instructions 2020-01-12 03:32:25 -05:00
k4yt3x
652b9f34bd fixing #180 bug2 attempt 2 2020-01-04 17:03:16 -05:00
k4yt3x
f3c50ea728 fixing #180 bug2 2020-01-04 16:59:51 -05:00
k4yt3x
880087241d updated waifu2x-ncnn-vulkan path key for new config structure 2020-01-04 16:56:08 -05:00
k4yt3x
067e17290a 1.6.1 changed driver paths to 3.0.0 config style 2020-01-04 16:49:13 -05:00
k4yt3x
fc675e7899 updated copyright date 2020-01-04 16:48:37 -05:00
K4YT3X
2d81270e44 Set theme jekyll-theme-slate 2019-12-22 06:00:02 +00:00
K4YT3X
3ace2447c2 Create CNAME 2019-12-22 05:48:39 +00:00
K4YT3X
30b0e2c7bf Delete CNAME 2019-12-22 05:48:25 +00:00
k4yt3x
d54fea0310 enables available memory warning for Linux platform 2019-12-19 23:03:58 -05:00
k4yt3x
332055a4e5 fixed temp directory cleaning problem on exit 2019-12-11 22:20:01 -05:00
k4yt3x
19e17b1a8f updated platform information 2019-12-11 21:59:38 -05:00
k4yt3x
9e745fb747 video2x_gui 1.1.3: fixed driver path key name 2019-12-11 21:59:20 -05:00
K4YT3X
fd5edead7e Merge pull request #171 from YOUSIKI/master
Check platform correctly
2019-11-30 19:56:08 +00:00
YOUSIKI
aa7c0b3f12 Check platform correctly
Continue if running on win32.
Exit if running on other platforms.
2019-11-30 22:56:34 +08:00
k4yt3x
65cc4c6afb Merge branch '3.0.0' 2019-11-26 04:14:13 -05:00
k4yt3x
bd2da021bd updated README for 3.0.0 2019-11-26 04:12:04 -05:00
k4yt3x
7e888db7b2 formatted YAML file 2019-11-26 04:10:01 -05:00
k4yt3x
7726a86e35 added platform check, made PermissionError more obvious 2019-11-26 04:03:03 -05:00
K4YT3X
a0595136ab Create CNAME 2019-11-18 06:43:00 +00:00
K4YT3X
5c93a5a73b Delete CNAME 2019-11-18 06:42:54 +00:00
K4YT3X
7b9b87fa9b Create CNAME 2019-11-17 05:41:13 +00:00
k4yt3x
cb255da65c added missing sys library import 2019-11-16 02:54:25 -05:00
k4yt3x
d503325a62 changed YAML load to FullLoader to make it WIndows-compatible 2019-11-16 02:52:33 -05:00
k4yt3x
954233c238 changed default config path from JSON to YAML 2019-11-16 01:51:01 -05:00
k4yt3x
95416f68a8 make the GUI script YAML-compatible 2019-11-16 01:50:41 -05:00
k4yt3x
dce778b3bf 1.1.2 fixed repeated output file extensions 2019-11-15 22:40:48 -05:00
k4yt3x
c537dd726c changed YAML loader to Windows compatible FullLoader 2019-11-15 22:40:19 -05:00
k4yt3x
b1918a4a8a fixed pixel_formats print bug and subprocess execution bug 2019-11-15 02:39:27 -05:00
k4yt3x
7ae9618785 updated file modification date 2019-11-15 02:38:39 -05:00
k4yt3x
eb3d29103c fixed anime4k path key 2019-11-15 02:38:11 -05:00
k4yt3x
a188f6ebda 1.6.0 added support for YAML, better exception handling 2019-11-15 02:04:12 -05:00
k4yt3x
fd67dfca11 docstring modifications, added JDK warning 2019-11-15 02:03:44 -05:00
k4yt3x
b1f29f1098 fixed NCNN Vulkan driver argument error 2019-11-15 01:09:55 -05:00
k4yt3x
fe7c0c840d fixed type error for waifu2x-ncnn-vulkan argument 2019-10-24 21:33:05 -04:00
k4yt3x
dc2410d4da renaming variables, using YAML to replace JSON 2019-10-19 21:53:12 -04:00
k4yt3x
91ac512d57 remove .exe extension for Linux compatibility 2019-10-19 21:52:31 -04:00
k4yt3x
732288f075 renamed waifu2x_settings to driver_settings 2019-10-19 21:52:11 -04:00
k4yt3x
e812c228c3 updated file permissions 2019-10-19 21:51:18 -04:00
k4yt3x
9841fa9577 converted JSON config into YAML 2019-10-19 21:45:44 -04:00
k4yt3x
5391f59847 renamed bin to src since python isn't binary 2019-10-19 21:40:00 -04:00
k4yt3x
6180ccd9b2 fixing folder upscaling function 2019-08-29 16:03:15 -04:00
k4yt3x
e10315f89e using contextlib instead of try-catch 2019-08-25 23:03:37 -04:00
k4yt3x
6521e071ce enhanced logo-printing 2019-08-25 23:03:18 -04:00
k4yt3x
a685e91b14 removed unused dependencies 2019-08-25 23:02:59 -04:00
k4yt3x
9d72bad314 temporary fix for Anime4K download 2019-08-20 22:40:26 -04:00
k4yt3x
2b63b5f0a6 added total time display 2019-08-17 23:28:37 -04:00
k4yt3x
9c84228c17 added screenshot for video2x gui 2019-08-17 23:19:47 -04:00
k4yt3x
f9bb191336 fixed gui compatibility with anime4k, added constraints for a4k 2019-08-17 18:16:47 -04:00
K4YT3X
7a3af366f8 Merge pull request #137 from k4yt3x/2.10.0-anime4k
2.10.0 anime4k
2019-08-16 15:49:33 +00:00
k4yt3x
d4223f93ec fixed a4k not installed bug 2019-08-16 02:53:07 -04:00
k4yt3x
c5c764a91f GUI 1.1.1: Added support for Anime4K 2019-08-16 02:49:38 -04:00
k4yt3x
ba6472c9fc exe build is out of beta 2019-08-16 02:49:22 -04:00
k4yt3x
dc1244580f updated README for 2.10.0 2019-08-16 00:46:43 -04:00
k4yt3x
0347d05127 setup script 1.5.0: added support for Anime4K 2019-08-16 00:36:45 -04:00
k4yt3x
45f5bf91e6 updated anime4k path 2019-08-16 00:36:22 -04:00
k4yt3x
5e78beabcf added thread dialog for anime4k 2019-08-16 00:19:36 -04:00
k4yt3x
6c5b7ea17f moved constants into one single place 2019-08-15 23:57:24 -04:00
k4yt3x
d9610abae0 fixed thread-unsafe printing 2019-08-15 23:56:55 -04:00
k4yt3x
47471352ba 2.10.0 added anime4k support 2019-08-15 23:34:24 -04:00
k4yt3x
969cfe5c0d fixing issue #134 2019-08-11 23:57:10 -04:00
k4yt3x
e12db41213 updated bug report template for newest version of v2x 2019-08-07 23:07:55 -04:00
k4yt3x
7a69d58e28 placed version number into window title 2019-08-07 22:56:41 -04:00
k4yt3x
ad6308d385 relocated docstrings 2019-08-07 22:54:39 -04:00
k4yt3x
8c3a85ac42 GUI 1.1.0 added basic menu bar and better output file naming check 2019-08-07 22:54:33 -04:00
k4yt3x
db603062e9 updated main file date 2019-08-03 23:15:36 -04:00
k4yt3x
807276fcc3 fixing converter frame filename issues 2019-08-03 23:15:19 -04:00
k4yt3x
9be46f7d62 fixing support for waifu2x_ncnn_vulkan 2019-08-03 23:15:03 -04:00
k4yt3x
f42a8ab71f fixed waifu2x-ncnn-vulkan argument errors 2019-08-03 21:29:13 -04:00
k4yt3x
e3b2624977 fixed the issue where subprocess tries to execute pathlib.Path 2019-08-03 19:45:22 -04:00
20 changed files with 1089 additions and 709 deletions

View File

@@ -11,10 +11,11 @@ assignees: K4YT3X
|Module|Version|
|-|-|
|`video2x`||
|`ffmpeg`||
|`Video2X`||
|`FFmpeg`||
|`waifu2x-caffe`||
|`waifu2x-converter-cpp`||
|`waifu2x-ncnn-vulkan`||
## Symptom

1
CNAME Normal file
View File

@@ -0,0 +1 @@
video2x.org

View File

@@ -2,11 +2,11 @@
### Official Discussion Group (Telegram): https://t.me/video2x
## Download Builds (beta)
## Download Builds (Windows)
You can go to the [releases page](https://github.com/k4yt3x/video2x/releases) to download the latest builds of `Video2X`. The exe files will require no Python or Python module installation.
The **`full`** package provides all packages that will possibly be needed by `Video2X`, including `FFmpeg`, `waifu2x-caffe`, `waifu2x-converter-cpp`, and `waifu2x-ncnn-vulkan`. The config file (`video2x.json`) is also already configured for the environment. All you need to do is just to launch `video2x.exe`.
The **`full`** package provides all packages that will possibly be needed by `Video2X`, including `FFmpeg`, `waifu2x-caffe`, `waifu2x-converter-cpp`, `waifu2x-ncnn-vulkan`, and `Anime4K`. The config file (`video2x.json`) is also already configured for the environment. All you need to do is just to launch `video2x.exe`.
The **`light`** package provides only the most basic functions of `Video2X`. Only `video2x.exe`, `video2x_setup.exe` and `video2x.json` are included. To setup dependencies (e.g. `FFmpeg` and `Waifu2X`) automatically, simply launch `video2x_setup.exe`.
@@ -14,36 +14,39 @@ The **`light`** package provides only the most basic functions of `Video2X`. Onl
Component names that are **bolded** can be automatically downloaded and configured with the `video2x_setup.py` script.
1. Operating System: Windows
1. Operating System: Windows / Linux
2. AMD GPU / Nvidia GPU
3. AMD GPU driver / Nvidia GPU driver / Nvidia CUDNN
4. [**FFmpeg**](https://ffmpeg.zeranoe.com/builds/)
5. [**waifu2x-caffe**](https://github.com/lltcggie/waifu2x-caffe/releases) / [**waifu2x-converter-cpp**](https://github.com/DeadSix27/waifu2x-converter-cpp/releases) / [**waifu2x-ncnn-vulkan**](https://github.com/nihui/waifu2x-ncnn-vulkan)
5. One of the following drivers
- [**waifu2x-caffe**](https://github.com/lltcggie/waifu2x-caffe/releases)
- [**waifu2x-converter-cpp**](https://github.com/DeadSix27/waifu2x-converter-cpp/releases)
- [**waifu2x-ncnn-vulkan**](https://github.com/nihui/waifu2x-ncnn-vulkan)
- [**Anime4K**](https://github.com/bloc97/Anime4K)
## Recent Changes
### 3.0.0 (November 26, 2019)
- Linux compatibility
- Configuration file changed to YAML format
- You may still use a JSON-formatted config file. To do so, please specify `-c video2x.json`.
- Other code clean-up and optimization
### 2.10.0 (August 16, 2019)
- **Added support for [Anime4K](https://github.com/bloc97/Anime4K)**
### 2.9.0 (July 27, 2019)
- Changed file handling method from `os` to `pathlib`
- Removed f_string dependency and support for legacy versions of Python
- Organized file import statements
### 2.8.1 (July 9, 2019)
### Setup Script 1.6.0 (November 26, 2019)
- Added automatic pixel format detection
- Added automatic color bit depth detection
### 2.8.0 (June 25, 2019)
- Added support for [waifu2x-ncnn-vulkan](https://github.com/nihui/waifu2x-ncnn-vulkan)
### 2.7.1 (April 18, 2019)
- Fixed video2x custom temp folder bug found by @cr08 .
### Setup Script 1.3.0 (June 25, 2019)
- Added automatic installation support for `waifu2x-ncnn-vulkan`
- Added compatibility for new YAML configuration file
- Added better exception handling
## Description
@@ -61,6 +64,8 @@ Clip is from trailer of animated movie "千と千尋の神隠し". Copyright bel
## Screenshot
![video2x_gui](https://user-images.githubusercontent.com/21986859/63219691-ee478400-c166-11e9-8c15-dcf59228bd96.png)
![screenshot](https://user-images.githubusercontent.com/21986859/40265170-39c0caae-5b01-11e8-8371-8b6c24769639.png)
---
@@ -89,14 +94,18 @@ If you have any questions, first try visiting our [Q&A](https://github.com/k4yt3
### Prerequisites
- **Python 3**
- **Python 3**
Download: https://www.python.org/downloads/windows/
- **FFmpeg Windows Build**
Download: https://ffmpeg.org/download.html
- **waifu2x-caffe** (for Nvidia CUDA/CUDNN)
- **FFmpeg Windows Build**
Download: https://ffmpeg.org/download.html
- **waifu2x-caffe** (for Nvidia CUDA/CUDNN)
Download: https://github.com/lltcggie/waifu2x-caffe/releases
- **waifu2x-converter-cpp** (required for AMD, OpenCL and OpenGL processing)
- **waifu2x-converter-cpp** (required for AMD, OpenCL and OpenGL processing)
Download: https://github.com/DeadSix27/waifu2x-converter-cpp/releases
- **waifu2x-ncnn-vulkan**
Download: https://github.com/nihui/waifu2x-ncnn-vulkan/releases
- **Anime4K**
Download: https://github.com/bloc97/Anime4K/releases
### Installing Dependencies
@@ -144,7 +153,7 @@ Enlarge the video to 1920x1080 using CUDA. You may also use the `-r/--ratio` opt
python video2x.py -i sample_input.mp4 -o sample_output.mp4 -m gpu --width=1920 --height=1080
```
### Nvidia CNDNN
### Nvidia CUDNN
Enlarge the video to 1920x1080 using CUDNN. You may also use the `-r/--ratio` option.
@@ -166,9 +175,17 @@ python video2x.py -i sample_input.mp4 -o sample_output.mp4 -m gpu -r 2 -d waifu2
python video2x.py -i sample_input.mp4 -o sample_output.mp4 -m gpu -r 2 -d waifu2x_ncnn_vulkan
```
### Anime4K
Enlarge the video by 2 times using Anime4K. **Remember to install and configure [JRE 12](https://www.oracle.com/technetwork/java/javase/downloads/java-archive-javase12-5440181.html) path in the configuration file.**
```shell
python video2x.py -i sample_input.mp4 -o sample_output.mp4 -r 2 -d anime4k
```
### CPU
Enlarge the video to 1920x1080 using the CPU. You may also use the `-r/--ratio` option. This is potentially much slower than using a GPU. The configuration file for this method is similar to the previous methods.
Enlarge the video to 1920x1080 using the CPU. You may also use the `-r/--ratio` option. **waifu2x-based upscalers potentially run much slower than using a GPU, but Anime4K is more CPU-dependant**. The configuration file for this method is similar to the previous methods.
```shell
python video2x.py -i sample_input.mp4 -o sample_output.mp4 -m cpu --width=1920 --height=1080
@@ -231,7 +248,7 @@ https://www.gnu.org/licenses/gpl-3.0.txt
![GPLv3 Icon](https://www.gnu.org/graphics/gplv3-127x51.png)
(C) 2018-2019 K4YT3X
(C) 2018-2020 K4YT3X
## Credits
@@ -241,6 +258,7 @@ This project relies on the following software and projects.
- [waifu2x-caffe](https://github.com/lltcggie/waifu2x-caffe)
- [waifu2x-converter-cpp](https://github.com/DeadSix27/waifu2x-converter-cpp)
- [waifu2x-ncnn-vulkan](https://github.com/nihui/waifu2x-ncnn-vulkan)
- [Anime4K](https://github.com/bloc97/Anime4K)
## Special Thanks

1
_config.yml Normal file
View File

@@ -0,0 +1 @@
theme: jekyll-theme-slate

View File

@@ -1,374 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Video2x GUI
Author: K4YT3X
Date Created: July 27, 2019
Last Modified: July 27, 2019
Description: GUI for Video2X
"""
# local imports
from exceptions import *
from upscaler import Upscaler
# built-in imports
from tkinter import *
from tkinter import messagebox
from tkinter import ttk
from tkinter.filedialog import *
import json
import pathlib
import tempfile
import threading
import time
import tkinter as tk
VERSION = '1.0.0'
# global static variables
AVAILABLE_METHODS = {'GPU': 'gpu',
'CUDNN': 'cudnn',
'CPU': 'cpu'
}
AVAILABLE_DRIVERS = {
'Waifu2X Caffe': 'waifu2x_caffe',
'Waifu2X Converter CPP': 'waifu2x_converter',
'Waifu2x NCNN Vulkan': 'waifu2x_ncnn_vulkan'
}
IMAGE_FORMATS = {'PNG', 'JPG'}
class Video2xGui():
def __init__(self):
self.running = False
# create main window
self.main_window = Tk()
self.main_window.title('Video2X GUI')
self.main_frame = Frame()
self.main_frame.pack(fill=BOTH, expand=True)
# file frame
self.file_frame = Frame(self.main_frame)
self.file_frame.pack(fill=X, padx=5, pady=5, expand=True)
# input file
self.input_file = StringVar()
label_text = StringVar()
label_text.set('Input File')
Label(self.file_frame, textvariable=label_text, relief=RIDGE, width=10).grid(row=0, column=0, padx=5, pady=5, sticky=W)
Entry(self.file_frame, textvariable=self.input_file, width=60).grid(row=0, column=1, padx=5, pady=5, sticky=W)
Button(self.file_frame, text='Select', command=self._select_input).grid(row=0, column=2, padx=5, pady=5, sticky=W)
# output file
self.output_file = StringVar()
label_text = StringVar()
label_text.set('Output File')
Label(self.file_frame, textvariable=label_text, relief=RIDGE, width=10).grid(row=1, column=0, padx=5, pady=5, sticky=W)
Entry(self.file_frame, textvariable=self.output_file, width=60).grid(row=1, column=1, padx=5, pady=5, sticky=W)
Button(self.file_frame, text='Select', command=self._select_output).grid(row=1, column=2, padx=5, pady=5, sticky=W)
# options
self.options_frame = Frame()
# self.options_left.pack(fill=X, padx=5, pady=5, expand=True)
self.options_frame.pack(fill=X, padx=5, pady=5, expand=True)
self.options_left = Frame(self.options_frame)
# self.options_left.pack(fill=X, padx=5, pady=5, expand=True)
self.options_left.grid(row=0, column=0, padx=5, pady=5, sticky=N)
# width
self.width = IntVar()
# self.width.set(1920)
Label(self.options_left, text='Width', relief=RIDGE, width=15).grid(row=0, column=0, padx=2, pady=3)
width_field = Entry(self.options_left, textvariable=self.width)
width_field.grid(row=0, column=1, padx=2, pady=3, sticky=W)
# height
self.height = IntVar()
# self.height.set(1080)
Label(self.options_left, text='Height', relief=RIDGE, width=15).grid(row=1, column=0, padx=2, pady=3)
height_field = Entry(self.options_left, textvariable=self.height)
height_field.grid(row=1, column=1, padx=2, pady=3, sticky=W)
# scale ratio
self.scale_ratio = DoubleVar()
# self.scale_ratio.set(2.0)
Label(self.options_left, text='Scale Ratio', relief=RIDGE, width=15).grid(row=2, column=0, padx=2, pady=3)
scale_ratio_field = Entry(self.options_left, textvariable=self.scale_ratio)
scale_ratio_field.grid(row=2, column=1, padx=2, pady=3, sticky=W)
# image format
self.image_format = StringVar(self.options_left)
self.image_format.set('PNG')
Label(self.options_left, text='Image Format', relief=RIDGE, width=15).grid(row=3, column=0, padx=2, pady=3)
image_format_menu = OptionMenu(self.options_left, self.image_format, *IMAGE_FORMATS)
image_format_menu.grid(row=3, column=1, padx=2, pady=3, sticky=W)
# options
self.options_right = Frame(self.options_frame)
# self.options_left.pack(fill=X, padx=5, pady=5, expand=True)
self.options_right.grid(row=0, column=1, padx=5, pady=5, sticky=N)
# threads
self.threads = IntVar()
self.threads.set(1)
Label(self.options_right, text='Threads', relief=RIDGE, width=15).grid(row=0, column=0, padx=2, pady=3)
threads_field = Entry(self.options_right, textvariable=self.threads)
threads_field.grid(row=0, column=1, padx=2, pady=3, sticky=W)
# method
self.method = StringVar(self.options_left)
self.method.set('GPU')
Label(self.options_right, text='Method', relief=RIDGE, width=15).grid(row=1, column=0, padx=2, pady=3)
method_menu = OptionMenu(self.options_right, self.method, *AVAILABLE_METHODS)
method_menu.grid(row=1, column=1, padx=2, pady=3, sticky=W)
# driver
self.driver = StringVar(self.options_left)
self.driver.set('Waifu2X Caffe')
Label(self.options_right, text='Driver', relief=RIDGE, width=15).grid(row=2, column=0, padx=2, pady=3)
driver_menu = OptionMenu(self.options_right, self.driver, *AVAILABLE_DRIVERS)
driver_menu.grid(row=2, column=1, padx=2, pady=3, sticky=W)
# preserve frames
self.preserve_frames = BooleanVar(self.options_left)
self.preserve_frames.set(True)
Label(self.options_right, text='Preserve Frames', relief=RIDGE, width=15).grid(row=3, column=0, padx=2, pady=3)
preserve_frames_menu = OptionMenu(self.options_right, self.preserve_frames, *{True, False})
preserve_frames_menu.grid(row=3, column=1, padx=2, pady=3, sticky=W)
# progress bar
self.progress_bar_frame = Frame()
self.progress_bar_frame.pack(fill=X, padx=5, pady=5, expand=True)
self.progress_bar = ttk.Progressbar(self.progress_bar_frame, orient='horizontal', length=100, mode='determinate')
self.progress_bar.pack(fill=X)
# start button frame
self.start_frame = Frame()
self.start_frame.pack(fill=X, padx=5, pady=5, expand=True)
# start button
self.start_button_text = StringVar()
self.start_button_text.set('Start')
Button(self.start_frame, textvariable=self.start_button_text, command=self._launch_upscaling, width=20).pack(side=RIGHT)
self.main_frame.mainloop()
def _launch_upscaling(self):
# prevent launching multiple instances
if self.running:
messagebox.showerror('Error', 'Video2X is already running')
return
# arguments sanity check
if self.input_file.get() == '':
messagebox.showerror('Error', 'You must specify input video file/directory path')
return
if self.output_file.get() == '':
messagebox.showerror('Error', 'You must specify output video file/directory path')
return
if (self.driver.get() == 'waifu2x_converter' or self.driver.get() == 'waifu2x_ncnn_vulkan') and self.width.get() and self.height.get():
messagebox.showerror('Error', 'Waifu2x Converter CPP/NCNN accepts only scaling ratio')
return
if self.driver.get() == 'waifu2x_ncnn_vulkan' and (self.scale_ratio.get() > 2 or not self.scale_ratio.get().is_integer()):
messagebox.showerror('Error', 'Scaling ratio must be 1 or 2 for waifu2x_ncnn_vulkan')
return
if (self.width.get() or self.height.get()) and self.scale_ratio.get():
messagebox.showerror('Error', 'You can only specify either scaling ratio or output width and height')
return
if (self.width.get() and not self.height.get()) or (not self.width.get() and self.height.get()):
messagebox.showerror('Error', 'You must specify both width and height')
return
if (not self.width.get() or not self.height.get()) and not self.scale_ratio.get():
messagebox.showerror('Error', 'You must specify either output dimensions or scaling ratio')
return
upscale = threading.Thread(target=self._upscale)
upscale.start()
self.running = True
self.start_button_text.set('Running')
def _upscale(self):
# read configuration file
config = read_config('video2x.json')
config = absolutify_paths(config)
input_file = pathlib.Path(self.input_file.get())
output_file = pathlib.Path(self.output_file.get())
driver = AVAILABLE_DRIVERS[self.driver.get()]
if driver == 'waifu2x_caffe':
waifu2x_settings = config['waifu2x_caffe']
if not pathlib.Path(waifu2x_settings['waifu2x_caffe_path']).is_file():
messagebox.showerror('Error', 'Specified waifu2x-caffe directory doesn\'t exist\nPlease check the configuration file settings')
raise FileNotFoundError(waifu2x_settings['waifu2x_caffe_path'])
elif driver == 'waifu2x_converter':
waifu2x_settings = config['waifu2x_converter']
if not pathlib.Path(waifu2x_settings['waifu2x_converter_path']).is_dir():
messagebox.showerror('Error', 'Specified waifu2x-converter-cpp directory doesn\'t exist\nPlease check the configuration file settings')
elif driver == 'waifu2x_ncnn_vulkan':
waifu2x_settings = config['waifu2x_ncnn_vulkan']
if not pathlib.Path(waifu2x_settings['waifu2x_ncnn_vulkan_path']).is_file():
messagebox.showerror('Error', 'Specified waifu2x_ncnn_vulkan directory doesn\'t exist\nPlease check the configuration file settings')
raise FileNotFoundError(waifu2x_settings['waifu2x_ncnn_vulkan_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 isinstance(config['video2x']['video2x_cache_directory'], str):
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():
messagebox.showerror('Error', 'Specified cache directory is a file/link')
raise FileExistsError('Specified cache directory is a file/link')
elif not video2x_cache_directory.exists():
# try creating the cache directory
if messagebox.askyesno('Question', f'Specified cache directory {video2x_cache_directory} does not exist\nCreate directory?'):
try:
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 e:
messagebox.showerror('Error', f'Unable to create {video2x_cache_directory}\nAborting...')
raise e
else:
raise FileNotFoundError('Could not create cache directory')
# load more settings from gui
width = self.width.get()
height = self.height.get()
scale_ratio = self.scale_ratio.get()
image_format = self.image_format.get()
threads = self.threads.get()
method = AVAILABLE_METHODS[self.method.get()]
preserve_frames = self.preserve_frames.get()
self.upscaler = Upscaler(input_video=input_file, output_video=output_file, method=method, waifu2x_settings=waifu2x_settings, ffmpeg_settings=ffmpeg_settings)
# set optional options
self.upscaler.waifu2x_driver = driver
self.upscaler.scale_width = width
self.upscaler.scale_height = height
self.upscaler.scale_ratio = scale_ratio
self.upscaler.model_dir = None
self.upscaler.threads = threads
self.upscaler.video2x_cache_directory = video2x_cache_directory
self.upscaler.image_format = image_format
self.upscaler.preserve_frames = preserve_frames
# run upscaler
self.upscaler.create_temp_directories()
# start progress bar
progress_bar = threading.Thread(target=self._progress_bar)
progress_bar.start()
# start upscaling
self.upscaler.run()
self.upscaler.cleanup_temp_directories()
# show message when upscaling completes
messagebox.showinfo('Info', 'Upscaling Completed')
self.progress_bar['value'] = 100
self.running = False
self.start_button_text.set('Start')
def _progress_bar(self):
""" 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.
"""
# initialize variables early
self.upscaler.progress_bar_exit_signal = False
self.upscaler.total_frames_upscaled = 0
self.upscaler.total_frames = 1
# initialize progress bar values
self.progress_bar['value'] = 0
while not self.upscaler.progress_bar_exit_signal:
self.progress_bar['value'] = int(100 * self.upscaler.total_frames_upscaled / self.upscaler.total_frames)
time.sleep(1)
def _select_input(self):
self.input_file.set(askopenfilename(title='Select Input File'))
self.output_file.set(f'{self.input_file.get()}_output.mp4')
def _select_output(self):
self.output_file.set(asksaveasfilename(title='Select Output File'))
def read_config(config_file):
""" Reads configuration file
Returns a dictionary read by JSON.
"""
with open(config_file, 'r') as raw_config:
config = json.load(raw_config)
return config
def absolutify_paths(config):
""" Check to see if paths to binaries are absolute
This function checks if paths to binary files are absolute.
If not, then absolutify the path.
Arguments:
config {dict} -- configuration file dictionary
Returns:
dict -- configuration file dictionary
"""
current_directory = pathlib.Path(sys.argv[0]).parent.absolute()
# check waifu2x-caffe path
if not re.match('^[a-z]:', config['waifu2x_caffe']['waifu2x_caffe_path'], re.IGNORECASE):
config['waifu2x_caffe']['waifu2x_caffe_path'] = current_directory / config['waifu2x_caffe']['waifu2x_caffe_path']
# check waifu2x-converter-cpp path
if not re.match('^[a-z]:', config['waifu2x_converter']['waifu2x_converter_path'], re.IGNORECASE):
config['waifu2x_converter']['waifu2x_converter_path'] = current_directory / config['waifu2x_converter']['waifu2x_converter_path']
# check waifu2x_ncnn_vulkan path
if not re.match('^[a-z]:', config['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path'], re.IGNORECASE):
config['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path'] = current_directory / config['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path']
# check ffmpeg path
if not re.match('^[a-z]:', config['ffmpeg']['ffmpeg_path'], re.IGNORECASE):
config['ffmpeg']['ffmpeg_path'] = current_directory / config['ffmpeg']['ffmpeg_path']
# check video2x cache path
if config['video2x']['video2x_cache_directory']:
if not re.match('^[a-z]:', config['video2x']['video2x_cache_directory'], re.IGNORECASE):
config['video2x']['video2x_cache_directory'] = current_directory / config['video2x']['video2x_cache_directory']
return config
video2x_gui = Video2xGui()

View File

@@ -1,112 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Waifu2x NCNN Vulkan Driver
Author: SAT3LL
Date Created: June 26, 2019
Last Modified: July 27, 2019
Dev: K4YT3X
Description: This class is a high-level wrapper
for waifu2x_ncnn_vulkan.
"""
# built-in imports
import os
import subprocess
import threading
# third-party imports
from avalon_framework import Avalon
class Waifu2xNcnnVulkan:
"""This class communicates with waifu2x ncnn vulkan engine
An object will be created for this class, containing information
about the binary address and the processing method. When being called
by the main program, other detailed information will be passed to
the upscale function.
"""
def __init__(self, waifu2x_settings):
self.waifu2x_settings = waifu2x_settings
# arguments passed through command line overwrites config file values
# waifu2x_ncnn_vulkan can't find its own model directory if its not in the current dir
# so change to it
os.chdir(os.path.join(self.waifu2x_settings['waifu2x_ncnn_vulkan_path'], '..'))
self.print_lock = threading.Lock()
def upscale(self, input_directory, output_directory, scale_ratio, upscaler_exceptions):
"""This is the core function for WAIFU2X class
Arguments:
input_directory {string} -- source directory path
output_directory {string} -- output directory path
ratio {int} -- output video ratio
"""
try:
# overwrite config file settings
self.waifu2x_settings['input'] = input_directory
self.waifu2x_settings['output'] = output_directory
# print thread start message
self.print_lock.acquire()
Avalon.debug_info(f'[upscaler] Thread {threading.current_thread().name} started')
self.print_lock.release()
# waifu2x_ncnn_vulkan does not have long-opts, we'll have a dictionary that maps "our" config long-opt
# names to their short opts
waifu2x_ncnn_vulkan_opt_flag = {
'input': '-i',
'output': '-o',
'noise-level': '-n',
'scale-ratio': '-s',
'tile-size': '-t',
'model-path': '-m',
'gpu': '-g',
'load-proc-save_threads': '-j',
'verbose': '-v'
}
execute = [self.waifu2x_settings['waifu2x_ncnn_vulkan_path']]
for key in self.waifu2x_settings.keys():
value = self.waifu2x_settings[key]
if key == 'waifu2x_ncnn_vulkan_path':
continue
elif key == 'input':
execute.append(waifu2x_ncnn_vulkan_opt_flag[key])
execute.append(input_directory)
elif key == 'output':
execute.append(waifu2x_ncnn_vulkan_opt_flag[key])
execute.append(output_directory)
elif key == 'scale-ratio':
execute.append(waifu2x_ncnn_vulkan_opt_flag[key])
# waifu2x_ncnn_vulkan does not accept an arbitrary scale ratio, max is 2
if scale_ratio == 1:
execute.append('1')
else:
execute.append('2')
# allow upper if cases to take precedence
elif value is None or value is False:
continue
else:
execute.append(waifu2x_ncnn_vulkan_opt_flag[key])
execute.append(str(value))
Avalon.debug_info(f'Executing: {execute}')
subprocess.run(execute, check=True, stderr=subprocess.DEVNULL)
# print thread exiting message
self.print_lock.acquire()
Avalon.debug_info(f'[upscaler] Thread {threading.current_thread().name} exiting')
self.print_lock.release()
return 0
except Exception as e:
upscaler_exceptions.append(e)

92
src/anime4k.py Executable file
View File

@@ -0,0 +1,92 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Anime4K Driver
Author: K4YT3X
Date Created: August 15, 2019
Last Modified: November 15, 2019
Description: This class is a high-level wrapper
for Anime4k.
"""
# built-in imports
import subprocess
import threading
# third-party imports
from avalon_framework import Avalon
class Anime4k:
"""This class communicates with Anime4K engine
An object will be created for this class, containing information
about the binary address and the processing method. When being called
by the main program, other detailed information will be passed to
the upscale function.
"""
def __init__(self, driver_settings):
self.driver_settings = driver_settings
self.print_lock = threading.Lock()
def upscale(self, input_directory, output_directory, scale_ratio, upscaler_exceptions, push_strength=None, push_grad_strength=None):
""" Anime4K wrapper
Arguments:
file_in {string} -- input file path
file_out {string} -- output file path
Keyword Arguments:
scale {int} -- scale ratio (default: {None})
push_strength {int} -- residual push strength (default: {None})
push_grad_strength {int} -- residual gradient push strength (default: {None})
Returns:
subprocess.Popen.returncode -- command line return value of execution
"""
try:
# return value is the sum of all execution return codes
return_value = 0
# get a list lof all image files in input_directory
extracted_frame_files = [f for f in input_directory.iterdir() if str(f).lower().endswith('.png') or str(f).lower().endswith('.jpg')]
# upscale each image in input_directory
for image in extracted_frame_files:
execute = [
self.driver_settings['java_path'],
'-jar',
self.driver_settings['path'],
str(image.absolute()),
str(output_directory / image.name),
str(scale_ratio)
]
# optional arguments
kwargs = [
'push_strength',
'push_grad_strength'
]
# if optional argument specified, append value to execution list
for arg in kwargs:
if locals()[arg] is not None:
execute.extend([locals([arg])])
self.print_lock.acquire()
Avalon.debug_info(f'Executing: {execute}', )
self.print_lock.release()
return_value += subprocess.run(execute, check=True).returncode
# print thread exiting message
self.print_lock.acquire()
Avalon.debug_info(f'[upscaler] Thread {threading.current_thread().name} exiting')
self.print_lock.release()
# return command execution return code
return return_value
except Exception as e:
upscaler_exceptions.append(e)

0
bin/exceptions.py → src/exceptions.py Normal file → Executable file
View File

39
bin/ffmpeg.py → src/ffmpeg.py Normal file → Executable file
View File

@@ -4,14 +4,13 @@
Name: Video2X FFmpeg Controller
Author: K4YT3X
Date Created: Feb 24, 2018
Last Modified: July 27, 2019
Last Modified: November 15, 2019
Description: This class handles all FFmpeg related operations.
"""
# built-in imports
import json
import os
import pathlib
import subprocess
@@ -31,8 +30,8 @@ class Ffmpeg:
self.ffmpeg_settings = ffmpeg_settings
self.ffmpeg_path = pathlib.Path(self.ffmpeg_settings['ffmpeg_path'])
self.ffmpeg_binary = self.ffmpeg_path / 'ffmpeg.exe'
self.ffmpeg_probe_binary = self.ffmpeg_path / 'ffprobe.exe'
self.ffmpeg_binary = self.ffmpeg_path / 'ffmpeg'
self.ffmpeg_probe_binary = self.ffmpeg_path / 'ffprobe'
self.image_format = image_format
self.pixel_format = None
@@ -68,7 +67,7 @@ class Ffmpeg:
pass
# print pixel formats for debugging
Avalon.debug_info(pixel_formats)
Avalon.debug_info(str(pixel_formats))
return pixel_formats
@@ -119,6 +118,8 @@ class Ffmpeg:
self.ffmpeg_binary
]
execute.extend(self._read_configuration(phase='video_to_frames'))
execute.extend([
'-i',
input_video
@@ -130,8 +131,6 @@ class Ffmpeg:
extracted_frames / f'extracted_%0d.{self.image_format}'
])
execute.extend(self._read_configuration(phase='video_to_frames'))
self._execute(execute)
def convert_video(self, framerate, resolution, upscaled_frames):
@@ -152,6 +151,9 @@ class Ffmpeg:
resolution
]
# read other options
execute.extend(self._read_configuration(phase='frames_to_video'))
# read FFmpeg input options
execute.extend(self._read_configuration(phase='frames_to_video', section='input_options'))
@@ -159,8 +161,7 @@ class Ffmpeg:
# Dev: SAT3LL
# rename all .png.png suffixes to .png
import re
import shutil
regex = re.compile(r'\.png\.png$')
regex = re.compile(r'\.png\.png$', re.IGNORECASE)
for frame_name in upscaled_frames.iterdir():
(upscaled_frames / frame_name).rename(upscaled_frames / regex.sub('.png', str(frame_name)))
# END WORKAROUND
@@ -174,9 +175,6 @@ class Ffmpeg:
# read FFmpeg output options
execute.extend(self._read_configuration(phase='frames_to_video', section='output_options'))
# read other options
execute.extend(self._read_configuration(phase='frames_to_video'))
# specify output file location
execute.extend([
upscaled_frames / 'no_audio.mp4'
@@ -193,12 +191,17 @@ class Ffmpeg:
upscaled_frames {string} -- directory containing upscaled frames
"""
execute = [
self.ffmpeg_binary,
self.ffmpeg_binary
]
execute.extend(self._read_configuration(phase='migrating_tracks'))
execute.extend([
'-i',
upscaled_frames / 'no_audio.mp4',
'-i',
input_video
]
])
execute.extend(self._read_configuration(phase='migrating_tracks', section='output_options'))
@@ -206,8 +209,6 @@ class Ffmpeg:
output_video
])
execute.extend(self._read_configuration(phase='migrating_tracks'))
self._execute(execute)
def _read_configuration(self, phase, section=None):
@@ -278,9 +279,9 @@ class Ffmpeg:
Returns:
int -- execution return code
"""
Avalon.debug_info(f'Executing: {execute}')
# turn all list elements into string to avoid errors
execute = [str(e) for e in execute]
return subprocess.run(execute, shell=True, check=True).returncode
Avalon.debug_info(f'Executing: {execute}')
return subprocess.run(execute, check=True).returncode

1
bin/image_cleaner.py → src/image_cleaner.py Normal file → Executable file
View File

@@ -12,7 +12,6 @@ that have already been upscaled.
"""
# built-in imports
import os
import threading
import time

View File

@@ -2,5 +2,6 @@ avalon_framework
colorama
GPUtil
psutil
pyyaml
requests
tqdm

39
bin/upscaler.py → src/upscaler.py Normal file → Executable file
View File

@@ -4,7 +4,7 @@
Name: Video2X Upscaler
Author: K4YT3X
Date Created: December 10, 2018
Last Modified: July 27, 2019
Last Modified: December 11, 2019
Dev: SAT3LL
@@ -15,6 +15,7 @@ Licensed under the GNU General Public License Version 3 (GNU GPL v3),
"""
# local imports
from anime4k import Anime4k
from exceptions import *
from ffmpeg import Ffmpeg
from image_cleaner import ImageCleaner
@@ -24,6 +25,7 @@ from waifu2x_ncnn_vulkan import Waifu2xNcnnVulkan
# built-in imports
from fractions import Fraction
import contextlib
import copy
import pathlib
import re
@@ -37,6 +39,8 @@ import traceback
from avalon_framework import Avalon
from tqdm import tqdm
AVAILABLE_DRIVERS = ['waifu2x_caffe', 'waifu2x_converter', 'waifu2x_ncnn_vulkan', 'anime4k']
class Upscaler:
""" An instance of this class is a upscaler that will
@@ -47,12 +51,12 @@ class Upscaler:
ArgumentError -- if argument is not valid
"""
def __init__(self, input_video, output_video, method, waifu2x_settings, ffmpeg_settings):
def __init__(self, input_video, output_video, method, driver_settings, ffmpeg_settings):
# mandatory arguments
self.input_video = input_video
self.output_video = output_video
self.method = method
self.waifu2x_settings = waifu2x_settings
self.driver_settings = driver_settings
self.ffmpeg_settings = ffmpeg_settings
# optional arguments
@@ -78,7 +82,7 @@ class Upscaler:
"""delete temp directories when done
"""
if not self.preserve_frames:
for directory in [self.extracted_frames, self.upscaled_frames]:
for directory in [self.extracted_frames, self.upscaled_frames, self.video2x_cache_directory]:
try:
# avalon framework cannot be used if python is shutting down
# therefore, plain print is used
@@ -122,7 +126,7 @@ class Upscaler:
previous_cycle_frames = 0
while not self.progress_bar_exit_signal:
try:
with contextlib.suppress(FileNotFoundError):
self.total_frames_upscaled = len([f for f in self.upscaled_frames.iterdir() if str(f)[-4:] == f'.{self.image_format}'])
delta = self.total_frames_upscaled - previous_cycle_frames
previous_cycle_frames = self.total_frames_upscaled
@@ -131,10 +135,8 @@ class Upscaler:
if self.total_frames_upscaled >= self.total_frames:
return
# adds the detla into the progress bar
# adds the delta into the progress bar
progress_bar.update(delta)
except FileNotFoundError:
pass
time.sleep(1)
@@ -156,26 +158,28 @@ class Upscaler:
self.upscaler_exceptions = []
# initialize waifu2x driver
drivers = ['waifu2x_caffe', 'waifu2x_converter', 'waifu2x_ncnn_vulkan']
drivers = AVAILABLE_DRIVERS
if self.waifu2x_driver not in drivers:
raise UnrecognizedDriverError(f'Unrecognized waifu2x driver: {self.waifu2x_driver}')
# it's easier to do multi-threading with waifu2x_converter
# the number of threads can be passed directly to waifu2x_converter
if self.waifu2x_driver == 'waifu2x_converter':
w2 = Waifu2xConverter(self.waifu2x_settings, self.model_dir)
w2 = Waifu2xConverter(self.driver_settings, self.model_dir)
progress_bar = threading.Thread(target=self._progress_bar, args=([self.extracted_frames],))
progress_bar.start()
w2.upscale(self.extracted_frames, self.upscaled_frames, self.scale_ratio, self.threads, self.image_format, self.upscaler_exceptions)
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}', image)
renamed = re.sub(f'_\[.*-.*\]\[x(\d+(\.\d+)?)\]\.{self.image_format}', f'.{self.image_format}', str(image))
(self.upscaled_frames / image).rename(self.upscaled_frames / renamed)
self.progress_bar_exit_signal = True
progress_bar.join()
return
# drivers that are to be multi-threaded by video2x
else:
# create a container for all upscaler threads
upscaler_threads = []
@@ -218,7 +222,7 @@ class Upscaler:
# create a separate w2 instance for each thread
if self.waifu2x_driver == 'waifu2x_caffe':
w2 = Waifu2xCaffe(copy.deepcopy(self.waifu2x_settings), self.method, self.model_dir, self.bit_depth)
w2 = Waifu2xCaffe(copy.deepcopy(self.driver_settings), self.method, self.model_dir, self.bit_depth)
if self.scale_ratio:
thread = threading.Thread(target=w2.upscale,
args=(thread_info[0],
@@ -240,7 +244,16 @@ class Upscaler:
# if the driver being used is waifu2x_ncnn_vulkan
elif self.waifu2x_driver == 'waifu2x_ncnn_vulkan':
w2 = Waifu2xNcnnVulkan(copy.deepcopy(self.waifu2x_settings))
w2 = Waifu2xNcnnVulkan(copy.deepcopy(self.driver_settings))
thread = threading.Thread(target=w2.upscale,
args=(thread_info[0],
self.upscaled_frames,
self.scale_ratio,
self.upscaler_exceptions))
# if the driver being used is anime4k
elif self.waifu2x_driver == 'anime4k':
w2 = Anime4k(copy.deepcopy(self.driver_settings))
thread = threading.Thread(target=w2.upscale,
args=(thread_info[0],
self.upscaled_frames,

View File

@@ -41,15 +41,19 @@
},
"waifu2x_ncnn_vulkan": {
"waifu2x_ncnn_vulkan_path": "C:\\Users\\K4YT3X\\AppData\\Local\\video2x\\waifu2x-ncnn-vulkan\\waifu2x-ncnn-vulkan.exe",
"input": null,
"output": null,
"noise-level": 2,
"scale-ratio": null,
"tile-size": 200,
"model-path": null,
"gpu": 0,
"load-proc-save_threads": null,
"verbose": null
"v": null,
"i": null,
"o": null,
"n": 2,
"s": 2,
"t": 400,
"m": "models-cunet",
"g": 0,
"j": "1:2:2"
},
"anime4k": {
"anime4k_path": "C:\\Users\\K4YT3X\\AppData\\Local\\video2x\\anime4k\\Anime4K.jar",
"java_path": "C:\\Program Files\\Java\\jdk-12.0.2\\bin\\java.exe"
},
"ffmpeg": {
"ffmpeg_path": "C:\\Users\\K4YT3X\\AppData\\Local\\video2x\\ffmpeg-latest-win64-static\\bin",
@@ -73,6 +77,7 @@
"-b:v": null,
"-pix_fmt": null
},
"-hwaccel": "auto",
"-y": true
},
"migrating_tracks": {
@@ -85,6 +90,7 @@
"-c": "copy",
"-pix_fmt": null
},
"-hwaccel": "auto",
"-y": true
}
},

178
bin/video2x.py → src/video2x.py Normal file → Executable file
View File

@@ -11,17 +11,17 @@ __ __ _ _ ___ __ __
Name: Video2X Controller
Author: K4YT3X
Creator: K4YT3X
Date Created: Feb 24, 2018
Last Modified: July 27, 2019
Last Modified: January 4, 2020
Dev: BrianPetkovsek
Dev: SAT3LL
Editor: BrianPetkovsek
Editor: SAT3LL
Licensed under the GNU General Public License Version 3 (GNU GPL v3),
available at: https://www.gnu.org/licenses/gpl-3.0.txt
(C) 2018-2019 K4YT3X
(C) 2018 - 2020 K4YT3X
Video2X is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -44,13 +44,12 @@ smooth and edges sharp.
# local imports
from exceptions import *
from upscaler import AVAILABLE_DRIVERS
from upscaler import Upscaler
# built-in imports
import argparse
import glob
import json
import os
import contextlib
import pathlib
import re
import shutil
@@ -58,13 +57,30 @@ import sys
import tempfile
import time
import traceback
import yaml
# third-party imports
from avalon_framework import Avalon
import GPUtil
import psutil
VERSION = '2.9.0'
VERSION = '3.0.0'
LEGAL_INFO = f'''Video2X Version: {VERSION}
Author: K4YT3X
License: GNU GPL v3
Github Page: https://github.com/k4yt3x/video2x
Contact: k4yt3x@k4yt3x.com'''
LOGO = r'''
__ __ _ _ ___ __ __
\ \ / / (_) | | |__ \ \ \ / /
\ \ / / _ __| | ___ ___ ) | \ V /
\ \/ / | | / _` | / _ \ / _ \ / / > <
\ / | | | (_| | | __/ | (_) | / /_ / . \
\/ |_| \__,_| \___| \___/ |____| /_/ \_\
'''
# each thread might take up to 2.5 GB during initialization.
# (system memory, not to be confused with GPU memory)
@@ -72,7 +88,7 @@ SYS_MEM_PER_THREAD = 2.5
GPU_MEM_PER_THREAD = 3.5
def process_arguments():
def parse_arguments():
"""Processes CLI arguments
This function parses all arguments
@@ -89,10 +105,10 @@ def process_arguments():
# upscaler options
upscaler_options = parser.add_argument_group('Upscaler Options')
upscaler_options.add_argument('-m', '--method', help='upscaling method', action='store', default='gpu', choices=['cpu', 'gpu', 'cudnn'])
upscaler_options.add_argument('-d', '--driver', help='waifu2x driver', action='store', default='waifu2x_caffe', choices=['waifu2x_caffe', 'waifu2x_converter', 'waifu2x_ncnn_vulkan'])
upscaler_options.add_argument('-d', '--driver', help='upscaling driver', action='store', default='waifu2x_caffe', choices=AVAILABLE_DRIVERS)
upscaler_options.add_argument('-y', '--model_dir', type=pathlib.Path, help='directory containing model JSON files', action='store')
upscaler_options.add_argument('-t', '--threads', help='number of threads to use for upscaling', action='store', type=int, default=1)
upscaler_options.add_argument('-c', '--config', type=pathlib.Path, help='video2x config file location', action='store', default=pathlib.Path(sys.argv[0]).parent.absolute() / 'video2x.json')
upscaler_options.add_argument('-c', '--config', type=pathlib.Path, help='video2x config file location', action='store', default=pathlib.Path(sys.argv[0]).parent.absolute() / 'video2x.yaml')
upscaler_options.add_argument('-b', '--batch', help='enable batch mode (select all default values to questions)', action='store_true')
# scaling options
@@ -111,18 +127,9 @@ def process_arguments():
def print_logo():
"""print video2x logo"""
logo = r'''
__ __ _ _ ___ __ __
\ \ / / (_) | | |__ \ \ \ / /
\ \ / / _ __| | ___ ___ ) | \ V /
\ \/ / | | / _` | / _ \ / _ \ / / > <
\ / | | | (_| | | __/ | (_) | / /_ / . \
\/ |_| \__,_| \___| \___/ |____| /_/ \_\
'''
print(logo)
print('\n Video2X Video Enlarger')
spaces = ((44 - len(f'Version {VERSION}')) // 2) * ' '
print(f'{Avalon.FM.BD}\n{spaces} Version {VERSION}\n{Avalon.FM.RST}')
print(LOGO)
print(f'\n{"Video2X Video Enlarger".rjust(40, " ")}')
print(f'\n{Avalon.FM.BD}{f"Version {VERSION}".rjust(36, " ")}{Avalon.FM.RST}\n')
def check_memory():
@@ -138,19 +145,17 @@ def check_memory():
# check if Nvidia-smi is available
# GPUtil requires nvidia-smi.exe to interact with GPU
if args.method == 'gpu' or args.method == 'cudnn':
if args.method in ['gpu', 'cudnn']:
if not (shutil.which('nvidia-smi') or
pathlib.Path(r'C:\Program Files\NVIDIA Corporation\NVSMI\nvidia-smi.exe').is_file()):
# Nvidia System Management Interface not available
Avalon.warning('Nvidia-smi not available, skipping available memory check')
Avalon.warning('If you experience error \"cudaSuccess out of memory\", try reducing number of threads you\'re using')
else:
try:
with contextlib.suppress(ValueError):
# "0" is GPU ID. Both waifu2x drivers use the first GPU available, therefore only 0 makes sense
gpu_memory_available = (GPUtil.getGPUs()[0].memoryTotal - GPUtil.getGPUs()[0].memoryUsed) / 1024
memory_status.append(('GPU', gpu_memory_available))
except ValueError:
pass
# go though each checkable memory type and check availability
for memory_type, memory_available in memory_status:
@@ -182,14 +187,18 @@ def check_memory():
Avalon.warning('Proceed with caution')
def read_config(config_file):
""" Reads configuration file
def read_config(config_file: pathlib.Path) -> dict:
""" read video2x configurations from config file
Returns a dictionary read by JSON.
Arguments:
config_file {pathlib.Path} -- video2x configuration file pathlib.Path
Returns:
dict -- dictionary of video2x configuration
"""
with open(config_file, 'r') as raw_config:
config = json.load(raw_config)
return config
with open(config_file, 'r') as config:
return yaml.load(config, Loader=yaml.FullLoader)
def absolutify_paths(config):
@@ -218,6 +227,10 @@ def absolutify_paths(config):
if not re.match('^[a-z]:', config['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path'], re.IGNORECASE):
config['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path'] = current_directory / config['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path']
# check anime4k path
if not re.match('^[a-z]:', config['anime4k']['anime4k_path'], re.IGNORECASE):
config['anime4k']['anime4k_path'] = current_directory / config['anime4k']['anime4k_path']
# check ffmpeg path
if not re.match('^[a-z]:', config['ffmpeg']['ffmpeg_path'], re.IGNORECASE):
config['ffmpeg']['ffmpeg_path'] = current_directory / config['ffmpeg']['ffmpeg_path']
@@ -240,17 +253,13 @@ if __name__ != '__main__':
# print video2x logo
print_logo()
# process CLI arguments
args = process_arguments()
# parse command line arguments
args = parse_arguments()
# display version and lawful informaition
if args.version:
print(f'Video2X Version: {VERSION}')
print('Author: K4YT3X')
print('License: GNU GPL v3')
print('Github Page: https://github.com/k4yt3x/video2x')
print('Contact: k4yt3x@k4yt3x.com')
exit(0)
print(LEGAL_INFO)
sys.exit(0)
# arguments sanity check
if not args.input:
@@ -259,10 +268,10 @@ if not args.input:
if not args.output:
Avalon.error('You must specify output video file/directory path')
raise ArgumentError('output video path not specified')
if (args.driver == 'waifu2x_converter' or args.driver == 'waifu2x_ncnn_vulkan') and args.width and args.height:
Avalon.error('Waifu2x Converter CPP/NCNN accepts only scaling ratio')
raise ArgumentError('waifu2x-converter supports only scaling ratio')
if args.driver == 'waifu2x_ncnn_vulkan' and (args.ratio > 2 or not args.ratio.is_integer()):
if (args.driver in ['waifu2x_converter', 'waifu2x_ncnn_vulkan', 'anime4k']) and args.width and args.height:
Avalon.error('Selected driver accepts only scaling ratio')
raise ArgumentError('selected driver supports only scaling ratio')
if args.driver == 'waifu2x_ncnn_vulkan' and args.ratio is not None and (args.ratio > 2 or not args.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 (args.width or args.height) and args.ratio:
@@ -272,32 +281,45 @@ if (args.width and not args.height) or (not args.width and args.height):
Avalon.error('You must specify both width and height')
raise ArgumentError('only one of width or height is specified')
# check available memory
check_memory()
# check available memory if driver is waifu2x-based
if args.driver in ['waifu2x_caffe', 'waifu2x_converter', 'waifu2x_ncnn_vulkan']:
check_memory()
# read configurations from JSON
# anime4k runs significantly faster with more threads
if args.driver == 'anime4k' and args.threads <= 1:
Avalon.warning('Anime4K runs significantly faster with more threads')
if Avalon.ask('Use more threads of Anime4K?', default=True, batch=args.batch):
while True:
try:
threads = Avalon.gets('Amount of threads to use [5]: ', default=5, batch=args.batch)
args.threads = int(threads)
break
except ValueError:
if threads == '':
args.threads = 5
break
Avalon.error(f'{threads} is not a valid integer')
# read configurations from configuration file
config = read_config(args.config)
config = absolutify_paths(config)
# config = absolutify_paths(config)
# load waifu2x configuration
if args.driver == 'waifu2x_caffe':
waifu2x_settings = config['waifu2x_caffe']
if not pathlib.Path(waifu2x_settings['waifu2x_caffe_path']).is_file():
Avalon.error('Specified waifu2x-caffe directory doesn\'t exist')
driver_settings = config[args.driver]
# check if driver path exists
if not pathlib.Path(driver_settings['path']).is_file():
if not pathlib.Path(f'{driver_settings["path"]}.exe').is_file():
Avalon.error('Specified driver executable directory doesn\'t exist')
Avalon.error('Please check the configuration file settings')
raise FileNotFoundError(waifu2x_settings['waifu2x_caffe_path'])
elif args.driver == 'waifu2x_converter':
waifu2x_settings = config['waifu2x_converter']
if not pathlib.Path(waifu2x_settings['waifu2x_converter_path']).is_dir():
Avalon.error('Specified waifu2x-converter-cpp directory doesn\'t exist')
Avalon.error('Please check the configuration file settings')
raise FileNotFoundError(waifu2x_settings['waifu2x_converter_path'])
elif args.driver == 'waifu2x_ncnn_vulkan':
waifu2x_settings = config['waifu2x_ncnn_vulkan']
if not pathlib.Path(waifu2x_settings['waifu2x_ncnn_vulkan_path']).is_file():
Avalon.error('Specified waifu2x_ncnn_vulkan directory doesn\'t exist')
Avalon.error('Please check the configuration file settings')
raise FileNotFoundError(waifu2x_settings['waifu2x_ncnn_vulkan_path'])
raise FileNotFoundError(driver_settings['path'])
# if the driver is Anime4K, check if JDK 12 is installed
if args.driver == 'anime4k':
if not pathlib.Path('C:/Program Files/Java/jdk-12.0.2/bin/java.exe').is_file():
Avalon.warning('Cannot find JDK 12 at its default installation location')
Avalon.warning('Please ensure you have JDK 12 installed and configured')
# read FFmpeg configuration
ffmpeg_settings = config['ffmpeg']
@@ -353,12 +375,12 @@ try:
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('.*\..*$', str(args.output)):
if not re.search(r'.*\..*$', str(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=args.input, output_video=args.output, method=args.method, waifu2x_settings=waifu2x_settings, ffmpeg_settings=ffmpeg_settings)
upscaler = Upscaler(input_video=args.input, output_video=args.output, method=args.method, driver_settings=driver_settings, ffmpeg_settings=ffmpeg_settings)
# set optional options
upscaler.waifu2x_driver = args.driver
@@ -380,9 +402,13 @@ try:
elif args.input.is_dir():
# upscale videos in a directory
Avalon.info(f'Upscaling videos in directory: {args.input}')
# make output directory if it doesn't exist
args.output.mkdir(parents=True, exist_ok=True)
for input_video in [f for f in args.input.iterdir() if f.is_file()]:
output_video = args.output / input_video
upscaler = Upscaler(input_video=args.input / input_video, output_video=output_video, method=args.method, waifu2x_settings=waifu2x_settings, ffmpeg_settings=ffmpeg_settings)
output_video = args.output / input_video.name
upscaler = Upscaler(input_video=input_video, output_video=output_video, method=args.method, driver_settings=driver_settings, ffmpeg_settings=ffmpeg_settings)
# set optional options
upscaler.waifu2x_driver = args.driver
@@ -404,13 +430,17 @@ try:
raise FileNotFoundError(f'{args.input} is neither file nor directory')
Avalon.info(f'Program completed, taking {round((time.time() - begin_time), 5)} seconds')
except Exception:
Avalon.error('An exception has occurred')
traceback.print_exc()
# try cleaning up temp directories
with contextlib.suppress(Exception):
upscaler.cleanup_temp_directories()
finally:
# remove Video2X cache directory
try:
with contextlib.suppress(FileNotFoundError):
if not preserve_frames:
shutil.rmtree(video2x_cache_directory)
except FileNotFoundError:
pass

89
src/video2x.yaml Normal file
View File

@@ -0,0 +1,89 @@
---
waifu2x_caffe:
path: C:\Users\K4YT3X\AppData\Local\video2x\waifu2x-caffe\waifu2x-caffe-cui
input_extention_list:
output_extention:
mode: noise_scale
scale_ratio:
scale_width:
scale_height:
noise_level: 3
process: gpu
crop_size: 128
output_quality: -1
output_depth: 8
batch_size: 1
gpu: 0
tta: 0
input_path:
output_path:
model_dir:
crop_w:
crop_h:
waifu2x_converter:
path: C:\Users\K4YT3X\AppData\Local\video2x\waifu2x-converter-cpp
output-format:
png-compression:
image-quality:
block-size:
disable-gpu:
force-OpenCL:
processor:
jobs:
model-dir:
scale-ratio:
noise-level: 3
mode: noise-scale
silent: true
output:
input:
waifu2x_ncnn_vulkan:
path: C:\Users\K4YT3X\AppData\Local\video2x\waifu2x-ncnn-vulkan\waifu2x-ncnn-vulkan
v:
i:
o:
n: 2
s: 2
t: 400
m: models-cunet
g: 0
j: "1:2:2"
anime4k:
path: C:\Users\K4YT3X\AppData\Local\video2x\anime4k\Anime4K.jar
java_path: C:\Program Files\Java\jdk-12.0.2\bin\java.exe
ffmpeg:
ffmpeg_path: C:\Users\K4YT3X\AppData\Local\video2x\ffmpeg-latest-win64-static\bin
video_to_frames:
output_options:
"-qscale:v":
"-pix_fmt": rgba64be
"-hwaccel": auto
"-y": true
frames_to_video:
input_options:
"-qscale:v":
"-qscale:a":
"-f": image2
output_options:
"-vcodec": libx264
"-crf": 17
"-b:v":
"-pix_fmt":
"-hwaccel": auto
"-y": true
migrating_tracks:
output_options:
"-map":
- 0:v?
- 1:a?
- 1:s?
- 1:d?
- 1:t?
"-c": copy
"-pix_fmt":
"-hwaccel": auto
"-y": true
video2x:
video2x_cache_directory:
image_format: png
preserve_frames: false

462
src/video2x_gui.py Executable file
View File

@@ -0,0 +1,462 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Creator: Video2X GUI
Author: K4YT3X
Date Created: July 27, 2019
Last Modified: December 11, 2019
Description: A simple GUI for Video2X made with tkinter.
"""
# local imports
from exceptions import *
from upscaler import Upscaler
# built-in imports
from tkinter import *
from tkinter import messagebox
from tkinter import ttk
from tkinter.filedialog import *
import contextlib
import pathlib
import sys
import tempfile
import threading
import time
import yaml
VERSION = '1.1.3'
VIDEO2X_CONFIG = pathlib.Path(sys.argv[0]).parent.absolute() / 'video2x.yaml'
LEGAL_INFO = f'''Video2X GUI Version: {VERSION}
Author: K4YT3X
License: GNU GPL v3
Github Page: https://github.com/k4yt3x/video2x
Contact: k4yt3x@k4yt3x.com'''
# global static variables
AVAILABLE_METHODS = {
'GPU': 'gpu',
'CUDNN': 'cudnn',
'CPU': 'cpu'
}
AVAILABLE_DRIVERS = {
'Waifu2X Caffe': 'waifu2x_caffe',
'Waifu2X Converter CPP': 'waifu2x_converter',
'Waifu2x NCNN Vulkan': 'waifu2x_ncnn_vulkan',
'Anime4K': 'anime4k'
}
IMAGE_FORMATS = {'PNG', 'JPG'}
DEMUXER_EXTENSIONS = {'3dostr', '4xm', 'aa', 'aac', 'ac3', 'acm', 'act',
'adf', 'adp', 'ads', 'adx', 'aea', 'afc', 'aiff', 'aix', 'alaw',
'alias_pix', 'alsa', 'amr', 'amrnb', 'amrwb', 'anm', 'apc', 'ape',
'apng', 'aptx', 'aptx_hd', 'aqtitle', 'asf', 'asf_o', 'ass', 'ast',
'au', 'avi', 'avisynth', 'avr', 'avs', 'avs2', 'bethsoftvid', 'bfi',
'bfstm', 'bin', 'bink', 'bit', 'bmp_pipe', 'bmv', 'boa', 'brender_pix',
'brstm', 'c93', 'caf', 'cavsvideo', 'cdg', 'cdxl', 'cine', 'codec2',
'codec2raw', 'concat', 'dash', 'data', 'daud', 'dcstr', 'dds_pipe',
'dfa', 'dirac', 'dnxhd', 'dpx_pipe', 'dsf', 'dsicin', 'dss', 'dts',
'dtshd', 'dv', 'dvbsub', 'dvbtxt', 'dxa', 'ea', 'ea_cdata', 'eac3',
'epaf', 'exr_pipe', 'f32be', 'f32le', 'f64be', 'f64le', 'fbdev',
'ffmetadata', 'film_cpk', 'filmstrip', 'fits', 'flac', 'flic', 'flv',
'frm', 'fsb', 'g722', 'g723_1', 'g726', 'g726le', 'g729', 'gdv', 'genh',
'gif', 'gsm', 'gxf', 'h261', 'h263', 'h264', 'hevc', 'hls', 'applehttp',
'hnm', 'ico', 'idcin', 'idf', 'iec61883', 'iff', 'ilbc', 'image2',
'image2pipe', 'ingenient', 'ipmovie', 'ircam', 'iss', 'iv8', 'ivf',
'ivr', 'j2k_pipe', 'jack', 'jacosub', 'jpeg_pipe', 'jpegls_pipe',
'jv', 'kmsgrab', 'lavfi', 'libcdio', 'libdc1394', 'libgme', 'libopenmpt',
'live_flv', 'lmlm4', 'loas', 'lrc', 'lvf', 'lxf', 'm4v', 'matroska', 'webm',
'mgsts', 'microdvd', 'mjpeg', 'mjpeg_2000', 'mlp', 'mlv', 'mm', 'mmf',
'mov', 'mp4', 'm4a', '3gp', '3g2', 'mj2', 'mp3', 'mpc', 'mpc8', 'mpeg',
'mpegts', 'mpegtsraw', 'mpegvideo', 'mpjpeg', 'mpl2', 'mpsub', 'msf',
'msnwctcp', 'mtaf', 'mtv', 'mulaw', 'musx', 'mv', 'mvi', 'mxf', 'mxg',
'nc', 'nistsphere', 'nsp', 'nsv', 'nut', 'nuv', 'ogg', 'oma', 'openal',
'oss', 'paf', 'pam_pipe', 'pbm_pipe', 'pcx_pipe', 'pgm_pipe', 'pgmyuv_pipe',
'pictor_pipe', 'pjs', 'pmp', 'png_pipe', 'ppm_pipe', 'psd_pipe', 'psxstr',
'pulse', 'pva', 'pvf', 'qcp', 'qdraw_pipe', 'r3d', 'rawvideo', 'realtext',
'redspark', 'rl2', 'rm', 'roq', 'rpl', 'rsd', 'rso', 'rtp', 'rtsp',
's16be', 's16le', 's24be', 's24le', 's32be', 's32le', 's337m', 's8',
'sami', 'sap', 'sbc', 'sbg', 'scc', 'sdp', 'sdr2', 'sds', 'sdx', 'ser',
'sgi_pipe', 'shn', 'siff', 'sln', 'smjpeg', 'smk', 'smush', 'sndio',
'sol', 'sox', 'spdif', 'srt', 'stl', 'subviewer', 'subviewer1', 'sunrast_pipe',
'sup', 'svag', 'svg_pipe', 'swf', 'tak', 'tedcaptions', 'thp', 'tiertexseq',
'tiff_pipe', 'tmv', 'truehd', 'tta', 'tty', 'txd', 'ty', 'u16be', 'u16le',
'u24be', 'u24le', 'u32be', 'u32le', 'u8', 'v210', 'v210x', 'vag', 'vc1',
'vc1test', 'vidc', 'video4linux2', 'v4l2', 'vivo', 'vmd', 'vobsub', 'voc',
'vpk', 'vplayer', 'vqf', 'w64', 'wav', 'wc3movie', 'webm_dash_manifest',
'webp_pipe', 'webvtt', 'wsaud', 'wsd', 'wsvqa', 'wtv', 'wv', 'wve', 'x11grab',
'xa', 'xbin', 'xmv', 'xpm_pipe', 'xvag', 'xwd_pipe', 'xwma', 'yop', 'yuv4mpegpipe'}
class Video2xGui():
def __init__(self):
self.running = False
# create main window
self.main_window = Tk()
self.main_window.title(f'Video2X GUI {VERSION}')
self.main_frame = Frame()
self.main_frame.pack(fill=BOTH, expand=True)
# add menu bar
self.menu_bar = Menu(self.main_frame)
# file menu
self.file_menu = Menu(self.menu_bar, tearoff=0)
self.file_menu.add_command(label='Exit', command=self.main_frame.quit)
self.menu_bar.add_cascade(label='File', menu=self.file_menu)
# help menu
self.help_menu = Menu(self.menu_bar, tearoff=0)
self.help_menu.add_command(label='About', command=self._display_help)
self.menu_bar.add_cascade(label='Help', menu=self.help_menu)
self.main_window.config(menu=self.menu_bar)
# file frame
self.file_frame = Frame(self.main_frame)
self.file_frame.pack(fill=X, padx=5, pady=5, expand=True)
# input file
self.input_file = StringVar()
label_text = StringVar()
label_text.set('Input File')
Label(self.file_frame, textvariable=label_text, relief=RIDGE, width=10).grid(row=0, column=0, padx=5, pady=5, sticky=W)
Entry(self.file_frame, textvariable=self.input_file, width=60).grid(row=0, column=1, padx=5, pady=5, sticky=W)
Button(self.file_frame, text='Select', command=self._select_input).grid(row=0, column=2, padx=5, pady=5, sticky=W)
# output file
self.output_file = StringVar()
label_text = StringVar()
label_text.set('Output File')
Label(self.file_frame, textvariable=label_text, relief=RIDGE, width=10).grid(row=1, column=0, padx=5, pady=5, sticky=W)
Entry(self.file_frame, textvariable=self.output_file, width=60).grid(row=1, column=1, padx=5, pady=5, sticky=W)
Button(self.file_frame, text='Select', command=self._select_output).grid(row=1, column=2, padx=5, pady=5, sticky=W)
# options
self.options_frame = Frame()
# self.options_left.pack(fill=X, padx=5, pady=5, expand=True)
self.options_frame.pack(fill=X, padx=5, pady=5, expand=True)
self.options_left = Frame(self.options_frame)
# self.options_left.pack(fill=X, padx=5, pady=5, expand=True)
self.options_left.grid(row=0, column=0, padx=5, pady=5, sticky=N)
# width
self.width = IntVar()
# self.width.set(1920)
Label(self.options_left, text='Width', relief=RIDGE, width=15).grid(row=0, column=0, padx=2, pady=3)
width_field = Entry(self.options_left, textvariable=self.width)
width_field.grid(row=0, column=1, padx=2, pady=3, sticky=W)
# height
self.height = IntVar()
# self.height.set(1080)
Label(self.options_left, text='Height', relief=RIDGE, width=15).grid(row=1, column=0, padx=2, pady=3)
height_field = Entry(self.options_left, textvariable=self.height)
height_field.grid(row=1, column=1, padx=2, pady=3, sticky=W)
# scale ratio
self.scale_ratio = DoubleVar()
# self.scale_ratio.set(2.0)
Label(self.options_left, text='Scale Ratio', relief=RIDGE, width=15).grid(row=2, column=0, padx=2, pady=3)
scale_ratio_field = Entry(self.options_left, textvariable=self.scale_ratio)
scale_ratio_field.grid(row=2, column=1, padx=2, pady=3, sticky=W)
# image format
self.image_format = StringVar(self.options_left)
self.image_format.set('PNG')
Label(self.options_left, text='Image Format', relief=RIDGE, width=15).grid(row=3, column=0, padx=2, pady=3)
image_format_menu = OptionMenu(self.options_left, self.image_format, *IMAGE_FORMATS)
image_format_menu.grid(row=3, column=1, padx=2, pady=3, sticky=W)
# options
self.options_right = Frame(self.options_frame)
# self.options_left.pack(fill=X, padx=5, pady=5, expand=True)
self.options_right.grid(row=0, column=1, padx=5, pady=5, sticky=N)
# threads
self.threads = IntVar()
self.threads.set(1)
Label(self.options_right, text='Threads', relief=RIDGE, width=15).grid(row=0, column=0, padx=2, pady=3)
threads_field = Entry(self.options_right, textvariable=self.threads)
threads_field.grid(row=0, column=1, padx=2, pady=3, sticky=W)
# method
self.method = StringVar(self.options_left)
self.method.set('GPU')
Label(self.options_right, text='Method', relief=RIDGE, width=15).grid(row=1, column=0, padx=2, pady=3)
method_menu = OptionMenu(self.options_right, self.method, *AVAILABLE_METHODS)
method_menu.grid(row=1, column=1, padx=2, pady=3, sticky=W)
# driver
self.driver = StringVar(self.options_left)
self.driver.set('Waifu2X Caffe')
Label(self.options_right, text='Driver', relief=RIDGE, width=15).grid(row=2, column=0, padx=2, pady=3)
driver_menu = OptionMenu(self.options_right, self.driver, *AVAILABLE_DRIVERS)
driver_menu.grid(row=2, column=1, padx=2, pady=3, sticky=W)
# preserve frames
self.preserve_frames = BooleanVar(self.options_left)
self.preserve_frames.set(False)
Label(self.options_right, text='Preserve Frames', relief=RIDGE, width=15).grid(row=3, column=0, padx=2, pady=3)
preserve_frames_menu = OptionMenu(self.options_right, self.preserve_frames, *{True, False})
preserve_frames_menu.grid(row=3, column=1, padx=2, pady=3, sticky=W)
# progress bar
self.progress_bar_frame = Frame()
self.progress_bar_frame.pack(fill=X, padx=5, pady=5, expand=True)
self.progress_bar = ttk.Progressbar(self.progress_bar_frame, orient='horizontal', length=100, mode='determinate')
self.progress_bar.pack(fill=X)
# start button frame
self.start_frame = Frame()
self.start_frame.pack(fill=X, padx=5, pady=5, expand=True)
# start button
self.start_button_text = StringVar()
self.start_button_text.set('Start')
Button(self.start_frame, textvariable=self.start_button_text, command=self._launch_upscaling, width=20).pack(side=RIGHT)
self.main_frame.mainloop()
def _display_help(self):
messagebox.showinfo('About', LEGAL_INFO)
def _launch_upscaling(self):
# prevent launching multiple instances
if self.running:
messagebox.showerror('Error', 'Video2X is already running')
return
# arguments sanity check
if self.input_file.get() == '':
messagebox.showerror('Error', 'You must specify input video file/directory path')
return
if self.output_file.get() == '':
messagebox.showerror('Error', 'You must specify output video file/directory path')
return
if (self.driver.get() in ['Waifu2X Converter CPP', 'Waifu2x NCNN Vulkan', 'Anime4K']) and self.width.get() and self.height.get():
messagebox.showerror('Error', f'Selected driver \"{self.driver.get()}\" accepts only scaling ratio')
return
if self.driver.get() == 'waifu2x_ncnn_vulkan' and (self.scale_ratio.get() > 2 or not self.scale_ratio.get().is_integer()):
messagebox.showerror('Error', 'Scaling ratio must be 1 or 2 for waifu2x_ncnn_vulkan')
return
if (self.width.get() or self.height.get()) and self.scale_ratio.get():
messagebox.showerror('Error', 'You can only specify either scaling ratio or output width and height')
return
if (self.width.get() and not self.height.get()) or (not self.width.get() and self.height.get()):
messagebox.showerror('Error', 'You must specify both width and height')
return
if (not self.width.get() or not self.height.get()) and not self.scale_ratio.get():
messagebox.showerror('Error', 'You must specify either output dimensions or scaling ratio')
return
upscale = threading.Thread(target=self._upscale)
upscale.start()
self.running = True
self.start_button_text.set('Running')
def _upscale(self):
try:
# start timer
begin_time = time.time()
# read configuration file
config = read_config(VIDEO2X_CONFIG)
config = absolutify_paths(config)
input_file = pathlib.Path(self.input_file.get())
output_file = pathlib.Path(self.output_file.get())
driver = AVAILABLE_DRIVERS[self.driver.get()]
# load specified driver's config into driver_settings
driver_settings = config[driver]
# if executable doesn't exist, show warning
if not pathlib.Path(driver_settings['path']).is_file() and not pathlib.Path(f'{driver_settings["path"]}.exe').is_file():
messagebox.showerror('Error', 'Specified driver directory doesn\'t exist\nPlease 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 isinstance(config['video2x']['video2x_cache_directory'], str):
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():
messagebox.showerror('Error', 'Specified cache directory is a file/link')
raise FileExistsError('Specified cache directory is a file/link')
elif not video2x_cache_directory.exists():
# try creating the cache directory
if messagebox.askyesno('Question', f'Specified cache directory {video2x_cache_directory} does not exist\nCreate directory?'):
try:
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 e:
messagebox.showerror('Error', f'Unable to create {video2x_cache_directory}\nAborting...')
raise e
else:
raise FileNotFoundError('Could not create cache directory')
# load more settings from gui
width = self.width.get()
height = self.height.get()
scale_ratio = self.scale_ratio.get()
image_format = self.image_format.get()
threads = self.threads.get()
method = AVAILABLE_METHODS[self.method.get()]
preserve_frames = self.preserve_frames.get()
self.upscaler = Upscaler(input_video=input_file, output_video=output_file, method=method, driver_settings=driver_settings, ffmpeg_settings=ffmpeg_settings)
# set optional options
self.upscaler.waifu2x_driver = driver
self.upscaler.scale_width = width
self.upscaler.scale_height = height
self.upscaler.scale_ratio = scale_ratio
self.upscaler.model_dir = None
self.upscaler.threads = threads
self.upscaler.video2x_cache_directory = video2x_cache_directory
self.upscaler.image_format = image_format
self.upscaler.preserve_frames = preserve_frames
# run upscaler
self.upscaler.create_temp_directories()
# start progress bar
progress_bar = threading.Thread(target=self._progress_bar)
progress_bar.start()
# start upscaling
self.upscaler.run()
self.upscaler.cleanup_temp_directories()
# show message when upscaling completes
messagebox.showinfo('Info', f'Upscaling Completed\nTime Taken: {round((time.time() - begin_time), 5)} seconds')
self.progress_bar['value'] = 100
self.running = False
self.start_button_text.set('Start')
except Exception as e:
messagebox.showerror('Error', f'Upscaler ran into an error:\n{e}')
# try cleaning up temp directories
with contextlib.suppress(Exception):
self.upscaler.cleanup_temp_directories()
def _progress_bar(self):
""" 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.
"""
# initialize variables early
self.upscaler.progress_bar_exit_signal = False
self.upscaler.total_frames_upscaled = 0
self.upscaler.total_frames = 1
# initialize progress bar values
self.progress_bar['value'] = 0
while not self.upscaler.progress_bar_exit_signal:
self.progress_bar['value'] = int(100 * self.upscaler.total_frames_upscaled / self.upscaler.total_frames)
time.sleep(1)
def _select_input(self):
self.input_file.set(askopenfilename(title='Select Input File'))
# remove input file extension
input_filename = str(self.input_file.get())
for extension in DEMUXER_EXTENSIONS:
if input_filename.endswith(f'.{extension}'):
input_filename = input_filename[:-1 - len(extension)]
# try to set an output file name automatically
output_file = pathlib.Path(f'{input_filename}_output.mp4')
output_file_id = 0
while output_file.is_file() and output_file_id <= 10:
output_file = pathlib.Path(f'{input_filename}_output_{output_file_id}.mp4')
output_file_id += 1
if not output_file.exists():
self.output_file.set(str(output_file))
def _select_output(self):
self.output_file.set(asksaveasfilename(title='Select Output File'))
def read_config(config_file):
""" Reads configuration file
Returns a dictionary read by parsing Video2X config.
"""
with open(config_file, 'r') as raw_config:
config = yaml.load(raw_config, Loader=yaml.FullLoader)
return config
def absolutify_paths(config):
""" Check to see if paths to binaries are absolute
This function checks if paths to binary files are absolute.
If not, then absolutify the path.
Arguments:
config {dict} -- configuration file dictionary
Returns:
dict -- configuration file dictionary
"""
current_directory = pathlib.Path(sys.argv[0]).parent.absolute()
# check waifu2x-caffe path
if not re.match('^[a-z]:', config['waifu2x_caffe']['path'], re.IGNORECASE):
config['waifu2x_caffe']['path'] = current_directory / config['waifu2x_caffe']['path']
# check waifu2x-converter-cpp path
if not re.match('^[a-z]:', config['waifu2x_converter']['path'], re.IGNORECASE):
config['waifu2x_converter']['path'] = current_directory / config['waifu2x_converter']['path']
# check waifu2x_ncnn_vulkan path
if not re.match('^[a-z]:', config['waifu2x_ncnn_vulkan']['path'], re.IGNORECASE):
config['waifu2x_ncnn_vulkan']['path'] = current_directory / config['waifu2x_ncnn_vulkan']['path']
# check ffmpeg path
if not re.match('^[a-z]:', config['ffmpeg']['ffmpeg_path'], re.IGNORECASE):
config['ffmpeg']['ffmpeg_path'] = current_directory / config['ffmpeg']['ffmpeg_path']
# check video2x cache path
if config['video2x']['video2x_cache_directory']:
if not re.match('^[a-z]:', config['video2x']['video2x_cache_directory'], re.IGNORECASE):
config['video2x']['video2x_cache_directory'] = current_directory / config['video2x']['video2x_cache_directory']
return config
video2x_gui = Video2xGui()

138
bin/video2x_setup.py → src/video2x_setup.py Normal file → Executable file
View File

@@ -2,17 +2,12 @@
# -*- coding: utf-8 -*-
"""
Name: Video2X Setup Script
Author: K4YT3X
Author: BrianPetkovsek
Creator: K4YT3X
Date Created: November 28, 2018
Last Modified: July 30, 2019
Last Modified: January 4, 2020
Dev: SAT3LL
Licensed under the GNU General Public License Version 3 (GNU GPL v3),
available at: https://www.gnu.org/licenses/gpl-3.0.txt
(C) 2018-2019 K4YT3X
Editor: BrianPetkovsek
Editor: SAT3LL
Description: This script helps installing all dependencies of video2x
and generates a configuration for it.
@@ -22,11 +17,13 @@ Installation Details:
- waifu2x-caffe: %LOCALAPPDATA%\\video2x\\waifu2x-caffe
- waifu2x-cpp-converter: %LOCALAPPDATA%\\video2x\\waifu2x-converter-cpp
- waifu2x_ncnn_vulkan: %LOCALAPPDATA%\\video2x\\waifu2x-ncnn-vulkan
- anime4k: %LOCALAPPDATA%\\video2x\\anime4k
"""
# built-in imports
from datetime import timedelta
import argparse
import json
import contextlib
import os
import pathlib
import re
@@ -34,6 +31,7 @@ import shutil
import subprocess
import sys
import tempfile
import time
import traceback
import urllib
import zipfile
@@ -43,20 +41,22 @@ import zipfile
# later in the script.
# import requests
VERSION = '1.4.0'
VERSION = '1.6.1'
# global static variables
LOCALAPPDATA = pathlib.Path(os.getenv('localappdata'))
VIDEO2X_CONFIG = pathlib.Path(sys.argv[0]).parent.absolute() / 'video2x.yaml'
DRIVER_OPTIONS = ['all', 'waifu2x_caffe', 'waifu2x_converter', 'waifu2x_ncnn_vulkan', 'anime4k']
def process_arguments():
def parse_arguments():
"""Processes CLI arguments
"""
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
# video options
general_options = parser.add_argument_group('General Options')
general_options.add_argument('-d', '--driver', help='driver to download and configure', action='store', choices=['all', 'waifu2x_caffe', 'waifu2x_converter', 'waifu2x_ncnn_vulkan'], default='all')
general_options.add_argument('-d', '--driver', help='driver to download and configure', action='store', choices=DRIVER_OPTIONS, default='all')
# parse arguments
return parser.parse_args()
@@ -79,19 +79,22 @@ class Video2xSetup:
print('\nInstalling Python libraries')
self._install_python_requirements()
print('\nInstalling FFMPEG')
print('\nInstalling FFmpeg')
self._install_ffmpeg()
if self.driver == 'all':
self._install_waifu2x_caffe()
self._install_waifu2x_converter_cpp()
self._install_waifu2x_ncnn_vulkan()
self._install_anime4k()
elif self.driver == 'waifu2x_caffe':
self._install_waifu2x_caffe()
elif self.driver == 'waifu2x_converter':
self._install_waifu2x_converter_cpp()
elif self.driver == 'waifu2x_ncnn_vulkan':
self._install_waifu2x_ncnn_vulkan()
elif self.driver == 'anime4k':
self._install_anime4k()
print('\nGenerating Video2X configuration file')
self._generate_config()
@@ -137,7 +140,7 @@ class Video2xSetup:
import requests
# Get latest release of waifu2x-caffe via GitHub API
latest_release = json.loads(requests.get('https://api.github.com/repos/lltcggie/waifu2x-caffe/releases/latest').content.decode('utf-8'))
latest_release = requests.get('https://api.github.com/repos/lltcggie/waifu2x-caffe/releases/latest').json()
for a in latest_release['assets']:
if 'waifu2x-caffe.zip' in a['browser_download_url']:
@@ -154,7 +157,7 @@ class Video2xSetup:
import requests
# Get latest release of waifu2x-caffe via GitHub API
latest_release = json.loads(requests.get('https://api.github.com/repos/DeadSix27/waifu2x-converter-cpp/releases/latest').content.decode('utf-8'))
latest_release = requests.get('https://api.github.com/repos/DeadSix27/waifu2x-converter-cpp/releases/latest').json()
for a in latest_release['assets']:
if re.search(r'waifu2x-DeadSix27-win64_v[0-9]*\.zip', a['browser_download_url']):
@@ -171,7 +174,7 @@ class Video2xSetup:
import requests
# Get latest release of waifu2x-ncnn-vulkan via Github API
latest_release = json.loads(requests.get('https://api.github.com/repos/nihui/waifu2x-ncnn-vulkan/releases/latest').content.decode('utf-8'))
latest_release = requests.get('https://api.github.com/repos/nihui/waifu2x-ncnn-vulkan/releases/latest').json()
for a in latest_release['assets']:
if re.search(r'waifu2x-ncnn-vulkan-\d*\.zip', a['browser_download_url']):
@@ -190,34 +193,66 @@ class Video2xSetup:
# rename the newly extracted directory
(LOCALAPPDATA / 'video2x' / zipf.namelist()[0]).rename(waifu2x_ncnn_vulkan_directory)
def _install_anime4k(self):
""" Install Anime4K
"""
print('\nInstalling Anime4K')
"""
import requests
# get latest release of Anime4K via Github API
# at the time of writing this portion, Anime4K doesn't yet have a stable release
# therefore releases/latest won't work
latest_release = requests.get('https://api.github.com/repos/bloc97/Anime4K/releases').json()[0]
for a in latest_release['assets']:
if 'Anime4K_Java.zip' in a['browser_download_url']:
anime4k_zip = download(a['browser_download_url'], tempfile.gettempdir())
self.trash.append(anime4k_zip)
"""
# since Java pre-compiled release has been removed from download
# page, we use this cached version as a temporary solution
anime4k_zip = download('https://files.flexio.org/Resources/anime4k.zip', tempfile.gettempdir())
self.trash.append(anime4k_zip)
# extract and rename
with zipfile.ZipFile(anime4k_zip) as zipf:
zipf.extractall(LOCALAPPDATA / 'video2x' / 'anime4k')
def _generate_config(self):
""" Generate video2x config
"""
# Open current video2x.json file as template
with open('video2x.json', 'r') as template:
template_dict = json.load(template)
import yaml
# open current video2x configuration file as template
with open(VIDEO2X_CONFIG, 'r') as template:
template_dict = yaml.load(template, Loader=yaml.FullLoader)
template.close()
# configure only the specified drivers
if self.driver == 'all':
template_dict['waifu2x_caffe']['waifu2x_caffe_path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-caffe' / 'waifu2x-caffe-cui.exe')
template_dict['waifu2x_converter']['waifu2x_converter_path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-converter-cpp')
template_dict['waifu2x_ncnn_vulkan']['waifu2x_ncnn_vulkan_path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-ncnn-vulkan' / 'waifu2x-ncnn-vulkan.exe')
template_dict['waifu2x_caffe']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-caffe' / 'waifu2x-caffe-cui.exe')
template_dict['waifu2x_converter']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-converter-cpp')
template_dict['waifu2x_ncnn_vulkan']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-ncnn-vulkan' / 'waifu2x-ncnn-vulkan.exe')
template_dict['anime4k']['path'] = str(LOCALAPPDATA / 'video2x' / 'anime4k' / 'Anime4K.jar')
elif self.driver == 'waifu2x_caffe':
template_dict['waifu2x_caffe']['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.exe')
elif self.driver == 'waifu2x_converter':
template_dict['waifu2x_converter']['waifu2x_converter_path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-converter-cpp')
template_dict['waifu2x_converter']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-converter-cpp')
elif self.driver == 'waifu2x_ncnn_vulkan':
template_dict['waifu2x_ncnn_vulkan']['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.exe')
elif self.driver == 'anime4k':
template_dict['anime4k']['path'] = str(LOCALAPPDATA / 'video2x' / 'anime4k' / 'Anime4K.jar')
template_dict['ffmpeg']['ffmpeg_path'] = str(LOCALAPPDATA / 'video2x' / 'ffmpeg-latest-win64-static' / 'bin')
template_dict['video2x']['video2x_cache_directory'] = None
template_dict['video2x']['preserve_frames'] = False
# Write configuration into file
with open('video2x.json', 'w') as config:
json.dump(template_dict, config, indent=4)
config.close()
# write configuration into file
with open(VIDEO2X_CONFIG, 'w') as config:
yaml.dump(template_dict, config)
def download(url, save_path, chunk_size=4096):
@@ -238,10 +273,8 @@ def download(url, save_path, chunk_size=4096):
file_name = None
if 'content-disposition' in stream.headers:
disposition = stream.headers['content-disposition']
try:
with contextlib.suppress(IndexError):
file_name = re.findall("filename=(.+)", disposition)[0].strip('"')
except IndexError:
pass
if file_name is None:
# output_file = f'{save_path}\\{stream.url.split("/")[-1]}'
@@ -286,7 +319,20 @@ def pip_install(file):
if __name__ == '__main__':
try:
args = process_arguments()
# set default exit code
EXIT_CODE = 0
# get start time
start_time = time.time()
# check platform
if sys.platform != 'win32':
print('This script is currently only compatible with Windows')
EXIT_CODE = 1
sys.exit(1)
# parse command line arguments
args = parse_arguments()
print('Video2X Setup Script')
print(f'Version: {VERSION}')
@@ -300,8 +346,20 @@ if __name__ == '__main__':
setup = Video2xSetup(args.driver, download_python_modules)
setup.run()
print('\nScript finished successfully')
except Exception:
except SystemExit:
pass
# if PermissionError is raised
# user needs to run this with higher privilege
except PermissionError:
traceback.print_exc()
print('You might have insufficient privilege for this script to run')
print('Try running this script with Administrator privileges')
EXIT_CODE = 1
# for any exception in the script
except Exception:
traceback.print_exc()
print('An error has occurred')
print('Video2X Automatic Setup has failed')
@@ -313,4 +371,12 @@ if __name__ == '__main__':
traceback.print_exc()
print('An error occurred while trying to cleanup files')
exit(1)
EXIT_CODE = 1
# regardless if script finishes successfully or not
# print script execution summary
finally:
print('Script finished')
print(f'Time taken: {timedelta(seconds=round(time.time() - start_time))}')
input('Press [ENTER] to exit script')
sys.exit(EXIT_CODE)

34
bin/waifu2x_caffe.py → src/waifu2x_caffe.py Normal file → Executable file
View File

@@ -4,7 +4,7 @@
Name: Waifu2x Caffe Driver
Author: K4YT3X
Date Created: Feb 24, 2018
Last Modified: July 27, 2019
Last Modified: October 6, 2019
Description: This class is a high-level wrapper
for waifu2x-caffe.
@@ -27,11 +27,11 @@ class Waifu2xCaffe:
the upscale function.
"""
def __init__(self, waifu2x_settings, process, model_dir, bit_depth):
self.waifu2x_settings = waifu2x_settings
self.waifu2x_settings['process'] = process
self.waifu2x_settings['model_dir'] = model_dir
self.waifu2x_settings['output_depth'] = bit_depth
def __init__(self, driver_settings, process, model_dir, bit_depth):
self.driver_settings = driver_settings
self.driver_settings['process'] = process
self.driver_settings['model_dir'] = model_dir
self.driver_settings['output_depth'] = bit_depth
# arguments passed through command line overwrites config file values
self.process = process
@@ -50,16 +50,16 @@ class Waifu2xCaffe:
try:
# overwrite config file settings
self.waifu2x_settings['input_path'] = input_directory
self.waifu2x_settings['output_path'] = output_directory
self.driver_settings['input_path'] = input_directory
self.driver_settings['output_path'] = output_directory
if scale_ratio:
self.waifu2x_settings['scale_ratio'] = scale_ratio
self.driver_settings['scale_ratio'] = scale_ratio
elif scale_width and scale_height:
self.waifu2x_settings['scale_width'] = scale_width
self.waifu2x_settings['scale_height'] = scale_height
self.driver_settings['scale_width'] = scale_width
self.driver_settings['scale_height'] = scale_height
self.waifu2x_settings['output_extention'] = image_format
self.driver_settings['output_extention'] = image_format
# print thread start message
self.print_lock.acquire()
@@ -67,15 +67,15 @@ class Waifu2xCaffe:
self.print_lock.release()
# list to be executed
execute = []
# initialize the list with waifu2x binary path as the first element
execute = [str(self.driver_settings['path'])]
execute.append(self.waifu2x_settings['waifu2x_caffe_path'])
for key in self.waifu2x_settings.keys():
for key in self.driver_settings.keys():
value = self.waifu2x_settings[key]
value = self.driver_settings[key]
# is executable key or null or None means that leave this option out (keep default)
if key == 'waifu2x_caffe_path' or value is None or value is False:
if key == 'path' or value is None or value is False:
continue
else:
if len(key) == 1:

42
bin/waifu2x_converter.py → src/waifu2x_converter.py Normal file → Executable file
View File

@@ -4,7 +4,7 @@
Name: Waifu2x Converter CPP Driver
Author: K4YT3X
Date Created: February 8, 2019
Last Modified: July 27, 2019
Last Modified: October 6, 2019
Description: This class is a high-level wrapper
for waifu2x-converter-cpp.
@@ -28,9 +28,9 @@ class Waifu2xConverter:
the upscale function.
"""
def __init__(self, waifu2x_settings, model_dir):
self.waifu2x_settings = waifu2x_settings
self.waifu2x_settings['model_dir'] = model_dir
def __init__(self, driver_settings, model_dir):
self.driver_settings = driver_settings
self.driver_settings['model_dir'] = model_dir
self.print_lock = threading.Lock()
def upscale(self, input_directory, output_directory, scale_ratio, jobs, image_format, upscaler_exceptions):
@@ -46,25 +46,16 @@ class Waifu2xConverter:
try:
# overwrite config file settings
self.waifu2x_settings['input'] = input_directory
self.waifu2x_settings['output'] = output_directory
# temporary fix for https://github.com/DeadSix27/waifu2x-converter-cpp/issues/109
"""
self.waifu2x_settings['i'] = input_directory
self.waifu2x_settings['o'] = output_directory
self.waifu2x_settings['input'] = None
self.waifu2x_settings['output'] = None
"""
self.waifu2x_settings['scale-ratio'] = scale_ratio
self.waifu2x_settings['jobs'] = jobs
self.waifu2x_settings['output-format'] = image_format
self.driver_settings['input'] = input_directory
self.driver_settings['output'] = output_directory
self.driver_settings['scale-ratio'] = scale_ratio
self.driver_settings['jobs'] = jobs
self.driver_settings['output-format'] = image_format
# models_rgb must be specified manually for waifu2x-converter-cpp
# if it's not specified in the arguments, create automatically
if self.waifu2x_settings['model-dir'] is None:
self.waifu2x_settings['model-dir'] = pathlib.Path(self.waifu2x_settings['waifu2x_converter_path']) / 'models_rgb'
if self.driver_settings['model-dir'] is None:
self.driver_settings['model-dir'] = pathlib.Path(self.driver_settings['waifu2x_converter_path']) / 'models_rgb'
# print thread start message
self.print_lock.acquire()
@@ -72,15 +63,16 @@ class Waifu2xConverter:
self.print_lock.release()
# list to be executed
execute = []
# initialize the list with waifu2x binary path as the first element
execute = [str(pathlib.Path(self.driver_settings['path']) / 'waifu2x-converter-cpp.exe')]
for key in self.waifu2x_settings.keys():
for key in self.driver_settings.keys():
value = self.waifu2x_settings[key]
value = self.driver_settings[key]
# the key doesn't need to be passed in this case
if key == 'waifu2x_converter_path':
execute.append(pathlib.Path(str(value)) / 'waifu2x-converter-cpp.exe')
if key == 'path':
continue
# null or None means that leave this option out (keep default)
elif value is None or value is False:

94
src/waifu2x_ncnn_vulkan.py Executable file
View File

@@ -0,0 +1,94 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Name: Waifu2x NCNN Vulkan Driver
Creator: SAT3LL
Date Created: June 26, 2019
Last Modified: November 15, 2019
Editor: K4YT3X
Last Modified: January 4, 2020
Description: This class is a high-level wrapper
for waifu2x_ncnn_vulkan.
"""
# built-in imports
import os
import subprocess
import threading
# third-party imports
from avalon_framework import Avalon
class Waifu2xNcnnVulkan:
"""This class communicates with waifu2x ncnn vulkan engine
An object will be created for this class, containing information
about the binary address and the processing method. When being called
by the main program, other detailed information will be passed to
the upscale function.
"""
def __init__(self, driver_settings):
self.driver_settings = driver_settings
# arguments passed through command line overwrites config file values
# waifu2x_ncnn_vulkan can't find its own model directory if its not in the current dir
# so change to it
os.chdir(os.path.join(self.driver_settings['path'], '..'))
self.print_lock = threading.Lock()
def upscale(self, input_directory, output_directory, scale_ratio, upscaler_exceptions):
"""This is the core function for WAIFU2X class
Arguments:
input_directory {string} -- source directory path
output_directory {string} -- output directory path
ratio {int} -- output video ratio
"""
try:
# overwrite config file settings
self.driver_settings['i'] = input_directory
self.driver_settings['o'] = output_directory
self.driver_settings['s'] = int(scale_ratio)
# print thread start message
self.print_lock.acquire()
Avalon.debug_info(f'[upscaler] Thread {threading.current_thread().name} started')
self.print_lock.release()
# list to be executed
# initialize the list with waifu2x binary path as the first element
execute = [str(self.driver_settings['path'])]
for key in self.driver_settings.keys():
value = self.driver_settings[key]
# is executable key or null or None means that leave this option out (keep default)
if key == 'path' or value is None or value is False:
continue
else:
if len(key) == 1:
execute.append(f'-{key}')
else:
execute.append(f'--{key}')
execute.append(str(value))
Avalon.debug_info(f'Executing: {execute}')
completed_command = subprocess.run(execute, check=True)
# print thread exiting message
self.print_lock.acquire()
Avalon.debug_info(f'[upscaler] Thread {threading.current_thread().name} exiting')
self.print_lock.release()
# return command execution return code
return completed_command.returncode
except Exception as e:
upscaler_exceptions.append(e)