mirror of
https://github.com/k4yt3x/video2x.git
synced 2026-02-14 00:54:47 +08:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
146044505b | ||
|
|
a7d41cafdf | ||
|
|
875572c75a | ||
|
|
c7353c4bf2 | ||
|
|
0af68bb39c | ||
|
|
19cb823591 | ||
|
|
6c1a714a1e | ||
|
|
837aca371e | ||
|
|
6538abd6e5 | ||
|
|
a57465e866 | ||
|
|
405c4b6636 | ||
|
|
3d46e1a822 | ||
|
|
7b501f2c67 | ||
|
|
98e2cbdb04 | ||
|
|
25156077f2 |
60
README.md
60
README.md
@@ -2,24 +2,34 @@
|
|||||||
|
|
||||||
### Official Discussion Group (Telegram): https://t.me/video2x
|
### Official Discussion Group (Telegram): https://t.me/video2x
|
||||||
|
|
||||||
|
## Download Builds (beta)
|
||||||
|
|
||||||
|
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` and `waifu2x-converter-cpp`. 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 `FFmpeg`, `waifu2x-caffe` or `waifu2x-converter-cpp` automatically, simply launch `video2x_setup.exe`.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Component names that are **bolded** are mandatory.
|
Component names that are **bolded** can be automatically downloaded and configured with the `video2x_setup.py` script.
|
||||||
|
|
||||||
Component names that are *italicized* can be automatically downloaded and configured with the `video2x_setup.py` script.
|
|
||||||
|
|
||||||
1. Operating System: Windows
|
1. Operating System: Windows
|
||||||
1. AMD GPU / Nvidia GPU
|
2. AMD GPU / Nvidia GPU
|
||||||
1. AMD GPU driver / Nvidia GPU driver / Nvidia CUDNN
|
3. AMD GPU driver / Nvidia GPU driver / Nvidia CUDNN
|
||||||
1. [***FFMPEG***](https://ffmpeg.zeranoe.com/builds/)
|
4. [**FFmpeg**](https://ffmpeg.zeranoe.com/builds/)
|
||||||
1. [***waifu2x-caffe***](https://github.com/lltcggie/waifu2x-caffe/releases) / [***waifu2x-converter-cpp***](https://github.com/DeadSix27/waifu2x-converter-cpp/releases)
|
5. [**waifu2x-caffe**](https://github.com/lltcggie/waifu2x-caffe/releases) / [**waifu2x-converter-cpp**](https://github.com/DeadSix27/waifu2x-converter-cpp/releases)
|
||||||
|
|
||||||
## Recent Changes
|
## Recent Changes
|
||||||
|
|
||||||
|
### 2.7.1 (April 18, 2019)
|
||||||
|
|
||||||
|
- Fixed video2x custom temp folder bug found by @cr08 .
|
||||||
|
|
||||||
### 2.7.0 (March 30, 2019)
|
### 2.7.0 (March 30, 2019)
|
||||||
|
|
||||||
- Added support for different extracted image formats.
|
- Added support for different extracted image formats.
|
||||||
- Redesigned FFMPEG wrapper, FFMPEG settings are now customizable in the `video2x.json` config file.
|
- Redesigned FFmpeg wrapper, FFmpeg settings are now customizable in the `video2x.json` config file.
|
||||||
- Other minor enhancements and adjustments (e.g. argument -> method variable)
|
- Other minor enhancements and adjustments (e.g. argument -> method variable)
|
||||||
|
|
||||||
### Setup Script 1.2.0 (March 26, 2019)
|
### Setup Script 1.2.0 (March 26, 2019)
|
||||||
@@ -27,12 +37,6 @@ Component names that are *italicized* can be automatically downloaded and config
|
|||||||
- `video2x_setup.py` script can now automatically download and configure `waifu2x-converter-cpp`.
|
- `video2x_setup.py` script can now automatically download and configure `waifu2x-converter-cpp`.
|
||||||
- replaced old progress indicator with progress bar.
|
- replaced old progress indicator with progress bar.
|
||||||
|
|
||||||
### 2.6.3 (March 24, 2019)
|
|
||||||
|
|
||||||
- Added image cleaner by @BrianPetkovsek which removes upscaled frames.
|
|
||||||
- Fixed some PEP8 issues.
|
|
||||||
- Exceptions in waifu2x are now caught, and script will now stop on waifu2x error instead of keep going on to FFMPEG.
|
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
Video2X is an automation software based on waifu2x image enlarging engine. It extracts frames from a video, enlarge it by a number of times without losing any details or quality, keeping lines smooth and edges sharp.
|
Video2X is an automation software based on waifu2x image enlarging engine. It extracts frames from a video, enlarge it by a number of times without losing any details or quality, keeping lines smooth and edges sharp.
|
||||||
@@ -55,17 +59,21 @@ Clip is from trailer of animated movie "千と千尋の神隠し". Copyright bel
|
|||||||
|
|
||||||
## Documentations
|
## Documentations
|
||||||
|
|
||||||
### [Video2X Wiki](https://github.com/K4YT3X/video2x/wiki)
|
### [Video2X Wiki](https://github.com/k4yt3x/video2x/wiki)
|
||||||
|
|
||||||
You can find all detailed user-facing and developer-facing documentations in the [Video2X Wiki](https://github.com/K4YT3X/video2x/wiki). It covers everything from step-by-step instructions for beginners, to the code structure of this program for advanced users and developers. If this README page doesn't answer all your questions, the wiki page is where you should head to.
|
You can find all detailed user-facing and developer-facing documentations in the [Video2X Wiki](https://github.com/k4yt3x/video2x/wiki). It covers everything from step-by-step instructions for beginners, to the code structure of this program for advanced users and developers. If this README page doesn't answer all your questions, the wiki page is where you should head to.
|
||||||
|
|
||||||
### [Step-By-Step Tutorial](https://github.com/K4YT3X/video2x/wiki/Step-By-Step-Tutorial) (Nvidia GPUs)
|
### [Step-By-Step Tutorial](https://github.com/k4yt3x/video2x/wiki/Step-By-Step-Tutorial)
|
||||||
|
|
||||||
For those who want a detailed walk-through of how to use `Video2X`, you can head to the [Step-By-Step Tutorial](https://github.com/K4YT3X/video2x/wiki/Step-By-Step-Tutorial) wiki page. It includes almost every step you need to perform in order to enlarge your first video.
|
For those who want a detailed walk-through of how to use `Video2X`, you can head to the [Step-By-Step Tutorial](https://github.com/k4yt3x/video2x/wiki/Step-By-Step-Tutorial) wiki page. It includes almost every step you need to perform in order to enlarge your first video.
|
||||||
|
|
||||||
### [Waifu2X Drivers](https://github.com/K4YT3X/video2x/wiki/Waifu2X-Drivers)
|
### [Waifu2X Drivers](https://github.com/k4yt3x/video2x/wiki/Waifu2X-Drivers)
|
||||||
|
|
||||||
Go to the [Waifu2X Drivers](https://github.com/K4YT3X/video2x/wiki/Waifu2X-Drivers) wiki page if you want to see a detailed description on the different types of `waifu2x` drivers implemented by `Video2X`. This wiki page contains detailed difference between different drivers, and how to download and set each of them up for `Video2X`.
|
Go to the [Waifu2X Drivers](https://github.com/k4yt3x/video2x/wiki/Waifu2X-Drivers) wiki page if you want to see a detailed description on the different types of `waifu2x` drivers implemented by `Video2X`. This wiki page contains detailed difference between different drivers, and how to download and set each of them up for `Video2X`.
|
||||||
|
|
||||||
|
### [Q&A](https://github.com/k4yt3x/video2x/wiki/Q&A)
|
||||||
|
|
||||||
|
If you have any questions, first try visiting our [Q&A](https://github.com/k4yt3x/video2x/wiki/Q&A) page to see if your question is answered there. If not, open an issue and we will respond to your questions ASAP.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -75,7 +83,7 @@ Go to the [Waifu2X Drivers](https://github.com/K4YT3X/video2x/wiki/Waifu2X-Drive
|
|||||||
|
|
||||||
- **Python 3**
|
- **Python 3**
|
||||||
Download: https://www.python.org/downloads/windows/
|
Download: https://www.python.org/downloads/windows/
|
||||||
- **FFMPEG Windows Build**
|
- **FFmpeg Windows Build**
|
||||||
Download: https://ffmpeg.org/download.html
|
Download: https://ffmpeg.org/download.html
|
||||||
- **waifu2x-caffe** (for Nvidia CUDA/CUDNN)
|
- **waifu2x-caffe** (for Nvidia CUDA/CUDNN)
|
||||||
Download: https://github.com/lltcggie/waifu2x-caffe/releases
|
Download: https://github.com/lltcggie/waifu2x-caffe/releases
|
||||||
@@ -87,7 +95,7 @@ Download: https://github.com/DeadSix27/waifu2x-converter-cpp/releases
|
|||||||
First, clone the video2x repository.
|
First, clone the video2x repository.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
git clone https://github.com/K4YT3X/video2x.git
|
git clone https://github.com/k4yt3x/video2x.git
|
||||||
cd video2x/bin
|
cd video2x/bin
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -215,10 +223,16 @@ https://www.gnu.org/licenses/gpl-3.0.txt
|
|||||||
|
|
||||||
This project relies on the following software and projects.
|
This project relies on the following software and projects.
|
||||||
|
|
||||||
- [FFMPEG]('https://www.ffmpeg.org/')
|
- [FFmpeg]('https://www.ffmpeg.org/')
|
||||||
- [waifu2x-caffe](https://github.com/lltcggie/waifu2x-caffe)
|
- [waifu2x-caffe](https://github.com/lltcggie/waifu2x-caffe)
|
||||||
- [waifu2x-converter-cpp](https://github.com/DeadSix27/waifu2x-converter-cpp)
|
- [waifu2x-converter-cpp](https://github.com/DeadSix27/waifu2x-converter-cpp)
|
||||||
|
|
||||||
|
## Special Thanks
|
||||||
|
|
||||||
|
Appreciations given to the following contributors:
|
||||||
|
|
||||||
|
- @BrianPetkovsek
|
||||||
|
|
||||||
## Related Resources
|
## Related Resources
|
||||||
|
|
||||||
- [Dandere2x](https://github.com/CardinalPanda/dandere2x): `Dandere2x` is a lossy video upscaler also built around `waifu2x`, but with video compression techniques to shorten the time needed to process a video.
|
- [Dandere2x](https://github.com/CardinalPanda/dandere2x): `Dandere2x` is a lossy video upscaler also built around `waifu2x`, but with video compression techniques to shorten the time needed to process a video.
|
||||||
121
bin/ffmpeg.py
121
bin/ffmpeg.py
@@ -4,7 +4,7 @@
|
|||||||
Name: FFMPEG Class
|
Name: FFMPEG Class
|
||||||
Author: K4YT3X
|
Author: K4YT3X
|
||||||
Date Created: Feb 24, 2018
|
Date Created: Feb 24, 2018
|
||||||
Last Modified: March 30, 2019
|
Last Modified: May 4, 2019
|
||||||
|
|
||||||
Description: This class handles all FFMPEG related
|
Description: This class handles all FFMPEG related
|
||||||
operations.
|
operations.
|
||||||
@@ -29,10 +29,10 @@ class Ffmpeg:
|
|||||||
# add a forward slash to directory if not present
|
# add a forward slash to directory if not present
|
||||||
# otherwise there will be a format error
|
# otherwise there will be a format error
|
||||||
if self.ffmpeg_path[-1] != '/' and self.ffmpeg_path[-1] != '\\':
|
if self.ffmpeg_path[-1] != '/' and self.ffmpeg_path[-1] != '\\':
|
||||||
self.ffmpeg_path = '{}\\'.format(self.ffmpeg_path)
|
self.ffmpeg_path = f'{self.ffmpeg_path}\\'
|
||||||
|
|
||||||
self.ffmpeg_binary = '{}ffmpeg.exe'.format(self.ffmpeg_path)
|
self.ffmpeg_binary = f'{self.ffmpeg_path}ffmpeg.exe'
|
||||||
self.ffmpeg_probe_binary = '{}ffprobe.exe'.format(self.ffmpeg_path)
|
self.ffmpeg_probe_binary = f'{self.ffmpeg_path}ffprobe.exe'
|
||||||
self.image_format = image_format
|
self.image_format = image_format
|
||||||
|
|
||||||
def get_video_info(self, input_video):
|
def get_video_info(self, input_video):
|
||||||
@@ -59,10 +59,10 @@ class Ffmpeg:
|
|||||||
'-show_format',
|
'-show_format',
|
||||||
'-show_streams',
|
'-show_streams',
|
||||||
'-i',
|
'-i',
|
||||||
'{}'.format(input_video)
|
input_video
|
||||||
]
|
]
|
||||||
|
|
||||||
Avalon.debug_info('Executing: {}'.format(' '.join(execute)))
|
Avalon.debug_info(f'Executing: {" ".join(execute)}')
|
||||||
json_str = subprocess.run(execute, check=True, stdout=subprocess.PIPE).stdout
|
json_str = subprocess.run(execute, check=True, stdout=subprocess.PIPE).stdout
|
||||||
return json.loads(json_str.decode('utf-8'))
|
return json.loads(json_str.decode('utf-8'))
|
||||||
|
|
||||||
@@ -74,15 +74,21 @@ class Ffmpeg:
|
|||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
input_video {string} -- input video path
|
input_video {string} -- input video path
|
||||||
extracted_frames {string} -- video output folder
|
extracted_frames {string} -- video output directory
|
||||||
"""
|
"""
|
||||||
execute = [
|
execute = [
|
||||||
self.ffmpeg_binary,
|
self.ffmpeg_binary
|
||||||
|
]
|
||||||
|
|
||||||
|
execute.extend(self._read_configuration(phase='video_to_frames'))
|
||||||
|
|
||||||
|
execute.extend([
|
||||||
'-i',
|
'-i',
|
||||||
input_video,
|
input_video,
|
||||||
'{}\\extracted_%0d.{}'.format(extracted_frames, self.image_format)
|
f'{extracted_frames}\\extracted_%0d.{self.image_format}'
|
||||||
]
|
])
|
||||||
self._execute(execute=execute, phase='video_to_frames')
|
|
||||||
|
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
|
||||||
@@ -93,19 +99,37 @@ class Ffmpeg:
|
|||||||
Arguments:
|
Arguments:
|
||||||
framerate {float} -- target video framerate
|
framerate {float} -- target video framerate
|
||||||
resolution {string} -- target video resolution
|
resolution {string} -- target video resolution
|
||||||
upscaled_frames {string} -- source images folder
|
upscaled_frames {string} -- source images directory
|
||||||
"""
|
"""
|
||||||
execute = [
|
execute = [
|
||||||
self.ffmpeg_binary,
|
self.ffmpeg_binary,
|
||||||
'-r',
|
'-r',
|
||||||
str(framerate),
|
str(framerate),
|
||||||
'-s',
|
'-s',
|
||||||
resolution,
|
resolution
|
||||||
'-i',
|
|
||||||
'{}\\extracted_%d.{}'.format(upscaled_frames, self.image_format),
|
|
||||||
'{}\\no_audio.mp4'.format(upscaled_frames)
|
|
||||||
]
|
]
|
||||||
self._execute(execute=execute, phase='frames_to_video')
|
|
||||||
|
# read FFmpeg input options
|
||||||
|
execute.extend(self._read_configuration(phase='frames_to_video', section='input_options'))
|
||||||
|
|
||||||
|
# append input frames path into command
|
||||||
|
execute.extend([
|
||||||
|
'-i',
|
||||||
|
f'{upscaled_frames}\\extracted_%d.{self.image_format}'
|
||||||
|
])
|
||||||
|
|
||||||
|
# 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([
|
||||||
|
f'{upscaled_frames}\\no_audio.mp4'
|
||||||
|
])
|
||||||
|
|
||||||
|
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
|
||||||
@@ -118,30 +142,71 @@ class Ffmpeg:
|
|||||||
execute = [
|
execute = [
|
||||||
self.ffmpeg_binary,
|
self.ffmpeg_binary,
|
||||||
'-i',
|
'-i',
|
||||||
'{}\\no_audio.mp4'.format(upscaled_frames),
|
f'{upscaled_frames}\\no_audio.mp4',
|
||||||
'-i',
|
'-i',
|
||||||
input_video,
|
input_video
|
||||||
output_video
|
|
||||||
]
|
]
|
||||||
self._execute(execute=execute, phase='migrating_tracks')
|
|
||||||
|
|
||||||
def _execute(self, execute, phase):
|
execute.extend(self._read_configuration(phase='frames_to_video', section='output_options'))
|
||||||
|
|
||||||
for key in self.ffmpeg_settings[phase].keys():
|
execute.extend([
|
||||||
|
output_video
|
||||||
|
])
|
||||||
|
|
||||||
value = self.ffmpeg_settings[phase][key]
|
execute.extend(self._read_configuration(phase='migrating_tracks'))
|
||||||
|
|
||||||
|
self._execute(execute)
|
||||||
|
|
||||||
|
def _read_configuration(self, phase, section=None):
|
||||||
|
""" read configuration from JSON
|
||||||
|
|
||||||
|
Read the configurations (arguments) from the JSON
|
||||||
|
configuration file and append them to the end of the
|
||||||
|
FFmpeg command.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
execute {list} -- list of arguments to be executed
|
||||||
|
phase {str} -- phase of operation
|
||||||
|
"""
|
||||||
|
|
||||||
|
configuration = []
|
||||||
|
|
||||||
|
# if section is specified, read configurations or keys
|
||||||
|
# from only that section
|
||||||
|
if section:
|
||||||
|
source = self.ffmpeg_settings[phase][section].keys()
|
||||||
|
else:
|
||||||
|
source = self.ffmpeg_settings[phase].keys()
|
||||||
|
|
||||||
|
for key in source:
|
||||||
|
|
||||||
|
if section:
|
||||||
|
value = self.ffmpeg_settings[phase][section][key]
|
||||||
|
else:
|
||||||
|
value = self.ffmpeg_settings[phase][key]
|
||||||
|
|
||||||
# null or None means that leave this option out (keep default)
|
# null or None means that leave this option out (keep default)
|
||||||
if value is None or value is False:
|
if value is None or value is False or isinstance(value, list) or isinstance(value, dict):
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
execute.append(key)
|
configuration.append(key)
|
||||||
|
|
||||||
# true means key is an option
|
# true means key is an option
|
||||||
if value is True:
|
if value is True:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
execute.append(str(value))
|
configuration.append(str(value))
|
||||||
|
|
||||||
Avalon.debug_info('Executing: {}'.format(execute))
|
return configuration
|
||||||
|
|
||||||
|
def _execute(self, execute):
|
||||||
|
""" execute command
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
execute {list} -- list of arguments to be executed
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int -- execution return code
|
||||||
|
"""
|
||||||
|
Avalon.debug_info(f'Executing: {execute}')
|
||||||
return subprocess.run(execute, shell=True, check=True).returncode
|
return subprocess.run(execute, shell=True, check=True).returncode
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Name: Video2X Image Cleaner
|
|||||||
Author: BrianPetkovsek
|
Author: BrianPetkovsek
|
||||||
Author: K4YT3X
|
Author: K4YT3X
|
||||||
Date Created: March 24, 2019
|
Date Created: March 24, 2019
|
||||||
Last Modified: March 24, 2019
|
Last Modified: April 28, 2019
|
||||||
|
|
||||||
Description: This class is to remove the extracted frames
|
Description: This class is to remove the extracted frames
|
||||||
that have already been upscaled.
|
that have already been upscaled.
|
||||||
@@ -27,11 +27,11 @@ class ImageCleaner(threading.Thread):
|
|||||||
threading.Thread
|
threading.Thread
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, input_folder, output_folder, num_threads):
|
def __init__(self, input_directory, output_directory, threads):
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
self.input_folder = input_folder
|
self.input_directory = input_directory
|
||||||
self.output_folder = output_folder
|
self.output_directory = output_directory
|
||||||
self.num_threads = num_threads
|
self.threads = threads
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@@ -53,23 +53,23 @@ class ImageCleaner(threading.Thread):
|
|||||||
""" remove frames that have already been upscaled
|
""" remove frames that have already been upscaled
|
||||||
|
|
||||||
This method compares the files in the extracted frames
|
This method compares the files in the extracted frames
|
||||||
folder with the upscaled frames folder, and removes
|
directory with the upscaled frames directory, and removes
|
||||||
the frames that has already been upscaled.
|
the frames that has already been upscaled.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# list all images in the extracted frames
|
# list all images in the extracted frames
|
||||||
output_frames = [f for f in os.listdir(self.output_folder) if os.path.isfile(os.path.join(self.output_folder, f))]
|
output_frames = [f for f in os.listdir(self.output_directory) if os.path.isfile(os.path.join(self.output_directory, f))]
|
||||||
|
|
||||||
# compare and remove frames downscaled images that finished being upscaled
|
# compare and remove frames downscaled images that finished being upscaled
|
||||||
# within each thread's extracted frames folder
|
# within each thread's extracted frames directory
|
||||||
for i in range(self.num_threads):
|
for i in range(self.threads):
|
||||||
dir_path = os.path.join(self.input_folder, str(i))
|
dir_path = os.path.join(self.input_directory, str(i))
|
||||||
|
|
||||||
# for each file within all the folders
|
# for each file within all the directories
|
||||||
for f in os.listdir(dir_path):
|
for f in os.listdir(dir_path):
|
||||||
file_path = os.path.join(dir_path, f)
|
file_path = os.path.join(dir_path, f)
|
||||||
|
|
||||||
# if file also exists in the output folder, then the file
|
# if file also exists in the output directory, then the file
|
||||||
# has already been processed, thus not needed anymore
|
# has already been processed, thus not needed anymore
|
||||||
if os.path.isfile(file_path) and f in output_frames:
|
if os.path.isfile(file_path) and f in output_frames:
|
||||||
os.remove(file_path)
|
os.remove(file_path)
|
||||||
|
|||||||
@@ -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: March 30, 2019
|
Last Modified: April 28, 2019
|
||||||
|
|
||||||
Licensed under the GNU General Public License Version 3 (GNU GPL v3),
|
Licensed under the GNU General Public License Version 3 (GNU GPL v3),
|
||||||
available at: https://www.gnu.org/licenses/gpl-3.0.txt
|
available at: https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
@@ -30,7 +30,7 @@ import time
|
|||||||
|
|
||||||
class Upscaler:
|
class Upscaler:
|
||||||
""" An instance of this class is a upscaler that will
|
""" An instance of this class is a upscaler that will
|
||||||
upscale all images in the given folder.
|
upscale all images in the given directory.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
Exception -- all exceptions
|
Exception -- all exceptions
|
||||||
@@ -52,25 +52,27 @@ class Upscaler:
|
|||||||
self.scale_ratio = None
|
self.scale_ratio = None
|
||||||
self.model_dir = None
|
self.model_dir = None
|
||||||
self.threads = 5
|
self.threads = 5
|
||||||
self.video2x_cache_folder = '{}\\video2x'.format(tempfile.gettempdir())
|
self.video2x_cache_directory = f'{tempfile.gettempdir()}\\video2x'
|
||||||
self.image_format = 'png'
|
self.image_format = 'png'
|
||||||
self.preserve_frames = False
|
self.preserve_frames = False
|
||||||
|
|
||||||
# create temporary folder/directories
|
def create_temp_directories(self):
|
||||||
self.extracted_frames = tempfile.mkdtemp(dir=self.video2x_cache_folder)
|
"""create temporary directory
|
||||||
Avalon.debug_info('Extracted frames are being saved to: {}'.format(self.extracted_frames))
|
"""
|
||||||
self.upscaled_frames = tempfile.mkdtemp(dir=self.video2x_cache_folder)
|
self.extracted_frames = tempfile.mkdtemp(dir=self.video2x_cache_directory)
|
||||||
Avalon.debug_info('Upscaled frames are being saved to: {}'.format(self.upscaled_frames))
|
Avalon.debug_info(f'Extracted frames are being saved to: {self.extracted_frames}')
|
||||||
|
self.upscaled_frames = tempfile.mkdtemp(dir=self.video2x_cache_directory)
|
||||||
|
Avalon.debug_info(f'Upscaled frames are being saved to: {self.upscaled_frames}')
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup_temp_directories(self):
|
||||||
# delete temp directories when done
|
"""delete temp directories when done
|
||||||
# avalon framework cannot be used if python is shutting down
|
"""
|
||||||
# therefore, plain print is used
|
|
||||||
if not self.preserve_frames:
|
if not self.preserve_frames:
|
||||||
|
|
||||||
for directory in [self.extracted_frames, self.upscaled_frames]:
|
for directory in [self.extracted_frames, self.upscaled_frames]:
|
||||||
try:
|
try:
|
||||||
print('Cleaning up cache directory: {}'.format(directory))
|
# avalon framework cannot be used if python is shutting down
|
||||||
|
# therefore, plain print is used
|
||||||
|
print(f'Cleaning up cache directory: {directory}')
|
||||||
shutil.rmtree(directory)
|
shutil.rmtree(directory)
|
||||||
except (OSError, FileNotFoundError):
|
except (OSError, FileNotFoundError):
|
||||||
pass
|
pass
|
||||||
@@ -87,18 +89,18 @@ class Upscaler:
|
|||||||
elif not self.method:
|
elif not self.method:
|
||||||
raise ArgumentError('You need to specify the enlarging processing unit')
|
raise ArgumentError('You need to specify the enlarging processing unit')
|
||||||
|
|
||||||
def _progress_bar(self, extracted_frames_folders):
|
def _progress_bar(self, extracted_frames_directories):
|
||||||
""" This method prints a progress bar
|
""" This method prints a progress bar
|
||||||
|
|
||||||
This method prints a progress bar by keeping track
|
This method prints a progress bar by keeping track
|
||||||
of the amount of frames in the input directory/folder
|
of the amount of frames in the input directory
|
||||||
and the output directory/folder. This is originally
|
and the output directory. This is originally
|
||||||
suggested by @ArmandBernard.
|
suggested by @ArmandBernard.
|
||||||
"""
|
"""
|
||||||
# get number of extracted frames
|
# get number of extracted frames
|
||||||
total_frames = 0
|
total_frames = 0
|
||||||
for folder in extracted_frames_folders:
|
for directory in extracted_frames_directories:
|
||||||
total_frames += len([f for f in os.listdir(folder) if f[-4:] == '.png'])
|
total_frames += len([f for f in os.listdir(directory) if f[-4:] == '.png'])
|
||||||
|
|
||||||
with tqdm(total=total_frames, ascii=True, desc='Upscaling Progress') as progress_bar:
|
with tqdm(total=total_frames, ascii=True, desc='Upscaling Progress') as progress_bar:
|
||||||
|
|
||||||
@@ -150,8 +152,8 @@ class Upscaler:
|
|||||||
|
|
||||||
w2.upscale(self.extracted_frames, self.upscaled_frames, self.scale_ratio, self.threads, self.image_format, self.upscaler_exceptions)
|
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 os.listdir(self.upscaled_frames) if os.path.isfile(os.path.join(self.upscaled_frames, f))]:
|
for image in [f for f in os.listdir(self.upscaled_frames) if os.path.isfile(os.path.join(self.upscaled_frames, f))]:
|
||||||
renamed = re.sub('_\[.*-.*\]\[x(\d+(\.\d+)?)\]\.{}'.format(self.image_format), '.{}'.format(self.image_format), image)
|
renamed = re.sub(f'_\[.*-.*\]\[x(\d+(\.\d+)?)\]\.{self.image_format}', f'.{self.image_format}', image)
|
||||||
shutil.move('{}\\{}'.format(self.upscaled_frames, image), '{}\\{}'.format(self.upscaled_frames, renamed))
|
shutil.move(f'{self.upscaled_frames}\\{image}', f'{self.upscaled_frames}\\{renamed}')
|
||||||
|
|
||||||
self.progress_bar_exit_signal = True
|
self.progress_bar_exit_signal = True
|
||||||
progress_bar.join()
|
progress_bar.join()
|
||||||
@@ -168,25 +170,25 @@ class Upscaler:
|
|||||||
if len(frames) < self.threads:
|
if len(frames) < self.threads:
|
||||||
self.threads = len(frames)
|
self.threads = len(frames)
|
||||||
|
|
||||||
# create a folder for each thread and append folder
|
# create a directory for each thread and append directory
|
||||||
# name into a list
|
# name into a list
|
||||||
|
|
||||||
thread_pool = []
|
thread_pool = []
|
||||||
thread_folders = []
|
thread_directories = []
|
||||||
for thread_id in range(self.threads):
|
for thread_id in range(self.threads):
|
||||||
thread_folder = '{}\\{}'.format(self.extracted_frames, str(thread_id))
|
thread_directory = f'{self.extracted_frames}\\{str(thread_id)}'
|
||||||
thread_folders.append(thread_folder)
|
thread_directories.append(thread_directory)
|
||||||
|
|
||||||
# delete old folders and create new folders
|
# delete old directories and create new directories
|
||||||
if os.path.isdir(thread_folder):
|
if os.path.isdir(thread_directory):
|
||||||
shutil.rmtree(thread_folder)
|
shutil.rmtree(thread_directory)
|
||||||
os.mkdir(thread_folder)
|
os.mkdir(thread_directory)
|
||||||
|
|
||||||
# append folder path into list
|
# append directory path into list
|
||||||
thread_pool.append((thread_folder, thread_id))
|
thread_pool.append((thread_directory, thread_id))
|
||||||
|
|
||||||
# evenly distribute images into each folder
|
# evenly distribute images into each directory
|
||||||
# until there is none left in the folder
|
# until there is none left in the directory
|
||||||
for image in frames:
|
for image in frames:
|
||||||
# move image
|
# move image
|
||||||
shutil.move(image, thread_pool[0][0])
|
shutil.move(image, thread_pool[0][0])
|
||||||
@@ -206,7 +208,7 @@ class Upscaler:
|
|||||||
upscaler_threads.append(thread)
|
upscaler_threads.append(thread)
|
||||||
|
|
||||||
# start progress bar in a different thread
|
# start progress bar in a different thread
|
||||||
progress_bar = threading.Thread(target=self._progress_bar, args=(thread_folders,))
|
progress_bar = threading.Thread(target=self._progress_bar, args=(thread_directories,))
|
||||||
progress_bar.start()
|
progress_bar.start()
|
||||||
|
|
||||||
# create the clearer and start it
|
# create the clearer and start it
|
||||||
@@ -255,7 +257,7 @@ class Upscaler:
|
|||||||
elif self.waifu2x_driver == 'waifu2x_converter':
|
elif self.waifu2x_driver == 'waifu2x_converter':
|
||||||
w2 = Waifu2xConverter(self.waifu2x_settings, self.model_dir)
|
w2 = Waifu2xConverter(self.waifu2x_settings, self.model_dir)
|
||||||
else:
|
else:
|
||||||
raise Exception('Unrecognized waifu2x driver: {}'.format(self.waifu2x_driver))
|
raise Exception(f'Unrecognized waifu2x driver: {self.waifu2x_driver}')
|
||||||
|
|
||||||
# extract frames from video
|
# extract frames from video
|
||||||
fm.extract_frames(self.input_video, self.extracted_frames)
|
fm.extract_frames(self.input_video, self.extracted_frames)
|
||||||
@@ -279,14 +281,14 @@ class Upscaler:
|
|||||||
|
|
||||||
# get average frame rate of video stream
|
# get average frame rate of video stream
|
||||||
framerate = float(Fraction(video_info['streams'][video_stream_index]['avg_frame_rate']))
|
framerate = float(Fraction(video_info['streams'][video_stream_index]['avg_frame_rate']))
|
||||||
Avalon.info('Framerate: {}'.format(framerate))
|
Avalon.info(f'Framerate: {framerate}')
|
||||||
|
|
||||||
# width/height will be coded width/height x upscale factor
|
# width/height will be coded width/height x upscale factor
|
||||||
if self.scale_ratio:
|
if self.scale_ratio:
|
||||||
coded_width = video_info['streams'][video_stream_index]['coded_width']
|
original_width = video_info['streams'][video_stream_index]['width']
|
||||||
coded_height = video_info['streams'][video_stream_index]['coded_height']
|
original_height = video_info['streams'][video_stream_index]['height']
|
||||||
self.scale_width = int(self.scale_ratio * coded_width)
|
self.scale_width = int(self.scale_ratio * original_width)
|
||||||
self.scale_height = int(self.scale_ratio * coded_height)
|
self.scale_height = int(self.scale_ratio * original_height)
|
||||||
|
|
||||||
# upscale images one by one using waifu2x
|
# upscale images one by one using waifu2x
|
||||||
Avalon.info('Starting to upscale extracted images')
|
Avalon.info('Starting to upscale extracted images')
|
||||||
@@ -297,7 +299,7 @@ class Upscaler:
|
|||||||
Avalon.info('Converting extracted frames into video')
|
Avalon.info('Converting extracted frames into video')
|
||||||
|
|
||||||
# use user defined output size
|
# use user defined output size
|
||||||
fm.convert_video(framerate, '{}x{}'.format(self.scale_width, self.scale_height), self.upscaled_frames)
|
fm.convert_video(framerate, f'{self.scale_width}x{self.scale_height}', self.upscaled_frames)
|
||||||
Avalon.info('Conversion completed')
|
Avalon.info('Conversion completed')
|
||||||
|
|
||||||
# migrate audio tracks and subtitles
|
# migrate audio tracks and subtitles
|
||||||
|
|||||||
@@ -47,26 +47,32 @@
|
|||||||
"-y": true
|
"-y": true
|
||||||
},
|
},
|
||||||
"frames_to_video": {
|
"frames_to_video": {
|
||||||
"-qscale:v": null,
|
"input_options":{
|
||||||
"-qscale:a": null,
|
"-qscale:v": null,
|
||||||
"-f": "image2",
|
"-qscale:a": null,
|
||||||
"-vcodec": "libx264",
|
"-f": "image2"
|
||||||
"-crf": 25,
|
},
|
||||||
"-pix_fmt": "yuv420p",
|
"output_options":{
|
||||||
"-hwaccel": "auto",
|
"-vcodec": "libx264",
|
||||||
|
"-crf": 17,
|
||||||
|
"-b:v": null,
|
||||||
|
"-pix_fmt": "yuv420p"
|
||||||
|
},
|
||||||
"-y": true
|
"-y": true
|
||||||
},
|
},
|
||||||
"migrating_tracks": {
|
"migrating_tracks": {
|
||||||
"-map": "0:v:0?",
|
"output_options":{
|
||||||
"-map": "1?",
|
"-map": "0:v:0?",
|
||||||
"-c": "copy",
|
"-map": "1?",
|
||||||
"-map": "-1:v?",
|
"-c": "copy",
|
||||||
"-hwaccel": "auto",
|
"-map": "-1:v?",
|
||||||
|
"-pix_fmt": "yuv420p"
|
||||||
|
},
|
||||||
"-y": true
|
"-y": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"video2x": {
|
"video2x": {
|
||||||
"video2x_cache_folder": null,
|
"video2x_cache_directory": null,
|
||||||
"image_format": "png",
|
"image_format": "png",
|
||||||
"preserve_frames": false
|
"preserve_frames": false
|
||||||
}
|
}
|
||||||
|
|||||||
129
bin/video2x.py
129
bin/video2x.py
@@ -13,7 +13,7 @@ __ __ _ _ ___ __ __
|
|||||||
Name: Video2X Controller
|
Name: Video2X Controller
|
||||||
Author: K4YT3X
|
Author: K4YT3X
|
||||||
Date Created: Feb 24, 2018
|
Date Created: Feb 24, 2018
|
||||||
Last Modified: March 30, 2019
|
Last Modified: April 28, 2019
|
||||||
|
|
||||||
Licensed under the GNU General Public License Version 3 (GNU GPL v3),
|
Licensed under the GNU General Public License Version 3 (GNU GPL v3),
|
||||||
available at: https://www.gnu.org/licenses/gpl-3.0.txt
|
available at: https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
@@ -45,13 +45,14 @@ import GPUtil
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import psutil
|
import psutil
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
VERSION = '2.7.0'
|
VERSION = '2.7.1'
|
||||||
|
|
||||||
# each thread might take up to 2.5 GB during initialization.
|
# each thread might take up to 2.5 GB during initialization.
|
||||||
# (system memory, not to be confused with GPU memory)
|
# (system memory, not to be confused with GPU memory)
|
||||||
@@ -77,9 +78,9 @@ def process_arguments():
|
|||||||
upscaler_options = parser.add_argument_group('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'], required=True)
|
upscaler_options.add_argument('-m', '--method', help='Upscaling method', action='store', default='gpu', choices=['cpu', 'gpu', 'cudnn'], required=True)
|
||||||
upscaler_options.add_argument('-d', '--driver', help='Waifu2x driver', action='store', default='waifu2x_caffe', choices=['waifu2x_caffe', 'waifu2x_converter'])
|
upscaler_options.add_argument('-d', '--driver', help='Waifu2x driver', action='store', default='waifu2x_caffe', choices=['waifu2x_caffe', 'waifu2x_converter'])
|
||||||
upscaler_options.add_argument('-y', '--model_dir', help='Folder containing model JSON files', action='store')
|
upscaler_options.add_argument('-y', '--model_dir', 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=5)
|
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', help='Video2X config file location', action='store', default='{}\\video2x.json'.format(os.path.dirname(os.path.abspath(sys.argv[0]))))
|
upscaler_options.add_argument('-c', '--config', help='Video2X config file location', action='store', default=f'{os.path.dirname(os.path.abspath(sys.argv[0]))}\\video2x.json')
|
||||||
upscaler_options.add_argument('-b', '--batch', help='Enable batch mode (select all default values to questions)', action='store_true')
|
upscaler_options.add_argument('-b', '--batch', help='Enable batch mode (select all default values to questions)', action='store_true')
|
||||||
|
|
||||||
# scaling options
|
# scaling options
|
||||||
@@ -100,8 +101,8 @@ def print_logo():
|
|||||||
print(' \\ / | | | (_| | | __/ | (_) | / /_ / . \\')
|
print(' \\ / | | | (_| | | __/ | (_) | / /_ / . \\')
|
||||||
print(' \\/ |_| \\__,_| \\___| \\___/ |____| /_/ \\_\\')
|
print(' \\/ |_| \\__,_| \\___| \\___/ |____| /_/ \\_\\')
|
||||||
print('\n Video2X Video Enlarger')
|
print('\n Video2X Video Enlarger')
|
||||||
spaces = ((44 - len("Version {}".format(VERSION))) // 2) * " "
|
spaces = ((44 - len(f'Version {VERSION}')) // 2) * ' '
|
||||||
print('{}\n{} Version {}\n{}'.format(Avalon.FM.BD, spaces, VERSION, Avalon.FM.RST))
|
print(f'{Avalon.FM.BD}\n{spaces} Version {VERSION}\n{Avalon.FM.RST}')
|
||||||
|
|
||||||
|
|
||||||
def check_memory():
|
def check_memory():
|
||||||
@@ -140,17 +141,17 @@ def check_memory():
|
|||||||
|
|
||||||
# if user doesn't even have enough memory to run even one thread
|
# if user doesn't even have enough memory to run even one thread
|
||||||
if memory_available < mem_per_thread:
|
if memory_available < mem_per_thread:
|
||||||
Avalon.warning('You might have insufficient amount of {} memory available to run this program ({} GB)'.format(memory_type, memory_available))
|
Avalon.warning(f'You might have insufficient amount of {memory_type} memory available to run this program ({memory_available} GB)')
|
||||||
Avalon.warning('Proceed with caution')
|
Avalon.warning('Proceed with caution')
|
||||||
if args.threads > 1:
|
if args.threads > 1:
|
||||||
if Avalon.ask('Reduce number of threads to avoid crashing?', default=True, batch=args.batch):
|
if Avalon.ask('Reduce number of threads to avoid crashing?', default=True, batch=args.batch):
|
||||||
args.threads = 1
|
args.threads = 1
|
||||||
# if memory available is less than needed, warn the user
|
# if memory available is less than needed, warn the user
|
||||||
elif memory_available < (mem_per_thread * args.threads):
|
elif memory_available < (mem_per_thread * args.threads):
|
||||||
Avalon.warning('Each waifu2x-caffe thread will require up to 2.5 GB of system memory')
|
Avalon.warning(f'Each waifu2x-caffe thread will require up to {SYS_MEM_PER_THREAD} GB of system memory')
|
||||||
Avalon.warning('You demanded {} threads to be created, but you only have {} GB {} memory available'.format(args.threads, round(memory_available, 4), memory_type))
|
Avalon.warning(f'You demanded {args.threads} threads to be created, but you only have {round(memory_available, 4)} GB {memory_type} memory available')
|
||||||
Avalon.warning('{} GB of {} memory is recommended for {} threads'.format(mem_per_thread * args.threads, memory_type, args.threads))
|
Avalon.warning(f'{mem_per_thread * args.threads} GB of {memory_type} memory is recommended for {args.threads} threads')
|
||||||
Avalon.warning('With your current amount of {} memory available, {} threads is recommended'.format(memory_type, int(memory_available // mem_per_thread)))
|
Avalon.warning(f'With your current amount of {memory_type} memory available, {int(memory_available // mem_per_thread)} threads is recommended')
|
||||||
|
|
||||||
# ask the user if he / she wants to change to the recommended
|
# ask the user if he / she wants to change to the recommended
|
||||||
# number of threads
|
# number of threads
|
||||||
@@ -170,12 +171,46 @@ def read_config(config_file):
|
|||||||
return 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 = os.path.dirname(os.path.abspath(sys.argv[0]))
|
||||||
|
|
||||||
|
# check waifu2x-caffe path
|
||||||
|
if not re.match('^[a-z]:', config['waifu2x_caffe']['waifu2x_caffe_path'], re.IGNORECASE):
|
||||||
|
config['waifu2x_caffe']['waifu2x_caffe_path'] = f'{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'] = f'{current_directory}\\{config["waifu2x_converter"]["waifu2x_converter_path"]}'
|
||||||
|
|
||||||
|
# check ffmpeg path
|
||||||
|
if not re.match('^[a-z]:', config['ffmpeg']['ffmpeg_path'], re.IGNORECASE):
|
||||||
|
config['ffmpeg']['ffmpeg_path'] = f'{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'] = f'{current_directory}\\{config["video2x"]["video2x_cache_directory"]}'
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
# /////////////////// Execution /////////////////// #
|
# /////////////////// Execution /////////////////// #
|
||||||
|
|
||||||
# this is not a library
|
# this is not a library
|
||||||
if __name__ != '__main__':
|
if __name__ != '__main__':
|
||||||
Avalon.error('This file cannot be imported')
|
Avalon.error('This file cannot be imported')
|
||||||
raise ImportError('{} cannot be imported'.format(__file__))
|
raise ImportError(f'{__file__} cannot be imported')
|
||||||
|
|
||||||
print_logo()
|
print_logo()
|
||||||
|
|
||||||
@@ -198,6 +233,7 @@ check_memory()
|
|||||||
|
|
||||||
# read configurations from JSON
|
# read configurations from JSON
|
||||||
config = read_config(args.config)
|
config = read_config(args.config)
|
||||||
|
config = absolutify_paths(config)
|
||||||
|
|
||||||
# load waifu2x configuration
|
# load waifu2x configuration
|
||||||
if args.driver == 'waifu2x_caffe':
|
if args.driver == 'waifu2x_caffe':
|
||||||
@@ -220,26 +256,26 @@ elif args.driver == 'waifu2x_converter':
|
|||||||
ffmpeg_settings = config['ffmpeg']
|
ffmpeg_settings = config['ffmpeg']
|
||||||
|
|
||||||
# load video2x settings
|
# load video2x settings
|
||||||
video2x_cache_folder = config['video2x']['video2x_cache_folder']
|
video2x_cache_directory = config['video2x']['video2x_cache_directory']
|
||||||
image_format = config['video2x']['image_format'].lower()
|
image_format = config['video2x']['image_format'].lower()
|
||||||
preserve_frames = config['video2x']['preserve_frames']
|
preserve_frames = config['video2x']['preserve_frames']
|
||||||
|
|
||||||
# create temp directories if they don't exist
|
# create temp directories if they don't exist
|
||||||
if not video2x_cache_folder:
|
if not video2x_cache_directory:
|
||||||
video2x_cache_folder = '{}\\video2x'.format(tempfile.gettempdir())
|
video2x_cache_directory = f'{tempfile.gettempdir()}\\video2x'
|
||||||
|
|
||||||
if video2x_cache_folder and not os.path.isdir(video2x_cache_folder):
|
if video2x_cache_directory and not os.path.isdir(video2x_cache_directory):
|
||||||
if not os.path.isfile(video2x_cache_folder) and not os.path.islink(video2x_cache_folder):
|
if not os.path.isfile(video2x_cache_directory) and not os.path.islink(video2x_cache_directory):
|
||||||
Avalon.warning('Specified cache folder/directory {} does not exist'.format(video2x_cache_folder))
|
Avalon.warning(f'Specified cache directory {video2x_cache_directory} does not exist')
|
||||||
if Avalon.ask('Create folder/directory?', default=True, batch=args.batch):
|
if Avalon.ask('Create directory?', default=True, batch=args.batch):
|
||||||
if os.mkdir(video2x_cache_folder) is None:
|
if os.mkdir(video2x_cache_directory) is None:
|
||||||
Avalon.info('{} created'.format(video2x_cache_folder))
|
Avalon.info(f'{video2x_cache_directory} created')
|
||||||
else:
|
else:
|
||||||
Avalon.error('Unable to create {}'.format(video2x_cache_folder))
|
Avalon.error(f'Unable to create {video2x_cache_directory}')
|
||||||
Avalon.error('Aborting...')
|
Avalon.error('Aborting...')
|
||||||
exit(1)
|
exit(1)
|
||||||
else:
|
else:
|
||||||
Avalon.error('Specified cache folder/directory is a file/link')
|
Avalon.error('Specified cache directory is a file/link')
|
||||||
Avalon.error('Unable to continue, exiting...')
|
Avalon.error('Unable to continue, exiting...')
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
@@ -249,9 +285,21 @@ try:
|
|||||||
# start timer
|
# start timer
|
||||||
begin_time = time.time()
|
begin_time = time.time()
|
||||||
|
|
||||||
|
# if input specified is a single file
|
||||||
if os.path.isfile(args.input):
|
if os.path.isfile(args.input):
|
||||||
""" Upscale single video file """
|
""" Upscale single video file """
|
||||||
Avalon.info('Upscaling single video file: {}'.format(args.input))
|
Avalon.info(f'Upscaling single video file: {args.input}')
|
||||||
|
|
||||||
|
# check for input output format mismatch
|
||||||
|
if os.path.isdir(args.output):
|
||||||
|
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('.*\..*$', 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, waifu2x_settings=waifu2x_settings, ffmpeg_settings=ffmpeg_settings)
|
||||||
|
|
||||||
# set optional options
|
# set optional options
|
||||||
@@ -261,18 +309,21 @@ try:
|
|||||||
upscaler.scale_ratio = args.ratio
|
upscaler.scale_ratio = args.ratio
|
||||||
upscaler.model_dir = args.model_dir
|
upscaler.model_dir = args.model_dir
|
||||||
upscaler.threads = args.threads
|
upscaler.threads = args.threads
|
||||||
upscaler.video2x_cache_folder = video2x_cache_folder
|
upscaler.video2x_cache_directory = video2x_cache_directory
|
||||||
upscaler.image_format = image_format
|
upscaler.image_format = image_format
|
||||||
upscaler.preserve_frames = preserve_frames
|
upscaler.preserve_frames = preserve_frames
|
||||||
|
|
||||||
# run upscaler-
|
# run upscaler
|
||||||
|
upscaler.create_temp_directories()
|
||||||
upscaler.run()
|
upscaler.run()
|
||||||
upscaler.cleanup()
|
upscaler.cleanup_temp_directories()
|
||||||
|
|
||||||
|
# if input specified is a directory
|
||||||
elif os.path.isdir(args.input):
|
elif os.path.isdir(args.input):
|
||||||
""" Upscale videos in a folder/directory """
|
""" Upscale videos in a directory """
|
||||||
Avalon.info('Upscaling videos in folder/directory: {}'.format(args.input))
|
Avalon.info(f'Upscaling videos in directory: {args.input}')
|
||||||
for input_video in [f for f in os.listdir(args.input) if os.path.isfile(os.path.join(args.input, f))]:
|
for input_video in [f for f in os.listdir(args.input) if os.path.isfile(os.path.join(args.input, f))]:
|
||||||
output_video = '{}\\{}'.format(args.output, input_video)
|
output_video = f'{args.output}\\{input_video}'
|
||||||
upscaler = Upscaler(input_video=os.path.join(args.input, input_video), output_video=output_video, method=args.method, waifu2x_settings=waifu2x_settings, ffmpeg_settings=ffmpeg_settings)
|
upscaler = Upscaler(input_video=os.path.join(args.input, input_video), output_video=output_video, method=args.method, waifu2x_settings=waifu2x_settings, ffmpeg_settings=ffmpeg_settings)
|
||||||
|
|
||||||
# set optional options
|
# set optional options
|
||||||
@@ -282,26 +333,26 @@ try:
|
|||||||
upscaler.scale_ratio = args.ratio
|
upscaler.scale_ratio = args.ratio
|
||||||
upscaler.model_dir = args.model_dir
|
upscaler.model_dir = args.model_dir
|
||||||
upscaler.threads = args.threads
|
upscaler.threads = args.threads
|
||||||
upscaler.video2x_cache_folder = video2x_cache_folder
|
upscaler.video2x_cache_directory = video2x_cache_directory
|
||||||
upscaler.image_format = image_format
|
upscaler.image_format = image_format
|
||||||
upscaler.preserve_frames = preserve_frames
|
upscaler.preserve_frames = preserve_frames
|
||||||
|
|
||||||
# run upscaler
|
# run upscaler
|
||||||
|
upscaler.create_temp_directories()
|
||||||
upscaler.run()
|
upscaler.run()
|
||||||
upscaler.cleanup()
|
upscaler.cleanup_temp_directories()
|
||||||
else:
|
else:
|
||||||
Avalon.error('Input path is neither a file nor a folder/directory')
|
Avalon.error('Input path is neither a file nor a directory')
|
||||||
raise FileNotFoundError('{} is neither file nor folder/directory'.format(args.input))
|
raise FileNotFoundError(f'{args.input} is neither file nor directory')
|
||||||
|
|
||||||
Avalon.info('Program completed, taking {} seconds'.format(round((time.time() - begin_time), 5)))
|
Avalon.info(f'Program completed, taking {round((time.time() - begin_time), 5)} seconds')
|
||||||
except Exception:
|
except Exception:
|
||||||
Avalon.error('An exception has occurred')
|
Avalon.error('An exception has occurred')
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
Avalon.warning('If you experience error \"cudaSuccess out of memory\", try reducing number of threads you\'re using')
|
|
||||||
finally:
|
finally:
|
||||||
# remove Video2X Cache folder
|
# remove Video2X Cache directory
|
||||||
try:
|
try:
|
||||||
if not preserve_frames:
|
if not preserve_frames:
|
||||||
shutil.rmtree(video2x_cache_folder)
|
shutil.rmtree(video2x_cache_directory)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
Name: Video2X Setup Script
|
Name: Video2X Setup Script
|
||||||
Author: K4YT3X
|
Author: K4YT3X
|
||||||
Date Created: November 28, 2018
|
Date Created: November 28, 2018
|
||||||
Last Modified: March 26, 2019
|
Last Modified: April 28, 2019
|
||||||
|
|
||||||
Licensed under the GNU General Public License Version 3 (GNU GPL v3),
|
Licensed under the GNU General Public License Version 3 (GNU GPL v3),
|
||||||
available at: https://www.gnu.org/licenses/gpl-3.0.txt
|
available at: https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
@@ -23,6 +23,7 @@ import argparse
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import traceback
|
import traceback
|
||||||
import zipfile
|
import zipfile
|
||||||
@@ -32,7 +33,7 @@ import zipfile
|
|||||||
# later in the script.
|
# later in the script.
|
||||||
# import requests
|
# import requests
|
||||||
|
|
||||||
VERSION = '1.2.0'
|
VERSION = '1.2.1'
|
||||||
|
|
||||||
|
|
||||||
def process_arguments():
|
def process_arguments():
|
||||||
@@ -55,14 +56,15 @@ class Video2xSetup:
|
|||||||
script. All files will be installed under %LOCALAPPDATA%\\video2x.
|
script. All files will be installed under %LOCALAPPDATA%\\video2x.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, driver):
|
def __init__(self, driver, download_python_modules):
|
||||||
self.driver = driver
|
self.driver = driver
|
||||||
|
self.download_python_modules = download_python_modules
|
||||||
self.trash = []
|
self.trash = []
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
if self.download_python_modules:
|
||||||
print('\nInstalling Python libraries')
|
print('\nInstalling Python libraries')
|
||||||
self._install_python_requirements()
|
self._install_python_requirements()
|
||||||
|
|
||||||
print('\nInstalling FFMPEG')
|
print('\nInstalling FFMPEG')
|
||||||
self._install_ffmpeg()
|
self._install_ffmpeg()
|
||||||
@@ -94,7 +96,7 @@ class Video2xSetup:
|
|||||||
"""
|
"""
|
||||||
for file in self.trash:
|
for file in self.trash:
|
||||||
try:
|
try:
|
||||||
print('Deleting: {}'.format(file))
|
print(f'Deleting: {file}')
|
||||||
os.remove(file)
|
os.remove(file)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
@@ -108,7 +110,7 @@ class Video2xSetup:
|
|||||||
self.trash.append(ffmpeg_zip)
|
self.trash.append(ffmpeg_zip)
|
||||||
|
|
||||||
with zipfile.ZipFile(ffmpeg_zip) as zipf:
|
with zipfile.ZipFile(ffmpeg_zip) as zipf:
|
||||||
zipf.extractall('{}\\video2x'.format(os.getenv('localappdata')))
|
zipf.extractall(f'{os.getenv("localappdata")}\\video2x')
|
||||||
|
|
||||||
def _install_waifu2x_caffe(self):
|
def _install_waifu2x_caffe(self):
|
||||||
""" Install waifu2x_caffe
|
""" Install waifu2x_caffe
|
||||||
@@ -125,7 +127,7 @@ class Video2xSetup:
|
|||||||
self.trash.append(waifu2x_caffe_zip)
|
self.trash.append(waifu2x_caffe_zip)
|
||||||
|
|
||||||
with zipfile.ZipFile(waifu2x_caffe_zip) as zipf:
|
with zipfile.ZipFile(waifu2x_caffe_zip) as zipf:
|
||||||
zipf.extractall('{}\\video2x'.format(os.getenv('localappdata')))
|
zipf.extractall(f'{os.getenv("localappdata")}\\video2x')
|
||||||
|
|
||||||
def _install_waifu2x_converter_cpp(self):
|
def _install_waifu2x_converter_cpp(self):
|
||||||
""" Install waifu2x_caffe
|
""" Install waifu2x_caffe
|
||||||
@@ -143,7 +145,7 @@ class Video2xSetup:
|
|||||||
self.trash.append(waifu2x_converter_cpp_zip)
|
self.trash.append(waifu2x_converter_cpp_zip)
|
||||||
|
|
||||||
with zipfile.ZipFile(waifu2x_converter_cpp_zip) as zipf:
|
with zipfile.ZipFile(waifu2x_converter_cpp_zip) as zipf:
|
||||||
zipf.extractall('{}\\video2x\\waifu2x-converter-cpp'.format(os.getenv('localappdata')))
|
zipf.extractall(f'{os.getenv("localappdata")}\\video2x\\waifu2x-converter-cpp')
|
||||||
|
|
||||||
def _generate_config(self):
|
def _generate_config(self):
|
||||||
""" Generate video2x config
|
""" Generate video2x config
|
||||||
@@ -153,24 +155,24 @@ class Video2xSetup:
|
|||||||
template_dict = json.load(template)
|
template_dict = json.load(template)
|
||||||
template.close()
|
template.close()
|
||||||
|
|
||||||
|
local_app_data = os.getenv('localappdata')
|
||||||
|
|
||||||
# configure only the specified drivers
|
# configure only the specified drivers
|
||||||
if self.driver == 'all':
|
if self.driver == 'all':
|
||||||
template_dict['waifu2x_caffe']['waifu2x_caffe_path'] = '{}\\video2x\\waifu2x-caffe\\waifu2x-caffe-cui.exe'.format(os.getenv('localappdata'))
|
template_dict['waifu2x_caffe']['waifu2x_caffe_path'] = f'{local_app_data}\\video2x\\waifu2x-caffe\\waifu2x-caffe-cui.exe'
|
||||||
template_dict['waifu2x_converter']['waifu2x_converter_path'] = '{}\\video2x\\waifu2x-converter-cpp'.format(os.getenv('localappdata'))
|
template_dict['waifu2x_converter']['waifu2x_converter_path'] = f'{local_app_data}\\video2x\\waifu2x-converter-cpp'
|
||||||
elif self.driver == 'waifu2x_caffe':
|
elif self.driver == 'waifu2x_caffe':
|
||||||
template_dict['waifu2x_caffe']['waifu2x_caffe_path'] = '{}\\video2x\\waifu2x-caffe\\waifu2x-caffe-cui.exe'.format(os.getenv('localappdata'))
|
template_dict['waifu2x_caffe']['waifu2x_caffe_path'] = f'{local_app_data}\\video2x\\waifu2x-caffe\\waifu2x-caffe-cui.exe'
|
||||||
elif self.driver == 'waifu2x_converter':
|
elif self.driver == 'waifu2x_converter':
|
||||||
template_dict['waifu2x_converter']['waifu2x_converter_path'] = '{}\\video2x\\waifu2x-converter-cpp'.format(os.getenv('localappdata'))
|
template_dict['waifu2x_converter']['waifu2x_converter_path'] = f'{local_app_data}\\video2x\\waifu2x-converter-cpp'
|
||||||
|
|
||||||
template_dict['ffmpeg']['ffmpeg_path'] = '{}\\video2x\\ffmpeg-latest-win64-static\\bin'.format(os.getenv('localappdata'))
|
template_dict['ffmpeg']['ffmpeg_path'] = f'{local_app_data}\\video2x\\ffmpeg-latest-win64-static\\bin'
|
||||||
template_dict['ffmpeg']['ffmpeg_hwaccel'] = 'auto'
|
template_dict['video2x']['video2x_cache_directory'] = None
|
||||||
template_dict['ffmpeg']['extra_arguments'] = []
|
|
||||||
template_dict['video2x']['video2x_cache_folder'] = False
|
|
||||||
template_dict['video2x']['preserve_frames'] = False
|
template_dict['video2x']['preserve_frames'] = False
|
||||||
|
|
||||||
# Write configuration into file
|
# Write configuration into file
|
||||||
with open('video2x.json', 'w') as config:
|
with open('video2x.json', 'w') as config:
|
||||||
json.dump(template_dict, config, indent=2)
|
json.dump(template_dict, config, indent=4)
|
||||||
config.close()
|
config.close()
|
||||||
|
|
||||||
|
|
||||||
@@ -180,10 +182,10 @@ def download(url, save_path, chunk_size=4096):
|
|||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
output_file = '{}\\{}'.format(save_path, url.split('/')[-1])
|
output_file = f'{save_path}\\{url.split("/")[-1]}'
|
||||||
print('Downloading: {}'.format(url))
|
print(f'Downloading: {url}')
|
||||||
print('Chunk size: {}'.format(chunk_size))
|
print(f'Chunk size: {chunk_size}')
|
||||||
print('Saving to: {}'.format(output_file))
|
print(f'Saving to: {output_file}')
|
||||||
|
|
||||||
stream = requests.get(url, stream=True)
|
stream = requests.get(url, stream=True)
|
||||||
total_size = int(stream.headers['content-length'])
|
total_size = int(stream.headers['content-length'])
|
||||||
@@ -211,11 +213,19 @@ def pip_install(package):
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
args = process_arguments()
|
args = process_arguments()
|
||||||
print('Video2x Setup Script')
|
print('Video2X Setup Script')
|
||||||
print('Version: {}'.format(VERSION))
|
print(f'Version: {VERSION}')
|
||||||
setup = Video2xSetup(args.driver)
|
|
||||||
|
# do not install pip modules if script
|
||||||
|
# is packaged in exe format
|
||||||
|
download_python_modules = True
|
||||||
|
if sys.argv[0].endswith('.exe'):
|
||||||
|
print('\nScript is packaged as exe, skipping pip module download')
|
||||||
|
download_python_modules = False
|
||||||
|
|
||||||
|
setup = Video2xSetup(args.driver, download_python_modules)
|
||||||
setup.run()
|
setup.run()
|
||||||
print('\n Script finished successfully')
|
print('\nScript finished successfully')
|
||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
print('An error has occurred')
|
print('An error has occurred')
|
||||||
|
|||||||
@@ -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: March 30, 2019
|
Last Modified: April 28, 2019
|
||||||
|
|
||||||
Description: This class is a high-level wrapper
|
Description: This class is a high-level wrapper
|
||||||
for waifu2x-caffe.
|
for waifu2x-caffe.
|
||||||
@@ -33,20 +33,20 @@ class Waifu2xCaffe:
|
|||||||
self.model_dir = model_dir
|
self.model_dir = model_dir
|
||||||
self.print_lock = threading.Lock()
|
self.print_lock = threading.Lock()
|
||||||
|
|
||||||
def upscale(self, input_folder, output_folder, scale_ratio, scale_width, scale_height, image_format, upscaler_exceptions):
|
def upscale(self, input_directory, output_directory, scale_ratio, scale_width, scale_height, image_format, upscaler_exceptions):
|
||||||
"""This is the core function for WAIFU2X class
|
"""This is the core function for WAIFU2X class
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
input_folder {string} -- source folder path
|
input_directory {string} -- source directory path
|
||||||
output_folder {string} -- output folder path
|
output_directory {string} -- output directory path
|
||||||
width {int} -- output video width
|
width {int} -- output video width
|
||||||
height {int} -- output video height
|
height {int} -- output video height
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# overwrite config file settings
|
# overwrite config file settings
|
||||||
self.waifu2x_settings['input_path'] = input_folder
|
self.waifu2x_settings['input_path'] = input_directory
|
||||||
self.waifu2x_settings['output_path'] = output_folder
|
self.waifu2x_settings['output_path'] = output_directory
|
||||||
|
|
||||||
if scale_ratio:
|
if scale_ratio:
|
||||||
self.waifu2x_settings['scale_ratio'] = scale_ratio
|
self.waifu2x_settings['scale_ratio'] = scale_ratio
|
||||||
@@ -58,7 +58,7 @@ class Waifu2xCaffe:
|
|||||||
|
|
||||||
# print thread start message
|
# print thread start message
|
||||||
self.print_lock.acquire()
|
self.print_lock.acquire()
|
||||||
Avalon.debug_info('[upscaler] Thread {} started'.format(threading.current_thread().name))
|
Avalon.debug_info(f'[upscaler] Thread {threading.current_thread().name} started')
|
||||||
self.print_lock.release()
|
self.print_lock.release()
|
||||||
|
|
||||||
# list to be executed
|
# list to be executed
|
||||||
@@ -74,17 +74,17 @@ class Waifu2xCaffe:
|
|||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
if len(key) == 1:
|
if len(key) == 1:
|
||||||
execute.append('-{}'.format(key))
|
execute.append(f'-{key}')
|
||||||
else:
|
else:
|
||||||
execute.append('--{}'.format(key))
|
execute.append(f'--{key}')
|
||||||
execute.append(str(value))
|
execute.append(str(value))
|
||||||
|
|
||||||
Avalon.debug_info('Executing: {}'.format(execute))
|
Avalon.debug_info(f'Executing: {execute}')
|
||||||
completed_command = subprocess.run(execute, check=True)
|
completed_command = subprocess.run(execute, check=True)
|
||||||
|
|
||||||
# print thread exiting message
|
# print thread exiting message
|
||||||
self.print_lock.acquire()
|
self.print_lock.acquire()
|
||||||
Avalon.debug_info('[upscaler] Thread {} exiting'.format(threading.current_thread().name))
|
Avalon.debug_info(f'[upscaler] Thread {threading.current_thread().name} exiting')
|
||||||
self.print_lock.release()
|
self.print_lock.release()
|
||||||
|
|
||||||
# return command execution return code
|
# return command execution return code
|
||||||
|
|||||||
@@ -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: March 30, 2019
|
Last Modified: April 28, 2019
|
||||||
|
|
||||||
Description: This class is a high-level wrapper
|
Description: This class is a high-level wrapper
|
||||||
for waifu2x-converter-cpp.
|
for waifu2x-converter-cpp.
|
||||||
@@ -28,26 +28,26 @@ class Waifu2xConverter:
|
|||||||
self.waifu2x_settings['model_dir'] = model_dir
|
self.waifu2x_settings['model_dir'] = model_dir
|
||||||
self.print_lock = threading.Lock()
|
self.print_lock = threading.Lock()
|
||||||
|
|
||||||
def upscale(self, input_folder, output_folder, scale_ratio, jobs, image_format, upscaler_exceptions):
|
def upscale(self, input_directory, output_directory, scale_ratio, jobs, image_format, upscaler_exceptions):
|
||||||
""" Waifu2x Converter Driver Upscaler
|
""" Waifu2x Converter Driver Upscaler
|
||||||
This method executes the upscaling of extracted frames.
|
This method executes the upscaling of extracted frames.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
input_folder {string} -- source folder path
|
input_directory {string} -- source directory path
|
||||||
output_folder {string} -- output folder path
|
output_directory {string} -- output directory path
|
||||||
scale_ratio {int} -- frames' scale ratio
|
scale_ratio {int} -- frames' scale ratio
|
||||||
threads {int} -- number of threads
|
threads {int} -- number of threads
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# overwrite config file settings
|
# overwrite config file settings
|
||||||
self.waifu2x_settings['input'] = input_folder
|
self.waifu2x_settings['input'] = input_directory
|
||||||
self.waifu2x_settings['output'] = output_folder
|
self.waifu2x_settings['output'] = output_directory
|
||||||
|
|
||||||
# temporary fix for https://github.com/DeadSix27/waifu2x-converter-cpp/issues/109
|
# temporary fix for https://github.com/DeadSix27/waifu2x-converter-cpp/issues/109
|
||||||
"""
|
"""
|
||||||
self.waifu2x_settings['i'] = input_folder
|
self.waifu2x_settings['i'] = input_directory
|
||||||
self.waifu2x_settings['o'] = output_folder
|
self.waifu2x_settings['o'] = output_directory
|
||||||
self.waifu2x_settings['input'] = None
|
self.waifu2x_settings['input'] = None
|
||||||
self.waifu2x_settings['output'] = None
|
self.waifu2x_settings['output'] = None
|
||||||
"""
|
"""
|
||||||
@@ -59,11 +59,11 @@ class Waifu2xConverter:
|
|||||||
# models_rgb must be specified manually for waifu2x-converter-cpp
|
# models_rgb must be specified manually for waifu2x-converter-cpp
|
||||||
# if it's not specified in the arguments, create automatically
|
# if it's not specified in the arguments, create automatically
|
||||||
if self.waifu2x_settings['model-dir'] is None:
|
if self.waifu2x_settings['model-dir'] is None:
|
||||||
self.waifu2x_settings['model-dir'] = '{}\\models_rgb'.format(self.waifu2x_settings['waifu2x_converter_path'])
|
self.waifu2x_settings['model-dir'] = f'{self.waifu2x_settings["waifu2x_converter_path"]}\\models_rgb'
|
||||||
|
|
||||||
# print thread start message
|
# print thread start message
|
||||||
self.print_lock.acquire()
|
self.print_lock.acquire()
|
||||||
Avalon.debug_info('[upscaler] Thread {} started'.format(threading.current_thread().name))
|
Avalon.debug_info(f'[upscaler] Thread {threading.current_thread().name} started')
|
||||||
self.print_lock.release()
|
self.print_lock.release()
|
||||||
|
|
||||||
# list to be executed
|
# list to be executed
|
||||||
@@ -75,16 +75,16 @@ class Waifu2xConverter:
|
|||||||
|
|
||||||
# the key doesn't need to be passed in this case
|
# the key doesn't need to be passed in this case
|
||||||
if key == 'waifu2x_converter_path':
|
if key == 'waifu2x_converter_path':
|
||||||
execute.append('{}\\waifu2x-converter-cpp.exe'.format(str(value)))
|
execute.append(f'{str(value)}\\waifu2x-converter-cpp.exe')
|
||||||
|
|
||||||
# null or None means that leave this option out (keep default)
|
# null or None means that leave this option out (keep default)
|
||||||
elif value is None or value is False:
|
elif value is None or value is False:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
if len(key) == 1:
|
if len(key) == 1:
|
||||||
execute.append('-{}'.format(key))
|
execute.append(f'-{key}')
|
||||||
else:
|
else:
|
||||||
execute.append('--{}'.format(key))
|
execute.append(f'--{key}')
|
||||||
|
|
||||||
# true means key is an option
|
# true means key is an option
|
||||||
if value is True:
|
if value is True:
|
||||||
@@ -92,7 +92,7 @@ class Waifu2xConverter:
|
|||||||
|
|
||||||
execute.append(str(value))
|
execute.append(str(value))
|
||||||
|
|
||||||
Avalon.debug_info('Executing: {}'.format(execute))
|
Avalon.debug_info(f'Executing: {execute}')
|
||||||
return subprocess.run(execute, check=True).returncode
|
return subprocess.run(execute, check=True).returncode
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Reference in New Issue
Block a user