mirror of
				https://github.com/k4yt3x/video2x.git
				synced 2025-11-04 06:31:00 +01:00 
			
		
		
		
	added image and GIF upscale support
This commit is contained in:
		
							parent
							
								
									5cf3271aad
								
							
						
					
					
						commit
						e305d0188e
					
				@ -3,6 +3,8 @@ colorama
 | 
				
			|||||||
patool
 | 
					patool
 | 
				
			||||||
psutil
 | 
					psutil
 | 
				
			||||||
pyqt5
 | 
					pyqt5
 | 
				
			||||||
 | 
					python-magic
 | 
				
			||||||
 | 
					python-magic-bin
 | 
				
			||||||
pyunpack
 | 
					pyunpack
 | 
				
			||||||
pyyaml
 | 
					pyyaml
 | 
				
			||||||
requests
 | 
					requests
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										188
									
								
								src/upscaler.py
									
									
									
									
									
								
							
							
						
						
									
										188
									
								
								src/upscaler.py
									
									
									
									
									
								
							@ -4,7 +4,7 @@
 | 
				
			|||||||
Name: Video2X Upscaler
 | 
					Name: Video2X Upscaler
 | 
				
			||||||
Author: K4YT3X
 | 
					Author: K4YT3X
 | 
				
			||||||
Date Created: December 10, 2018
 | 
					Date Created: December 10, 2018
 | 
				
			||||||
Last Modified: May 10, 2020
 | 
					Last Modified: May 11, 2020
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Description: This file contains the Upscaler class. Each
 | 
					Description: This file contains the Upscaler class. Each
 | 
				
			||||||
instance of the Upscaler class is an upscaler on an image or
 | 
					instance of the Upscaler class is an upscaler on an image or
 | 
				
			||||||
@ -16,6 +16,7 @@ from exceptions import *
 | 
				
			|||||||
from image_cleaner import ImageCleaner
 | 
					from image_cleaner import ImageCleaner
 | 
				
			||||||
from progress_monitor import ProgressMonitor
 | 
					from progress_monitor import ProgressMonitor
 | 
				
			||||||
from wrappers.ffmpeg import Ffmpeg
 | 
					from wrappers.ffmpeg import Ffmpeg
 | 
				
			||||||
 | 
					from wrappers.gifski import Gifski
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# built-in imports
 | 
					# built-in imports
 | 
				
			||||||
from fractions import Fraction
 | 
					from fractions import Fraction
 | 
				
			||||||
@ -24,7 +25,6 @@ import copy
 | 
				
			|||||||
import gettext
 | 
					import gettext
 | 
				
			||||||
import importlib
 | 
					import importlib
 | 
				
			||||||
import locale
 | 
					import locale
 | 
				
			||||||
import os
 | 
					 | 
				
			||||||
import pathlib
 | 
					import pathlib
 | 
				
			||||||
import queue
 | 
					import queue
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
@ -36,6 +36,7 @@ import traceback
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# third-party imports
 | 
					# third-party imports
 | 
				
			||||||
from avalon_framework import Avalon
 | 
					from avalon_framework import Avalon
 | 
				
			||||||
 | 
					import magic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# internationalization constants
 | 
					# internationalization constants
 | 
				
			||||||
DOMAIN = 'video2x'
 | 
					DOMAIN = 'video2x'
 | 
				
			||||||
@ -67,12 +68,13 @@ class Upscaler:
 | 
				
			|||||||
        ArgumentError -- if argument is not valid
 | 
					        ArgumentError -- if argument is not valid
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, input_path, output_path, driver_settings, ffmpeg_settings):
 | 
					    def __init__(self, input_path, output_path, driver_settings, ffmpeg_settings, gifski_settings):
 | 
				
			||||||
        # mandatory arguments
 | 
					        # mandatory arguments
 | 
				
			||||||
        self.input = input_path
 | 
					        self.input = input_path
 | 
				
			||||||
        self.output = output_path
 | 
					        self.output = output_path
 | 
				
			||||||
        self.driver_settings = driver_settings
 | 
					        self.driver_settings = driver_settings
 | 
				
			||||||
        self.ffmpeg_settings = ffmpeg_settings
 | 
					        self.ffmpeg_settings = ffmpeg_settings
 | 
				
			||||||
 | 
					        self.gifski_settings = gifski_settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # optional arguments
 | 
					        # optional arguments
 | 
				
			||||||
        self.driver = 'waifu2x_caffe'
 | 
					        self.driver = 'waifu2x_caffe'
 | 
				
			||||||
@ -86,9 +88,9 @@ class Upscaler:
 | 
				
			|||||||
        self.running = False
 | 
					        self.running = False
 | 
				
			||||||
        self.total_frames_upscaled = 0
 | 
					        self.total_frames_upscaled = 0
 | 
				
			||||||
        self.total_frames = 0
 | 
					        self.total_frames = 0
 | 
				
			||||||
        self.total_videos = 0
 | 
					        self.total_files = 0
 | 
				
			||||||
        self.total_processed = 0
 | 
					        self.total_processed = 0
 | 
				
			||||||
        self.current_input_video = pathlib.Path()
 | 
					        self.current_input_file = pathlib.Path()
 | 
				
			||||||
        self.last_frame_upscaled = pathlib.Path()
 | 
					        self.last_frame_upscaled = pathlib.Path()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def create_temp_directories(self):
 | 
					    def create_temp_directories(self):
 | 
				
			||||||
@ -154,10 +156,10 @@ class Upscaler:
 | 
				
			|||||||
                Avalon.error(_('Input and output path type mismatch'))
 | 
					                Avalon.error(_('Input and output path type mismatch'))
 | 
				
			||||||
                Avalon.error(_('Input is single file but output is directory'))
 | 
					                Avalon.error(_('Input is single file but output is directory'))
 | 
				
			||||||
                raise ArgumentError('input output path type mismatch')
 | 
					                raise ArgumentError('input output path type mismatch')
 | 
				
			||||||
            if not re.search(r'.*\..*$', str(self.output)):
 | 
					            if self.output.suffix == '':
 | 
				
			||||||
                Avalon.error(_('No suffix found in output file path'))
 | 
					                Avalon.error(_('No suffix found in output file path'))
 | 
				
			||||||
                Avalon.error(_('Suffix must be specified for FFmpeg'))
 | 
					                Avalon.error(_('Suffix must be specified'))
 | 
				
			||||||
                raise ArgumentError('no output video suffix specified')
 | 
					                raise ArgumentError('no output file suffix specified')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # if input is a directory
 | 
					        # if input is a directory
 | 
				
			||||||
        elif self.input.is_dir():
 | 
					        elif self.input.is_dir():
 | 
				
			||||||
@ -238,6 +240,14 @@ class Upscaler:
 | 
				
			|||||||
                self.driver_settings['scale_width'] = None
 | 
					                self.driver_settings['scale_width'] = None
 | 
				
			||||||
                self.driver_settings['scale_height'] = None
 | 
					                self.driver_settings['scale_height'] = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # temporary file type check for Anime4KCPP
 | 
				
			||||||
 | 
					        # it doesn't support GIF processing yet
 | 
				
			||||||
 | 
					        if self.driver == 'anime4kcpp':
 | 
				
			||||||
 | 
					            for task in self.processing_queue.queue:
 | 
				
			||||||
 | 
					                if task[0].suffix.lower() == '.gif':
 | 
				
			||||||
 | 
					                    Avalon.error(_('Anime4KCPP doesn\'t yet support GIF processing'))
 | 
				
			||||||
 | 
					                    raise AttributeError('Anime4KCPP doesn\'t yet support GIF file processing')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _upscale_frames(self):
 | 
					    def _upscale_frames(self):
 | 
				
			||||||
        """ Upscale video frames with waifu2x-caffe
 | 
					        """ Upscale video frames with waifu2x-caffe
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -393,9 +403,8 @@ class Upscaler:
 | 
				
			|||||||
        # load options from upscaler class into driver settings
 | 
					        # load options from upscaler class into driver settings
 | 
				
			||||||
        self.driver_object.load_configurations(self)
 | 
					        self.driver_object.load_configurations(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # parse arguments for waifu2x
 | 
					        # initialize FFmpeg object
 | 
				
			||||||
        # check argument sanity
 | 
					        self.ffmpeg_object = Ffmpeg(self.ffmpeg_settings, self.image_format)
 | 
				
			||||||
        self._check_arguments()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # define processing queue
 | 
					        # define processing queue
 | 
				
			||||||
        self.processing_queue = queue.Queue()
 | 
					        self.processing_queue = queue.Queue()
 | 
				
			||||||
@ -408,17 +417,17 @@ class Upscaler:
 | 
				
			|||||||
            for input_path in self.input:
 | 
					            for input_path in self.input:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if input_path.is_file():
 | 
					                if input_path.is_file():
 | 
				
			||||||
                    output_video = self.output / input_path.name
 | 
					                    output_path = self.output / input_path.name
 | 
				
			||||||
                    self.processing_queue.put((input_path.absolute(), output_video.absolute()))
 | 
					                    self.processing_queue.put((input_path.absolute(), output_path.absolute()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                elif input_path.is_dir():
 | 
					                elif input_path.is_dir():
 | 
				
			||||||
                    for input_video in [f for f in input_path.iterdir() if f.is_file()]:
 | 
					                    for input_path in [f for f in input_path.iterdir() if f.is_file()]:
 | 
				
			||||||
                        output_video = self.output / input_video.name
 | 
					                        output_path = self.output / input_path.name
 | 
				
			||||||
                        self.processing_queue.put((input_video.absolute(), output_video.absolute()))
 | 
					                        self.processing_queue.put((input_path.absolute(), output_path.absolute()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # if input specified is single file
 | 
					        # if input specified is single file
 | 
				
			||||||
        elif self.input.is_file():
 | 
					        elif self.input.is_file():
 | 
				
			||||||
            Avalon.info(_('Upscaling single video file: {}').format(self.input))
 | 
					            Avalon.info(_('Upscaling single file: {}').format(self.input))
 | 
				
			||||||
            self.processing_queue.put((self.input.absolute(), self.output.absolute()))
 | 
					            self.processing_queue.put((self.input.absolute(), self.output.absolute()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # if input specified is a directory
 | 
					        # if input specified is a directory
 | 
				
			||||||
@ -426,36 +435,63 @@ class Upscaler:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            # make output directory if it doesn't exist
 | 
					            # make output directory if it doesn't exist
 | 
				
			||||||
            self.output.mkdir(parents=True, exist_ok=True)
 | 
					            self.output.mkdir(parents=True, exist_ok=True)
 | 
				
			||||||
            for input_video in [f for f in self.input.iterdir() if f.is_file()]:
 | 
					            for input_path in [f for f in self.input.iterdir() if f.is_file()]:
 | 
				
			||||||
                output_video = self.output / input_video.name
 | 
					                output_path = self.output / input_path.name
 | 
				
			||||||
                self.processing_queue.put((input_video.absolute(), output_video.absolute()))
 | 
					                self.processing_queue.put((input_path.absolute(), output_path.absolute()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # record video count for external calls
 | 
					        # check argument sanity before running
 | 
				
			||||||
        self.total_videos = self.processing_queue.qsize()
 | 
					        self._check_arguments()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # record file count for external calls
 | 
				
			||||||
 | 
					        self.total_files = self.processing_queue.qsize()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
            while not self.processing_queue.empty():
 | 
					            while not self.processing_queue.empty():
 | 
				
			||||||
            self.current_input_video, output_video = self.processing_queue.get()
 | 
					 | 
				
			||||||
            # drivers that have native support for video processing
 | 
					 | 
				
			||||||
            if self.driver == 'anime4kcpp':
 | 
					 | 
				
			||||||
                # append FFmpeg path to the end of PATH
 | 
					 | 
				
			||||||
                # Anime4KCPP will then use FFmpeg to migrate audio tracks
 | 
					 | 
				
			||||||
                os.environ['PATH'] += f';{self.ffmpeg_settings["ffmpeg_path"]}'
 | 
					 | 
				
			||||||
                Avalon.info(_('Starting to upscale extracted images'))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # run Anime4KCPP
 | 
					                # reset current processing progress for new job
 | 
				
			||||||
                self.process_pool.append(self.driver_object.upscale(self.current_input_video, output_video))
 | 
					                self.total_frames_upscaled = 0
 | 
				
			||||||
 | 
					                self.total_frames = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # get new job from queue
 | 
				
			||||||
 | 
					                self.current_input_file, output_path = self.processing_queue.get()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # get file type
 | 
				
			||||||
 | 
					                input_file_mime_type = magic.from_file(str(self.current_input_file.absolute()), mime=True)
 | 
				
			||||||
 | 
					                input_file_type = input_file_mime_type.split('/')[0]
 | 
				
			||||||
 | 
					                input_file_subtype = input_file_mime_type.split('/')[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # start handling input
 | 
				
			||||||
 | 
					                # if input file is a static image
 | 
				
			||||||
 | 
					                if input_file_type == 'image' and input_file_subtype != 'gif':
 | 
				
			||||||
 | 
					                    Avalon.info(_('Starting to upscale image'))
 | 
				
			||||||
 | 
					                    self.process_pool.append(self.driver_object.upscale(self.current_input_file, output_path))
 | 
				
			||||||
 | 
					                    self._wait()
 | 
				
			||||||
 | 
					                    Avalon.info(_('Upscaling completed'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    # static images don't require GIF or video encoding
 | 
				
			||||||
 | 
					                    # go to the next task
 | 
				
			||||||
 | 
					                    self.processing_queue.task_done()
 | 
				
			||||||
 | 
					                    self.total_processed += 1
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # if input file is a image/gif file or a video
 | 
				
			||||||
 | 
					                elif input_file_mime_type == 'image/gif' or input_file_type == 'video':
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    # drivers that have native support for video processing
 | 
				
			||||||
 | 
					                    if input_file_type == 'video' and self.driver == 'anime4kcpp':
 | 
				
			||||||
 | 
					                        Avalon.info(_('Starting to upscale video with Anime4KCPP'))
 | 
				
			||||||
 | 
					                        # enable video processing mode for Anime4KCPP
 | 
				
			||||||
 | 
					                        self.driver_settings['videoMode'] = True
 | 
				
			||||||
 | 
					                        self.process_pool.append(self.driver_object.upscale(self.current_input_file, output_path))
 | 
				
			||||||
                        self._wait()
 | 
					                        self._wait()
 | 
				
			||||||
                        Avalon.info(_('Upscaling completed'))
 | 
					                        Avalon.info(_('Upscaling completed'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    else:
 | 
					                    else:
 | 
				
			||||||
                try:
 | 
					 | 
				
			||||||
                        self.create_temp_directories()
 | 
					                        self.create_temp_directories()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    # initialize objects for ffmpeg and waifu2x-caffe
 | 
					                        # get video information JSON using FFprobe
 | 
				
			||||||
                    fm = Ffmpeg(self.ffmpeg_settings, self.image_format)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        Avalon.info(_('Reading video information'))
 | 
					                        Avalon.info(_('Reading video information'))
 | 
				
			||||||
                    video_info = fm.get_video_info(self.current_input_video)
 | 
					                        video_info = self.ffmpeg_object.probe_file_info(self.current_input_file)
 | 
				
			||||||
                        # analyze original video with FFprobe and retrieve framerate
 | 
					                        # analyze original video with FFprobe and retrieve framerate
 | 
				
			||||||
                        # width, height = info['streams'][0]['width'], info['streams'][0]['height']
 | 
					                        # width, height = info['streams'][0]['width'], info['streams'][0]['height']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -471,53 +507,74 @@ class Upscaler:
 | 
				
			|||||||
                            Avalon.error(_('Aborting: No video stream found'))
 | 
					                            Avalon.error(_('Aborting: No video stream found'))
 | 
				
			||||||
                            raise StreamNotFoundError('no video stream found')
 | 
					                            raise StreamNotFoundError('no video stream found')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    # extract frames from video
 | 
					 | 
				
			||||||
                    self.process_pool.append((fm.extract_frames(self.current_input_video, self.extracted_frames)))
 | 
					 | 
				
			||||||
                    self._wait()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        # get average frame rate of video stream
 | 
					                        # get average frame rate of video stream
 | 
				
			||||||
                        framerate = float(Fraction(video_info['streams'][video_stream_index]['r_frame_rate']))
 | 
					                        framerate = float(Fraction(video_info['streams'][video_stream_index]['r_frame_rate']))
 | 
				
			||||||
                    fm.pixel_format = video_info['streams'][video_stream_index]['pix_fmt']
 | 
					                        # self.ffmpeg_object.pixel_format = video_info['streams'][video_stream_index]['pix_fmt']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        # extract frames from video
 | 
				
			||||||
 | 
					                        self.process_pool.append((self.ffmpeg_object.extract_frames(self.current_input_file, self.extracted_frames)))
 | 
				
			||||||
 | 
					                        self._wait()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        # if driver is waifu2x-caffe
 | 
				
			||||||
 | 
					                        # pass pixel format output depth information
 | 
				
			||||||
                        if self.driver == 'waifu2x_caffe':
 | 
					                        if self.driver == 'waifu2x_caffe':
 | 
				
			||||||
                            # get a dict of all pixel formats and corresponding bit depth
 | 
					                            # get a dict of all pixel formats and corresponding bit depth
 | 
				
			||||||
                        pixel_formats = fm.get_pixel_formats()
 | 
					                            pixel_formats = self.ffmpeg_object.get_pixel_formats()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            # try getting pixel format's corresponding bti depth
 | 
					                            # try getting pixel format's corresponding bti depth
 | 
				
			||||||
                            try:
 | 
					                            try:
 | 
				
			||||||
                            self.driver_settings['output_depth'] = pixel_formats[fm.pixel_format]
 | 
					                                self.driver_settings['output_depth'] = pixel_formats[self.ffmpeg_object.pixel_format]
 | 
				
			||||||
                            except KeyError:
 | 
					                            except KeyError:
 | 
				
			||||||
                            Avalon.error(_('Unsupported pixel format: {}').format(fm.pixel_format))
 | 
					                                Avalon.error(_('Unsupported pixel format: {}').format(self.ffmpeg_object.pixel_format))
 | 
				
			||||||
                            raise UnsupportedPixelError(f'unsupported pixel format {fm.pixel_format}')
 | 
					                                raise UnsupportedPixelError(f'unsupported pixel format {self.ffmpeg_object.pixel_format}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        Avalon.info(_('Framerate: {}').format(framerate))
 | 
					                        Avalon.info(_('Framerate: {}').format(framerate))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        # width/height will be coded width/height x upscale factor
 | 
					                        # width/height will be coded width/height x upscale factor
 | 
				
			||||||
                    original_width = video_info['streams'][video_stream_index]['width']
 | 
					                        # original_width = video_info['streams'][video_stream_index]['width']
 | 
				
			||||||
                    original_height = video_info['streams'][video_stream_index]['height']
 | 
					                        # original_height = video_info['streams'][video_stream_index]['height']
 | 
				
			||||||
                    scale_width = int(self.scale_ratio * original_width)
 | 
					                        # scale_width = int(self.scale_ratio * original_width)
 | 
				
			||||||
                    scale_height = int(self.scale_ratio * original_height)
 | 
					                        # scale_height = int(self.scale_ratio * original_height)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        # upscale images one by one using waifu2x
 | 
					                        # upscale images one by one using waifu2x
 | 
				
			||||||
                    Avalon.info(_('Starting to upscale extracted images'))
 | 
					                        Avalon.info(_('Starting to upscale extracted frames'))
 | 
				
			||||||
                        self._upscale_frames()
 | 
					                        self._upscale_frames()
 | 
				
			||||||
                        Avalon.info(_('Upscaling completed'))
 | 
					                        Avalon.info(_('Upscaling completed'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    # frames to Video
 | 
					                # if file is none of: image, image/gif, video
 | 
				
			||||||
                    Avalon.info(_('Converting extracted frames into video'))
 | 
					                # skip to the next task
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    Avalon.error(_('File {} ({}) neither an image of a video').format(self.current_input_file, input_file_mime_type))
 | 
				
			||||||
 | 
					                    Avalon.warning(_('Skipping this file'))
 | 
				
			||||||
 | 
					                    self.processing_queue.task_done()
 | 
				
			||||||
 | 
					                    self.total_processed += 1
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    # use user defined output size
 | 
					                # start handling output
 | 
				
			||||||
                    self.process_pool.append(fm.assemble_video(framerate,
 | 
					                # output can be either GIF or video
 | 
				
			||||||
                                                               f'{scale_width}x{scale_height}',
 | 
					
 | 
				
			||||||
                                                               self.upscaled_frames))
 | 
					                # if the desired output is gif file
 | 
				
			||||||
 | 
					                if output_path.suffix.lower() == '.gif':
 | 
				
			||||||
 | 
					                    Avalon.info(_('Converting extracted frames into GIF image'))
 | 
				
			||||||
 | 
					                    gifski_object = Gifski(self.gifski_settings)
 | 
				
			||||||
 | 
					                    self.process_pool.append(gifski_object.make_gif(self.upscaled_frames, output_path, framerate, self.image_format))
 | 
				
			||||||
 | 
					                    self._wait()
 | 
				
			||||||
 | 
					                    Avalon.info(_('Conversion completed'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # if the desired output is video
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    # frames to video
 | 
				
			||||||
 | 
					                    Avalon.info(_('Converting extracted frames into video'))
 | 
				
			||||||
 | 
					                    self.process_pool.append(self.ffmpeg_object.assemble_video(framerate, self.upscaled_frames))
 | 
				
			||||||
 | 
					                    # f'{scale_width}x{scale_height}',
 | 
				
			||||||
                    self._wait()
 | 
					                    self._wait()
 | 
				
			||||||
                    Avalon.info(_('Conversion completed'))
 | 
					                    Avalon.info(_('Conversion completed'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    try:
 | 
					                    try:
 | 
				
			||||||
                        # migrate audio tracks and subtitles
 | 
					                        # migrate audio tracks and subtitles
 | 
				
			||||||
                        Avalon.info(_('Migrating audio, subtitles and other streams to upscaled video'))
 | 
					                        Avalon.info(_('Migrating audio, subtitles and other streams to upscaled video'))
 | 
				
			||||||
                        self.process_pool.append(fm.migrate_streams(self.current_input_video,
 | 
					                        self.process_pool.append(self.ffmpeg_object.migrate_streams(self.current_input_file,
 | 
				
			||||||
                                                                    output_video,
 | 
					                                                                                    output_path,
 | 
				
			||||||
                                                                                    self.upscaled_frames))
 | 
					                                                                                    self.upscaled_frames))
 | 
				
			||||||
                        self._wait()
 | 
					                        self._wait()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -527,8 +584,12 @@ class Upscaler:
 | 
				
			|||||||
                        Avalon.error(_('Failed to migrate streams'))
 | 
					                        Avalon.error(_('Failed to migrate streams'))
 | 
				
			||||||
                        Avalon.warning(_('Trying to output video without additional streams'))
 | 
					                        Avalon.warning(_('Trying to output video without additional streams'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if input_file_mime_type == 'image/gif':
 | 
				
			||||||
 | 
					                            (self.upscaled_frames / self.ffmpeg_object.intermediate_file_name).replace(output_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        else:
 | 
				
			||||||
                            # construct output file path
 | 
					                            # construct output file path
 | 
				
			||||||
                        output_video_path = output_video.parent / f'{output_video.stem}{fm.intermediate_file_name.suffix}'
 | 
					                            output_video_path = output_path.parent / f'{output_path.stem}{self.ffmpeg_object.intermediate_file_name.suffix}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            # if output file already exists, cancel
 | 
					                            # if output file already exists, cancel
 | 
				
			||||||
                            if output_video_path.exists():
 | 
					                            if output_video_path.exists():
 | 
				
			||||||
@ -537,10 +598,12 @@ class Upscaler:
 | 
				
			|||||||
                            # otherwise, rename intermediate file to the output file
 | 
					                            # otherwise, rename intermediate file to the output file
 | 
				
			||||||
                            else:
 | 
					                            else:
 | 
				
			||||||
                                Avalon.info(_('Writing intermediate file to: {}').format(output_video_path.absolute()))
 | 
					                                Avalon.info(_('Writing intermediate file to: {}').format(output_video_path.absolute()))
 | 
				
			||||||
                            (self.upscaled_frames / fm.intermediate_file_name).rename(output_video_path)
 | 
					                                (self.upscaled_frames / self.ffmpeg_object.intermediate_file_name).rename(output_video_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    # destroy temp directories
 | 
					                # increment total number of files processed
 | 
				
			||||||
                self.cleanup_temp_directories()
 | 
					                self.cleanup_temp_directories()
 | 
				
			||||||
 | 
					                self.processing_queue.task_done()
 | 
				
			||||||
 | 
					                self.total_processed += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        except (Exception, KeyboardInterrupt, SystemExit) as e:
 | 
					        except (Exception, KeyboardInterrupt, SystemExit) as e:
 | 
				
			||||||
            with contextlib.suppress(ValueError):
 | 
					            with contextlib.suppress(ValueError):
 | 
				
			||||||
@ -548,8 +611,5 @@ class Upscaler:
 | 
				
			|||||||
                self.running = False
 | 
					                self.running = False
 | 
				
			||||||
            raise e
 | 
					            raise e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # increment total number of videos processed
 | 
					 | 
				
			||||||
            self.total_processed += 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # signal upscaling completion
 | 
					        # signal upscaling completion
 | 
				
			||||||
        self.running = False
 | 
					        self.running = False
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,7 @@ __      __  _       _                  ___   __   __
 | 
				
			|||||||
Name: Video2X Controller
 | 
					Name: Video2X Controller
 | 
				
			||||||
Creator: K4YT3X
 | 
					Creator: K4YT3X
 | 
				
			||||||
Date Created: Feb 24, 2018
 | 
					Date Created: Feb 24, 2018
 | 
				
			||||||
Last Modified: May 10, 2020
 | 
					Last Modified: May 11, 2020
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Editor: BrianPetkovsek
 | 
					Editor: BrianPetkovsek
 | 
				
			||||||
Last Modified: June 17, 2019
 | 
					Last Modified: June 17, 2019
 | 
				
			||||||
@ -181,6 +181,10 @@ driver_settings['path'] = os.path.expandvars(driver_settings['path'])
 | 
				
			|||||||
ffmpeg_settings = config['ffmpeg']
 | 
					ffmpeg_settings = config['ffmpeg']
 | 
				
			||||||
ffmpeg_settings['ffmpeg_path'] = os.path.expandvars(ffmpeg_settings['ffmpeg_path'])
 | 
					ffmpeg_settings['ffmpeg_path'] = os.path.expandvars(ffmpeg_settings['ffmpeg_path'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# read Gifski configuration
 | 
				
			||||||
 | 
					gifski_settings = config['gifski']
 | 
				
			||||||
 | 
					gifski_settings['gifski_path'] = os.path.expandvars(gifski_settings['gifski_path'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# load video2x settings
 | 
					# load video2x settings
 | 
				
			||||||
image_format = config['video2x']['image_format'].lower()
 | 
					image_format = config['video2x']['image_format'].lower()
 | 
				
			||||||
preserve_frames = config['video2x']['preserve_frames']
 | 
					preserve_frames = config['video2x']['preserve_frames']
 | 
				
			||||||
@ -213,7 +217,8 @@ try:
 | 
				
			|||||||
    upscaler = Upscaler(input_path=video2x_args.input,
 | 
					    upscaler = Upscaler(input_path=video2x_args.input,
 | 
				
			||||||
                        output_path=video2x_args.output,
 | 
					                        output_path=video2x_args.output,
 | 
				
			||||||
                        driver_settings=driver_settings,
 | 
					                        driver_settings=driver_settings,
 | 
				
			||||||
                        ffmpeg_settings=ffmpeg_settings)
 | 
					                        ffmpeg_settings=ffmpeg_settings,
 | 
				
			||||||
 | 
					                        gifski_settings=gifski_settings)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # set upscaler optional options
 | 
					    # set upscaler optional options
 | 
				
			||||||
    upscaler.driver = video2x_args.driver
 | 
					    upscaler.driver = video2x_args.driver
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
# Name: Video2X Configuration File
 | 
					# Name: Video2X Configuration File
 | 
				
			||||||
# Creator: K4YT3X
 | 
					# Creator: K4YT3X
 | 
				
			||||||
# Date Created: October 23, 2018
 | 
					# Date Created: October 23, 2018
 | 
				
			||||||
# Last Modified: May 9, 2020
 | 
					# Last Modified: May 11, 2020
 | 
				
			||||||
# Values here are the default values. Change the value here to
 | 
					# Values here are the default values. Change the value here to
 | 
				
			||||||
#   save the default value permanently.
 | 
					#   save the default value permanently.
 | 
				
			||||||
# Items commented out are parameters irrelevant to this context
 | 
					# Items commented out are parameters irrelevant to this context
 | 
				
			||||||
@ -87,7 +87,7 @@ anime4kcpp:
 | 
				
			|||||||
  zoomFactor: 2.0 # zoom factor for resizing (double [=2])
 | 
					  zoomFactor: 2.0 # zoom factor for resizing (double [=2])
 | 
				
			||||||
  threads: 16 # Threads count for video processing (unsigned int [=16])
 | 
					  threads: 16 # Threads count for video processing (unsigned int [=16])
 | 
				
			||||||
  fastMode: false # Faster but maybe low quality
 | 
					  fastMode: false # Faster but maybe low quality
 | 
				
			||||||
  videoMode: true # Video process
 | 
					  videoMode: false # Video process
 | 
				
			||||||
  preview: null # Preview image
 | 
					  preview: null # Preview image
 | 
				
			||||||
  preprocessing: False # Enable pre processing
 | 
					  preprocessing: False # Enable pre processing
 | 
				
			||||||
  postprocessing: False # Enable post processing
 | 
					  postprocessing: False # Enable post processing
 | 
				
			||||||
@ -101,9 +101,9 @@ anime4kcpp:
 | 
				
			|||||||
ffmpeg:
 | 
					ffmpeg:
 | 
				
			||||||
  ffmpeg_path: '%LOCALAPPDATA%\video2x\ffmpeg-latest-win64-static\bin'
 | 
					  ffmpeg_path: '%LOCALAPPDATA%\video2x\ffmpeg-latest-win64-static\bin'
 | 
				
			||||||
  intermediate_file_name: 'intermediate.mkv'
 | 
					  intermediate_file_name: 'intermediate.mkv'
 | 
				
			||||||
  # step 1: extract all frames from original video
 | 
					  # step 1: extract all frames from original input
 | 
				
			||||||
  # into temporary directory
 | 
					  # into temporary directory
 | 
				
			||||||
  video_to_frames:
 | 
					  input_to_frames:
 | 
				
			||||||
    output_options:
 | 
					    output_options:
 | 
				
			||||||
      '-qscale:v': null
 | 
					      '-qscale:v': null
 | 
				
			||||||
      '-pix_fmt': rgba64be
 | 
					      '-pix_fmt': rgba64be
 | 
				
			||||||
@ -138,6 +138,17 @@ ffmpeg:
 | 
				
			|||||||
      '-metadata': 'comment=Upscaled by Video2X'
 | 
					      '-metadata': 'comment=Upscaled by Video2X'
 | 
				
			||||||
    '-hwaccel': auto
 | 
					    '-hwaccel': auto
 | 
				
			||||||
    '-y': true
 | 
					    '-y': true
 | 
				
			||||||
 | 
					gifski:
 | 
				
			||||||
 | 
					  gifski_path: '%LOCALAPPDATA%\video2x\gifski\win\gifski'
 | 
				
			||||||
 | 
					  # output: null # Destination file to write to
 | 
				
			||||||
 | 
					  # fps: 20 # Animation frames per second (for PNG frames only) [default: 20]
 | 
				
			||||||
 | 
					  fast: false # 3 times faster encoding, but 10% lower quality and bigger file
 | 
				
			||||||
 | 
					  quality: 100 # Lower quality may give smaller file
 | 
				
			||||||
 | 
					  width: null # Maximum width
 | 
				
			||||||
 | 
					  height: null # Maximum height (if width is also set)
 | 
				
			||||||
 | 
					  once: false # Do not loop the GIF
 | 
				
			||||||
 | 
					  nosort: false # Use files exactly in the order given, rather than sorted
 | 
				
			||||||
 | 
					  quiet: false # Do not show a progress bar
 | 
				
			||||||
video2x:
 | 
					video2x:
 | 
				
			||||||
  video2x_cache_directory: null # default: %TEMP%\video2x
 | 
					  video2x_cache_directory: null # default: %TEMP%\video2x
 | 
				
			||||||
  image_format: png
 | 
					  image_format: png
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@
 | 
				
			|||||||
Creator: Video2X GUI
 | 
					Creator: Video2X GUI
 | 
				
			||||||
Author: K4YT3X
 | 
					Author: K4YT3X
 | 
				
			||||||
Date Created: May 5, 2020
 | 
					Date Created: May 5, 2020
 | 
				
			||||||
Last Modified: May 10, 2020
 | 
					Last Modified: May 11, 2020
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# local imports
 | 
					# local imports
 | 
				
			||||||
@ -25,6 +25,7 @@ import yaml
 | 
				
			|||||||
from PyQt5 import QtGui, uic
 | 
					from PyQt5 import QtGui, uic
 | 
				
			||||||
from PyQt5.QtCore import *
 | 
					from PyQt5.QtCore import *
 | 
				
			||||||
from PyQt5.QtWidgets import *
 | 
					from PyQt5.QtWidgets import *
 | 
				
			||||||
 | 
					import magic
 | 
				
			||||||
# QObject, pyqtSlot, pyqtSignal, QRunnable, QThreadPool, QAbstractTableModel, Qt
 | 
					# QObject, pyqtSlot, pyqtSignal, QRunnable, QThreadPool, QAbstractTableModel, Qt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VERSION = '2.0.0'
 | 
					VERSION = '2.0.0'
 | 
				
			||||||
@ -110,13 +111,34 @@ class InputTableModel(QAbstractTableModel):
 | 
				
			|||||||
    def data(self, index, role):
 | 
					    def data(self, index, role):
 | 
				
			||||||
        if role == Qt.DisplayRole:
 | 
					        if role == Qt.DisplayRole:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            file_path = self._data[index.row()]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if index.column() == 0:
 | 
					            if index.column() == 0:
 | 
				
			||||||
                return str(self._data[index.row()].absolute())
 | 
					                return str(file_path.absolute())
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                if self._data[index.row()].is_file():
 | 
					
 | 
				
			||||||
                    return 'File'
 | 
					                # determine file type
 | 
				
			||||||
                elif self._data[index.row()].is_dir():
 | 
					                # if path is a folder
 | 
				
			||||||
 | 
					                if file_path.is_dir():
 | 
				
			||||||
                    return 'Folder'
 | 
					                    return 'Folder'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # if path is single file
 | 
				
			||||||
 | 
					                # determine file type
 | 
				
			||||||
 | 
					                elif file_path.is_file():
 | 
				
			||||||
 | 
					                    input_file_mime_type = magic.from_file(str(file_path.absolute()), mime=True)
 | 
				
			||||||
 | 
					                    input_file_type = input_file_mime_type.split('/')[0]
 | 
				
			||||||
 | 
					                    input_file_subtype = input_file_mime_type.split('/')[1]
 | 
				
			||||||
 | 
					                    if input_file_type == 'image':
 | 
				
			||||||
 | 
					                        if input_file_subtype == 'gif':
 | 
				
			||||||
 | 
					                            return 'GIF'
 | 
				
			||||||
 | 
					                        return 'Image'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    elif input_file_type == 'video':
 | 
				
			||||||
 | 
					                        return 'Video'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        return 'Unknown'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    return 'Unknown'
 | 
					                    return 'Unknown'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -373,6 +395,10 @@ class Video2XMainWindow(QMainWindow):
 | 
				
			|||||||
        self.ffmpeg_settings = self.config['ffmpeg']
 | 
					        self.ffmpeg_settings = self.config['ffmpeg']
 | 
				
			||||||
        self.ffmpeg_settings['ffmpeg_path'] = str(pathlib.Path(os.path.expandvars(self.ffmpeg_settings['ffmpeg_path'])).absolute())
 | 
					        self.ffmpeg_settings['ffmpeg_path'] = str(pathlib.Path(os.path.expandvars(self.ffmpeg_settings['ffmpeg_path'])).absolute())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # read Gifski configuration
 | 
				
			||||||
 | 
					        self.gifski_settings = self.config['gifski']
 | 
				
			||||||
 | 
					        self.gifski_settings['gifski_path'] = str(pathlib.Path(os.path.expandvars(self.gifski_settings['gifski_path'])).absolute())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # set cache directory path
 | 
					        # set cache directory path
 | 
				
			||||||
        if self.config['video2x']['video2x_cache_directory'] is None:
 | 
					        if self.config['video2x']['video2x_cache_directory'] is None:
 | 
				
			||||||
            self.config['video2x']['video2x_cache_directory'] = str((pathlib.Path(tempfile.gettempdir()) / 'video2x').absolute())
 | 
					            self.config['video2x']['video2x_cache_directory'] = str((pathlib.Path(tempfile.gettempdir()) / 'video2x').absolute())
 | 
				
			||||||
@ -585,7 +611,34 @@ class Video2XMainWindow(QMainWindow):
 | 
				
			|||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if input_path.is_file():
 | 
					        if input_path.is_file():
 | 
				
			||||||
            output_path = input_path.parent / f'{input_path.stem}_output.mp4'
 | 
					
 | 
				
			||||||
 | 
					            # generate suffix automatically
 | 
				
			||||||
 | 
					            input_file_mime_type = magic.from_file(str(input_path.absolute()), mime=True)
 | 
				
			||||||
 | 
					            input_file_type = input_file_mime_type.split('/')[0]
 | 
				
			||||||
 | 
					            input_file_subtype = input_file_mime_type.split('/')[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # if input file is an image
 | 
				
			||||||
 | 
					            if input_file_type == 'image':
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # if file is a gif, use .gif
 | 
				
			||||||
 | 
					                if input_file_subtype == 'gif':
 | 
				
			||||||
 | 
					                    suffix = '.gif'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # otherwise, use .png by default for all images
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    suffix = '.png'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # if input is video, use .mp4 as output by default
 | 
				
			||||||
 | 
					            elif input_file_type == 'video':
 | 
				
			||||||
 | 
					                suffix = '.mp4'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # if failed to detect file type
 | 
				
			||||||
 | 
					            # use input file's suffix
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                suffix = input_path.suffix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            output_path = input_path.parent / f'{input_path.stem}_output{suffix}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        elif input_path.is_dir():
 | 
					        elif input_path.is_dir():
 | 
				
			||||||
            output_path = input_path.parent / f'{input_path.stem}_output'
 | 
					            output_path = input_path.parent / f'{input_path.stem}_output'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -593,7 +646,7 @@ class Video2XMainWindow(QMainWindow):
 | 
				
			|||||||
        output_path_id = 0
 | 
					        output_path_id = 0
 | 
				
			||||||
        while output_path.exists() and output_path_id <= 1000:
 | 
					        while output_path.exists() and output_path_id <= 1000:
 | 
				
			||||||
            if input_path.is_file():
 | 
					            if input_path.is_file():
 | 
				
			||||||
                output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}.mp4')
 | 
					                output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}{suffix}')
 | 
				
			||||||
            elif input_path.is_dir():
 | 
					            elif input_path.is_dir():
 | 
				
			||||||
                output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}')
 | 
					                output_path = input_path.parent / pathlib.Path(f'{input_path.stem}_output_{output_path_id}')
 | 
				
			||||||
            output_path_id += 1
 | 
					            output_path_id += 1
 | 
				
			||||||
@ -693,8 +746,8 @@ You can [submit an issue on GitHub](https://github.com/k4yt3x/video2x/issues/new
 | 
				
			|||||||
                                    self.upscaler.total_frames_upscaled,
 | 
					                                    self.upscaler.total_frames_upscaled,
 | 
				
			||||||
                                    self.upscaler.total_frames,
 | 
					                                    self.upscaler.total_frames,
 | 
				
			||||||
                                    self.upscaler.total_processed,
 | 
					                                    self.upscaler.total_processed,
 | 
				
			||||||
                                    self.upscaler.total_videos,
 | 
					                                    self.upscaler.total_files,
 | 
				
			||||||
                                    self.upscaler.current_input_video,
 | 
					                                    self.upscaler.current_input_file,
 | 
				
			||||||
                                    self.upscaler.last_frame_upscaled))
 | 
					                                    self.upscaler.last_frame_upscaled))
 | 
				
			||||||
            time.sleep(1)
 | 
					            time.sleep(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -707,8 +760,8 @@ You can [submit an issue on GitHub](https://github.com/k4yt3x/video2x/issues/new
 | 
				
			|||||||
        total_frames_upscaled = progress_information[1]
 | 
					        total_frames_upscaled = progress_information[1]
 | 
				
			||||||
        total_frames = progress_information[2]
 | 
					        total_frames = progress_information[2]
 | 
				
			||||||
        total_processed = progress_information[3]
 | 
					        total_processed = progress_information[3]
 | 
				
			||||||
        total_videos = progress_information[4]
 | 
					        total_files = progress_information[4]
 | 
				
			||||||
        current_input_video = progress_information[5]
 | 
					        current_input_file = progress_information[5]
 | 
				
			||||||
        last_frame_upscaled = progress_information[6]
 | 
					        last_frame_upscaled = progress_information[6]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # calculate fields based on frames and time elapsed
 | 
					        # calculate fields based on frames and time elapsed
 | 
				
			||||||
@ -727,10 +780,10 @@ You can [submit an issue on GitHub](https://github.com/k4yt3x/video2x/issues/new
 | 
				
			|||||||
        self.time_elapsed_label.setText('Time Elapsed: {}'.format(time.strftime("%H:%M:%S", time.gmtime(time_elapsed))))
 | 
					        self.time_elapsed_label.setText('Time Elapsed: {}'.format(time.strftime("%H:%M:%S", time.gmtime(time_elapsed))))
 | 
				
			||||||
        self.time_remaining_label.setText('Time Remaining: {}'.format(time.strftime("%H:%M:%S", time.gmtime(time_remaining))))
 | 
					        self.time_remaining_label.setText('Time Remaining: {}'.format(time.strftime("%H:%M:%S", time.gmtime(time_remaining))))
 | 
				
			||||||
        self.rate_label.setText('Rate (FPS): {}'.format(round(rate, 2)))
 | 
					        self.rate_label.setText('Rate (FPS): {}'.format(round(rate, 2)))
 | 
				
			||||||
        self.overall_progress_label.setText('Overall Progress: {}/{}'.format(total_processed, total_videos))
 | 
					        self.overall_progress_label.setText('Overall Progress: {}/{}'.format(total_processed, total_files))
 | 
				
			||||||
        self.overall_progress_bar.setMaximum(total_videos)
 | 
					        self.overall_progress_bar.setMaximum(total_files)
 | 
				
			||||||
        self.overall_progress_bar.setValue(total_processed)
 | 
					        self.overall_progress_bar.setValue(total_processed)
 | 
				
			||||||
        self.currently_processing_label.setText('Currently Processing: {}'.format(str(current_input_video.name)))
 | 
					        self.currently_processing_label.setText('Currently Processing: {}'.format(str(current_input_file.name)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # if show frame is checked, show preview image
 | 
					        # if show frame is checked, show preview image
 | 
				
			||||||
        if self.frame_preview_show_preview_check_box.isChecked() and last_frame_upscaled.is_file():
 | 
					        if self.frame_preview_show_preview_check_box.isChecked() and last_frame_upscaled.is_file():
 | 
				
			||||||
@ -798,7 +851,8 @@ You can [submit an issue on GitHub](https://github.com/k4yt3x/video2x/issues/new
 | 
				
			|||||||
            self.upscaler = Upscaler(input_path=input_directory,
 | 
					            self.upscaler = Upscaler(input_path=input_directory,
 | 
				
			||||||
                                     output_path=output_directory,
 | 
					                                     output_path=output_directory,
 | 
				
			||||||
                                     driver_settings=self.driver_settings,
 | 
					                                     driver_settings=self.driver_settings,
 | 
				
			||||||
                                     ffmpeg_settings=self.ffmpeg_settings)
 | 
					                                     ffmpeg_settings=self.ffmpeg_settings,
 | 
				
			||||||
 | 
					                                     gifski_settings=self.gifski_settings)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # set optional options
 | 
					            # set optional options
 | 
				
			||||||
            self.upscaler.driver = AVAILABLE_DRIVERS[self.driver_combo_box.currentText()]
 | 
					            self.upscaler.driver = AVAILABLE_DRIVERS[self.driver_combo_box.currentText()]
 | 
				
			||||||
 | 
				
			|||||||
@ -31,6 +31,7 @@ import re
 | 
				
			|||||||
import shutil
 | 
					import shutil
 | 
				
			||||||
import subprocess
 | 
					import subprocess
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
 | 
					import tarfile
 | 
				
			||||||
import tempfile
 | 
					import tempfile
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
import traceback
 | 
					import traceback
 | 
				
			||||||
@ -42,12 +43,19 @@ import zipfile
 | 
				
			|||||||
# later in the script.
 | 
					# later in the script.
 | 
				
			||||||
# import requests
 | 
					# import requests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VERSION = '1.8.0'
 | 
					VERSION = '2.0.0'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# global static variables
 | 
					# global static variables
 | 
				
			||||||
LOCALAPPDATA = pathlib.Path(os.getenv('localappdata'))
 | 
					LOCALAPPDATA = pathlib.Path(os.getenv('localappdata'))
 | 
				
			||||||
VIDEO2X_CONFIG = pathlib.Path(__file__).parent.absolute() / 'video2x.yaml'
 | 
					VIDEO2X_CONFIG = pathlib.Path(__file__).parent.absolute() / 'video2x.yaml'
 | 
				
			||||||
DRIVER_OPTIONS = ['all', 'ffmpeg', 'waifu2x_caffe', 'waifu2x_converter_cpp', 'waifu2x_ncnn_vulkan', 'anime4kcpp', 'srmd_ncnn_vulkan']
 | 
					DRIVER_OPTIONS = ['all',
 | 
				
			||||||
 | 
					                  'ffmpeg',
 | 
				
			||||||
 | 
					                  'gifski',
 | 
				
			||||||
 | 
					                  'waifu2x_caffe',
 | 
				
			||||||
 | 
					                  'waifu2x_converter_cpp',
 | 
				
			||||||
 | 
					                  'waifu2x_ncnn_vulkan',
 | 
				
			||||||
 | 
					                  'anime4kcpp',
 | 
				
			||||||
 | 
					                  'srmd_ncnn_vulkan']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def parse_arguments():
 | 
					def parse_arguments():
 | 
				
			||||||
@ -76,32 +84,21 @@ class Video2xSetup:
 | 
				
			|||||||
        self.trash = []
 | 
					        self.trash = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def run(self):
 | 
					    def run(self):
 | 
				
			||||||
 | 
					        # regardless of which driver to install
 | 
				
			||||||
 | 
					        # always ensure Python modules are installed and up-to-date
 | 
				
			||||||
        if self.download_python_modules:
 | 
					        if self.download_python_modules:
 | 
				
			||||||
            print('\nInstalling Python libraries')
 | 
					            print('\nInstalling Python libraries')
 | 
				
			||||||
            self._install_python_requirements()
 | 
					            self._install_python_requirements()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # if all drivers are to be installed
 | 
				
			||||||
        if self.driver == 'all':
 | 
					        if self.driver == 'all':
 | 
				
			||||||
            self._install_ffmpeg()
 | 
					            DRIVER_OPTIONS.remove('all')
 | 
				
			||||||
            self._install_waifu2x_caffe()
 | 
					            for driver in DRIVER_OPTIONS:
 | 
				
			||||||
            self._install_waifu2x_converter_cpp()
 | 
					                getattr(self, f'_install_{driver}')()
 | 
				
			||||||
            self._install_waifu2x_ncnn_vulkan()
 | 
					 | 
				
			||||||
            self._install_anime4kcpp()
 | 
					 | 
				
			||||||
            self._install_srmd_ncnn_vulkan()
 | 
					 | 
				
			||||||
        elif self.driver == 'ffmpeg':
 | 
					 | 
				
			||||||
            self._install_ffmpeg()
 | 
					 | 
				
			||||||
        elif self.driver == 'waifu2x_caffe':
 | 
					 | 
				
			||||||
            self._install_waifu2x_caffe()
 | 
					 | 
				
			||||||
        elif self.driver == 'waifu2x_converter_cpp':
 | 
					 | 
				
			||||||
            self._install_waifu2x_converter_cpp()
 | 
					 | 
				
			||||||
        elif self.driver == 'waifu2x_ncnn_vulkan':
 | 
					 | 
				
			||||||
            self._install_waifu2x_ncnn_vulkan()
 | 
					 | 
				
			||||||
        elif self.driver == 'anime4kcpp':
 | 
					 | 
				
			||||||
            self._install_anime4kcpp()
 | 
					 | 
				
			||||||
        elif self.driver == 'srmd_ncnn_vulkan':
 | 
					 | 
				
			||||||
            self._install_srmd_ncnn_vulkan()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        print('\nGenerating Video2X configuration file')
 | 
					        # install only the selected driver
 | 
				
			||||||
        self._generate_config()
 | 
					        else:
 | 
				
			||||||
 | 
					            getattr(self, f'_install_{self.driver}')()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        print('\nCleaning up temporary files')
 | 
					        print('\nCleaning up temporary files')
 | 
				
			||||||
        self._cleanup()
 | 
					        self._cleanup()
 | 
				
			||||||
@ -139,6 +136,22 @@ class Video2xSetup:
 | 
				
			|||||||
        with zipfile.ZipFile(ffmpeg_zip) as zipf:
 | 
					        with zipfile.ZipFile(ffmpeg_zip) as zipf:
 | 
				
			||||||
            zipf.extractall(LOCALAPPDATA / 'video2x')
 | 
					            zipf.extractall(LOCALAPPDATA / 'video2x')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _install_gifski(self):
 | 
				
			||||||
 | 
					        print('\nInstalling Gifski')
 | 
				
			||||||
 | 
					        import requests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Get latest release of waifu2x-ncnn-vulkan via Github API
 | 
				
			||||||
 | 
					        latest_release = requests.get('https://api.github.com/repos/ImageOptim/gifski/releases/latest').json()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for a in latest_release['assets']:
 | 
				
			||||||
 | 
					            if re.search(r'gifski-.*\.tar\.xz', a['browser_download_url']):
 | 
				
			||||||
 | 
					                gifski_tar_gz = download(a['browser_download_url'], tempfile.gettempdir())
 | 
				
			||||||
 | 
					                self.trash.append(gifski_tar_gz)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # extract and rename
 | 
				
			||||||
 | 
					        with tarfile.open(gifski_tar_gz) as archive:
 | 
				
			||||||
 | 
					            archive.extractall(LOCALAPPDATA / 'video2x' / 'gifski')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _install_waifu2x_caffe(self):
 | 
					    def _install_waifu2x_caffe(self):
 | 
				
			||||||
        """ Install waifu2x_caffe
 | 
					        """ Install waifu2x_caffe
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -248,42 +261,6 @@ class Video2xSetup:
 | 
				
			|||||||
            # rename the newly extracted directory
 | 
					            # rename the newly extracted directory
 | 
				
			||||||
            (LOCALAPPDATA / 'video2x' / zipf.namelist()[0]).rename(srmd_ncnn_vulkan_directory)
 | 
					            (LOCALAPPDATA / 'video2x' / zipf.namelist()[0]).rename(srmd_ncnn_vulkan_directory)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _generate_config(self):
 | 
					 | 
				
			||||||
        """ Generate video2x config
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        import yaml
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # open current video2x configuration file as template
 | 
					 | 
				
			||||||
        with open(VIDEO2X_CONFIG, 'r') as template:
 | 
					 | 
				
			||||||
            template_dict = yaml.load(template, Loader=yaml.FullLoader)
 | 
					 | 
				
			||||||
            template.close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # configure only the specified drivers
 | 
					 | 
				
			||||||
        if self.driver == 'all':
 | 
					 | 
				
			||||||
            template_dict['waifu2x_caffe']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-caffe' / 'waifu2x-caffe-cui')
 | 
					 | 
				
			||||||
            template_dict['waifu2x_converter_cpp']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-converter-cpp' / 'waifu2x-converter-cpp')
 | 
					 | 
				
			||||||
            template_dict['waifu2x_ncnn_vulkan']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-ncnn-vulkan' / 'waifu2x-ncnn-vulkan')
 | 
					 | 
				
			||||||
            template_dict['srmd_ncnn_vulkan']['path'] = str(LOCALAPPDATA / 'video2x' / 'srmd-ncnn-vulkan' / 'srmd-ncnn-vulkan')
 | 
					 | 
				
			||||||
            template_dict['anime4kcpp']['path'] = str(LOCALAPPDATA / 'video2x' / 'anime4kcpp' / 'CLI' / 'Anime4KCPP_CLI' / 'Anime4KCPP_CLI')
 | 
					 | 
				
			||||||
        elif self.driver == 'waifu2x_caffe':
 | 
					 | 
				
			||||||
            template_dict['waifu2x_caffe']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-caffe' / 'waifu2x-caffe-cui')
 | 
					 | 
				
			||||||
        elif self.driver == 'waifu2x_converter_cpp':
 | 
					 | 
				
			||||||
            template_dict['waifu2x_converter_cpp']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-converter-cpp' / 'waifu2x-converter-cpp')
 | 
					 | 
				
			||||||
        elif self.driver == 'waifu2x_ncnn_vulkan':
 | 
					 | 
				
			||||||
            template_dict['waifu2x_ncnn_vulkan']['path'] = str(LOCALAPPDATA / 'video2x' / 'waifu2x-ncnn-vulkan' / 'waifu2x-ncnn-vulkan')
 | 
					 | 
				
			||||||
        elif self.driver == 'srmd_ncnn_vulkan':
 | 
					 | 
				
			||||||
            template_dict['srmd_ncnn_vulkan']['path'] = str(LOCALAPPDATA / 'video2x' / 'srmd-ncnn-vulkan' / 'srmd-ncnn-vulkan')
 | 
					 | 
				
			||||||
        elif self.driver == 'anime4kcpp':
 | 
					 | 
				
			||||||
            template_dict['anime4kcpp']['path'] = str(LOCALAPPDATA / 'video2x' / 'anime4kcpp' / 'CLI' / 'Anime4KCPP_CLI' / 'Anime4KCPP_CLI')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        template_dict['ffmpeg']['ffmpeg_path'] = str(LOCALAPPDATA / 'video2x' / 'ffmpeg-latest-win64-static' / 'bin')
 | 
					 | 
				
			||||||
        template_dict['video2x']['video2x_cache_directory'] = None
 | 
					 | 
				
			||||||
        template_dict['video2x']['preserve_frames'] = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # write configuration into file
 | 
					 | 
				
			||||||
        with open(VIDEO2X_CONFIG, 'w') as config:
 | 
					 | 
				
			||||||
            yaml.dump(template_dict, config)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
def download(url, save_path, chunk_size=4096):
 | 
					def download(url, save_path, chunk_size=4096):
 | 
				
			||||||
    """ Download file to local with requests library
 | 
					    """ Download file to local with requests library
 | 
				
			||||||
 | 
				
			|||||||
@ -69,6 +69,10 @@ class WrapperMain:
 | 
				
			|||||||
        self.driver_settings['zoomFactor'] = upscaler.scale_ratio
 | 
					        self.driver_settings['zoomFactor'] = upscaler.scale_ratio
 | 
				
			||||||
        self.driver_settings['threads'] = upscaler.processes
 | 
					        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 upscale(self, input_file, output_file):
 | 
					    def upscale(self, input_file, output_file):
 | 
				
			||||||
        """This is the core function for WAIFU2X class
 | 
					        """This is the core function for WAIFU2X class
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -90,14 +94,14 @@ class WrapperMain:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # list to be executed
 | 
					        # list to be executed
 | 
				
			||||||
        # initialize the list with waifu2x binary path as the first element
 | 
					        # initialize the list with waifu2x binary path as the first element
 | 
				
			||||||
        execute = [self.driver_settings.pop('path')]
 | 
					        execute = [self.driver_settings['path']]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for key in self.driver_settings.keys():
 | 
					        for key in self.driver_settings.keys():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            value = self.driver_settings[key]
 | 
					            value = self.driver_settings[key]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # null or None means that leave this option out (keep default)
 | 
					            # null or None means that leave this option out (keep default)
 | 
				
			||||||
            if value is None or value is False:
 | 
					            if key == 'path' or value is None or value is False:
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                if len(key) == 1:
 | 
					                if len(key) == 1:
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@ Description: This class handles all FFmpeg related operations.
 | 
				
			|||||||
# built-in imports
 | 
					# built-in imports
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
import pathlib
 | 
					import pathlib
 | 
				
			||||||
 | 
					import shlex
 | 
				
			||||||
import subprocess
 | 
					import subprocess
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# third-party imports
 | 
					# third-party imports
 | 
				
			||||||
@ -36,7 +37,7 @@ class Ffmpeg:
 | 
				
			|||||||
        # video metadata
 | 
					        # video metadata
 | 
				
			||||||
        self.image_format = image_format
 | 
					        self.image_format = image_format
 | 
				
			||||||
        self.intermediate_file_name = pathlib.Path(self.ffmpeg_settings['intermediate_file_name'])
 | 
					        self.intermediate_file_name = pathlib.Path(self.ffmpeg_settings['intermediate_file_name'])
 | 
				
			||||||
        self.pixel_format = None
 | 
					        self.pixel_format = self.ffmpeg_settings['input_to_frames']['output_options']['-pix_fmt']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_pixel_formats(self):
 | 
					    def get_pixel_formats(self):
 | 
				
			||||||
        """ Get a dictionary of supported pixel formats
 | 
					        """ Get a dictionary of supported pixel formats
 | 
				
			||||||
@ -49,8 +50,8 @@ class Ffmpeg:
 | 
				
			|||||||
        """
 | 
					        """
 | 
				
			||||||
        execute = [
 | 
					        execute = [
 | 
				
			||||||
            self.ffmpeg_probe_binary,
 | 
					            self.ffmpeg_probe_binary,
 | 
				
			||||||
            '-v',
 | 
					            # '-v',
 | 
				
			||||||
            'quiet',
 | 
					            # 'quiet',
 | 
				
			||||||
            '-pix_fmts'
 | 
					            '-pix_fmts'
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -74,7 +75,7 @@ class Ffmpeg:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return pixel_formats
 | 
					        return pixel_formats
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_video_info(self, input_video):
 | 
					    def probe_file_info(self, input_video):
 | 
				
			||||||
        """ Gets input video information
 | 
					        """ Gets input video information
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        This method reads input video information
 | 
					        This method reads input video information
 | 
				
			||||||
@ -104,31 +105,25 @@ class Ffmpeg:
 | 
				
			|||||||
        # turn elements into str
 | 
					        # turn elements into str
 | 
				
			||||||
        execute = [str(e) for e in execute]
 | 
					        execute = [str(e) for e in execute]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Avalon.debug_info(f'Executing: {" ".join(execute)}')
 | 
					        Avalon.debug_info(f'Executing: {shlex.join(execute)}')
 | 
				
			||||||
        json_str = subprocess.run(execute, check=True, stdout=subprocess.PIPE).stdout
 | 
					        json_str = subprocess.run(execute, check=True, stdout=subprocess.PIPE).stdout
 | 
				
			||||||
        return json.loads(json_str.decode('utf-8'))
 | 
					        return json.loads(json_str.decode('utf-8'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def extract_frames(self, input_video, extracted_frames):
 | 
					    def extract_frames(self, input_file, extracted_frames):
 | 
				
			||||||
        """Extract every frame from original videos
 | 
					        """ extract frames from video or GIF file
 | 
				
			||||||
 | 
					 | 
				
			||||||
        This method extracts every frame from input video using FFmpeg
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            input_video {string} -- input video path
 | 
					 | 
				
			||||||
            extracted_frames {string} -- video output directory
 | 
					 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        execute = [
 | 
					        execute = [
 | 
				
			||||||
            self.ffmpeg_binary
 | 
					            self.ffmpeg_binary
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        execute.extend(self._read_configuration(phase='video_to_frames'))
 | 
					        execute.extend(self._read_configuration(phase='input_to_frames'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        execute.extend([
 | 
					        execute.extend([
 | 
				
			||||||
            '-i',
 | 
					            '-i',
 | 
				
			||||||
            input_video
 | 
					            input_file
 | 
				
			||||||
        ])
 | 
					        ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        execute.extend(self._read_configuration(phase='video_to_frames', section='output_options'))
 | 
					        execute.extend(self._read_configuration(phase='input_to_frames', section='output_options'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        execute.extend([
 | 
					        execute.extend([
 | 
				
			||||||
            extracted_frames / f'extracted_%0d.{self.image_format}'
 | 
					            extracted_frames / f'extracted_%0d.{self.image_format}'
 | 
				
			||||||
@ -136,7 +131,7 @@ class Ffmpeg:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return(self._execute(execute))
 | 
					        return(self._execute(execute))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def assemble_video(self, framerate, resolution, upscaled_frames):
 | 
					    def assemble_video(self, framerate, upscaled_frames):
 | 
				
			||||||
        """Converts images into videos
 | 
					        """Converts images into videos
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        This method converts a set of images into a video
 | 
					        This method converts a set of images into a video
 | 
				
			||||||
@ -149,9 +144,9 @@ class Ffmpeg:
 | 
				
			|||||||
        execute = [
 | 
					        execute = [
 | 
				
			||||||
            self.ffmpeg_binary,
 | 
					            self.ffmpeg_binary,
 | 
				
			||||||
            '-r',
 | 
					            '-r',
 | 
				
			||||||
            str(framerate),
 | 
					            str(framerate)
 | 
				
			||||||
            '-s',
 | 
					            # '-s',
 | 
				
			||||||
            resolution
 | 
					            # resolution
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # read other options
 | 
					        # read other options
 | 
				
			||||||
@ -274,17 +269,9 @@ class Ffmpeg:
 | 
				
			|||||||
        return configuration
 | 
					        return configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _execute(self, execute):
 | 
					    def _execute(self, execute):
 | 
				
			||||||
        """ execute command
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            execute {list} -- list of arguments to be executed
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Returns:
 | 
					 | 
				
			||||||
            int -- execution return code
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        # turn all list elements into string to avoid errors
 | 
					        # turn all list elements into string to avoid errors
 | 
				
			||||||
        execute = [str(e) for e in execute]
 | 
					        execute = [str(e) for e in execute]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Avalon.debug_info(f'Executing: {execute}')
 | 
					        Avalon.debug_info(f'Executing: {shlex.join(execute)}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return subprocess.Popen(execute)
 | 
					        return subprocess.Popen(execute)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										70
									
								
								src/wrappers/gifski.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/wrappers/gifski.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,70 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python3
 | 
				
			||||||
 | 
					# -*- coding: utf-8 -*-
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					Name: Gifski Wrapper
 | 
				
			||||||
 | 
					Creator: K4YT3X
 | 
				
			||||||
 | 
					Date Created: May 11, 2020
 | 
				
			||||||
 | 
					Last Modified: May 11, 2020
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Description: High-level wrapper for Gifski.
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# built-in imports
 | 
				
			||||||
 | 
					import pathlib
 | 
				
			||||||
 | 
					import subprocess
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# third-party imports
 | 
				
			||||||
 | 
					from avalon_framework import Avalon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Gifski:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, gifski_settings):
 | 
				
			||||||
 | 
					        self.gifski_settings = gifski_settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def make_gif(self, upscaled_frames: pathlib.Path, output_path: pathlib.Path, framerate: float, image_format: str) -> subprocess.Popen:
 | 
				
			||||||
 | 
					        execute = [
 | 
				
			||||||
 | 
					            self.gifski_settings['gifski_path'],
 | 
				
			||||||
 | 
					            '-o',
 | 
				
			||||||
 | 
					            output_path,
 | 
				
			||||||
 | 
					            '--fps',
 | 
				
			||||||
 | 
					            int(round(framerate, 0))
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # load configurations from config file
 | 
				
			||||||
 | 
					        execute.extend(self._load_configuration())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # append frames location
 | 
				
			||||||
 | 
					        execute.extend([upscaled_frames / f'extracted_*.{image_format}'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return(self._execute(execute))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _load_configuration(self):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        configuration = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for key in self.gifski_settings.keys():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            value = self.gifski_settings[key]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # null or None means that leave this option out (keep default)
 | 
				
			||||||
 | 
					            if key == 'gifski_path' or value is None or value is False:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                if len(key) == 1:
 | 
				
			||||||
 | 
					                    configuration.append(f'-{key}')
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    configuration.append(f'--{key}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # true means key is an option
 | 
				
			||||||
 | 
					                if value is not True:
 | 
				
			||||||
 | 
					                    configuration.append(str(value))
 | 
				
			||||||
 | 
					        return configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _execute(self, execute: list) -> subprocess.Popen:
 | 
				
			||||||
 | 
					        # turn all list elements into string to avoid errors
 | 
				
			||||||
 | 
					        execute = [str(e) for e in execute]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Avalon.debug_info(f'Executing: {execute}')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return subprocess.Popen(execute)
 | 
				
			||||||
@ -77,14 +77,14 @@ class WrapperMain:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # list to be executed
 | 
					        # list to be executed
 | 
				
			||||||
        # initialize the list with the binary path as the first element
 | 
					        # initialize the list with the binary path as the first element
 | 
				
			||||||
        execute = [self.driver_settings.pop('path')]
 | 
					        execute = [self.driver_settings['path']]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for key in self.driver_settings.keys():
 | 
					        for key in self.driver_settings.keys():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            value = self.driver_settings[key]
 | 
					            value = self.driver_settings[key]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # null or None means that leave this option out (keep default)
 | 
					            # null or None means that leave this option out (keep default)
 | 
				
			||||||
            if value is None or value is False:
 | 
					            if key == 'path' or value is None or value is False:
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                if len(key) == 1:
 | 
					                if len(key) == 1:
 | 
				
			||||||
 | 
				
			|||||||
@ -56,8 +56,8 @@ class WrapperMain:
 | 
				
			|||||||
        parser.add_argument('-m', '--mode', choices=['noise', 'scale', 'noise_scale', 'auto_scale'], help='image processing mode')
 | 
					        parser.add_argument('-m', '--mode', choices=['noise', 'scale', 'noise_scale', 'auto_scale'], help='image processing mode')
 | 
				
			||||||
        parser.add_argument('-e', '--output_extention', type=str, help='extention to output image file when output_path is (auto) or input_path is folder')
 | 
					        parser.add_argument('-e', '--output_extention', type=str, help='extention to output image file when output_path is (auto) or input_path is folder')
 | 
				
			||||||
        parser.add_argument('-l', '--input_extention_list', type=str, help='extention to input image file when input_path is folder')
 | 
					        parser.add_argument('-l', '--input_extention_list', type=str, help='extention to input image file when input_path is folder')
 | 
				
			||||||
        parser.add_argument('-o', '--output', type=str, help=argparse.SUPPRESS)  # help='path to output image file (when input_path is folder, output_path must be folder)')
 | 
					        parser.add_argument('-o', '--output_path', type=str, help=argparse.SUPPRESS)  # help='path to output image file (when input_path is folder, output_path must be folder)')
 | 
				
			||||||
        parser.add_argument('-i', '--input_file', type=str, help=argparse.SUPPRESS)  # help='(required) path to input image file')
 | 
					        parser.add_argument('-i', '--input_path', type=str, help=argparse.SUPPRESS)  # help='(required) path to input image file')
 | 
				
			||||||
        return parser.parse_args(arguments)
 | 
					        return parser.parse_args(arguments)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def load_configurations(self, upscaler):
 | 
					    def load_configurations(self, upscaler):
 | 
				
			||||||
@ -79,14 +79,14 @@ class WrapperMain:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # list to be executed
 | 
					        # list to be executed
 | 
				
			||||||
        # initialize the list with waifu2x binary path as the first element
 | 
					        # initialize the list with waifu2x binary path as the first element
 | 
				
			||||||
        execute = [self.driver_settings.pop('path')]
 | 
					        execute = [self.driver_settings['path']]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for key in self.driver_settings.keys():
 | 
					        for key in self.driver_settings.keys():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            value = self.driver_settings[key]
 | 
					            value = self.driver_settings[key]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # null or None means that leave this option out (keep default)
 | 
					            # null or None means that leave this option out (keep default)
 | 
				
			||||||
            if value is None or value is False:
 | 
					            if key == 'path' or value is None or value is False:
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                if len(key) == 1:
 | 
					                if len(key) == 1:
 | 
				
			||||||
 | 
				
			|||||||
@ -93,14 +93,14 @@ class WrapperMain:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # list to be executed
 | 
					        # list to be executed
 | 
				
			||||||
        # initialize the list with waifu2x binary path as the first element
 | 
					        # initialize the list with waifu2x binary path as the first element
 | 
				
			||||||
        execute = [self.driver_settings.pop('path')]
 | 
					        execute = [self.driver_settings['path']]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for key in self.driver_settings.keys():
 | 
					        for key in self.driver_settings.keys():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            value = self.driver_settings[key]
 | 
					            value = self.driver_settings[key]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # null or None means that leave this option out (keep default)
 | 
					            # null or None means that leave this option out (keep default)
 | 
				
			||||||
            if value is None or value is False:
 | 
					            if key == 'path' or value is None or value is False:
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                if len(key) == 1:
 | 
					                if len(key) == 1:
 | 
				
			||||||
 | 
				
			|||||||
@ -80,14 +80,14 @@ class WrapperMain:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # list to be executed
 | 
					        # list to be executed
 | 
				
			||||||
        # initialize the list with waifu2x binary path as the first element
 | 
					        # initialize the list with waifu2x binary path as the first element
 | 
				
			||||||
        execute = [self.driver_settings.pop('path')]
 | 
					        execute = [self.driver_settings['path']]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for key in self.driver_settings.keys():
 | 
					        for key in self.driver_settings.keys():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            value = self.driver_settings[key]
 | 
					            value = self.driver_settings[key]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # null or None means that leave this option out (keep default)
 | 
					            # null or None means that leave this option out (keep default)
 | 
				
			||||||
            if value is None or value is False:
 | 
					            if key == 'path' or value is None or value is False:
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                if len(key) == 1:
 | 
					                if len(key) == 1:
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user