diff --git a/src/locale/zh_CN/LC_MESSAGES/video2x.mo b/src/locale/zh_CN/LC_MESSAGES/video2x.mo index f4470bb..0942d7f 100644 Binary files a/src/locale/zh_CN/LC_MESSAGES/video2x.mo and b/src/locale/zh_CN/LC_MESSAGES/video2x.mo differ diff --git a/src/locale/zh_CN/LC_MESSAGES/zh_CN.mo b/src/locale/zh_CN/LC_MESSAGES/zh_CN.mo index f4470bb..0942d7f 100644 Binary files a/src/locale/zh_CN/LC_MESSAGES/zh_CN.mo and b/src/locale/zh_CN/LC_MESSAGES/zh_CN.mo differ diff --git a/src/locale/zh_CN/LC_MESSAGES/zh_CN.po b/src/locale/zh_CN/LC_MESSAGES/zh_CN.po index 622bf85..189c9bf 100644 --- a/src/locale/zh_CN/LC_MESSAGES/zh_CN.po +++ b/src/locale/zh_CN/LC_MESSAGES/zh_CN.po @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" -"POT-Creation-Date: 2020-05-22 17:51-0400\n" -"PO-Revision-Date: 2020-05-22 17:54-0400\n" +"POT-Creation-Date: 2020-09-09 13:04-0400\n" +"PO-Revision-Date: 2020-09-09 13:06-0400\n" "Last-Translator: \n" "Language-Team: \n" "Language: zh_CN\n" @@ -14,234 +14,230 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: pygettext.py 1.5\n" -"X-Generator: Poedit 2.3.1\n" +"X-Generator: Poedit 2.4.1\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: progress_monitor.py:42 -msgid "Upscaling Progress" -msgstr "放大进度" +#: progress_monitor.py:37 +msgid "Processing: {} (pass {}/{})" +msgstr "正在处理:{}(进度)" -#: upscaler.py:110 +#: upscaler.py:144 msgid "Specified or default cache directory is a file/link" msgstr "指定或默认的缓存目录是文件/链接" -#: upscaler.py:116 +#: upscaler.py:150 msgid "Creating cache directory {}" msgstr "创建缓存目录 {}" -#: upscaler.py:119 +#: upscaler.py:153 msgid "Unable to create {}" msgstr "无法创建 {}" -#: upscaler.py:124 +#: upscaler.py:158 msgid "Extracted frames are being saved to: {}" msgstr "提取的帧将被保存到:{}" -#: upscaler.py:126 +#: upscaler.py:160 msgid "Upscaled frames are being saved to: {}" msgstr "已放大的帧将被保存到:{}" -#: upscaler.py:136 +#: upscaler.py:170 msgid "Cleaning up cache directory: {}" msgstr "清理缓存目录:{}" -#: upscaler.py:141 +#: upscaler.py:175 msgid "Unable to delete: {}" msgstr "无法删除:{}" -#: upscaler.py:147 upscaler.py:162 upscaler.py:173 +#: upscaler.py:181 upscaler.py:196 upscaler.py:207 msgid "Input and output path type mismatch" msgstr "输入和输出路径类型不匹配" -#: upscaler.py:148 +#: upscaler.py:182 msgid "Input is multiple files but output is not directory" msgstr "输入是多个文件,但输出不是目录" -#: upscaler.py:152 +#: upscaler.py:186 msgid "Input path {} is neither a file nor a directory" msgstr "输入路径 {} 既不是文件也不是目录" -#: upscaler.py:156 upscaler.py:178 +#: upscaler.py:190 upscaler.py:212 msgid "Input directory and output directory cannot be the same" msgstr "输入目录和输出目录不能相同" -#: upscaler.py:163 +#: upscaler.py:197 msgid "Input is single file but output is directory" msgstr "所选的输入路径是单个文件,但输出路径是目录" -#: upscaler.py:166 +#: upscaler.py:200 msgid "No suffix found in output file path" msgstr "在输出文件路径中未找到后缀" -#: upscaler.py:167 +#: upscaler.py:201 msgid "Suffix must be specified" msgstr "必须指定文件后缀" -#: upscaler.py:174 +#: upscaler.py:208 msgid "Input is directory but output is existing single file" msgstr "输入是目录,但输出是现有的单个文件" -#: upscaler.py:183 +#: upscaler.py:217 msgid "Input path is neither a file nor a directory" msgstr "输入路径既不是文件也不是目录" -#: upscaler.py:192 +#: upscaler.py:226 msgid "FFmpeg or FFprobe cannot be found under the specified path" msgstr "在指定的路径下找不到 FFmpeg 或 FFprobe" -#: upscaler.py:193 upscaler.py:203 +#: upscaler.py:227 upscaler.py:237 msgid "Please check the configuration file settings" msgstr "请检查配置文件设置" -#: upscaler.py:202 +#: upscaler.py:236 msgid "Specified driver executable directory doesn't exist" msgstr "指定驱动的可执行文件不存在" -#: upscaler.py:229 +#: upscaler.py:263 msgid "Failed to parse driver argument: {}" msgstr "解析驱动程序参数失败:{}" -#: upscaler.py:261 +#: upscaler.py:283 msgid "Unrecognized driver: {}" msgstr "无法识别的驱动名称:{}" -#: upscaler.py:301 +#: upscaler.py:323 msgid "Starting progress monitor" msgstr "启动进度监视器" -#: upscaler.py:306 +#: upscaler.py:328 msgid "Starting upscaled image cleaner" msgstr "启动已放大图像清理程序" -#: upscaler.py:315 upscaler.py:332 +#: upscaler.py:337 upscaler.py:354 msgid "Killing progress monitor" msgstr "终结进度监视器" -#: upscaler.py:318 upscaler.py:335 +#: upscaler.py:340 upscaler.py:357 msgid "Killing upscaled image cleaner" msgstr "终结已放大图像清理程序" -#: upscaler.py:339 +#: upscaler.py:361 msgid "Terminating all processes" msgstr "正在终止所有进程" -#: upscaler.py:346 +#: upscaler.py:368 msgid "Main process waiting for subprocesses to exit" msgstr "主进程开始等待子进程结束" -#: upscaler.py:365 upscaler.py:369 +#: upscaler.py:387 upscaler.py:391 msgid "Subprocess {} exited with code {}" msgstr "子进程 {} 结束,返回码 {}" -#: upscaler.py:375 +#: upscaler.py:397 msgid "Stop signal received" msgstr "收到停止信号" -#: upscaler.py:380 +#: upscaler.py:402 msgid "Subprocess execution ran into an error" msgstr "子进程执行遇到错误" -#: upscaler.py:410 +#: upscaler.py:432 msgid "Loading files into processing queue" msgstr "正在将文件添加到处理队列中" -#: upscaler.py:415 -msgid "Loading files from multiple paths" -msgstr "正在从多个路径中导入文件" - -#: upscaler.py:416 upscaler.py:435 upscaler.py:442 +#: upscaler.py:433 msgid "Input path(s): {}" msgstr "输入路径:{}" -#: upscaler.py:434 -msgid "Loading single file" -msgstr "正在导入单个文件" - -#: upscaler.py:441 -msgid "Loading files from directory" -msgstr "正在从文件夹中导入文件" - -#: upscaler.py:455 -msgid "Loaded files into processing queue" -msgstr "文件已添加到处理队列" - -#: upscaler.py:458 -msgid "Input file: {}" -msgstr "输入文件:{}" - -#: upscaler.py:485 -msgid "Starting to upscale image" -msgstr "开始放大图像" - -#: upscaler.py:488 upscaler.py:550 -msgid "Upscaling completed" -msgstr "放大完成" - -#: upscaler.py:502 -msgid "Reading video information" -msgstr "读取视频信息" - -#: upscaler.py:516 -msgid "Aborting: No video stream found" -msgstr "程序中止:文件中未找到视频流" - -#: upscaler.py:521 -msgid "Framerate: {}" -msgstr "帧率:{}" - -#: upscaler.py:538 -msgid "Unsupported pixel format: {}" -msgstr "不支持的像素格式:{}" - -#: upscaler.py:548 -msgid "Starting to upscale extracted frames" -msgstr "开始对提取的帧进行放大" - -#: upscaler.py:555 +#: upscaler.py:495 msgid "File {} ({}) neither an image nor a video" msgstr "文件 {} ({}) 既不是图片也不是视频" -#: upscaler.py:556 +#: upscaler.py:496 msgid "Skipping this file" msgstr "将跳过此文件" -#: upscaler.py:566 +#: upscaler.py:521 +msgid "Loaded files into processing queue" +msgstr "文件已添加到处理队列" + +#: upscaler.py:524 +msgid "Input file: {}" +msgstr "输入文件:{}" + +#: upscaler.py:535 +msgid "Starting to upscale image" +msgstr "开始放大图像" + +#: upscaler.py:538 upscaler.py:689 +msgid "Upscaling completed" +msgstr "放大完成" + +#: upscaler.py:552 +msgid "Reading video information" +msgstr "读取视频信息" + +#: upscaler.py:564 +msgid "Aborting: No video stream found" +msgstr "程序中止:文件中未找到视频流" + +#: upscaler.py:571 +msgid "Framerate: {}" +msgstr "帧率:{}" + +#: upscaler.py:575 +msgid "Getting total number of frames in the file" +msgstr "正在获取文件中的总帧数" + +#: upscaler.py:650 +msgid "Upscaling jobs queue: {}" +msgstr "放大工作队列:{}" + +#: upscaler.py:666 +msgid "Unsupported pixel format: {}" +msgstr "不支持的像素格式:{}" + +#: upscaler.py:676 +msgid "Starting to upscale extracted frames" +msgstr "开始对提取的帧进行放大" + +#: upscaler.py:696 msgid "Converting extracted frames into GIF image" msgstr "正在将提取的帧转换为 GIF" -#: upscaler.py:570 upscaler.py:579 +#: upscaler.py:700 upscaler.py:709 msgid "Conversion completed" msgstr "转换已完成" -#: upscaler.py:575 +#: upscaler.py:705 msgid "Converting extracted frames into video" msgstr "正在将提取的帧转换为视频" -#: upscaler.py:583 +#: upscaler.py:713 msgid "Migrating audio, subtitles and other streams to upscaled video" msgstr "正在将音频、字幕和其他流迁移到放大后的视频" -#: upscaler.py:593 +#: upscaler.py:723 msgid "Failed to migrate streams" msgstr "迁移流失败" -#: upscaler.py:594 +#: upscaler.py:724 msgid "Trying to output video without additional streams" msgstr "正在尝试输出不含其他流的视频" -#: upscaler.py:610 +#: upscaler.py:740 msgid "Output video file exists" msgstr "输出目标文件已存在" -#: upscaler.py:614 +#: upscaler.py:744 msgid "Created temporary directory to contain file" msgstr "为文件创建了临时目录" -#: upscaler.py:617 +#: upscaler.py:747 msgid "Writing intermediate file to: {}" msgstr "正在将中间视频文件写入至:{}" -#: video2x.py:85 +#: video2x.py:86 msgid "" "Video2X CLI Version: {}\n" "Upscaler Version: {}\n" @@ -257,74 +253,108 @@ msgstr "" "GitHub 主页:https://github.com/k4yt3x/video2x\n" "联系方式:k4yt3x@k4yt3x.com" -#: video2x.py:108 +#: video2x.py:109 msgid "Video2X Options" msgstr "Video2X 选项" -#: video2x.py:109 +#: video2x.py:110 msgid "show this help message and exit" msgstr "显示此帮助消息并退出" -#: video2x.py:110 +#: video2x.py:117 msgid "source video file/directory" msgstr "源视频文件/目录" -#: video2x.py:111 +#: video2x.py:118 msgid "output video file/directory" msgstr "输出视频文件/目录" -#: video2x.py:112 +#: video2x.py:120 msgid "video2x config file path" msgstr "video2x 配置文件路径" -#: video2x.py:114 +#: video2x.py:122 +msgid "log file path" +msgstr "日志文件路径" + +#: video2x.py:124 +msgid "disable logging" +msgstr "禁用日志" + +#: video2x.py:125 msgid "display version, lawful information and exit" msgstr "显示版本和法律信息并退出" -#: video2x.py:117 +#: video2x.py:128 msgid "Upscaling Options" msgstr "视频放大选项" -#: video2x.py:118 -msgid "upscaling driver" -msgstr "视频放大驱动" - -#: video2x.py:119 +#: video2x.py:129 msgid "scaling ratio" msgstr "缩放比" -#: video2x.py:120 +#: video2x.py:130 +msgid "output width" +msgstr "输出宽度" + +#: video2x.py:131 +msgid "output height" +msgstr "输出高度" + +#: video2x.py:132 +msgid "upscaling driver" +msgstr "视频放大驱动" + +#: video2x.py:133 msgid "number of processes to use for upscaling" msgstr "并发进程数" -#: video2x.py:121 +#: video2x.py:134 msgid "preserve extracted and upscaled frames" msgstr "保留提取的和放大的帧" -#: video2x.py:161 +#: video2x.py:174 msgid "This file cannot be imported" msgstr "此文件无法被当作模块导入" -#: video2x.py:236 +#: video2x.py:190 +msgid "Specify either scaling ratio or scaling resolution, not both" +msgstr "您只能指定缩放比或输出分辨率两者之一" + +#: video2x.py:194 +msgid "Only one of scaling width and scaling height is specified" +msgstr "输出高度和宽度仅有其中一项被指定" + +#: video2x.py:200 +msgid "Redirecting console logs to {}" +msgstr "将控制台日志重定向到 {}" + +#: video2x.py:276 msgid "Program completed, taking {} seconds" msgstr "程序执行完毕,总计花费 {} 秒" -#: video2x.py:239 +#: video2x.py:279 msgid "An exception has occurred" msgstr "发生了异常" +#~ msgid "Upscaling Progress" +#~ msgstr "放大进度" + +#~ msgid "Loading files from multiple paths" +#~ msgstr "正在从多个路径中导入文件" + +#~ msgid "Loading single file" +#~ msgstr "正在导入单个文件" + +#~ msgid "Loading files from directory" +#~ msgstr "正在从文件夹中导入文件" + #~ msgid "Anime4KCPP doesn't yet support GIF processing" #~ msgstr "Anime4KCPP 尚不支持GIF处理" #~ msgid "Starting to upscale video with Anime4KCPP" #~ msgstr "开始用 Anime4KCPP 放大视频" -#~ msgid "output video width" -#~ msgstr "输出视频宽度" - -#~ msgid "output video height" -#~ msgstr "输出视频高度" - #~ msgid "You must specify input video file/directory path" #~ msgstr "您必须指定输入视频文件/目录路径" @@ -340,8 +370,5 @@ msgstr "发生了异常" #~ msgid "Scaling ratio must be one of 2, 3 or 4 for srmd_ncnn_vulkan" #~ msgstr "srmd_ncnn_vulkan 的缩放比必须为 2、3 或 4" -#~ msgid "You can only specify either scaling ratio or output width and height" -#~ msgstr "您只能指定缩放比或输出宽度和高度两者之一" - #~ msgid "You must specify both width and height" #~ msgstr "您必须同时指定宽度和高度" diff --git a/src/progress_monitor.py b/src/progress_monitor.py index ecafca1..1c8e7f2 100755 --- a/src/progress_monitor.py +++ b/src/progress_monitor.py @@ -4,7 +4,7 @@ Name: Video2X Upscale Progress Monitor Author: K4YT3X Date Created: May 7, 2020 -Last Modified: June 7, 2020 +Last Modified: September 9, 2020 """ # built-in imports @@ -34,12 +34,7 @@ class ProgressMonitor(threading.Thread): def run(self): self.running = True - # get number of extracted frames - self.upscaler.total_frames = 0 - for directory in self.extracted_frames_directories: - self.upscaler.total_frames += len([f for f in directory.iterdir() if str(f).lower().endswith(self.upscaler.extracted_frame_format.lower())]) - - with tqdm(total=self.upscaler.total_frames, ascii=True, desc=_('Upscaling Progress')) as progress_bar: + with tqdm(total=self.upscaler.total_frames, ascii=True, desc=_('Processing: {} (pass {}/{})').format(self.upscaler.current_input_file.name, self.upscaler.current_pass, len(self.upscaler.scaling_jobs))) as progress_bar: # tqdm update method adds the value to the progress # bar instead of setting the value. Therefore, a delta # needs to be calculated. diff --git a/src/upscaler.py b/src/upscaler.py index 100d02a..167c3f5 100755 --- a/src/upscaler.py +++ b/src/upscaler.py @@ -4,7 +4,7 @@ Name: Video2X Upscaler Author: K4YT3X Date Created: December 10, 2018 -Last Modified: June 29, 2020 +Last Modified: September 9, 2020 Description: This file contains the Upscaler class. Each instance of the Upscaler class is an upscaler on an image or @@ -25,6 +25,7 @@ import copy import gettext import importlib import locale +import math import mimetypes import pathlib import queue @@ -50,7 +51,7 @@ language.install() _ = language.gettext # version information -UPSCALER_VERSION = '4.2.2' +UPSCALER_VERSION = '4.3.0' # these names are consistent for # - driver selection in command line @@ -63,6 +64,12 @@ AVAILABLE_DRIVERS = ['waifu2x_caffe', 'realsr_ncnn_vulkan', 'anime4kcpp'] +DRIVER_FIXED_SCALING_RATIOS = { + 'waifu2x_ncnn_vulkan': [1, 2], + 'srmd_ncnn_vulkan': [2, 3, 4], + 'realsr_ncnn_vulkan': [4], +} + class Upscaler: """ An instance of this class is a upscaler that will @@ -82,6 +89,8 @@ class Upscaler: gifski_settings: dict, driver: str = 'waifu2x_caffe', scale_ratio: float = None, + scale_width: int = None, + scale_height: int = None, processes: int = 1, video2x_cache_directory: pathlib.Path = pathlib.Path(tempfile.gettempdir()) / 'video2x', extracted_frame_format: str = 'png', @@ -101,6 +110,8 @@ class Upscaler: # optional parameters self.driver = driver self.scale_ratio = scale_ratio + self.scale_width = scale_width + self.scale_height = scale_height self.processes = processes self.video2x_cache_directory = video2x_cache_directory self.extracted_frame_format = extracted_frame_format @@ -115,6 +126,8 @@ class Upscaler: self.total_frames = 0 self.total_files = 0 self.total_processed = 0 + self.scaling_jobs = [] + self.current_pass = 0 self.current_input_file = pathlib.Path() self.last_frame_upscaled = pathlib.Path() @@ -250,31 +263,19 @@ class Upscaler: Avalon.error(_('Failed to parse driver argument: {}').format(e.args[0])) raise e - # waifu2x-caffe scale_ratio, scale_width and scale_height check - if self.driver == 'waifu2x_caffe': - if (driver_settings['scale_width'] != 0 and driver_settings['scale_height'] == 0 or - driver_settings['scale_width'] == 0 and driver_settings['scale_height'] != 0): - Avalon.error(_('Only one of scale_width and scale_height is specified for waifu2x-caffe')) - raise AttributeError('only one of scale_width and scale_height is specified for waifu2x-caffe') - - # if scale_width and scale_height are specified, ensure scale_ratio is None - elif self.driver_settings['scale_width'] != 0 and self.driver_settings['scale_height'] != 0: - self.driver_settings['scale_ratio'] = None - - # if scale_width and scale_height not specified - # ensure they are None, not 0 - else: - self.driver_settings['scale_width'] = None - self.driver_settings['scale_height'] = None - - def _upscale_frames(self): + def _upscale_frames(self, input_directory: pathlib.Path, output_directory: pathlib.Path): """ Upscale video frames with waifu2x-caffe This function upscales all the frames extracted by ffmpeg using the waifu2x-caffe binary. - Arguments: - w2 {Waifu2x Object} -- initialized waifu2x object + Args: + input_directory (pathlib.Path): directory containing frames to upscale + output_directory (pathlib.Path): directory which upscaled frames should be exported to + + Raises: + UnrecognizedDriverError: raised when the given driver is not recognized + e: re-raised exception after an exception has been captured and finished processing in this scope """ # initialize waifu2x driver @@ -282,7 +283,7 @@ class Upscaler: raise UnrecognizedDriverError(_('Unrecognized driver: {}').format(self.driver)) # list all images in the extracted frames - frames = [(self.extracted_frames / f) for f in self.extracted_frames.iterdir() if f.is_file] + frames = [(input_directory / f) for f in input_directory.iterdir() if f.is_file] # if we have less images than processes, # create only the processes necessary @@ -293,7 +294,7 @@ class Upscaler: # name into a list process_directories = [] for process_id in range(self.processes): - process_directory = self.extracted_frames / str(process_id) + process_directory = input_directory / str(process_id) process_directories.append(process_directory) # delete old directories and create new directories @@ -303,7 +304,7 @@ class Upscaler: # waifu2x-converter-cpp will perform multi-threading within its own process if self.driver in ['waifu2x_converter_cpp', 'waifu2x_ncnn_vulkan', 'srmd_ncnn_vulkan', 'realsr_ncnn_vulkan', 'anime4kcpp']: - process_directories = [self.extracted_frames] + process_directories = [input_directory] else: # evenly distribute images into each directory @@ -316,7 +317,7 @@ class Upscaler: # create driver processes and start them for process_directory in process_directories: - self.process_pool.append(self.driver_object.upscale(process_directory, self.upscaled_frames)) + self.process_pool.append(self.driver_object.upscale(process_directory, output_directory)) # start progress bar in a different thread Avalon.debug_info(_('Starting progress monitor')) @@ -325,7 +326,7 @@ class Upscaler: # create the clearer and start it Avalon.debug_info(_('Starting upscaled image cleaner')) - self.image_cleaner = ImageCleaner(self.extracted_frames, self.upscaled_frames, len(self.process_pool)) + self.image_cleaner = ImageCleaner(input_directory, output_directory, len(self.process_pool)) self.image_cleaner.start() # wait for all process to exit @@ -343,11 +344,11 @@ class Upscaler: # if the driver is waifu2x-converter-cpp # images need to be renamed to be recognizable for FFmpeg if self.driver == 'waifu2x_converter_cpp': - for image in [f for f in self.upscaled_frames.iterdir() if f.is_file()]: + for image in [f for f in output_directory.iterdir() if f.is_file()]: renamed = re.sub(f'_\\[.*\\]\\[x(\\d+(\\.\\d+)?)\\]\\.{self.extracted_frame_format}', f'.{self.extracted_frame_format}', str(image.name)) - (self.upscaled_frames / image).rename(self.upscaled_frames / renamed) + (output_directory / image).rename(output_directory / renamed) # upscaling done, kill helper threads Avalon.debug_info(_('Killing progress monitor')) @@ -550,8 +551,6 @@ class Upscaler: # get video information JSON using FFprobe Avalon.info(_('Reading video information')) video_info = self.ffmpeg_object.probe_file_info(self.current_input_file) - # analyze original video with FFprobe and retrieve framerate - # width, height = info['streams'][0]['width'], info['streams'][0]['height'] # find index of video stream video_stream_index = None @@ -567,9 +566,89 @@ class Upscaler: # get average frame rate of video stream framerate = float(Fraction(video_info['streams'][video_stream_index]['r_frame_rate'])) + width = int(video_info['streams'][video_stream_index]['width']) + height = int(video_info['streams'][video_stream_index]['height']) Avalon.info(_('Framerate: {}').format(framerate)) # self.ffmpeg_object.pixel_format = video_info['streams'][video_stream_index]['pix_fmt'] + # get total number of frames + Avalon.info(_('Getting total number of frames in the file')) + + # if container stores total number of frames in nb_frames, fetch it directly + if 'nb_frames' in video_info['streams'][video_stream_index]: + self.total_frames = int(video_info['streams'][video_stream_index]['nb_frames']) + + # otherwise call FFprobe to count the total number of frames + else: + self.total_frames = self.ffmpeg_object.get_number_of_frames(self.current_input_file, video_stream_index) + + # if driver is one of the drivers that doesn't support arbitrary scaling ratio + # TODO: more documentations on this block + if self.driver in DRIVER_FIXED_SCALING_RATIOS: + + # if user specified output resolution + # calculate number of passes needed + if self.scale_width is not None and self.scale_height is not None: + self.scale_ratio = 2 + + # when scaled output resolution is smaller than target output resolution + # increase scaling ratio + while (self.scale_ratio * width) < self.scale_width: + self.scale_ratio += 1 + + while (self.scale_ratio * height) < self.scale_height: + self.scale_ratio += 1 + + # select the optimal driver scaling ratio to use + supported_scaling_ratios = sorted(DRIVER_FIXED_SCALING_RATIOS[self.driver]) + + remaining_scaling_ratio = math.ceil(self.scale_ratio) + self.scaling_jobs = [] + + while remaining_scaling_ratio > 1: + for ratio in supported_scaling_ratios: + if ratio >= remaining_scaling_ratio: + self.scaling_jobs.append(ratio) + remaining_scaling_ratio /= ratio + break + + else: + + found = False + for i in supported_scaling_ratios: + for j in supported_scaling_ratios: + if i * j >= remaining_scaling_ratio: + self.scaling_jobs.extend([i, j]) + remaining_scaling_ratio /= i * j + found = True + break + if found is True: + break + + if found is False: + self.scaling_jobs.append(supported_scaling_ratios[-1]) + remaining_scaling_ratio /= supported_scaling_ratios[-1] + + # calculate and set output bicubic downscaling filter + # always ensure that the output resolution is divisible by 2 + if self.scale_width is not None and self.scale_height is not None: + output_width = math.ceil(self.scale_width / 2.0) * 2 + output_height = math.ceil(self.scale_height / 2.0) * 2 + else: + output_width = math.ceil(width * self.scale_ratio / 2.0) * 2 + output_height = math.ceil(height * self.scale_ratio / 2.0) * 2 + + # append scaling filter to video assembly command + if self.ffmpeg_settings['assemble_video']['output_options'].get('-vf') is None: + self.ffmpeg_settings['assemble_video']['output_options']['-vf'] = f'scale={output_width}:{output_height}' + else: + self.ffmpeg_settings['assemble_video']['output_options']['-vf'] += f',scale={output_width}:{output_height}' + + else: + self.scaling_jobs = [self.scale_ratio] + + Avalon.debug_info(_('Upscaling jobs queue: {}').format(self.scaling_jobs)) + # extract frames from video self.process_pool.append((self.ffmpeg_object.extract_frames(self.current_input_file, self.extracted_frames))) self._wait() @@ -595,7 +674,18 @@ class Upscaler: # upscale images one by one using waifu2x Avalon.info(_('Starting to upscale extracted frames')) - self._upscale_frames() + + self.current_pass = 1 + self.driver_object.set_scale_ratio(self.scaling_jobs[0]) + self._upscale_frames(self.extracted_frames, self.upscaled_frames) + for job in self.scaling_jobs[1:]: + self.current_pass += 1 + self.driver_object.set_scale_ratio(job) + shutil.rmtree(self.extracted_frames) + shutil.move(self.upscaled_frames, self.extracted_frames) + self.upscaled_frames.mkdir(parents=True, exist_ok=True) + self._upscale_frames(self.extracted_frames, self.upscaled_frames) + Avalon.info(_('Upscaling completed')) # start handling output diff --git a/src/video2x.pot b/src/video2x.pot index bdc8db9..0e87a0a 100644 --- a/src/video2x.pot +++ b/src/video2x.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2020-05-22 17:51-0400\n" +"POT-Creation-Date: 2020-09-09 13:04-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,231 +15,227 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" -#: progress_monitor.py:42 -msgid "Upscaling Progress" +#: progress_monitor.py:37 +msgid "Processing: {} (pass {}/{})" msgstr "" -#: upscaler.py:110 +#: upscaler.py:144 msgid "Specified or default cache directory is a file/link" msgstr "" -#: upscaler.py:116 +#: upscaler.py:150 msgid "Creating cache directory {}" msgstr "" -#: upscaler.py:119 +#: upscaler.py:153 msgid "Unable to create {}" msgstr "" -#: upscaler.py:124 +#: upscaler.py:158 msgid "Extracted frames are being saved to: {}" msgstr "" -#: upscaler.py:126 +#: upscaler.py:160 msgid "Upscaled frames are being saved to: {}" msgstr "" -#: upscaler.py:136 +#: upscaler.py:170 msgid "Cleaning up cache directory: {}" msgstr "" -#: upscaler.py:141 +#: upscaler.py:175 msgid "Unable to delete: {}" msgstr "" -#: upscaler.py:147 upscaler.py:162 upscaler.py:173 +#: upscaler.py:181 upscaler.py:196 upscaler.py:207 msgid "Input and output path type mismatch" msgstr "" -#: upscaler.py:148 +#: upscaler.py:182 msgid "Input is multiple files but output is not directory" msgstr "" -#: upscaler.py:152 +#: upscaler.py:186 msgid "Input path {} is neither a file nor a directory" msgstr "" -#: upscaler.py:156 upscaler.py:178 +#: upscaler.py:190 upscaler.py:212 msgid "Input directory and output directory cannot be the same" msgstr "" -#: upscaler.py:163 +#: upscaler.py:197 msgid "Input is single file but output is directory" msgstr "" -#: upscaler.py:166 +#: upscaler.py:200 msgid "No suffix found in output file path" msgstr "" -#: upscaler.py:167 +#: upscaler.py:201 msgid "Suffix must be specified" msgstr "" -#: upscaler.py:174 +#: upscaler.py:208 msgid "Input is directory but output is existing single file" msgstr "" -#: upscaler.py:183 +#: upscaler.py:217 msgid "Input path is neither a file nor a directory" msgstr "" -#: upscaler.py:192 +#: upscaler.py:226 msgid "FFmpeg or FFprobe cannot be found under the specified path" msgstr "" -#: upscaler.py:193 upscaler.py:203 +#: upscaler.py:227 upscaler.py:237 msgid "Please check the configuration file settings" msgstr "" -#: upscaler.py:202 +#: upscaler.py:236 msgid "Specified driver executable directory doesn't exist" msgstr "" -#: upscaler.py:229 +#: upscaler.py:263 msgid "Failed to parse driver argument: {}" msgstr "" -#: upscaler.py:261 +#: upscaler.py:283 msgid "Unrecognized driver: {}" msgstr "" -#: upscaler.py:301 +#: upscaler.py:323 msgid "Starting progress monitor" msgstr "" -#: upscaler.py:306 +#: upscaler.py:328 msgid "Starting upscaled image cleaner" msgstr "" -#: upscaler.py:315 upscaler.py:332 +#: upscaler.py:337 upscaler.py:354 msgid "Killing progress monitor" msgstr "" -#: upscaler.py:318 upscaler.py:335 +#: upscaler.py:340 upscaler.py:357 msgid "Killing upscaled image cleaner" msgstr "" -#: upscaler.py:339 +#: upscaler.py:361 msgid "Terminating all processes" msgstr "" -#: upscaler.py:346 +#: upscaler.py:368 msgid "Main process waiting for subprocesses to exit" msgstr "" -#: upscaler.py:365 upscaler.py:369 +#: upscaler.py:387 upscaler.py:391 msgid "Subprocess {} exited with code {}" msgstr "" -#: upscaler.py:375 +#: upscaler.py:397 msgid "Stop signal received" msgstr "" -#: upscaler.py:380 +#: upscaler.py:402 msgid "Subprocess execution ran into an error" msgstr "" -#: upscaler.py:410 +#: upscaler.py:432 msgid "Loading files into processing queue" msgstr "" -#: upscaler.py:415 -msgid "Loading files from multiple paths" -msgstr "" - -#: upscaler.py:416 upscaler.py:435 upscaler.py:442 +#: upscaler.py:433 msgid "Input path(s): {}" msgstr "" -#: upscaler.py:434 -msgid "Loading single file" -msgstr "" - -#: upscaler.py:441 -msgid "Loading files from directory" -msgstr "" - -#: upscaler.py:455 -msgid "Loaded files into processing queue" -msgstr "" - -#: upscaler.py:458 -msgid "Input file: {}" -msgstr "" - -#: upscaler.py:485 -msgid "Starting to upscale image" -msgstr "" - -#: upscaler.py:488 upscaler.py:550 -msgid "Upscaling completed" -msgstr "" - -#: upscaler.py:502 -msgid "Reading video information" -msgstr "" - -#: upscaler.py:516 -msgid "Aborting: No video stream found" -msgstr "" - -#: upscaler.py:521 -msgid "Framerate: {}" -msgstr "" - -#: upscaler.py:538 -msgid "Unsupported pixel format: {}" -msgstr "" - -#: upscaler.py:548 -msgid "Starting to upscale extracted frames" -msgstr "" - -#: upscaler.py:555 +#: upscaler.py:495 msgid "File {} ({}) neither an image nor a video" msgstr "" -#: upscaler.py:556 +#: upscaler.py:496 msgid "Skipping this file" msgstr "" -#: upscaler.py:566 -msgid "Converting extracted frames into GIF image" +#: upscaler.py:521 +msgid "Loaded files into processing queue" msgstr "" -#: upscaler.py:570 upscaler.py:579 -msgid "Conversion completed" +#: upscaler.py:524 +msgid "Input file: {}" +msgstr "" + +#: upscaler.py:535 +msgid "Starting to upscale image" +msgstr "" + +#: upscaler.py:538 upscaler.py:689 +msgid "Upscaling completed" +msgstr "" + +#: upscaler.py:552 +msgid "Reading video information" +msgstr "" + +#: upscaler.py:564 +msgid "Aborting: No video stream found" +msgstr "" + +#: upscaler.py:571 +msgid "Framerate: {}" msgstr "" #: upscaler.py:575 +msgid "Getting total number of frames in the file" +msgstr "" + +#: upscaler.py:650 +msgid "Upscaling jobs queue: {}" +msgstr "" + +#: upscaler.py:666 +msgid "Unsupported pixel format: {}" +msgstr "" + +#: upscaler.py:676 +msgid "Starting to upscale extracted frames" +msgstr "" + +#: upscaler.py:696 +msgid "Converting extracted frames into GIF image" +msgstr "" + +#: upscaler.py:700 upscaler.py:709 +msgid "Conversion completed" +msgstr "" + +#: upscaler.py:705 msgid "Converting extracted frames into video" msgstr "" -#: upscaler.py:583 +#: upscaler.py:713 msgid "Migrating audio, subtitles and other streams to upscaled video" msgstr "" -#: upscaler.py:593 +#: upscaler.py:723 msgid "Failed to migrate streams" msgstr "" -#: upscaler.py:594 +#: upscaler.py:724 msgid "Trying to output video without additional streams" msgstr "" -#: upscaler.py:610 +#: upscaler.py:740 msgid "Output video file exists" msgstr "" -#: upscaler.py:614 +#: upscaler.py:744 msgid "Created temporary directory to contain file" msgstr "" -#: upscaler.py:617 +#: upscaler.py:747 msgid "Writing intermediate file to: {}" msgstr "" -#: video2x.py:85 +#: video2x.py:86 msgid "" "Video2X CLI Version: {}\n" "Upscaler Version: {}\n" @@ -249,59 +245,87 @@ msgid "" "Contact: k4yt3x@k4yt3x.com" msgstr "" -#: video2x.py:108 +#: video2x.py:109 msgid "Video2X Options" msgstr "" -#: video2x.py:109 +#: video2x.py:110 msgid "show this help message and exit" msgstr "" -#: video2x.py:110 +#: video2x.py:117 msgid "source video file/directory" msgstr "" -#: video2x.py:111 +#: video2x.py:118 msgid "output video file/directory" msgstr "" -#: video2x.py:112 +#: video2x.py:120 msgid "video2x config file path" msgstr "" -#: video2x.py:114 +#: video2x.py:122 +msgid "log file path" +msgstr "" + +#: video2x.py:124 +msgid "disable logging" +msgstr "" + +#: video2x.py:125 msgid "display version, lawful information and exit" msgstr "" -#: video2x.py:117 +#: video2x.py:128 msgid "Upscaling Options" msgstr "" -#: video2x.py:118 -msgid "upscaling driver" -msgstr "" - -#: video2x.py:119 +#: video2x.py:129 msgid "scaling ratio" msgstr "" -#: video2x.py:120 +#: video2x.py:130 +msgid "output width" +msgstr "" + +#: video2x.py:131 +msgid "output height" +msgstr "" + +#: video2x.py:132 +msgid "upscaling driver" +msgstr "" + +#: video2x.py:133 msgid "number of processes to use for upscaling" msgstr "" -#: video2x.py:121 +#: video2x.py:134 msgid "preserve extracted and upscaled frames" msgstr "" -#: video2x.py:161 +#: video2x.py:174 msgid "This file cannot be imported" msgstr "" -#: video2x.py:236 +#: video2x.py:190 +msgid "Specify either scaling ratio or scaling resolution, not both" +msgstr "" + +#: video2x.py:194 +msgid "Only one of scaling width and scaling height is specified" +msgstr "" + +#: video2x.py:200 +msgid "Redirecting console logs to {}" +msgstr "" + +#: video2x.py:276 msgid "Program completed, taking {} seconds" msgstr "" -#: video2x.py:239 +#: video2x.py:279 msgid "An exception has occurred" msgstr "" diff --git a/src/video2x.py b/src/video2x.py index 2f46f73..8579099 100755 --- a/src/video2x.py +++ b/src/video2x.py @@ -13,7 +13,7 @@ __ __ _ _ ___ __ __ Name: Video2X Controller Creator: K4YT3X Date Created: Feb 24, 2018 -Last Modified: June 29, 2020 +Last Modified: September 9, 2020 Editor: BrianPetkovsek Last Modified: June 17, 2019 @@ -81,7 +81,7 @@ language = gettext.translation(DOMAIN, LOCALE_DIRECTORY, [default_locale], fallb language.install() _ = language.gettext -CLI_VERSION = '4.2.0' +CLI_VERSION = '4.3.0' LEGAL_INFO = _('''Video2X CLI Version: {} Upscaler Version: {} @@ -107,7 +107,7 @@ def parse_arguments(): # video options video2x_options = parser.add_argument_group(_('Video2X Options')) - video2x_options.add_argument('-h', '--help', action='help', help=_('show this help message and exit')) + video2x_options.add_argument('--help', action='help', help=_('show this help message and exit')) # if help is in arguments list # do not require input and output path to be specified @@ -126,8 +126,10 @@ def parse_arguments(): # scaling options upscaling_options = parser.add_argument_group(_('Upscaling Options')) - upscaling_options.add_argument('-d', '--driver', help=_('upscaling driver'), choices=AVAILABLE_DRIVERS, default='waifu2x_caffe') - upscaling_options.add_argument('-r', '--ratio', help=_('scaling ratio'), action='store', type=float, default=2.0) + upscaling_options.add_argument('-r', '--ratio', help=_('scaling ratio'), action='store', type=float) + upscaling_options.add_argument('-w', '--width', help=_('output width'), action='store', type=float) + upscaling_options.add_argument('-h', '--height', help=_('output height'), action='store', type=float) + upscaling_options.add_argument('-d', '--driver', help=_('upscaling driver'), choices=AVAILABLE_DRIVERS, default='waifu2x_ncnn_vulkan') upscaling_options.add_argument('-p', '--processes', help=_('number of processes to use for upscaling'), action='store', type=int, default=1) upscaling_options.add_argument('--preserve_frames', help=_('preserve extracted and upscaled frames'), action='store_true') @@ -183,6 +185,15 @@ if video2x_args.version: print(LEGAL_INFO) sys.exit(0) +# additional checks on upscaling arguments +if video2x_args.ratio is not None and (video2x_args.width is not None or video2x_args.height is not None): + Avalon.error(_('Specify either scaling ratio or scaling resolution, not both')) + sys.exit(1) + +if bool(video2x_args.width) ^ bool(video2x_args.height): + Avalon.error(_('Only one of scaling width and scaling height is specified')) + sys.exit(1) + # redirect output to both terminal and log file if video2x_args.disable_logging is False: LOGFILE = video2x_args.log @@ -248,6 +259,8 @@ try: # optional parameters driver=video2x_args.driver, scale_ratio=video2x_args.ratio, + scale_width=video2x_args.width, + scale_height=video2x_args.height, processes=video2x_args.processes, video2x_cache_directory=video2x_cache_directory, extracted_frame_format=extracted_frame_format, diff --git a/src/video2x_gui.py b/src/video2x_gui.py index 244f727..9b8dafe 100755 --- a/src/video2x_gui.py +++ b/src/video2x_gui.py @@ -4,7 +4,7 @@ Creator: Video2X GUI Author: K4YT3X Date Created: May 5, 2020 -Last Modified: September 4, 2020 +Last Modified: September 9, 2020 """ # local imports @@ -34,7 +34,7 @@ from PyQt5.QtGui import * from PyQt5.QtWidgets import * import magic -GUI_VERSION = '2.7.3' +GUI_VERSION = '2.8.0' LEGAL_INFO = f'''Video2X GUI Version: {GUI_VERSION}\\ Upscaler Version: {UPSCALER_VERSION}\\ @@ -277,6 +277,10 @@ class Video2XMainWindow(QMainWindow): self.driver_combo_box.currentTextChanged.connect(self.update_gui_for_driver) self.processes_spin_box = self.findChild(QSpinBox, 'processesSpinBox') self.scale_ratio_double_spin_box = self.findChild(QDoubleSpinBox, 'scaleRatioDoubleSpinBox') + self.output_width_spin_box = self.findChild(QSpinBox, 'outputWidthSpinBox') + self.output_width_spin_box.valueChanged.connect(self.mutually_exclude_scale_ratio_resolution) + self.output_height_spin_box = self.findChild(QSpinBox, 'outputHeightSpinBox') + self.output_height_spin_box.valueChanged.connect(self.mutually_exclude_scale_ratio_resolution) self.output_file_name_format_string_line_edit = self.findChild(QLineEdit, 'outputFileNameFormatStringLineEdit') self.image_output_extension_line_edit = self.findChild(QLineEdit, 'imageOutputExtensionLineEdit') self.video_output_extension_line_edit = self.findChild(QLineEdit, 'videoOutputExtensionLineEdit') @@ -310,8 +314,6 @@ class Video2XMainWindow(QMainWindow): self.enable_line_edit_file_drop(self.waifu2x_caffe_path_line_edit) self.waifu2x_caffe_path_select_button = self.findChild(QPushButton, 'waifu2xCaffePathSelectButton') self.waifu2x_caffe_path_select_button.clicked.connect(lambda: self.select_driver_binary_path(self.waifu2x_caffe_path_line_edit)) - self.waifu2x_caffe_scale_width_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeScaleWidthSpinBox') - self.waifu2x_caffe_scale_height_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeScaleHeightSpinBox') self.waifu2x_caffe_mode_combo_box = self.findChild(QComboBox, 'waifu2xCaffeModeComboBox') self.waifu2x_caffe_noise_level_spin_box = self.findChild(QSpinBox, 'waifu2xCaffeNoiseLevelSpinBox') self.waifu2x_caffe_process_combo_box = self.findChild(QComboBox, 'waifu2xCaffeProcessComboBox') @@ -491,8 +493,6 @@ class Video2XMainWindow(QMainWindow): # waifu2x-caffe settings = self.config['waifu2x_caffe'] - self.waifu2x_caffe_scale_width_spin_box.setValue(settings['scale_width']) - self.waifu2x_caffe_scale_height_spin_box.setValue(settings['scale_height']) self.waifu2x_caffe_path_line_edit.setText(str(pathlib.Path(os.path.expandvars(settings['path'])).absolute())) self.waifu2x_caffe_mode_combo_box.setCurrentText(settings['mode']) self.waifu2x_caffe_noise_level_spin_box.setValue(settings['noise_level']) @@ -605,8 +605,6 @@ class Video2XMainWindow(QMainWindow): def resolve_driver_settings(self): # waifu2x-caffe - self.config['waifu2x_caffe']['scale_width'] = self.waifu2x_caffe_scale_width_spin_box.value() - self.config['waifu2x_caffe']['scale_height'] = self.waifu2x_caffe_scale_height_spin_box.value() self.config['waifu2x_caffe']['path'] = os.path.expandvars(self.waifu2x_caffe_path_line_edit.text()) self.config['waifu2x_caffe']['mode'] = self.waifu2x_caffe_mode_combo_box.currentText() self.config['waifu2x_caffe']['noise_level'] = self.waifu2x_caffe_noise_level_spin_box.value() @@ -831,6 +829,12 @@ class Video2XMainWindow(QMainWindow): with open(config_file, 'r') as config: return yaml.load(config, Loader=yaml.FullLoader) + def mutually_exclude_scale_ratio_resolution(self): + if self.output_width_spin_box.value() != 0 or self.output_height_spin_box.value() != 0: + self.scale_ratio_double_spin_box.setDisabled(True) + elif self.output_width_spin_box.value() == 0 and self.output_height_spin_box.value() == 0: + self.scale_ratio_double_spin_box.setDisabled(False) + def mutually_exclude_frame_interpolation_stream_copy(self): if self.ffmpeg_migrate_streams_output_options_frame_interpolation_spin_box.value() > 0: self.ffmpeg_migrate_streams_output_options_copy_streams_check_box.setChecked(False) @@ -842,24 +846,6 @@ class Video2XMainWindow(QMainWindow): def update_gui_for_driver(self): current_driver = AVAILABLE_DRIVERS[self.driver_combo_box.currentText()] - # update scale ratio constraints - if current_driver in ['waifu2x_caffe', 'waifu2x_converter_cpp', 'anime4kcpp']: - self.scale_ratio_double_spin_box.setMinimum(0.0) - self.scale_ratio_double_spin_box.setMaximum(999.0) - self.scale_ratio_double_spin_box.setValue(2.0) - elif current_driver == 'waifu2x_ncnn_vulkan': - self.scale_ratio_double_spin_box.setMinimum(1.0) - self.scale_ratio_double_spin_box.setMaximum(2.0) - self.scale_ratio_double_spin_box.setValue(2.0) - elif current_driver == 'srmd_ncnn_vulkan': - self.scale_ratio_double_spin_box.setMinimum(2.0) - self.scale_ratio_double_spin_box.setMaximum(4.0) - self.scale_ratio_double_spin_box.setValue(2.0) - elif current_driver == 'realsr_ncnn_vulkan': - self.scale_ratio_double_spin_box.setMinimum(4.0) - self.scale_ratio_double_spin_box.setMaximum(4.0) - self.scale_ratio_double_spin_box.setValue(4.0) - # update preferred processes/threads count if current_driver == 'anime4kcpp': self.processes_spin_box.setValue(16) @@ -1074,7 +1060,7 @@ It\'s also highly recommended for you to attach the [log file]({}) under the pro # initialize progress bar values upscale_begin_time = time.time() - progress_callback.emit((upscale_begin_time, 0, 0, 0, 0, pathlib.Path(), pathlib.Path())) + progress_callback.emit((upscale_begin_time, 0, 0, 0, 0, 0, [], pathlib.Path(), pathlib.Path())) # keep querying upscaling process and feed information to callback signal while self.upscaler.running: @@ -1084,6 +1070,8 @@ It\'s also highly recommended for you to attach the [log file]({}) under the pro self.upscaler.total_frames, self.upscaler.total_processed, self.upscaler.total_files, + self.upscaler.current_pass, + self.upscaler.scaling_jobs, self.upscaler.current_input_file, self.upscaler.last_frame_upscaled)) time.sleep(1) @@ -1098,8 +1086,10 @@ It\'s also highly recommended for you to attach the [log file]({}) under the pro total_frames = progress_information[2] total_processed = progress_information[3] total_files = progress_information[4] - current_input_file = progress_information[5] - last_frame_upscaled = progress_information[6] + current_pass = progress_information[5] + scaling_jobs = progress_information[6] + current_input_file = progress_information[7] + last_frame_upscaled = progress_information[8] # calculate fields based on frames and time elapsed time_elapsed = time.time() - upscale_begin_time @@ -1120,7 +1110,7 @@ It\'s also highly recommended for you to attach the [log file]({}) under the pro self.overall_progress_label.setText('Overall Progress: {}/{}'.format(total_processed, total_files)) self.overall_progress_bar.setMaximum(total_files) self.overall_progress_bar.setValue(total_processed) - self.currently_processing_label.setText('Currently Processing: {}'.format(str(current_input_file.name))) + self.currently_processing_label.setText('Currently Processing: {} (pass {}/{})'.format(str(current_input_file.name), current_pass, len(scaling_jobs))) # if show frame is checked, show preview image if self.frame_preview_show_preview_check_box.isChecked() and last_frame_upscaled.is_file(): @@ -1190,6 +1180,16 @@ It\'s also highly recommended for you to attach the [log file]({}) under the pro # load driver settings for the current driver self.driver_settings = self.config[AVAILABLE_DRIVERS[self.driver_combo_box.currentText()]] + # get scale ratio or resolution + if self.scale_ratio_double_spin_box.isEnabled(): + scale_ratio = self.scale_ratio_double_spin_box.value() + scale_width = scale_height = None + + else: + scale_ratio = None + scale_width = self.output_width_spin_box.value() + scale_height = self.output_height_spin_box.value() + self.upscaler = Upscaler( # required parameters input_path=input_directory, @@ -1200,7 +1200,9 @@ It\'s also highly recommended for you to attach the [log file]({}) under the pro # optional parameters driver=AVAILABLE_DRIVERS[self.driver_combo_box.currentText()], - scale_ratio=self.scale_ratio_double_spin_box.value(), + scale_ratio=scale_ratio, + scale_width=scale_width, + scale_height=scale_height, processes=self.processes_spin_box.value(), video2x_cache_directory=pathlib.Path(os.path.expandvars(self.cache_line_edit.text())), extracted_frame_format=self.config['video2x']['extracted_frame_format'].lower(), diff --git a/src/video2x_gui.ui b/src/video2x_gui.ui index 94c9d44..6b84e4c 100644 --- a/src/video2x_gui.ui +++ b/src/video2x_gui.ui @@ -6,8 +6,8 @@ 0 0 - 756 - 954 + 737 + 1020 @@ -40,8 +40,8 @@ 0 0 - 744 - 696 + 725 + 762 @@ -376,6 +376,11 @@ </li> </ul> + + + Waifu2X NCNN Vulkan + + Waifu2X Caffe @@ -386,11 +391,6 @@ Waifu2X Converter CPP - - - Waifu2X NCNN Vulkan - - SRMD NCNN Vulkan @@ -461,6 +461,42 @@ + + + + + + Output Width + + + + + + + 99999 + + + + + + + + + + + Output Height + + + + + + + 99999 + + + + + @@ -628,48 +664,6 @@ - - - - - - Scale Width - - - - - - - custom scale width (specifying this will overwrite scale_ratio) - - - 999999 - - - - - - - - - - - Scale Height - - - - - - - custom scale height (specifying this will overwrite scale_ratio) - - - 999999 - - - - - @@ -2946,7 +2940,7 @@ 0 0 - 756 + 737 21 diff --git a/src/wrappers/anime4kcpp.py b/src/wrappers/anime4kcpp.py index e0a34e5..e66f0eb 100755 --- a/src/wrappers/anime4kcpp.py +++ b/src/wrappers/anime4kcpp.py @@ -4,7 +4,7 @@ Name: Anime4KCPP Driver Author: K4YT3X Date Created: May 3, 2020 -Last Modified: September 1, 2020 +Last Modified: September 9, 2020 Description: This class is a high-level wrapper for Anime4KCPP. @@ -73,13 +73,16 @@ class WrapperMain: return parser.parse_args(arguments) def load_configurations(self, upscaler): - self.driver_settings['zoomFactor'] = upscaler.scale_ratio + # self.driver_settings['zoomFactor'] = upscaler.scale_ratio self.driver_settings['threads'] = upscaler.processes # append FFmpeg path to the end of PATH # Anime4KCPP will then use FFmpeg to migrate audio tracks os.environ['PATH'] += f';{upscaler.ffmpeg_settings["ffmpeg_path"]}' + def set_scale_ratio(self, scale_ratio: float): + self.driver_settings['zoomFactor'] = scale_ratio + def upscale(self, input_file, output_file): """This is the core function for WAIFU2X class diff --git a/src/wrappers/ffmpeg.py b/src/wrappers/ffmpeg.py index 133c54a..87eb06c 100755 --- a/src/wrappers/ffmpeg.py +++ b/src/wrappers/ffmpeg.py @@ -75,6 +75,37 @@ class Ffmpeg: return pixel_formats + def get_number_of_frames(self, input_file: str, video_stream_index: int) -> int: + """ Count the number of frames in a video + + Args: + input_file (str): input file path + video_stream_index (int): index number of the video stream + + Returns: + int: number of frames in the video + """ + + execute = [ + self.ffmpeg_probe_binary, + '-v', + 'quiet', + '-count_frames', + '-select_streams', + f'v:{video_stream_index}', + '-show_entries', + 'stream=nb_read_frames', + '-of', + 'default=nokey=1:noprint_wrappers=1', + input_file + ] + + # turn elements into str + execute = [str(e) for e in execute] + + Avalon.debug_info(f'Executing: {shlex.join(execute)}') + return int(subprocess.run(execute, check=True, stdout=subprocess.PIPE).stdout.decode().strip()) + def probe_file_info(self, input_video): """ Gets input video information @@ -134,6 +165,7 @@ class Ffmpeg: # specify output file execute.extend([ extracted_frames / f'extracted_%0d.{self.extracted_frame_format}' + # extracted_frames / f'frame_%06d.{self.extracted_frame_format}' ]) return(self._execute(execute)) @@ -175,6 +207,7 @@ class Ffmpeg: execute.extend([ '-i', upscaled_frames / f'extracted_%d.{self.extracted_frame_format}' + # upscaled_frames / f'%06d.{self.extracted_frame_format}' ]) # read FFmpeg output options diff --git a/src/wrappers/realsr_ncnn_vulkan.py b/src/wrappers/realsr_ncnn_vulkan.py index 2b81eb3..61aa34a 100755 --- a/src/wrappers/realsr_ncnn_vulkan.py +++ b/src/wrappers/realsr_ncnn_vulkan.py @@ -4,7 +4,7 @@ Name: RealSR NCNN Vulkan Driver Creator: K4YT3X Date Created: May 26, 2020 -Last Modified: May 26, 2020 +Last Modified: September 9, 2020 Description: This class is a high-level wrapper for realsr_ncnn_vulkan. @@ -44,7 +44,7 @@ class WrapperMain: parser.add_argument('-v', action='store_true', help='verbose output') parser.add_argument('-i', type=str, help=argparse.SUPPRESS) # help='input image path (jpg/png) or directory') parser.add_argument('-o', type=str, help=argparse.SUPPRESS) # help='output image path (png) or directory') - parser.add_argument('-s', type=int, choices=[4], help='upscale ratio') + parser.add_argument('-s', type=int, help='upscale ratio') parser.add_argument('-t', type=int, help='tile size (>=32/0=auto)') parser.add_argument('-m', type=str, help='realsr model path') parser.add_argument('-g', type=int, help='gpu device to use') @@ -53,9 +53,12 @@ class WrapperMain: return parser.parse_args(arguments) def load_configurations(self, upscaler): - self.driver_settings['s'] = int(upscaler.scale_ratio) + # self.driver_settings['s'] = int(upscaler.scale_ratio) self.driver_settings['j'] = '{}:{}:{}'.format(upscaler.processes, upscaler.processes, upscaler.processes) + def set_scale_ratio(self, scale_ratio: int): + self.driver_settings['s'] = int(scale_ratio) + def upscale(self, input_directory, output_directory): """This is the core function for RealSR NCNN Vulkan class diff --git a/src/wrappers/srmd_ncnn_vulkan.py b/src/wrappers/srmd_ncnn_vulkan.py index 2c2584f..437e305 100755 --- a/src/wrappers/srmd_ncnn_vulkan.py +++ b/src/wrappers/srmd_ncnn_vulkan.py @@ -4,7 +4,7 @@ Name: SRMD NCNN Vulkan Driver Creator: K4YT3X Date Created: April 26, 2020 -Last Modified: May 11, 2020 +Last Modified: September 9, 2020 Description: This class is a high-level wrapper for srmd_ncnn_vulkan. @@ -45,7 +45,7 @@ class WrapperMain: parser.add_argument('-i', type=str, help=argparse.SUPPRESS) # help='input image path (jpg/png) or directory') parser.add_argument('-o', type=str, help=argparse.SUPPRESS) # help='output image path (png) or directory') parser.add_argument('-n', type=int, choices=range(-1, 11), help='denoise level') - parser.add_argument('-s', type=int, choices=range(2, 5), help='upscale ratio') + parser.add_argument('-s', type=int, help='upscale ratio') parser.add_argument('-t', type=int, help='tile size (>=32)') parser.add_argument('-m', type=str, help='srmd model path') parser.add_argument('-g', type=int, help='gpu device to use') @@ -54,9 +54,12 @@ class WrapperMain: return parser.parse_args(arguments) def load_configurations(self, upscaler): - self.driver_settings['s'] = int(upscaler.scale_ratio) + # self.driver_settings['s'] = int(upscaler.scale_ratio) self.driver_settings['j'] = '{}:{}:{}'.format(upscaler.processes, upscaler.processes, upscaler.processes) + def set_scale_ratio(self, scale_ratio: int): + self.driver_settings['s'] = int(scale_ratio) + def upscale(self, input_directory, output_directory): """This is the core function for SRMD ncnn Vulkan class diff --git a/src/wrappers/waifu2x_caffe.py b/src/wrappers/waifu2x_caffe.py index efc684c..e4677a1 100755 --- a/src/wrappers/waifu2x_caffe.py +++ b/src/wrappers/waifu2x_caffe.py @@ -4,7 +4,7 @@ Name: Waifu2x Caffe Driver Author: K4YT3X Date Created: Feb 24, 2018 -Last Modified: June 7, 2020 +Last Modified: September 9, 2020 Description: This class is a high-level wrapper for waifu2x-caffe. @@ -63,13 +63,16 @@ class WrapperMain: def load_configurations(self, upscaler): # use scale width and scale height if specified - self.driver_settings['scale_ratio'] = upscaler.scale_ratio + # self.driver_settings['scale_ratio'] = upscaler.scale_ratio self.driver_settings['output_extention'] = upscaler.extracted_frame_format # bit_depth will be 12 at this point # it will up updated later self.driver_settings['output_depth'] = 12 + def set_scale_ratio(self, scale_ratio: float): + self.driver_settings['scale_ratio'] = scale_ratio + def upscale(self, input_directory, output_directory): """ start upscaling process """ diff --git a/src/wrappers/waifu2x_converter_cpp.py b/src/wrappers/waifu2x_converter_cpp.py index ce05b47..b545c23 100755 --- a/src/wrappers/waifu2x_converter_cpp.py +++ b/src/wrappers/waifu2x_converter_cpp.py @@ -4,7 +4,7 @@ Name: Waifu2x Converter CPP Driver Author: K4YT3X Date Created: February 8, 2019 -Last Modified: June 13, 2020 +Last Modified: September 9, 2020 Description: This class is a high-level wrapper for waifu2x-converter-cpp. @@ -67,10 +67,13 @@ class WrapperMain: return parser.parse_args(arguments) def load_configurations(self, upscaler): - self.driver_settings['scale-ratio'] = upscaler.scale_ratio + # self.driver_settings['scale-ratio'] = upscaler.scale_ratio self.driver_settings['jobs'] = upscaler.processes self.driver_settings['output-format'] = upscaler.extracted_frame_format.lower() + def set_scale_ratio(self, scale_ratio: float): + self.driver_settings['scale-ratio'] = scale_ratio + def upscale(self, input_directory, output_directory): """ Waifu2x Converter Driver Upscaler This method executes the upscaling of extracted frames. diff --git a/src/wrappers/waifu2x_ncnn_vulkan.py b/src/wrappers/waifu2x_ncnn_vulkan.py index 5ba4761..e89c40e 100755 --- a/src/wrappers/waifu2x_ncnn_vulkan.py +++ b/src/wrappers/waifu2x_ncnn_vulkan.py @@ -7,7 +7,7 @@ Date Created: June 26, 2019 Last Modified: May 11, 2020 Editor: K4YT3X -Last Modified: February 22, 2020 +Last Modified: September 9, 2020 Description: This class is a high-level wrapper for waifu2x_ncnn_vulkan. @@ -48,7 +48,7 @@ class WrapperMain: parser.add_argument('-i', type=str, help=argparse.SUPPRESS) # help='input image path (jpg/png) or directory') parser.add_argument('-o', type=str, help=argparse.SUPPRESS) # help='output image path (png) or directory') parser.add_argument('-n', type=int, choices=range(-1, 4), help='denoise level') - parser.add_argument('-s', type=int, choices=range(1, 3), help='upscale ratio') + parser.add_argument('-s', type=int, help='upscale ratio') parser.add_argument('-t', type=int, help='tile size (>=32)') parser.add_argument('-m', type=str, help='waifu2x model path') parser.add_argument('-g', type=int, help='gpu device to use') @@ -57,11 +57,14 @@ class WrapperMain: return parser.parse_args(arguments) def load_configurations(self, upscaler): - self.driver_settings['s'] = int(upscaler.scale_ratio) + # self.driver_settings['s'] = int(upscaler.scale_ratio) self.driver_settings['j'] = '{}:{}:{}'.format(upscaler.processes, upscaler.processes, upscaler.processes) + def set_scale_ratio(self, scale_ratio: int): + self.driver_settings['s'] = int(scale_ratio) + def upscale(self, input_directory, output_directory): - """This is the core function for WAIFU2X class + """ This is the core function for waifu2x class Arguments: input_directory {string} -- source directory path