From 714790a65345d5cb314abc750a46163cfe241281 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Mon, 25 Mar 2019 09:22:33 -0400 Subject: [PATCH 01/36] fixed waifu2x upscaling error --- bin/upscaler.py | 18 +++++++++--------- bin/waifu2x_caffe.py | 4 ++-- bin/waifu2x_converter.py | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/bin/upscaler.py b/bin/upscaler.py index befbd0c..29b0e51 100644 --- a/bin/upscaler.py +++ b/bin/upscaler.py @@ -142,6 +142,10 @@ class Upscaler: # progress bar thread exit signal self.progress_bar_exit_signal = False + # create a container for exceptions in threads + # if this thread is not empty, then an exception has occured + self.upscaler_exceptions = [] + # 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': @@ -149,7 +153,7 @@ class Upscaler: 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) + w2.upscale(self.extracted_frames, self.upscaled_frames, self.scale_ratio, self.threads, 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))]: renamed = re.sub('_\[.*-.*\]\[x(\d+(\.\d+)?)\]\.png', '.png', image) shutil.move('{}\\{}'.format(self.upscaled_frames, image), '{}\\{}'.format(self.upscaled_frames, renamed)) @@ -161,10 +165,6 @@ class Upscaler: # create a container for all upscaler threads upscaler_threads = [] - # create a container for exceptions in threads - # if this thread is not empty, then an exception has occured - self.threads_exceptions = [] - # list all images in the extracted frames frames = [os.path.join(self.extracted_frames, f) for f in os.listdir(self.extracted_frames) if os.path.isfile(os.path.join(self.extracted_frames, f))] @@ -202,9 +202,9 @@ class Upscaler: for thread_info in thread_pool: # create thread if self.scale_ratio: - thread = threading.Thread(target=w2.upscale, args=(thread_info[0], self.upscaled_frames, self.scale_ratio, False, False, self.threads_exceptions)) + thread = threading.Thread(target=w2.upscale, args=(thread_info[0], self.upscaled_frames, self.scale_ratio, False, False, self.upscaler_exceptions)) else: - thread = threading.Thread(target=w2.upscale, args=(thread_info[0], self.upscaled_frames, False, self.scale_width, self.scale_height, self.threads_exceptions)) + thread = threading.Thread(target=w2.upscale, args=(thread_info[0], self.upscaled_frames, False, self.scale_width, self.scale_height, self.upscaler_exceptions)) thread.name = thread_info[1] # add threads into the pool @@ -233,8 +233,8 @@ class Upscaler: self.progress_bar_exit_signal = True - if len(self.threads_exceptions) != 0: - raise(self.threads_exceptions[0]) + if len(self.upscaler_exceptions) != 0: + raise(self.upscaler_exceptions[0]) def run(self): """Main controller for Video2X diff --git a/bin/waifu2x_caffe.py b/bin/waifu2x_caffe.py index 43487ac..7e2237e 100644 --- a/bin/waifu2x_caffe.py +++ b/bin/waifu2x_caffe.py @@ -33,7 +33,7 @@ class Waifu2xCaffe: self.model_dir = model_dir self.print_lock = threading.Lock() - def upscale(self, input_folder, output_folder, scale_ratio, scale_width, scale_height, threads_exceptions): + def upscale(self, input_folder, output_folder, scale_ratio, scale_width, scale_height, upscaler_exceptions): """This is the core function for WAIFU2X class Arguments: @@ -88,4 +88,4 @@ class Waifu2xCaffe: # return command execution return code return completed_command.returncode except Exception as e: - threads_exceptions.append(e) + upscaler_exceptions.append(e) diff --git a/bin/waifu2x_converter.py b/bin/waifu2x_converter.py index ec78325..36d14a8 100644 --- a/bin/waifu2x_converter.py +++ b/bin/waifu2x_converter.py @@ -28,7 +28,7 @@ class Waifu2xConverter: self.waifu2x_settings['model_dir'] = model_dir self.print_lock = threading.Lock() - def upscale(self, input_folder, output_folder, scale_ratio, jobs, threads_exceptions): + def upscale(self, input_folder, output_folder, scale_ratio, jobs, upscaler_exceptions): """ Waifu2x Converter Driver Upscaler This method executes the upscaling of extracted frames. @@ -93,4 +93,4 @@ class Waifu2xConverter: return subprocess.run(execute, check=True).returncode except Exception as e: - threads_exceptions.append(e) + upscaler_exceptions.append(e) From 4bd9541a32416fea3715e9f904cb19319524e575 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Mon, 25 Mar 2019 10:00:19 -0400 Subject: [PATCH 02/36] scaling ratio can now be float --- bin/video2x.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/video2x.py b/bin/video2x.py index 2d37a06..96d1e1e 100644 --- a/bin/video2x.py +++ b/bin/video2x.py @@ -85,7 +85,7 @@ def process_arguments(): scaling_options = parser.add_argument_group('Scaling Options') scaling_options.add_argument('--width', help='Output video width', action='store', type=int, default=False) scaling_options.add_argument('--height', help='Output video height', action='store', type=int, default=False) - scaling_options.add_argument('-r', '--ratio', help='Scaling ratio', action='store', type=int, default=False) + scaling_options.add_argument('-r', '--ratio', help='Scaling ratio', action='store', type=float, default=False) # parse arguments return parser.parse_args() From ce3b69dce08a64159f93e158d31111150d998696 Mon Sep 17 00:00:00 2001 From: K4YT3X Date: Tue, 26 Mar 2019 10:18:43 -0400 Subject: [PATCH 03/36] fixed resolution bug --- bin/upscaler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/upscaler.py b/bin/upscaler.py index 29b0e51..8cee4d2 100644 --- a/bin/upscaler.py +++ b/bin/upscaler.py @@ -75,7 +75,7 @@ class Upscaler: for directory in [self.extracted_frames, self.upscaled_frames]: try: - print('Cleaning up cache directory: {}'.format()) + print('Cleaning up cache directory: {}'.format(directory)) shutil.rmtree(directory) except (OSError, FileNotFoundError): pass @@ -290,8 +290,8 @@ class Upscaler: if self.scale_ratio: coded_width = video_info['streams'][video_stream_index]['coded_width'] coded_height = video_info['streams'][video_stream_index]['coded_height'] - self.scale_width = self.scale_ratio * coded_width - self.scale_height = self.scale_ratio * coded_height + self.scale_width = int(self.scale_ratio * coded_width) + self.scale_height = int(self.scale_ratio * coded_height) # upscale images one by one using waifu2x Avalon.info('Starting to upscale extracted images') From c5a7129415c6a307217629b2878e51b7ddcc4800 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Tue, 26 Mar 2019 16:17:39 -0400 Subject: [PATCH 04/36] fixed directory cleaning errors, fixed resolution errors --- bin/upscaler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/upscaler.py b/bin/upscaler.py index 29b0e51..8cee4d2 100644 --- a/bin/upscaler.py +++ b/bin/upscaler.py @@ -75,7 +75,7 @@ class Upscaler: for directory in [self.extracted_frames, self.upscaled_frames]: try: - print('Cleaning up cache directory: {}'.format()) + print('Cleaning up cache directory: {}'.format(directory)) shutil.rmtree(directory) except (OSError, FileNotFoundError): pass @@ -290,8 +290,8 @@ class Upscaler: if self.scale_ratio: coded_width = video_info['streams'][video_stream_index]['coded_width'] coded_height = video_info['streams'][video_stream_index]['coded_height'] - self.scale_width = self.scale_ratio * coded_width - self.scale_height = self.scale_ratio * coded_height + self.scale_width = int(self.scale_ratio * coded_width) + self.scale_height = int(self.scale_ratio * coded_height) # upscale images one by one using waifu2x Avalon.info('Starting to upscale extracted images') From 53aac6c931d8864896a0da09f3765a5ad4fe8d8e Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Tue, 26 Mar 2019 16:18:18 -0400 Subject: [PATCH 05/36] added new keys for waifu2x-converter-cpp --- bin/video2x.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/bin/video2x.json b/bin/video2x.json index caac484..ba520ba 100644 --- a/bin/video2x.json +++ b/bin/video2x.json @@ -19,29 +19,29 @@ "crop_w": null, "crop_h": null }, - "waifu2x_converter": { "waifu2x_converter_path": "C:\\Users\\K4YT3X\\AppData\\Local\\video2x\\waifu2x-converter-cpp", - "block_size": null, + "output-format": "png", + "png-compression": null, + "image_quality": null, + "block-size": null, "disable-gpu": null, "force-OpenCL": null, "processor": null, "jobs": null, - "model_dir": null, - "scale_ratio": null, - "noise_level": 3, - "mode": "noise_scale", - "quiet": true, + "model-dir": null, + "scale-ratio": null, + "noise-level": 3, + "mode": "noise-scale", + "silent": true, "output": null, "input": null }, - - "ffmpeg":{ - "ffmpeg_path": "C:\\Users\\K4YT3X\\AppData\\Local\\video2x\\ffmpeg-4.1-win64-static\\bin", + "ffmpeg": { + "ffmpeg_path": "C:\\Users\\K4YT3X\\AppData\\Local\\video2x\\ffmpeg-latest-win64-static\\bin", "ffmpeg_hwaccel": "auto", "extra_arguments": [] }, - "video2x": { "video2x_cache_folder": false, "preserve_frames": false From 37dcf5185f5e6c1d24ef7a310e38837e22b07260 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Tue, 26 Mar 2019 16:18:37 -0400 Subject: [PATCH 06/36] 1.2.0 added automatic setup for waifu2x-converter-cpp --- bin/video2x_setup.py | 82 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 66 insertions(+), 16 deletions(-) diff --git a/bin/video2x_setup.py b/bin/video2x_setup.py index a302ea6..1c7d1a9 100644 --- a/bin/video2x_setup.py +++ b/bin/video2x_setup.py @@ -4,7 +4,7 @@ Name: Video2X Setup Script Author: K4YT3X Date Created: November 28, 2018 -Last Modified: March 9, 2019 +Last Modified: March 26, 2019 Licensed under the GNU General Public License Version 3 (GNU GPL v3), available at: https://www.gnu.org/licenses/gpl-3.0.txt @@ -19,6 +19,7 @@ Installation Details: - waifu2x-caffe: %LOCALAPPDATA%\\video2x\\waifu2x-caffe """ +import argparse import json import os import subprocess @@ -31,7 +32,20 @@ import zipfile # later in the script. # import requests -VERSION = '1.1.0' +VERSION = '1.2.0' + + +def process_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'], required=True) + + # parse arguments + return parser.parse_args() class Video2xSetup: @@ -41,7 +55,8 @@ class Video2xSetup: script. All files will be installed under %LOCALAPPDATA%\\video2x. """ - def __init__(self): + def __init__(self, driver): + self.driver = driver self.trash = [] def run(self): @@ -52,8 +67,13 @@ class Video2xSetup: print('\nInstalling FFMPEG') self._install_ffmpeg() - print('\nInstalling waifu2x-caffe') - self._install_waifu2x_caffe() + if self.driver == 'all': + self._install_waifu2x_caffe() + self._install_waifu2x_converter_cpp() + elif self.driver == 'waifu2x_caffe': + self._install_waifu2x_caffe() + elif self.driver == 'waifu2x_converter': + self._install_waifu2x_converter_cpp() print('\nGenerating Video2X configuration file') self._generate_config() @@ -66,7 +86,7 @@ class Video2xSetup: """ with open('requirements.txt', 'r') as req: for line in req: - package = line.split(' ')[0] + package = line.split('==')[0] pip_install(package) def _cleanup(self): @@ -82,7 +102,7 @@ class Video2xSetup: def _install_ffmpeg(self): """ Install FFMPEG """ - latest_release = 'https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-4.1-win64-static.zip' + latest_release = 'https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-latest-win64-static.zip' ffmpeg_zip = download(latest_release, tempfile.gettempdir()) self.trash.append(ffmpeg_zip) @@ -93,6 +113,7 @@ class Video2xSetup: def _install_waifu2x_caffe(self): """ Install waifu2x_caffe """ + print('\nInstalling waifu2x-caffe') import requests # Get latest release of waifu2x-caffe via GitHub API @@ -106,6 +127,24 @@ class Video2xSetup: with zipfile.ZipFile(waifu2x_caffe_zip) as zipf: zipf.extractall('{}\\video2x'.format(os.getenv('localappdata'))) + def _install_waifu2x_converter_cpp(self): + """ Install waifu2x_caffe + """ + print('\nInstalling waifu2x-converter-cpp') + import re + 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) + + for a in latest_release['assets']: + if re.search(r'waifu2x-DeadSix27-win64_v[0-9]*\.zip', a['browser_download_url']): + waifu2x_converter_cpp_zip = download(a['browser_download_url'], tempfile.gettempdir()) + self.trash.append(waifu2x_converter_cpp_zip) + + with zipfile.ZipFile(waifu2x_converter_cpp_zip) as zipf: + zipf.extractall('{}\\video2x\\waifu2x-converter-cpp'.format(os.getenv('localappdata'))) + def _generate_config(self): """ Generate video2x config """ @@ -113,9 +152,17 @@ class Video2xSetup: with open('video2x.json', 'r') as template: template_dict = json.load(template) template.close() - - template_dict['waifu2x_caffe']['waifu2x_caffe_path'] = '{}\\video2x\\waifu2x-caffe\\waifu2x-caffe-cui.exe'.format(os.getenv('localappdata')) - template_dict['ffmpeg']['ffmpeg_path'] = '{}\\video2x\\ffmpeg-4.1-win64-static\\bin'.format(os.getenv('localappdata')) + + # configure only the specified drivers + 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_converter']['waifu2x_converter_path'] = '{}\\video2x\\waifu2x-converter-cpp'.format(os.getenv('localappdata')) + elif self.driver == 'waifu2x_caffe': + template_dict['waifu2x_caffe']['waifu2x_caffe_path'] = '{}\\video2x\\waifu2x-caffe\\waifu2x-caffe-cui.exe'.format(os.getenv('localappdata')) + elif self.driver == 'waifu2x_converter': + template_dict['waifu2x_converter']['waifu2x_converter_path'] = '{}\\video2x\\waifu2x-converter-cpp'.format(os.getenv('localappdata')) + + template_dict['ffmpeg']['ffmpeg_path'] = '{}\\video2x\\ffmpeg-latest-win64-static\\bin'.format(os.getenv('localappdata')) template_dict['ffmpeg']['ffmpeg_hwaccel'] = 'auto' template_dict['ffmpeg']['extra_arguments'] = [] template_dict['video2x']['video2x_cache_folder'] = False @@ -130,6 +177,7 @@ class Video2xSetup: def download(url, save_path, chunk_size=4096): """ Download file to local with requests library """ + from tqdm import tqdm import requests output_file = '{}\\{}'.format(save_path, url.split('/')[-1]) @@ -138,14 +186,15 @@ def download(url, save_path, chunk_size=4096): print('Saving to: {}'.format(output_file)) stream = requests.get(url, stream=True) + total_size = int(stream.headers['content-length']) # Write content into file with open(output_file, 'wb') as output: - for chunk in stream.iter_content(chunk_size=chunk_size): - if chunk: - print('!', end='') - output.write(chunk) - print() + with tqdm(total=total_size, ascii=True) as progress_bar: + for chunk in stream.iter_content(chunk_size=chunk_size): + if chunk: + output.write(chunk) + progress_bar.update(len(chunk)) return output_file @@ -161,9 +210,10 @@ def pip_install(package): if __name__ == "__main__": try: + args = process_arguments() print('Video2x Setup Script') print('Version: {}'.format(VERSION)) - setup = Video2xSetup() + setup = Video2xSetup(args.driver) setup.run() print('\n Script finished successfully') except Exception: From fb99e8531cc02a66d0c2b8dc98fc32247609e121 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Tue, 26 Mar 2019 16:18:54 -0400 Subject: [PATCH 07/36] changed argument format for the newest version of waifu2x-converter --- bin/waifu2x_converter.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bin/waifu2x_converter.py b/bin/waifu2x_converter.py index 36d14a8..8e10e3a 100644 --- a/bin/waifu2x_converter.py +++ b/bin/waifu2x_converter.py @@ -45,18 +45,20 @@ class Waifu2xConverter: self.waifu2x_settings['output'] = output_folder # temporary fix for https://github.com/DeadSix27/waifu2x-converter-cpp/issues/109 + """ self.waifu2x_settings['i'] = input_folder self.waifu2x_settings['o'] = output_folder self.waifu2x_settings['input'] = None self.waifu2x_settings['output'] = None + """ - self.waifu2x_settings['scale_ratio'] = scale_ratio + self.waifu2x_settings['scale-ratio'] = scale_ratio self.waifu2x_settings['jobs'] = jobs # 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'] = '{}\\models_rgb'.format(self.waifu2x_settings['waifu2x_converter_path']) + if self.waifu2x_settings['model-dir'] is None: + self.waifu2x_settings['model-dir'] = '{}\\models_rgb'.format(self.waifu2x_settings['waifu2x_converter_path']) # print thread start message self.print_lock.acquire() From 715822229327d685d78fe78147cc9a042e0d9b03 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Tue, 26 Mar 2019 16:21:58 -0400 Subject: [PATCH 08/36] sorted lines --- bin/upscaler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/upscaler.py b/bin/upscaler.py index 8cee4d2..6d80d1c 100644 --- a/bin/upscaler.py +++ b/bin/upscaler.py @@ -13,10 +13,10 @@ Licensed under the GNU General Public License Version 3 (GNU GPL v3), """ from avalon_framework import Avalon -from image_cleaner import ImageCleaner from exceptions import * from ffmpeg import Ffmpeg from fractions import Fraction +from image_cleaner import ImageCleaner from tqdm import tqdm from waifu2x_caffe import Waifu2xCaffe from waifu2x_converter import Waifu2xConverter From a5d53bd7d5d5951be1ceeea23251641b336d3734 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Tue, 26 Mar 2019 17:58:29 -0400 Subject: [PATCH 09/36] removed redundant argparse arguments --- bin/video2x.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/video2x.py b/bin/video2x.py index 96d1e1e..a6d442a 100644 --- a/bin/video2x.py +++ b/bin/video2x.py @@ -76,16 +76,16 @@ def process_arguments(): 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('-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', default=None) + upscaler_options.add_argument('-y', '--model_dir', help='Folder 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('-c', '--config', help='Video2X config file location', action='store', default='{}\\video2x.json'.format(os.path.dirname(os.path.abspath(__file__)))) - upscaler_options.add_argument('-b', '--batch', help='Enable batch mode (select all default values to questions)', action='store_true', default=False) + upscaler_options.add_argument('-b', '--batch', help='Enable batch mode (select all default values to questions)', action='store_true') # scaling options scaling_options = parser.add_argument_group('Scaling Options') - scaling_options.add_argument('--width', help='Output video width', action='store', type=int, default=False) - scaling_options.add_argument('--height', help='Output video height', action='store', type=int, default=False) - scaling_options.add_argument('-r', '--ratio', help='Scaling ratio', action='store', type=float, default=False) + scaling_options.add_argument('--width', help='Output video width', action='store', type=int) + scaling_options.add_argument('--height', help='Output video height', action='store', type=int) + scaling_options.add_argument('-r', '--ratio', help='Scaling ratio', action='store', type=float) # parse arguments return parser.parse_args() From d468d6d93f269363218492d8e3172abfdd215ec1 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Tue, 26 Mar 2019 17:58:39 -0400 Subject: [PATCH 10/36] updated README for setup script 1.2.0 --- README.md | 62 ++++++++++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 452589d..4a73d21 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,15 @@ Component names that are *italicized* can be automatically downloaded and config 1. AMD GPU / Nvidia GPU 1. AMD GPU driver / Nvidia GPU driver / Nvidia CUDNN 1. [***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) +1. [***waifu2x-caffe***](https://github.com/lltcggie/waifu2x-caffe/releases) / [***waifu2x-converter-cpp***](https://github.com/DeadSix27/waifu2x-converter-cpp/releases) ## Recent Changes +### Setup Script 1.2.0 (March 26, 2019) + +- `video2x_setup.py` script can now automatically download and configure `waifu2x-converter-cpp`. +- replaced old progress indicator with progress bar. + ### 2.6.3 (March 24, 2019) - Added image cleaner by @BrianPetkovsek which removes upscaled frames. @@ -33,11 +38,6 @@ Component names that are *italicized* can be automatically downloaded and config - Added `-b, --batch` option which selects applies all default values for questions automatically. - **This new version will now require `avalon_framework>=1.6.3`**. Please run `pip install -U avalon_framework` to update the existing framework. -### 2.6.0 (March 9, 2019) - -- Complete redesign of configuration file format. The configuration file is now much more flexible and easy to look at. -- Various modifications done to the rest of the program to adapt to the changes made in the configuration file. This eliminated some problems existed in the previous version. - ## 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. @@ -66,7 +66,7 @@ You can find all detailed user-facing and developer-facing documentations in the ### [Step-By-Step Tutorial](https://github.com/K4YT3X/video2x/wiki/Step-By-Step-Tutorial) (Nvidia GPUs) -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. This tutorial currently only includes instructions for Nvidia GPUs, since AMD GPUs (OpenCL) requires installation of `waifu2x-converter-cpp` which cannot be installed automatically with Python at the moment due to its 7z compression format. +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) @@ -91,27 +91,25 @@ Download: https://github.com/DeadSix27/waifu2x-converter-cpp/releases First, clone the video2x repository. -```bash -$ git clone https://github.com/K4YT3X/video2x.git -$ cd video2x/bin +```shell +git clone https://github.com/K4YT3X/video2x.git +cd video2x/bin ``` -Then you may run the `video2x_setup.py` script to install and configure the depencies automatically. This script is designed and tested on Windows 10. +Then you may run the `video2x_setup.py` script to install and configure the dependencies automatically. This script is designed and tested on Windows 10. -This script will install `ffmpeg`, `waifu2x-caffe` to `%LOCALAPPDATA%\\video2x` and all python libraries. +This script will install the newest version of `ffmpeg`, either or both of `waifu2x-caffe` and `waifu2x-converter-cpp` to `%LOCALAPPDATA%\\video2x` and all required python libraries. -**`waifu2x-converter-cpp` cannot be installed automatically with this script.** Please follow the [`waifu2x-converter-cpp` installation instructions](https://github.com/K4YT3X/video2x/wiki/Waifu2X-Drivers#waifu2x-converter-cpp) to install it. - -```bash -$ python video2x_setup.py +```shell +python video2x_setup.py ``` Alternatively, you can also install the dependencies manually. Please refer to the prerequisites section to see what's needed. Then you'll need to install python dependencies before start using video2x. Install simply by executing the following command. -```bash -$ pip install -r requirements.txt +```shell +pip install -r requirements.txt ``` **Note that all command line arguments/options overwrite configuration file settings.** @@ -131,34 +129,32 @@ Clip is from anime "さくら荘のペットな彼女". Copyright belongs to " Enlarge the video to 1920x1080 using CUDA. You may also use the `-r/--ratio` option. -```bash -$ python video2x.py -i sample_input.mp4 -o sample_output.mp4 -m gpu --width=1920 --height=1080 +```shell +python video2x.py -i sample_input.mp4 -o sample_output.mp4 -m gpu --width=1920 --height=1080 ``` ### Nvidia CNDNN Enlarge the video to 1920x1080 using CUDNN. You may also use the `-r/--ratio` option. -```bash -$ python video2x.py -i sample_input.mp4 -o sample_output.mp4 -m cudnn --width=1920 --height=1080 +```shell +python video2x.py -i sample_input.mp4 -o sample_output.mp4 -m cudnn --width=1920 --height=1080 ``` ### AMD or Nvidia (waifu2x-converter-cpp OpenCL) Enlarge the video by 2 times using OpenCL. Note that `waifu2x-converter-cpp` doesn't support width and height. You'll also have to explicitly specify that the driver to be used is `waifu2x_converter`. -```bash -$ python video2x.py -i sample_input.mp4 -o sample_output.mp4 -m gpu -r 2 -d waifu2x_converter +```shell +python video2x.py -i sample_input.mp4 -o sample_output.mp4 -m gpu -r 2 -d waifu2x_converter ``` -Corresponding sample configuration is shown below. Note that the `waifu2x_path` is different from the configuration for `waifu2x-caffe`. Instead of the binary path, folder containing extracted `waifu2x-converter-cpp.exe` should be specified. - ### 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. -```bash -$ python video2x.py -i sample_input.mp4 -o sample_output.mp4 -m cpu --width=1920 --height=1080 +```shell +python video2x.py -i sample_input.mp4 -o sample_output.mp4 -m cpu --width=1920 --height=1080 ``` --- @@ -187,7 +183,7 @@ $ python video2x.py -i sample_input.mp4 -o sample_output.mp4 -m cpu --width=1920 Waifu2x driver (default: waifu2x_caffe) ### -y MODEL_DIR, --model_dir MODEL_DIR - Folder containing model JSON files (default: None) + Folder containing model JSON files ### -t THREADS, --threads THREADS Number of threads to use for upscaling (default: 5) @@ -196,18 +192,18 @@ $ python video2x.py -i sample_input.mp4 -o sample_output.mp4 -m cpu --width=1920 Video2X config file location (default: video2x\bin\video2x.json) ### -b, --batch - Enable batch mode (select all default values to questions) (default: False) + Enable batch mode (select all default values to questions) ## Scaling Options ### --width WIDTH - Output video width (default: False) + Output video width ### --height HEIGHT - Output video height (default: False) + Output video height ### -r RATIO, --ratio RATIO - Scaling ratio (default: False) + Scaling ratio --- From 562aaee056ed963f4bbc4c652564ba5a0d476620 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Tue, 26 Mar 2019 18:04:47 -0400 Subject: [PATCH 11/36] updated default driver settings --- bin/video2x_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/video2x_setup.py b/bin/video2x_setup.py index 1c7d1a9..0779018 100644 --- a/bin/video2x_setup.py +++ b/bin/video2x_setup.py @@ -42,7 +42,7 @@ def process_arguments(): # 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'], required=True) + general_options.add_argument('-d', '--driver', help='driver to download and configure', action='store', choices=['all', 'waifu2x_caffe', 'waifu2x_converter'], default='all') # parse arguments return parser.parse_args() From 78ae2e7ed255cd5fdc69d13e0bea744ef5b85f35 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Sat, 30 Mar 2019 14:01:36 -0400 Subject: [PATCH 12/36] added support for JPEG and other image format --- bin/upscaler.py | 37 ++++++++++++++++--------------------- bin/waifu2x_caffe.py | 6 ++++-- bin/waifu2x_converter.py | 5 +++-- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/bin/upscaler.py b/bin/upscaler.py index 6d80d1c..25bd0d5 100644 --- a/bin/upscaler.py +++ b/bin/upscaler.py @@ -4,7 +4,7 @@ Name: Video2X Upscaler Author: K4YT3X Date Created: December 10, 2018 -Last Modified: March 24, 2019 +Last Modified: March 30, 2019 Licensed under the GNU General Public License Version 3 (GNU GPL v3), available at: https://www.gnu.org/licenses/gpl-3.0.txt @@ -37,36 +37,31 @@ class Upscaler: ArgumentError -- if argument is not valid """ - def __init__(self, input_video, output_video, method, waifu2x_settings, ffmpeg_settings, waifu2x_driver='waifu2x_caffe', scale_width=False, scale_height=False, scale_ratio=False, model_dir=None, threads=5, video2x_cache_folder='{}\\video2x'.format(tempfile.gettempdir()), preserve_frames=False): + def __init__(self, input_video, output_video, method, waifu2x_settings, ffmpeg_settings): # mandatory arguments self.input_video = input_video self.output_video = output_video self.method = method self.waifu2x_settings = waifu2x_settings self.ffmpeg_settings = ffmpeg_settings - self.waifu2x_driver = waifu2x_driver - - # check sanity of waifu2x_driver option - if waifu2x_driver != 'waifu2x_caffe' and waifu2x_driver != 'waifu2x_converter': - raise Exception('Unrecognized waifu2x driver: {}'.format(waifu2x_driver)) # optional arguments - self.scale_width = scale_width - self.scale_height = scale_height - self.scale_ratio = scale_ratio - self.model_dir = model_dir - self.threads = threads + self.waifu2x_driver = 'waifu2x_caffe' + self.scale_width = None + self.scale_height = None + self.scale_ratio = None + self.model_dir = None + self.threads = 5 + self.video2x_cache_folder = '{}\\video2x'.format(tempfile.gettempdir()) + self.image_format = 'png' + self.preserve_frames = False # create temporary folder/directories - self.video2x_cache_folder = video2x_cache_folder self.extracted_frames = tempfile.mkdtemp(dir=self.video2x_cache_folder) Avalon.debug_info('Extracted frames are being saved to: {}'.format(self.extracted_frames)) - self.upscaled_frames = tempfile.mkdtemp(dir=self.video2x_cache_folder) Avalon.debug_info('Upscaled frames are being saved to: {}'.format(self.upscaled_frames)) - self.preserve_frames = preserve_frames - def cleanup(self): # delete temp directories when done # avalon framework cannot be used if python is shutting down @@ -153,9 +148,9 @@ class Upscaler: 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.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))]: - renamed = re.sub('_\[.*-.*\]\[x(\d+(\.\d+)?)\]\.png', '.png', image) + renamed = re.sub('_\[.*-.*\]\[x(\d+(\.\d+)?)\]\.{}'.format(self.image_format), '.{}'.format(self.image_format), image) shutil.move('{}\\{}'.format(self.upscaled_frames, image), '{}\\{}'.format(self.upscaled_frames, renamed)) self.progress_bar_exit_signal = True @@ -202,9 +197,9 @@ class Upscaler: for thread_info in thread_pool: # create thread if self.scale_ratio: - thread = threading.Thread(target=w2.upscale, args=(thread_info[0], self.upscaled_frames, self.scale_ratio, False, False, self.upscaler_exceptions)) + thread = threading.Thread(target=w2.upscale, args=(thread_info[0], self.upscaled_frames, self.scale_ratio, False, False, self.image_format, self.upscaler_exceptions)) else: - thread = threading.Thread(target=w2.upscale, args=(thread_info[0], self.upscaled_frames, False, self.scale_width, self.scale_height, self.upscaler_exceptions)) + thread = threading.Thread(target=w2.upscale, args=(thread_info[0], self.upscaled_frames, False, self.scale_width, self.scale_height, self.image_format, self.upscaler_exceptions)) thread.name = thread_info[1] # add threads into the pool @@ -252,7 +247,7 @@ class Upscaler: self.output_video = os.path.abspath(self.output_video) # initialize objects for ffmpeg and waifu2x-caffe - fm = Ffmpeg(self.ffmpeg_settings) + fm = Ffmpeg(self.ffmpeg_settings, self.image_format) # initialize waifu2x driver if self.waifu2x_driver == 'waifu2x_caffe': diff --git a/bin/waifu2x_caffe.py b/bin/waifu2x_caffe.py index 7e2237e..f716422 100644 --- a/bin/waifu2x_caffe.py +++ b/bin/waifu2x_caffe.py @@ -4,7 +4,7 @@ Name: Waifu2x Caffe Driver Author: K4YT3X Date Created: Feb 24, 2018 -Last Modified: March 24, 2019 +Last Modified: March 30, 2019 Description: This class is a high-level wrapper for waifu2x-caffe. @@ -33,7 +33,7 @@ class Waifu2xCaffe: self.model_dir = model_dir self.print_lock = threading.Lock() - def upscale(self, input_folder, output_folder, scale_ratio, scale_width, scale_height, upscaler_exceptions): + def upscale(self, input_folder, output_folder, scale_ratio, scale_width, scale_height, image_format, upscaler_exceptions): """This is the core function for WAIFU2X class Arguments: @@ -54,6 +54,8 @@ class Waifu2xCaffe: self.waifu2x_settings['scale_width'] = scale_width self.waifu2x_settings['scale_height'] = scale_height + self.waifu2x_settings['output_extention'] = image_format + # print thread start message self.print_lock.acquire() Avalon.debug_info('[upscaler] Thread {} started'.format(threading.current_thread().name)) diff --git a/bin/waifu2x_converter.py b/bin/waifu2x_converter.py index 8e10e3a..97f8245 100644 --- a/bin/waifu2x_converter.py +++ b/bin/waifu2x_converter.py @@ -4,7 +4,7 @@ Name: Waifu2x Converter CPP Driver Author: K4YT3X Date Created: February 8, 2019 -Last Modified: March 24, 2019 +Last Modified: March 30, 2019 Description: This class is a high-level wrapper for waifu2x-converter-cpp. @@ -28,7 +28,7 @@ class Waifu2xConverter: self.waifu2x_settings['model_dir'] = model_dir self.print_lock = threading.Lock() - def upscale(self, input_folder, output_folder, scale_ratio, jobs, upscaler_exceptions): + def upscale(self, input_folder, output_folder, scale_ratio, jobs, image_format, upscaler_exceptions): """ Waifu2x Converter Driver Upscaler This method executes the upscaling of extracted frames. @@ -54,6 +54,7 @@ class Waifu2xConverter: self.waifu2x_settings['scale-ratio'] = scale_ratio self.waifu2x_settings['jobs'] = jobs + self.waifu2x_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 From d9a19d823e2a338a12273d3b3407b9c40b0092d8 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Sat, 30 Mar 2019 14:02:05 -0400 Subject: [PATCH 13/36] made ffmpeg arguments customizable, added support for different frame formats --- bin/ffmpeg.py | 84 +++++++++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 46 deletions(-) diff --git a/bin/ffmpeg.py b/bin/ffmpeg.py index 7330cce..154a9d1 100644 --- a/bin/ffmpeg.py +++ b/bin/ffmpeg.py @@ -4,7 +4,7 @@ Name: FFMPEG Class Author: K4YT3X Date Created: Feb 24, 2018 -Last Modified: March 19, 2019 +Last Modified: March 30, 2019 Description: This class handles all FFMPEG related operations. @@ -22,13 +22,9 @@ class Ffmpeg: and inserting audio tracks to videos. """ - def __init__(self, ffmpeg_settings): + def __init__(self, ffmpeg_settings, image_format): self.ffmpeg_settings = ffmpeg_settings - self._parse_settings() - - def _parse_settings(self): - """ Parse ffmpeg settings - """ + self.ffmpeg_path = self.ffmpeg_settings['ffmpeg_path'] # add a forward slash to directory if not present # otherwise there will be a format error @@ -36,8 +32,8 @@ class Ffmpeg: self.ffmpeg_path = '{}\\'.format(self.ffmpeg_path) self.ffmpeg_binary = '{}ffmpeg.exe'.format(self.ffmpeg_path) - self.ffmpeg_hwaccel = self.ffmpeg_settings['ffmpeg_hwaccel'] - self.extra_arguments = self.ffmpeg_settings['extra_arguments'] + self.ffmpeg_probe_binary = '{}ffprobe.exe'.format(self.ffmpeg_path) + self.image_format = image_format def get_video_info(self, input_video): """ Gets input video information @@ -51,8 +47,11 @@ class Ffmpeg: Returns: dictionary -- JSON text of input video information """ + + # this execution command needs to be hard-coded + # since video2x only strictly recignizes this one format execute = [ - '{}ffprobe.exe'.format(self.ffmpeg_path), + self.ffmpeg_probe_binary, '-v', 'quiet', '-print_format', @@ -80,14 +79,10 @@ class Ffmpeg: execute = [ self.ffmpeg_binary, '-i', - '{}'.format(input_video), - '{}\\extracted_%0d.png'.format(extracted_frames), - '-y' + input_video, + '{}\\extracted_%0d.{}'.format(extracted_frames, self.image_format) ] - execute += self.extra_arguments - - Avalon.debug_info('Executing: {}'.format(' '.join(execute))) - subprocess.run(execute, shell=True, check=True) + self._execute(execute=execute, phase='video_to_frames') def convert_video(self, framerate, resolution, upscaled_frames): """Converts images into videos @@ -104,25 +99,13 @@ class Ffmpeg: self.ffmpeg_binary, '-r', str(framerate), - '-f', - 'image2', '-s', resolution, '-i', - '{}\\extracted_%d.png'.format(upscaled_frames), - '-vcodec', - 'libx264', - '-crf', - '25', - '-pix_fmt', - 'yuv420p', - '{}\\no_audio.mp4'.format(upscaled_frames), - '-y' + '{}\\extracted_%d.{}'.format(upscaled_frames, self.image_format), + '{}\\no_audio.mp4'.format(upscaled_frames) ] - execute += self.extra_arguments - - Avalon.debug_info('Executing: {}'.format(' '.join(execute))) - subprocess.run(execute, shell=True, check=True) + self._execute(execute=execute, phase='frames_to_video') def migrate_audio_tracks_subtitles(self, input_video, output_video, upscaled_frames): """ Migrates audio tracks and subtitles from input video to output video @@ -137,19 +120,28 @@ class Ffmpeg: '-i', '{}\\no_audio.mp4'.format(upscaled_frames), '-i', - '{}'.format(input_video), - '-map', - '0:v:0?', - '-map', - '1?', - '-c', - 'copy', - '-map', - '-1:v?', - '{}'.format(output_video), - '-y' + input_video, + output_video ] - execute += self.extra_arguments + self._execute(execute=execute, phase='migrating_tracks') - Avalon.debug_info('Executing: {}'.format(' '.join(execute))) - subprocess.run(execute, shell=True, check=True) + def _execute(self, execute, phase): + + for key in self.ffmpeg_settings[phase].keys(): + + value = self.ffmpeg_settings[phase][key] + + # null or None means that leave this option out (keep default) + if value is None or value is False: + continue + else: + execute.append(key) + + # true means key is an option + if value is True: + continue + + execute.append(str(value)) + + Avalon.debug_info('Executing: {}'.format(execute)) + return subprocess.run(execute, shell=True, check=True).returncode From 84d9c39bc954d6958e54e8d127f71d20fa71b2c5 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Sat, 30 Mar 2019 14:03:06 -0400 Subject: [PATCH 14/36] added ffmpeg configuration, updates to waifu2x config --- bin/video2x.json | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/bin/video2x.json b/bin/video2x.json index ba520ba..0f85815 100644 --- a/bin/video2x.json +++ b/bin/video2x.json @@ -1,6 +1,8 @@ { "waifu2x_caffe": { "waifu2x_caffe_path": "C:\\Users\\K4YT3X\\AppData\\Local\\video2x\\waifu2x-caffe\\waifu2x-caffe-cui.exe", + "input_extention_list": null, + "output_extention": null, "mode": "noise_scale", "scale_ratio": null, "scale_width": null, @@ -21,9 +23,9 @@ }, "waifu2x_converter": { "waifu2x_converter_path": "C:\\Users\\K4YT3X\\AppData\\Local\\video2x\\waifu2x-converter-cpp", - "output-format": "png", + "output-format": null, "png-compression": null, - "image_quality": null, + "image-quality": null, "block-size": null, "disable-gpu": null, "force-OpenCL": null, @@ -39,11 +41,33 @@ }, "ffmpeg": { "ffmpeg_path": "C:\\Users\\K4YT3X\\AppData\\Local\\video2x\\ffmpeg-latest-win64-static\\bin", - "ffmpeg_hwaccel": "auto", - "extra_arguments": [] + "video_to_frames": { + "-qscale:v": null, + "-hwaccel": "auto", + "-y": true + }, + "frames_to_video": { + "-qscale:v": null, + "-qscale:a": null, + "-f": "image2", + "-vcodec": "libx264", + "-crf": 25, + "-pix_fmt": "yuv420p", + "-hwaccel": "auto", + "-y": true + }, + "migrating_tracks": { + "-map": "0:v:0?", + "-map": "1?", + "-c": "copy", + "-map": "-1:v?", + "-hwaccel": "auto", + "-y": true + } }, "video2x": { - "video2x_cache_folder": false, + "video2x_cache_folder": null, + "image_format": "png", "preserve_frames": false } } \ No newline at end of file From 42cd69a393dd18d7d31954679453ebb804b42ec1 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Sat, 30 Mar 2019 14:03:52 -0400 Subject: [PATCH 15/36] redesigned ffmpeg wrapper, enhanced upscaler class methods, support for JPG --- bin/video2x.py | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/bin/video2x.py b/bin/video2x.py index a6d442a..e902ed1 100644 --- a/bin/video2x.py +++ b/bin/video2x.py @@ -13,7 +13,7 @@ __ __ _ _ ___ __ __ Name: Video2X Controller Author: K4YT3X Date Created: Feb 24, 2018 -Last Modified: March 24, 2019 +Last Modified: March 30, 2019 Licensed under the GNU General Public License Version 3 (GNU GPL v3), available at: https://www.gnu.org/licenses/gpl-3.0.txt @@ -50,7 +50,7 @@ import tempfile import time import traceback -VERSION = '2.6.3' +VERSION = '2.7.0' # each thread might take up to 2.5 GB during initialization. # (system memory, not to be confused with GPU memory) @@ -220,6 +220,7 @@ ffmpeg_settings = config['ffmpeg'] # load video2x settings video2x_cache_folder = config['video2x']['video2x_cache_folder'] +image_format = config['video2x']['image_format'].lower() preserve_frames = config['video2x']['preserve_frames'] # create temp directories if they don't exist @@ -250,7 +251,20 @@ try: if os.path.isfile(args.input): """ Upscale single video file """ Avalon.info('Upscaling single video file: {}'.format(args.input)) - upscaler = Upscaler(input_video=args.input, output_video=args.output, method=args.method, waifu2x_settings=waifu2x_settings, ffmpeg_settings=ffmpeg_settings, waifu2x_driver=args.driver, scale_width=args.width, scale_height=args.height, scale_ratio=args.ratio, model_dir=args.model_dir, threads=args.threads, video2x_cache_folder=video2x_cache_folder) + upscaler = Upscaler(input_video=args.input, output_video=args.output, method=args.method, waifu2x_settings=waifu2x_settings, ffmpeg_settings=ffmpeg_settings) + + # set optional options + upscaler.waifu2x_driver = args.driver + upscaler.scale_width = args.width + upscaler.scale_height = args.height + upscaler.scale_ratio = args.ratio + upscaler.model_dir = args.model_dir + upscaler.threads = args.threads + upscaler.video2x_cache_folder = video2x_cache_folder + upscaler.image_format = image_format + upscaler.preserve_frames = preserve_frames + + # run upscaler- upscaler.run() upscaler.cleanup() elif os.path.isdir(args.input): @@ -258,7 +272,20 @@ try: Avalon.info('Upscaling videos in folder/directory: {}'.format(args.input)) 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) - 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, waifu2x_driver=args.driver, scale_width=args.width, scale_height=args.height, scale_ratio=args.ratio, model_dir=args.model_dir, threads=args.threads, video2x_cache_folder=video2x_cache_folder) + 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 + upscaler.waifu2x_driver = args.driver + upscaler.scale_width = args.width + upscaler.scale_height = args.height + upscaler.scale_ratio = args.ratio + upscaler.model_dir = args.model_dir + upscaler.threads = args.threads + upscaler.video2x_cache_folder = video2x_cache_folder + upscaler.image_format = image_format + upscaler.preserve_frames = preserve_frames + + # run upscaler upscaler.run() upscaler.cleanup() else: From 690b33908919b22cb435cc7ac706e0ce07da9b39 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Sat, 30 Mar 2019 14:07:30 -0400 Subject: [PATCH 16/36] updated README for 2.7.0 --- README.md | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 4a73d21..235c20f 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,12 @@ Component names that are *italicized* can be automatically downloaded and config ## Recent Changes +### 2.7.0 (March 30, 2019) + +- Added support for different extracted image formats. +- Redesigned FFMPEG wrapper, FFMPEG settings are now customizable in the `video2x.json` config file. +- Other minor enhancements and adjustments (e.g. argument -> method variable) + ### Setup Script 1.2.0 (March 26, 2019) - `video2x_setup.py` script can now automatically download and configure `waifu2x-converter-cpp`. @@ -27,17 +33,6 @@ Component names that are *italicized* can be automatically downloaded and config - 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. -### 2.6.2 (March 19, 2019) - -- Removed `--model_dir` verification due to the rapidly evolving number of models added. -- Fixed model specifying bug. Users should now specify model using `--model_dir [path to folder containing model JSON files]`. -- Enhanced command execution method. - -### 2.6.1 (March 12, 2019) - -- Added `-b, --batch` option which selects applies all default values for questions automatically. -- **This new version will now require `avalon_framework>=1.6.3`**. Please run `pip install -U avalon_framework` to update the existing framework. - ## 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. From c87ffe10980449c80cdebe61ad007ba21adc033d Mon Sep 17 00:00:00 2001 From: K4YT3X Date: Sat, 30 Mar 2019 14:28:09 -0400 Subject: [PATCH 17/36] Update issue templates --- .github/ISSUE_TEMPLATE/bug-report.md | 31 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 16 ++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug-report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000..2d76abf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Use this template if you believe you've encountered a bug. +title: '' +labels: bug +assignees: K4YT3X + +--- + +## Environment Information + +|Module|Version| +|-|-| +|`video2x`|| +|`ffmpeg`|| +|`waifu2x-caffe`|| +|`waifu2x-converter-cpp`|| + +## Symptom + +Briefly describe what's going on. + +## Screenshots or Error Messages + +Please paste screenshots or error messages here. +Include as much details as you can, as this greatly helps debugging the problem. + +Please paste your error message in the code block down below: + +``` +``` diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..cd673e9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,16 @@ +--- +name: Feature request +about: Request a new feature to be added +title: '' +labels: enhancement +assignees: K4YT3X + +--- + +## Description + +Briefly describe the feature you want to be added into `video2x`. + +## Sources + +Paste links to descriptions of related documentations, websites and etc. here. From 4973ae5b8d665ed7a5240c1d7d881035538196c2 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Sun, 31 Mar 2019 02:17:10 -0400 Subject: [PATCH 18/36] made video2x compatible with pyinstaller --- bin/video2x.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/video2x.py b/bin/video2x.py index e902ed1..c89b499 100644 --- a/bin/video2x.py +++ b/bin/video2x.py @@ -46,6 +46,7 @@ import json import os import psutil import shutil +import sys import tempfile import time import traceback @@ -78,7 +79,7 @@ def process_arguments(): 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('-t', '--threads', help='Number of threads to use for upscaling', action='store', type=int, default=5) - upscaler_options.add_argument('-c', '--config', help='Video2X config file location', action='store', default='{}\\video2x.json'.format(os.path.dirname(os.path.abspath(__file__)))) + 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('-b', '--batch', help='Enable batch mode (select all default values to questions)', action='store_true') # scaling options From 25156077f2217ac451e839013e70b506a861b80f Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Sun, 31 Mar 2019 03:23:08 -0400 Subject: [PATCH 19/36] added function to absolutify paths set in config file for better exe support --- bin/video2x.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/bin/video2x.py b/bin/video2x.py index c89b499..fb4086f 100644 --- a/bin/video2x.py +++ b/bin/video2x.py @@ -45,6 +45,7 @@ import GPUtil import json import os import psutil +import re import shutil import sys import tempfile @@ -170,6 +171,40 @@ def read_config(config_file): 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_folder = 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'] = '{}\\{}'.format(current_folder, 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'] = '{}\\{}'.format(current_folder, 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'] = '{}\\{}'.format(current_folder, config['ffmpeg']['ffmpeg_path']) + + # check video2x cache path + if config['video2x']['video2x_cache_folder']: + if not re.match('^[a-z]:', config['video2x']['video2x_cache_folder'], re.IGNORECASE): + config['video2x']['video2x_cache_folder'] = '{}\\{}'.format(current_folder, config['video2x']['video2x_cache_folder']) + + return config + + # /////////////////// Execution /////////////////// # # this is not a library @@ -198,6 +233,7 @@ check_memory() # read configurations from JSON config = read_config(args.config) +config = absolutify_paths(config) # load waifu2x configuration if args.driver == 'waifu2x_caffe': @@ -249,6 +285,7 @@ try: # start timer begin_time = time.time() + # if input specified is a single file if os.path.isfile(args.input): """ Upscale single video file """ Avalon.info('Upscaling single video file: {}'.format(args.input)) @@ -268,6 +305,8 @@ try: # run upscaler- upscaler.run() upscaler.cleanup() + + # if input specified is a folder elif os.path.isdir(args.input): """ Upscale videos in a folder/directory """ Avalon.info('Upscaling videos in folder/directory: {}'.format(args.input)) From 98e2cbdb04787e13bb4c9f5d6770792a547671ac Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Sun, 31 Mar 2019 03:46:15 -0400 Subject: [PATCH 20/36] 1.2.1 added support for exe packaging --- bin/video2x_setup.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/bin/video2x_setup.py b/bin/video2x_setup.py index 0779018..6adb959 100644 --- a/bin/video2x_setup.py +++ b/bin/video2x_setup.py @@ -4,7 +4,7 @@ Name: Video2X Setup Script Author: K4YT3X Date Created: November 28, 2018 -Last Modified: March 26, 2019 +Last Modified: March 31, 2019 Licensed under the GNU General Public License Version 3 (GNU GPL v3), available at: https://www.gnu.org/licenses/gpl-3.0.txt @@ -23,6 +23,7 @@ import argparse import json import os import subprocess +import sys import tempfile import traceback import zipfile @@ -32,7 +33,7 @@ import zipfile # later in the script. # import requests -VERSION = '1.2.0' +VERSION = '1.2.1' def process_arguments(): @@ -55,14 +56,15 @@ class Video2xSetup: script. All files will be installed under %LOCALAPPDATA%\\video2x. """ - def __init__(self, driver): + def __init__(self, driver, download_python_modules): self.driver = driver + self.download_python_modules = download_python_modules self.trash = [] def run(self): - - print('\nInstalling Python libraries') - self._install_python_requirements() + if self.download_python_modules: + print('\nInstalling Python libraries') + self._install_python_requirements() print('\nInstalling FFMPEG') self._install_ffmpeg() @@ -165,12 +167,12 @@ class Video2xSetup: template_dict['ffmpeg']['ffmpeg_path'] = '{}\\video2x\\ffmpeg-latest-win64-static\\bin'.format(os.getenv('localappdata')) template_dict['ffmpeg']['ffmpeg_hwaccel'] = 'auto' template_dict['ffmpeg']['extra_arguments'] = [] - template_dict['video2x']['video2x_cache_folder'] = False + template_dict['video2x']['video2x_cache_folder'] = None template_dict['video2x']['preserve_frames'] = False # Write configuration into file with open('video2x.json', 'w') as config: - json.dump(template_dict, config, indent=2) + json.dump(template_dict, config, indent=4) config.close() @@ -211,11 +213,19 @@ def pip_install(package): if __name__ == "__main__": try: args = process_arguments() - print('Video2x Setup Script') + print('Video2X Setup Script') print('Version: {}'.format(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() - print('\n Script finished successfully') + print('\nScript finished successfully') except Exception: traceback.print_exc() print('An error has occurred') From 7b501f2c67a9f4281617c5c677fc772def9db4ec Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Sun, 31 Mar 2019 03:58:09 -0400 Subject: [PATCH 21/36] added video2x builds information --- README.md | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 235c20f..26cb3a6 100644 --- a/README.md +++ b/README.md @@ -2,24 +2,30 @@ ### 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 -Component names that are **bolded** are mandatory. - -Component names that are *italicized* can be automatically downloaded and configured with the `video2x_setup.py` script. +Component names that are **bolded** can be automatically downloaded and configured with the `video2x_setup.py` script. 1. Operating System: Windows -1. AMD GPU / Nvidia GPU -1. AMD GPU driver / Nvidia GPU driver / Nvidia CUDNN -1. [***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) +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) ## Recent Changes ### 2.7.0 (March 30, 2019) - 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) ### Setup Script 1.2.0 (March 26, 2019) @@ -31,7 +37,7 @@ Component names that are *italicized* can be automatically downloaded and config - 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. +- Exceptions in waifu2x are now caught, and script will now stop on waifu2x error instead of keep going on to FFmpeg. ## Description @@ -75,7 +81,7 @@ Go to the [Waifu2X Drivers](https://github.com/K4YT3X/video2x/wiki/Waifu2X-Drive - **Python 3** Download: https://www.python.org/downloads/windows/ -- **FFMPEG Windows Build** +- **FFmpeg Windows Build** Download: https://ffmpeg.org/download.html - **waifu2x-caffe** (for Nvidia CUDA/CUDNN) Download: https://github.com/lltcggie/waifu2x-caffe/releases @@ -215,7 +221,7 @@ https://www.gnu.org/licenses/gpl-3.0.txt 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-converter-cpp](https://github.com/DeadSix27/waifu2x-converter-cpp) From 3d46e1a822236db1cb25f970ae1c8bf492334f1d Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Mon, 1 Apr 2019 16:02:26 -0400 Subject: [PATCH 22/36] changed default crf value to visually lossless, added video bitrate limit option --- bin/video2x.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/video2x.json b/bin/video2x.json index 0f85815..bcdb833 100644 --- a/bin/video2x.json +++ b/bin/video2x.json @@ -51,7 +51,8 @@ "-qscale:a": null, "-f": "image2", "-vcodec": "libx264", - "-crf": 25, + "-crf": 17, + "-b:v": null, "-pix_fmt": "yuv420p", "-hwaccel": "auto", "-y": true From 405c4b66363c534eda8c08f4820080497c7bb1d3 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Tue, 2 Apr 2019 16:23:59 -0400 Subject: [PATCH 23/36] fixed error reading video resolution --- bin/upscaler.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/upscaler.py b/bin/upscaler.py index 25bd0d5..1d770f7 100644 --- a/bin/upscaler.py +++ b/bin/upscaler.py @@ -283,10 +283,10 @@ class Upscaler: # width/height will be coded width/height x upscale factor if self.scale_ratio: - coded_width = video_info['streams'][video_stream_index]['coded_width'] - coded_height = video_info['streams'][video_stream_index]['coded_height'] - self.scale_width = int(self.scale_ratio * coded_width) - self.scale_height = int(self.scale_ratio * coded_height) + original_width = video_info['streams'][video_stream_index]['width'] + original_height = video_info['streams'][video_stream_index]['height'] + self.scale_width = int(self.scale_ratio * original_width) + self.scale_height = int(self.scale_ratio * original_height) # upscale images one by one using waifu2x Avalon.info('Starting to upscale extracted images') From a57465e866375180a683e8a9d8c4925d040d094c Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Thu, 11 Apr 2019 14:53:46 -0400 Subject: [PATCH 24/36] added input and output file type mismatch check --- bin/video2x.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/bin/video2x.py b/bin/video2x.py index fb4086f..cada60f 100644 --- a/bin/video2x.py +++ b/bin/video2x.py @@ -13,7 +13,7 @@ __ __ _ _ ___ __ __ Name: Video2X Controller Author: K4YT3X Date Created: Feb 24, 2018 -Last Modified: March 30, 2019 +Last Modified: April 11, 2019 Licensed under the GNU General Public License Version 3 (GNU GPL v3), available at: https://www.gnu.org/licenses/gpl-3.0.txt @@ -289,6 +289,17 @@ try: if os.path.isfile(args.input): """ Upscale single video file """ Avalon.info('Upscaling single video file: {}'.format(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 folder') + raise Exception('input output path type mismatch') + elif not re.search('.*\..*$', args.input): + 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) # set optional options From 6538abd6e55905f899f2e59c25ea49cb4eb93ef9 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Thu, 11 Apr 2019 14:56:51 -0400 Subject: [PATCH 25/36] corrected a typo --- bin/video2x.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/video2x.py b/bin/video2x.py index cada60f..a3e1d96 100644 --- a/bin/video2x.py +++ b/bin/video2x.py @@ -295,7 +295,7 @@ try: Avalon.error('Input and output path type mismatch') Avalon.error('Input is single file but output is folder') raise Exception('input output path type mismatch') - elif not re.search('.*\..*$', args.input): + 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') From 837aca371eb3f1cc32a6aff1923c7c6a248387b3 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Thu, 18 Apr 2019 14:56:30 -0400 Subject: [PATCH 26/36] fixed temp folders bug --- bin/upscaler.py | 12 +++++++----- bin/video2x.py | 6 ++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/bin/upscaler.py b/bin/upscaler.py index 1d770f7..1a3ae64 100644 --- a/bin/upscaler.py +++ b/bin/upscaler.py @@ -56,20 +56,22 @@ class Upscaler: self.image_format = 'png' self.preserve_frames = False - # create temporary folder/directories + def create_temp_folders(self): + """create temporary folder/directories + """ self.extracted_frames = tempfile.mkdtemp(dir=self.video2x_cache_folder) Avalon.debug_info('Extracted frames are being saved to: {}'.format(self.extracted_frames)) self.upscaled_frames = tempfile.mkdtemp(dir=self.video2x_cache_folder) Avalon.debug_info('Upscaled frames are being saved to: {}'.format(self.upscaled_frames)) def cleanup(self): - # delete temp directories when done - # avalon framework cannot be used if python is shutting down - # therefore, plain print is used + """delete temp directories when done + """ if not self.preserve_frames: - for directory in [self.extracted_frames, self.upscaled_frames]: try: + # avalon framework cannot be used if python is shutting down + # therefore, plain print is used print('Cleaning up cache directory: {}'.format(directory)) shutil.rmtree(directory) except (OSError, FileNotFoundError): diff --git a/bin/video2x.py b/bin/video2x.py index a3e1d96..d7c1e48 100644 --- a/bin/video2x.py +++ b/bin/video2x.py @@ -52,7 +52,7 @@ import tempfile import time import traceback -VERSION = '2.7.0' +VERSION = '2.7.1' # each thread might take up to 2.5 GB during initialization. # (system memory, not to be confused with GPU memory) @@ -313,7 +313,8 @@ try: upscaler.image_format = image_format upscaler.preserve_frames = preserve_frames - # run upscaler- + # run upscaler + upscaler.create_temp_folders() upscaler.run() upscaler.cleanup() @@ -337,6 +338,7 @@ try: upscaler.preserve_frames = preserve_frames # run upscaler + upscaler.create_temp_folders() upscaler.run() upscaler.cleanup() else: From 6c1a714a1e55d9ed8c540283612fec5cdf5b7f5c Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Thu, 18 Apr 2019 14:57:30 -0400 Subject: [PATCH 27/36] updated README for 2.7.1 --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 26cb3a6..e66e846 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,10 @@ Component names that are **bolded** can be automatically downloaded and configur ## Recent Changes +### 2.7.1 (April 18, 2019) + +- Fixed video2x custom temp folder bug found by @cr08 . + ### 2.7.0 (March 30, 2019) - Added support for different extracted image formats. @@ -33,12 +37,6 @@ Component names that are **bolded** can be automatically downloaded and configur - `video2x_setup.py` script can now automatically download and configure `waifu2x-converter-cpp`. - 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 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. @@ -225,6 +223,12 @@ 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) +## Special Thanks + +Appreciations given to the following contributors: + +- @BrianPetkovsek + ## 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. \ No newline at end of file From 19cb823591d2d6cd568e4caa3b9405602350adfa Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Mon, 22 Apr 2019 01:26:36 -0400 Subject: [PATCH 28/36] all string formatting switched to f-string --- bin/ffmpeg.py | 22 +++++++++---------- bin/image_cleaner.py | 2 +- bin/upscaler.py | 22 +++++++++---------- bin/video2x.py | 47 ++++++++++++++++++++-------------------- bin/video2x_setup.py | 32 ++++++++++++++------------- bin/waifu2x_caffe.py | 12 +++++----- bin/waifu2x_converter.py | 14 ++++++------ 7 files changed, 76 insertions(+), 75 deletions(-) diff --git a/bin/ffmpeg.py b/bin/ffmpeg.py index 154a9d1..9fdc6c8 100644 --- a/bin/ffmpeg.py +++ b/bin/ffmpeg.py @@ -4,7 +4,7 @@ Name: FFMPEG Class Author: K4YT3X Date Created: Feb 24, 2018 -Last Modified: March 30, 2019 +Last Modified: April 21, 2019 Description: This class handles all FFMPEG related operations. @@ -29,10 +29,10 @@ class Ffmpeg: # add a forward slash to directory if not present # otherwise there will be a format error 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_probe_binary = '{}ffprobe.exe'.format(self.ffmpeg_path) + self.ffmpeg_binary = f'{self.ffmpeg_path}ffmpeg.exe' + self.ffmpeg_probe_binary = f'{self.ffmpeg_path}ffprobe.exe' self.image_format = image_format def get_video_info(self, input_video): @@ -59,10 +59,10 @@ class Ffmpeg: '-show_format', '-show_streams', '-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 return json.loads(json_str.decode('utf-8')) @@ -80,7 +80,7 @@ class Ffmpeg: self.ffmpeg_binary, '-i', 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') @@ -102,8 +102,8 @@ class Ffmpeg: '-s', resolution, '-i', - '{}\\extracted_%d.{}'.format(upscaled_frames, self.image_format), - '{}\\no_audio.mp4'.format(upscaled_frames) + f'{upscaled_frames}\\extracted_%d.{self.image_format}', + f'{upscaled_frames}\\no_audio.mp4' ] self._execute(execute=execute, phase='frames_to_video') @@ -118,7 +118,7 @@ class Ffmpeg: execute = [ self.ffmpeg_binary, '-i', - '{}\\no_audio.mp4'.format(upscaled_frames), + f'{upscaled_frames}\\no_audio.mp4', '-i', input_video, output_video @@ -143,5 +143,5 @@ class Ffmpeg: execute.append(str(value)) - Avalon.debug_info('Executing: {}'.format(execute)) + Avalon.debug_info(f'Executing: {execute}') return subprocess.run(execute, shell=True, check=True).returncode diff --git a/bin/image_cleaner.py b/bin/image_cleaner.py index 295682c..1f4106d 100644 --- a/bin/image_cleaner.py +++ b/bin/image_cleaner.py @@ -5,7 +5,7 @@ Name: Video2X Image Cleaner Author: BrianPetkovsek Author: K4YT3X Date Created: March 24, 2019 -Last Modified: March 24, 2019 +Last Modified: April 21, 2019 Description: This class is to remove the extracted frames that have already been upscaled. diff --git a/bin/upscaler.py b/bin/upscaler.py index 1a3ae64..a64ff7e 100644 --- a/bin/upscaler.py +++ b/bin/upscaler.py @@ -4,7 +4,7 @@ Name: Video2X Upscaler Author: K4YT3X Date Created: December 10, 2018 -Last Modified: March 30, 2019 +Last Modified: April 21, 2019 Licensed under the GNU General Public License Version 3 (GNU GPL v3), available at: https://www.gnu.org/licenses/gpl-3.0.txt @@ -52,7 +52,7 @@ class Upscaler: self.scale_ratio = None self.model_dir = None self.threads = 5 - self.video2x_cache_folder = '{}\\video2x'.format(tempfile.gettempdir()) + self.video2x_cache_folder = f'{tempfile.gettempdir()}\\video2x' self.image_format = 'png' self.preserve_frames = False @@ -60,9 +60,9 @@ class Upscaler: """create temporary folder/directories """ self.extracted_frames = tempfile.mkdtemp(dir=self.video2x_cache_folder) - Avalon.debug_info('Extracted frames are being saved to: {}'.format(self.extracted_frames)) + Avalon.debug_info(f'Extracted frames are being saved to: {self.extracted_frames}') self.upscaled_frames = tempfile.mkdtemp(dir=self.video2x_cache_folder) - Avalon.debug_info('Upscaled frames are being saved to: {}'.format(self.upscaled_frames)) + Avalon.debug_info(f'Upscaled frames are being saved to: {self.upscaled_frames}') def cleanup(self): """delete temp directories when done @@ -72,7 +72,7 @@ class Upscaler: try: # avalon framework cannot be used if python is shutting down # therefore, plain print is used - print('Cleaning up cache directory: {}'.format(directory)) + print(f'Cleaning up cache directory: {directory}') shutil.rmtree(directory) except (OSError, FileNotFoundError): pass @@ -152,8 +152,8 @@ class Upscaler: 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))]: - renamed = re.sub('_\[.*-.*\]\[x(\d+(\.\d+)?)\]\.{}'.format(self.image_format), '.{}'.format(self.image_format), image) - shutil.move('{}\\{}'.format(self.upscaled_frames, image), '{}\\{}'.format(self.upscaled_frames, renamed)) + renamed = re.sub(f'_\[.*-.*\]\[x(\d+(\.\d+)?)\]\.{self.image_format}', f'.{self.image_format}', image) + shutil.move(f'{self.upscaled_frames}\\{image}', f'{self.upscaled_frames}\\{renamed}') self.progress_bar_exit_signal = True progress_bar.join() @@ -176,7 +176,7 @@ class Upscaler: thread_pool = [] thread_folders = [] for thread_id in range(self.threads): - thread_folder = '{}\\{}'.format(self.extracted_frames, str(thread_id)) + thread_folder = f'{self.extracted_frames}\\{str(thread_id)}' thread_folders.append(thread_folder) # delete old folders and create new folders @@ -257,7 +257,7 @@ class Upscaler: elif self.waifu2x_driver == 'waifu2x_converter': w2 = Waifu2xConverter(self.waifu2x_settings, self.model_dir) else: - raise Exception('Unrecognized waifu2x driver: {}'.format(self.waifu2x_driver)) + raise Exception(f'Unrecognized waifu2x driver: {self.waifu2x_driver}') # extract frames from video fm.extract_frames(self.input_video, self.extracted_frames) @@ -281,7 +281,7 @@ class Upscaler: # get average frame rate of video stream 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 if self.scale_ratio: @@ -299,7 +299,7 @@ class Upscaler: Avalon.info('Converting extracted frames into video') # 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') # migrate audio tracks and subtitles diff --git a/bin/video2x.py b/bin/video2x.py index d7c1e48..cab306c 100644 --- a/bin/video2x.py +++ b/bin/video2x.py @@ -13,7 +13,7 @@ __ __ _ _ ___ __ __ Name: Video2X Controller Author: K4YT3X Date Created: Feb 24, 2018 -Last Modified: April 11, 2019 +Last Modified: April 21, 2019 Licensed under the GNU General Public License Version 3 (GNU GPL v3), available at: https://www.gnu.org/licenses/gpl-3.0.txt @@ -80,7 +80,7 @@ def process_arguments(): 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('-t', '--threads', help='Number of threads to use for upscaling', action='store', type=int, default=5) - 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') # scaling options @@ -101,8 +101,8 @@ def print_logo(): print(' \\ / | | | (_| | | __/ | (_) | / /_ / . \\') print(' \\/ |_| \\__,_| \\___| \\___/ |____| /_/ \\_\\') print('\n Video2X Video Enlarger') - spaces = ((44 - len("Version {}".format(VERSION))) // 2) * " " - print('{}\n{} Version {}\n{}'.format(Avalon.FM.BD, spaces, VERSION, Avalon.FM.RST)) + spaces = ((44 - len(f'Version {VERSION}')) // 2) * ' ' + print(f'{Avalon.FM.BD}\n{spaces} Version {VERSION}\n{Avalon.FM.RST}') def check_memory(): @@ -141,17 +141,17 @@ def check_memory(): # if user doesn't even have enough memory to run even one 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') if args.threads > 1: if Avalon.ask('Reduce number of threads to avoid crashing?', default=True, batch=args.batch): args.threads = 1 # if memory available is less than needed, warn the user 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('You demanded {} threads to be created, but you only have {} GB {} memory available'.format(args.threads, round(memory_available, 4), memory_type)) - Avalon.warning('{} GB of {} memory is recommended for {} threads'.format(mem_per_thread * args.threads, memory_type, args.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'Each waifu2x-caffe thread will require up to {SYS_MEM_PER_THREAD} GB of system memory') + 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(f'{mem_per_thread * args.threads} GB of {memory_type} memory is recommended for {args.threads} threads') + 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 # number of threads @@ -187,20 +187,20 @@ def absolutify_paths(config): # check waifu2x-caffe path if not re.match('^[a-z]:', config['waifu2x_caffe']['waifu2x_caffe_path'], re.IGNORECASE): - config['waifu2x_caffe']['waifu2x_caffe_path'] = '{}\\{}'.format(current_folder, config['waifu2x_caffe']['waifu2x_caffe_path']) + config['waifu2x_caffe']['waifu2x_caffe_path'] = f'{current_folder}\\{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'] = '{}\\{}'.format(current_folder, config['waifu2x_converter']['waifu2x_converter_path']) + config['waifu2x_converter']['waifu2x_converter_path'] = f'{current_folder}\\{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'] = '{}\\{}'.format(current_folder, config['ffmpeg']['ffmpeg_path']) + config['ffmpeg']['ffmpeg_path'] = f'{current_folder}\\{config["ffmpeg"]["ffmpeg_path"]}' # check video2x cache path if config['video2x']['video2x_cache_folder']: if not re.match('^[a-z]:', config['video2x']['video2x_cache_folder'], re.IGNORECASE): - config['video2x']['video2x_cache_folder'] = '{}\\{}'.format(current_folder, config['video2x']['video2x_cache_folder']) + config['video2x']['video2x_cache_folder'] = f'{current_folder}\\{config["video2x"]["video2x_cache_folder"]}' return config @@ -210,7 +210,7 @@ def absolutify_paths(config): # this is not a library if __name__ != '__main__': Avalon.error('This file cannot be imported') - raise ImportError('{} cannot be imported'.format(__file__)) + raise ImportError(f'{__file__} cannot be imported') print_logo() @@ -262,16 +262,16 @@ preserve_frames = config['video2x']['preserve_frames'] # create temp directories if they don't exist if not video2x_cache_folder: - video2x_cache_folder = '{}\\video2x'.format(tempfile.gettempdir()) + video2x_cache_folder = f'{tempfile.gettempdir()}\\video2x' if video2x_cache_folder and not os.path.isdir(video2x_cache_folder): if not os.path.isfile(video2x_cache_folder) and not os.path.islink(video2x_cache_folder): - Avalon.warning('Specified cache folder/directory {} does not exist'.format(video2x_cache_folder)) + Avalon.warning(f'Specified cache folder/directory {video2x_cache_folder} does not exist') if Avalon.ask('Create folder/directory?', default=True, batch=args.batch): if os.mkdir(video2x_cache_folder) is None: - Avalon.info('{} created'.format(video2x_cache_folder)) + Avalon.info(f'{video2x_cache_folder} created') else: - Avalon.error('Unable to create {}'.format(video2x_cache_folder)) + Avalon.error(f'Unable to create {video2x_cache_folder}') Avalon.error('Aborting...') exit(1) else: @@ -288,7 +288,7 @@ try: # if input specified is a single file if os.path.isfile(args.input): """ 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): @@ -321,9 +321,9 @@ try: # if input specified is a folder elif os.path.isdir(args.input): """ Upscale videos in a folder/directory """ - Avalon.info('Upscaling videos in folder/directory: {}'.format(args.input)) + Avalon.info(f'Upscaling videos in folder/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))]: - 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) # set optional options @@ -343,13 +343,12 @@ try: upscaler.cleanup() else: Avalon.error('Input path is neither a file nor a folder/directory') - raise FileNotFoundError('{} is neither file nor folder/directory'.format(args.input)) + raise FileNotFoundError(f'{args.input} is neither file nor folder/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: Avalon.error('An exception has occurred') traceback.print_exc() - Avalon.warning('If you experience error \"cudaSuccess out of memory\", try reducing number of threads you\'re using') finally: # remove Video2X Cache folder try: diff --git a/bin/video2x_setup.py b/bin/video2x_setup.py index 6adb959..608757e 100644 --- a/bin/video2x_setup.py +++ b/bin/video2x_setup.py @@ -4,7 +4,7 @@ Name: Video2X Setup Script Author: K4YT3X Date Created: November 28, 2018 -Last Modified: March 31, 2019 +Last Modified: April 21, 2019 Licensed under the GNU General Public License Version 3 (GNU GPL v3), available at: https://www.gnu.org/licenses/gpl-3.0.txt @@ -96,7 +96,7 @@ class Video2xSetup: """ for file in self.trash: try: - print('Deleting: {}'.format(file)) + print(f'Deleting: {file}') os.remove(file) except FileNotFoundError: pass @@ -110,7 +110,7 @@ class Video2xSetup: self.trash.append(ffmpeg_zip) 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): """ Install waifu2x_caffe @@ -127,7 +127,7 @@ class Video2xSetup: self.trash.append(waifu2x_caffe_zip) 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): """ Install waifu2x_caffe @@ -145,7 +145,7 @@ class Video2xSetup: self.trash.append(waifu2x_converter_cpp_zip) 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): """ Generate video2x config @@ -155,16 +155,18 @@ class Video2xSetup: template_dict = json.load(template) template.close() + local_app_data = os.getenv('localappdata') + # configure only the specified drivers 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_converter']['waifu2x_converter_path'] = '{}\\video2x\\waifu2x-converter-cpp'.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'] = f'{local_app_data}\\video2x\\waifu2x-converter-cpp' 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': - 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['ffmpeg']['extra_arguments'] = [] template_dict['video2x']['video2x_cache_folder'] = None @@ -182,10 +184,10 @@ def download(url, save_path, chunk_size=4096): from tqdm import tqdm import requests - output_file = '{}\\{}'.format(save_path, url.split('/')[-1]) - print('Downloading: {}'.format(url)) - print('Chunk size: {}'.format(chunk_size)) - print('Saving to: {}'.format(output_file)) + output_file = f'{save_path}\\{url.split("/")[-1]}' + print(f'Downloading: {url}') + print(f'Chunk size: {chunk_size}') + print(f'Saving to: {output_file}') stream = requests.get(url, stream=True) total_size = int(stream.headers['content-length']) @@ -214,7 +216,7 @@ if __name__ == "__main__": try: args = process_arguments() print('Video2X Setup Script') - print('Version: {}'.format(VERSION)) + print(f'Version: {VERSION}') # do not install pip modules if script # is packaged in exe format diff --git a/bin/waifu2x_caffe.py b/bin/waifu2x_caffe.py index f716422..c274f89 100644 --- a/bin/waifu2x_caffe.py +++ b/bin/waifu2x_caffe.py @@ -4,7 +4,7 @@ Name: Waifu2x Caffe Driver Author: K4YT3X Date Created: Feb 24, 2018 -Last Modified: March 30, 2019 +Last Modified: April 21, 2019 Description: This class is a high-level wrapper for waifu2x-caffe. @@ -58,7 +58,7 @@ class Waifu2xCaffe: # print thread start message 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() # list to be executed @@ -74,17 +74,17 @@ class Waifu2xCaffe: continue else: if len(key) == 1: - execute.append('-{}'.format(key)) + execute.append(f'-{key}') else: - execute.append('--{}'.format(key)) + execute.append(f'--{key}') execute.append(str(value)) - Avalon.debug_info('Executing: {}'.format(execute)) + Avalon.debug_info(f'Executing: {execute}') completed_command = subprocess.run(execute, check=True) # print thread exiting message 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() # return command execution return code diff --git a/bin/waifu2x_converter.py b/bin/waifu2x_converter.py index 97f8245..7136439 100644 --- a/bin/waifu2x_converter.py +++ b/bin/waifu2x_converter.py @@ -4,7 +4,7 @@ Name: Waifu2x Converter CPP Driver Author: K4YT3X Date Created: February 8, 2019 -Last Modified: March 30, 2019 +Last Modified: April 21, 2019 Description: This class is a high-level wrapper for waifu2x-converter-cpp. @@ -59,11 +59,11 @@ class Waifu2xConverter: # 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'] = '{}\\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 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() # list to be executed @@ -75,16 +75,16 @@ class Waifu2xConverter: # the key doesn't need to be passed in this case 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) elif value is None or value is False: continue else: if len(key) == 1: - execute.append('-{}'.format(key)) + execute.append(f'-{key}') else: - execute.append('--{}'.format(key)) + execute.append(f'--{key}') # true means key is an option if value is True: @@ -92,7 +92,7 @@ class Waifu2xConverter: execute.append(str(value)) - Avalon.debug_info('Executing: {}'.format(execute)) + Avalon.debug_info(f'Executing: {execute}') return subprocess.run(execute, check=True).returncode except Exception as e: From 0af68bb39c96617be8bb881442e341914c728304 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Mon, 29 Apr 2019 00:05:56 -0400 Subject: [PATCH 29/36] added description for Q&A page --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index e66e846..afe4ddc 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,10 @@ For those who want a detailed walk-through of how to use `Video2X`, you can head 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. + --- ## Quick Start From c7353c4bf2f5e1b0a96043994b55378329671416 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Mon, 29 Apr 2019 00:06:54 -0400 Subject: [PATCH 30/36] unified all "folder/directory" into "directory" to end this mess --- bin/ffmpeg.py | 6 ++-- bin/image_cleaner.py | 24 +++++++------- bin/upscaler.py | 52 +++++++++++++++--------------- bin/video2x.json | 3 +- bin/video2x.py | 68 ++++++++++++++++++++-------------------- bin/video2x_setup.py | 6 ++-- bin/waifu2x_caffe.py | 12 +++---- bin/waifu2x_converter.py | 16 +++++----- 8 files changed, 93 insertions(+), 94 deletions(-) diff --git a/bin/ffmpeg.py b/bin/ffmpeg.py index 9fdc6c8..cace942 100644 --- a/bin/ffmpeg.py +++ b/bin/ffmpeg.py @@ -4,7 +4,7 @@ Name: FFMPEG Class Author: K4YT3X Date Created: Feb 24, 2018 -Last Modified: April 21, 2019 +Last Modified: April 28, 2019 Description: This class handles all FFMPEG related operations. @@ -74,7 +74,7 @@ class Ffmpeg: Arguments: input_video {string} -- input video path - extracted_frames {string} -- video output folder + extracted_frames {string} -- video output directory """ execute = [ self.ffmpeg_binary, @@ -93,7 +93,7 @@ class Ffmpeg: Arguments: framerate {float} -- target video framerate resolution {string} -- target video resolution - upscaled_frames {string} -- source images folder + upscaled_frames {string} -- source images directory """ execute = [ self.ffmpeg_binary, diff --git a/bin/image_cleaner.py b/bin/image_cleaner.py index 1f4106d..3b878a8 100644 --- a/bin/image_cleaner.py +++ b/bin/image_cleaner.py @@ -5,7 +5,7 @@ Name: Video2X Image Cleaner Author: BrianPetkovsek Author: K4YT3X Date Created: March 24, 2019 -Last Modified: April 21, 2019 +Last Modified: April 28, 2019 Description: This class is to remove the extracted frames that have already been upscaled. @@ -27,11 +27,11 @@ class ImageCleaner(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) - self.input_folder = input_folder - self.output_folder = output_folder - self.num_threads = num_threads + self.input_directory = input_directory + self.output_directory = output_directory + self.threads = threads self.running = False def run(self): @@ -53,23 +53,23 @@ class ImageCleaner(threading.Thread): """ remove frames that have already been upscaled 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. """ # 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 - # within each thread's extracted frames folder - for i in range(self.num_threads): - dir_path = os.path.join(self.input_folder, str(i)) + # within each thread's extracted frames directory + for i in range(self.threads): + 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): 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 if os.path.isfile(file_path) and f in output_frames: os.remove(file_path) diff --git a/bin/upscaler.py b/bin/upscaler.py index a64ff7e..69da3e7 100644 --- a/bin/upscaler.py +++ b/bin/upscaler.py @@ -4,7 +4,7 @@ Name: Video2X Upscaler Author: K4YT3X Date Created: December 10, 2018 -Last Modified: April 21, 2019 +Last Modified: April 28, 2019 Licensed under the GNU General Public License Version 3 (GNU GPL v3), available at: https://www.gnu.org/licenses/gpl-3.0.txt @@ -30,7 +30,7 @@ import time class Upscaler: """ 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: Exception -- all exceptions @@ -52,19 +52,19 @@ class Upscaler: self.scale_ratio = None self.model_dir = None self.threads = 5 - self.video2x_cache_folder = f'{tempfile.gettempdir()}\\video2x' + self.video2x_cache_directory = f'{tempfile.gettempdir()}\\video2x' self.image_format = 'png' self.preserve_frames = False - def create_temp_folders(self): - """create temporary folder/directories + def create_temp_directories(self): + """create temporary directory """ - self.extracted_frames = tempfile.mkdtemp(dir=self.video2x_cache_folder) + self.extracted_frames = tempfile.mkdtemp(dir=self.video2x_cache_directory) Avalon.debug_info(f'Extracted frames are being saved to: {self.extracted_frames}') - self.upscaled_frames = tempfile.mkdtemp(dir=self.video2x_cache_folder) + 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 """ if not self.preserve_frames: @@ -89,18 +89,18 @@ class Upscaler: elif not self.method: 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 by keeping track - of the amount of frames in the input directory/folder - and the output directory/folder. This is originally + of the amount of frames in the input directory + and the output directory. This is originally suggested by @ArmandBernard. """ # get number of extracted frames total_frames = 0 - for folder in extracted_frames_folders: - total_frames += len([f for f in os.listdir(folder) if f[-4:] == '.png']) + for directory in extracted_frames_directories: + 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: @@ -170,25 +170,25 @@ class Upscaler: if len(frames) < self.threads: 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 thread_pool = [] - thread_folders = [] + thread_directories = [] for thread_id in range(self.threads): - thread_folder = f'{self.extracted_frames}\\{str(thread_id)}' - thread_folders.append(thread_folder) + thread_directory = f'{self.extracted_frames}\\{str(thread_id)}' + thread_directories.append(thread_directory) - # delete old folders and create new folders - if os.path.isdir(thread_folder): - shutil.rmtree(thread_folder) - os.mkdir(thread_folder) + # delete old directories and create new directories + if os.path.isdir(thread_directory): + shutil.rmtree(thread_directory) + os.mkdir(thread_directory) - # append folder path into list - thread_pool.append((thread_folder, thread_id)) + # append directory path into list + thread_pool.append((thread_directory, thread_id)) - # evenly distribute images into each folder - # until there is none left in the folder + # evenly distribute images into each directory + # until there is none left in the directory for image in frames: # move image shutil.move(image, thread_pool[0][0]) @@ -208,7 +208,7 @@ class Upscaler: upscaler_threads.append(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() # create the clearer and start it diff --git a/bin/video2x.json b/bin/video2x.json index bcdb833..270d536 100644 --- a/bin/video2x.json +++ b/bin/video2x.json @@ -62,12 +62,13 @@ "-map": "1?", "-c": "copy", "-map": "-1:v?", + "-pix_fmt": "yuv420p", "-hwaccel": "auto", "-y": true } }, "video2x": { - "video2x_cache_folder": null, + "video2x_cache_directory": null, "image_format": "png", "preserve_frames": false } diff --git a/bin/video2x.py b/bin/video2x.py index cab306c..2e2c231 100644 --- a/bin/video2x.py +++ b/bin/video2x.py @@ -13,7 +13,7 @@ __ __ _ _ ___ __ __ Name: Video2X Controller Author: K4YT3X Date Created: Feb 24, 2018 -Last Modified: April 21, 2019 +Last Modified: April 28, 2019 Licensed under the GNU General Public License Version 3 (GNU GPL v3), available at: https://www.gnu.org/licenses/gpl-3.0.txt @@ -78,7 +78,7 @@ def process_arguments(): 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('-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('-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') @@ -183,24 +183,24 @@ def absolutify_paths(config): Returns: dict -- configuration file dictionary """ - current_folder = os.path.dirname(os.path.abspath(sys.argv[0])) + 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_folder}\\{config["waifu2x_caffe"]["waifu2x_caffe_path"]}' + 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_folder}\\{config["waifu2x_converter"]["waifu2x_converter_path"]}' + 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_folder}\\{config["ffmpeg"]["ffmpeg_path"]}' + config['ffmpeg']['ffmpeg_path'] = f'{current_directory}\\{config["ffmpeg"]["ffmpeg_path"]}' # check video2x cache path - if config['video2x']['video2x_cache_folder']: - if not re.match('^[a-z]:', config['video2x']['video2x_cache_folder'], re.IGNORECASE): - config['video2x']['video2x_cache_folder'] = f'{current_folder}\\{config["video2x"]["video2x_cache_folder"]}' + 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 @@ -256,26 +256,26 @@ elif args.driver == 'waifu2x_converter': ffmpeg_settings = config['ffmpeg'] # 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() preserve_frames = config['video2x']['preserve_frames'] # create temp directories if they don't exist -if not video2x_cache_folder: - video2x_cache_folder = f'{tempfile.gettempdir()}\\video2x' +if not video2x_cache_directory: + video2x_cache_directory = f'{tempfile.gettempdir()}\\video2x' -if video2x_cache_folder and not os.path.isdir(video2x_cache_folder): - if not os.path.isfile(video2x_cache_folder) and not os.path.islink(video2x_cache_folder): - Avalon.warning(f'Specified cache folder/directory {video2x_cache_folder} does not exist') - if Avalon.ask('Create folder/directory?', default=True, batch=args.batch): - if os.mkdir(video2x_cache_folder) is None: - Avalon.info(f'{video2x_cache_folder} created') +if video2x_cache_directory and not os.path.isdir(video2x_cache_directory): + if not os.path.isfile(video2x_cache_directory) and not os.path.islink(video2x_cache_directory): + Avalon.warning(f'Specified cache directory {video2x_cache_directory} does not exist') + if Avalon.ask('Create directory?', default=True, batch=args.batch): + if os.mkdir(video2x_cache_directory) is None: + Avalon.info(f'{video2x_cache_directory} created') else: - Avalon.error(f'Unable to create {video2x_cache_folder}') + Avalon.error(f'Unable to create {video2x_cache_directory}') Avalon.error('Aborting...') exit(1) 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...') exit(1) @@ -293,7 +293,7 @@ try: # 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 folder') + 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') @@ -309,19 +309,19 @@ try: upscaler.scale_ratio = args.ratio upscaler.model_dir = args.model_dir upscaler.threads = args.threads - upscaler.video2x_cache_folder = video2x_cache_folder + upscaler.video2x_cache_directory = video2x_cache_directory upscaler.image_format = image_format upscaler.preserve_frames = preserve_frames # run upscaler - upscaler.create_temp_folders() + upscaler.create_temp_directories() upscaler.run() - upscaler.cleanup() + upscaler.cleanup_temp_directories() - # if input specified is a folder + # if input specified is a directory elif os.path.isdir(args.input): - """ Upscale videos in a folder/directory """ - Avalon.info(f'Upscaling videos in folder/directory: {args.input}') + """ Upscale videos in a directory """ + 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))]: 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) @@ -333,26 +333,26 @@ try: upscaler.scale_ratio = args.ratio upscaler.model_dir = args.model_dir upscaler.threads = args.threads - upscaler.video2x_cache_folder = video2x_cache_folder + upscaler.video2x_cache_directory = video2x_cache_directory upscaler.image_format = image_format upscaler.preserve_frames = preserve_frames # run upscaler - upscaler.create_temp_folders() + upscaler.create_temp_directories() upscaler.run() - upscaler.cleanup() + upscaler.cleanup_temp_directories() else: - Avalon.error('Input path is neither a file nor a folder/directory') - raise FileNotFoundError(f'{args.input} is neither file nor folder/directory') + Avalon.error('Input path is neither a file nor a directory') + 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() finally: - # remove Video2X Cache folder + # remove Video2X Cache directory try: if not preserve_frames: - shutil.rmtree(video2x_cache_folder) + shutil.rmtree(video2x_cache_directory) except FileNotFoundError: pass diff --git a/bin/video2x_setup.py b/bin/video2x_setup.py index 608757e..d5f814c 100644 --- a/bin/video2x_setup.py +++ b/bin/video2x_setup.py @@ -4,7 +4,7 @@ Name: Video2X Setup Script Author: K4YT3X Date Created: November 28, 2018 -Last Modified: April 21, 2019 +Last Modified: April 28, 2019 Licensed under the GNU General Public License Version 3 (GNU GPL v3), available at: https://www.gnu.org/licenses/gpl-3.0.txt @@ -167,9 +167,7 @@ class Video2xSetup: template_dict['waifu2x_converter']['waifu2x_converter_path'] = f'{local_app_data}\\video2x\\waifu2x-converter-cpp' template_dict['ffmpeg']['ffmpeg_path'] = f'{local_app_data}\\video2x\\ffmpeg-latest-win64-static\\bin' - template_dict['ffmpeg']['ffmpeg_hwaccel'] = 'auto' - template_dict['ffmpeg']['extra_arguments'] = [] - template_dict['video2x']['video2x_cache_folder'] = None + template_dict['video2x']['video2x_cache_directory'] = None template_dict['video2x']['preserve_frames'] = False # Write configuration into file diff --git a/bin/waifu2x_caffe.py b/bin/waifu2x_caffe.py index c274f89..a3ea560 100644 --- a/bin/waifu2x_caffe.py +++ b/bin/waifu2x_caffe.py @@ -4,7 +4,7 @@ Name: Waifu2x Caffe Driver Author: K4YT3X Date Created: Feb 24, 2018 -Last Modified: April 21, 2019 +Last Modified: April 28, 2019 Description: This class is a high-level wrapper for waifu2x-caffe. @@ -33,20 +33,20 @@ class Waifu2xCaffe: self.model_dir = model_dir 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 Arguments: - input_folder {string} -- source folder path - output_folder {string} -- output folder path + input_directory {string} -- source directory path + output_directory {string} -- output directory path width {int} -- output video width height {int} -- output video height """ try: # overwrite config file settings - self.waifu2x_settings['input_path'] = input_folder - self.waifu2x_settings['output_path'] = output_folder + self.waifu2x_settings['input_path'] = input_directory + self.waifu2x_settings['output_path'] = output_directory if scale_ratio: self.waifu2x_settings['scale_ratio'] = scale_ratio diff --git a/bin/waifu2x_converter.py b/bin/waifu2x_converter.py index 7136439..4539bc4 100644 --- a/bin/waifu2x_converter.py +++ b/bin/waifu2x_converter.py @@ -4,7 +4,7 @@ Name: Waifu2x Converter CPP Driver Author: K4YT3X Date Created: February 8, 2019 -Last Modified: April 21, 2019 +Last Modified: April 28, 2019 Description: This class is a high-level wrapper for waifu2x-converter-cpp. @@ -28,26 +28,26 @@ class Waifu2xConverter: self.waifu2x_settings['model_dir'] = model_dir 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 This method executes the upscaling of extracted frames. Arguments: - input_folder {string} -- source folder path - output_folder {string} -- output folder path + input_directory {string} -- source directory path + output_directory {string} -- output directory path scale_ratio {int} -- frames' scale ratio threads {int} -- number of threads """ try: # overwrite config file settings - self.waifu2x_settings['input'] = input_folder - self.waifu2x_settings['output'] = output_folder + 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_folder - self.waifu2x_settings['o'] = output_folder + self.waifu2x_settings['i'] = input_directory + self.waifu2x_settings['o'] = output_directory self.waifu2x_settings['input'] = None self.waifu2x_settings['output'] = None """ From 875572c75abaf6b978f5f86ef1e7b03f9941530f Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Mon, 29 Apr 2019 00:11:30 -0400 Subject: [PATCH 31/36] corrected repo links and step by step instruction description --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index afe4ddc..bd7223b 100644 --- a/README.md +++ b/README.md @@ -59,21 +59,21 @@ Clip is from trailer of animated movie "千と千尋の神隠し". Copyright bel ## 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) +### [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. +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. --- @@ -95,7 +95,7 @@ Download: https://github.com/DeadSix27/waifu2x-converter-cpp/releases First, clone the video2x repository. ```shell -git clone https://github.com/K4YT3X/video2x.git +git clone https://github.com/k4yt3x/video2x.git cd video2x/bin ``` From a7d41cafdfb478ee620ae33764d4bc13cee9f288 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Tue, 30 Apr 2019 03:53:40 -0400 Subject: [PATCH 32/36] changed default number of threads to 1 --- bin/video2x.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/video2x.py b/bin/video2x.py index 2e2c231..eceb841 100644 --- a/bin/video2x.py +++ b/bin/video2x.py @@ -79,7 +79,7 @@ def process_arguments(): 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('-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=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') From 146044505b845224c67c5316580a283f47bd91e8 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Mon, 6 May 2019 16:25:10 -0400 Subject: [PATCH 33/36] fixed issue #77 incorrect output video format --- bin/ffmpeg.py | 101 ++++++++++++++++++++++++++++++++++++++--------- bin/video2x.json | 32 ++++++++------- 2 files changed, 101 insertions(+), 32 deletions(-) diff --git a/bin/ffmpeg.py b/bin/ffmpeg.py index cace942..9acf63d 100644 --- a/bin/ffmpeg.py +++ b/bin/ffmpeg.py @@ -4,7 +4,7 @@ Name: FFMPEG Class Author: K4YT3X Date Created: Feb 24, 2018 -Last Modified: April 28, 2019 +Last Modified: May 4, 2019 Description: This class handles all FFMPEG related operations. @@ -77,12 +77,18 @@ class Ffmpeg: extracted_frames {string} -- video output directory """ execute = [ - self.ffmpeg_binary, + self.ffmpeg_binary + ] + + execute.extend(self._read_configuration(phase='video_to_frames')) + + execute.extend([ '-i', input_video, 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): """Converts images into videos @@ -100,12 +106,30 @@ class Ffmpeg: '-r', str(framerate), '-s', - resolution, - '-i', - f'{upscaled_frames}\\extracted_%d.{self.image_format}', - f'{upscaled_frames}\\no_audio.mp4' + resolution ] - 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): """ Migrates audio tracks and subtitles from input video to output video @@ -120,28 +144,69 @@ class Ffmpeg: '-i', f'{upscaled_frames}\\no_audio.mp4', '-i', - input_video, - output_video + input_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) - 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 else: - execute.append(key) + configuration.append(key) # true means key is an option if value is True: continue - execute.append(str(value)) + configuration.append(str(value)) + 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 diff --git a/bin/video2x.json b/bin/video2x.json index 270d536..69e0a9a 100644 --- a/bin/video2x.json +++ b/bin/video2x.json @@ -47,23 +47,27 @@ "-y": true }, "frames_to_video": { - "-qscale:v": null, - "-qscale:a": null, - "-f": "image2", - "-vcodec": "libx264", - "-crf": 17, - "-b:v": null, - "-pix_fmt": "yuv420p", - "-hwaccel": "auto", + "input_options":{ + "-qscale:v": null, + "-qscale:a": null, + "-f": "image2" + }, + "output_options":{ + "-vcodec": "libx264", + "-crf": 17, + "-b:v": null, + "-pix_fmt": "yuv420p" + }, "-y": true }, "migrating_tracks": { - "-map": "0:v:0?", - "-map": "1?", - "-c": "copy", - "-map": "-1:v?", - "-pix_fmt": "yuv420p", - "-hwaccel": "auto", + "output_options":{ + "-map": "0:v:0?", + "-map": "1?", + "-c": "copy", + "-map": "-1:v?", + "-pix_fmt": "yuv420p" + }, "-y": true } }, From 4cf83dc565fc8f3ced9d9b6d1bb48312958d6566 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Sat, 25 May 2019 23:20:31 -0400 Subject: [PATCH 34/36] fixing progress bar bug found by @CardinalPanda --- bin/upscaler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/upscaler.py b/bin/upscaler.py index 69da3e7..6fb736b 100644 --- a/bin/upscaler.py +++ b/bin/upscaler.py @@ -100,7 +100,7 @@ class Upscaler: # get number of extracted frames total_frames = 0 for directory in extracted_frames_directories: - total_frames += len([f for f in os.listdir(directory) if f[-4:] == '.png']) + total_frames += len([f for f in os.listdir(directory) if f[-4:] == f'.{self.image_format}']) with tqdm(total=total_frames, ascii=True, desc='Upscaling Progress') as progress_bar: @@ -111,7 +111,7 @@ class Upscaler: while not self.progress_bar_exit_signal: try: - total_frames_upscaled = len([f for f in os.listdir(self.upscaled_frames) if f[-4:] == '.png']) + total_frames_upscaled = len([f for f in os.listdir(self.upscaled_frames) if f[-4:] == f'.{self.image_format}']) delta = total_frames_upscaled - previous_cycle_frames previous_cycle_frames = total_frames_upscaled From 0f4daa12d265edadaf689e980fa2878df788f541 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Wed, 5 Jun 2019 12:18:51 -0400 Subject: [PATCH 35/36] fixing a bug found by @CardinalPanda --- bin/ffmpeg.py | 8 +++++--- bin/video2x.json | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/bin/ffmpeg.py b/bin/ffmpeg.py index 9acf63d..cba3333 100644 --- a/bin/ffmpeg.py +++ b/bin/ffmpeg.py @@ -4,7 +4,7 @@ Name: FFMPEG Class Author: K4YT3X Date Created: Feb 24, 2018 -Last Modified: May 4, 2019 +Last Modified: June 5, 2019 Description: This class handles all FFMPEG related operations. @@ -80,14 +80,16 @@ class Ffmpeg: self.ffmpeg_binary ] - execute.extend(self._read_configuration(phase='video_to_frames')) - execute.extend([ '-i', input_video, f'{extracted_frames}\\extracted_%0d.{self.image_format}' ]) + execute.extend(self._read_configuration(phase='video_to_frames', section='output_options')) + + execute.extend(self._read_configuration(phase='video_to_frames')) + self._execute(execute) def convert_video(self, framerate, resolution, upscaled_frames): diff --git a/bin/video2x.json b/bin/video2x.json index 69e0a9a..1e78a42 100644 --- a/bin/video2x.json +++ b/bin/video2x.json @@ -42,7 +42,9 @@ "ffmpeg": { "ffmpeg_path": "C:\\Users\\K4YT3X\\AppData\\Local\\video2x\\ffmpeg-latest-win64-static\\bin", "video_to_frames": { - "-qscale:v": null, + "output_options":{ + "-qscale:v": null + }, "-hwaccel": "auto", "-y": true }, From 6b3e1b97680e8017f11ae30a3a36de43ebb1c308 Mon Sep 17 00:00:00 2001 From: k4yt3x Date: Sun, 9 Jun 2019 21:31:13 -0400 Subject: [PATCH 36/36] fixing video to frames ffmpeg config parsing order error --- bin/ffmpeg.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bin/ffmpeg.py b/bin/ffmpeg.py index cba3333..91daf55 100644 --- a/bin/ffmpeg.py +++ b/bin/ffmpeg.py @@ -82,12 +82,15 @@ class Ffmpeg: execute.extend([ '-i', - input_video, - f'{extracted_frames}\\extracted_%0d.{self.image_format}' + input_video ]) execute.extend(self._read_configuration(phase='video_to_frames', section='output_options')) + execute.extend([ + f'{extracted_frames}\\extracted_%0d.{self.image_format}' + ]) + execute.extend(self._read_configuration(phase='video_to_frames')) self._execute(execute)