2018-12-11 20:52:48 +00:00
|
|
|
#!/usr/bin/env python3
|
2019-07-27 17:39:40 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2018-12-11 20:52:48 +00:00
|
|
|
"""
|
|
|
|
Name: Video2X Upscaler
|
|
|
|
Author: K4YT3X
|
|
|
|
Date Created: December 10, 2018
|
2020-05-06 08:45:48 +00:00
|
|
|
Last Modified: May 6, 2020
|
2019-06-25 23:25:27 +00:00
|
|
|
|
2020-03-23 13:05:24 +00:00
|
|
|
Description: This file contains the Upscaler class. Each
|
|
|
|
instance of the Upscaler class is an upscaler on an image or
|
|
|
|
a folder.
|
2018-12-11 20:52:48 +00:00
|
|
|
"""
|
|
|
|
|
2019-07-27 17:39:40 +00:00
|
|
|
# local imports
|
2018-12-21 23:42:03 +00:00
|
|
|
from exceptions import *
|
2019-03-26 20:21:58 +00:00
|
|
|
from image_cleaner import ImageCleaner
|
2020-05-03 23:20:23 +00:00
|
|
|
from wrappers.ffmpeg import Ffmpeg
|
2019-07-27 17:39:40 +00:00
|
|
|
|
|
|
|
# built-in imports
|
|
|
|
from fractions import Fraction
|
2019-08-26 03:03:37 +00:00
|
|
|
import contextlib
|
2019-06-14 03:49:18 +00:00
|
|
|
import copy
|
2020-05-06 08:45:48 +00:00
|
|
|
import gettext
|
2020-05-04 21:12:41 +00:00
|
|
|
import importlib
|
2020-05-06 08:45:48 +00:00
|
|
|
import locale
|
2020-05-03 23:20:23 +00:00
|
|
|
import os
|
2019-07-27 21:29:33 +00:00
|
|
|
import pathlib
|
2019-02-08 21:48:35 +00:00
|
|
|
import re
|
2018-12-11 20:52:48 +00:00
|
|
|
import shutil
|
2020-02-26 10:27:57 +00:00
|
|
|
import sys
|
2018-12-11 20:52:48 +00:00
|
|
|
import tempfile
|
|
|
|
import threading
|
2019-03-05 00:34:57 +00:00
|
|
|
import time
|
2019-07-27 21:29:33 +00:00
|
|
|
import traceback
|
2018-12-11 20:52:48 +00:00
|
|
|
|
2019-07-27 17:39:40 +00:00
|
|
|
# third-party imports
|
|
|
|
from avalon_framework import Avalon
|
|
|
|
from tqdm import tqdm
|
|
|
|
|
2020-05-06 08:45:48 +00:00
|
|
|
# internationalization constants
|
|
|
|
DOMAIN = 'video2x'
|
|
|
|
LOCALE_DIRECTORY = pathlib.Path(__file__).parent.absolute() / 'locale'
|
|
|
|
|
|
|
|
# getting default locale settings
|
|
|
|
default_locale, encoding = locale.getdefaultlocale()
|
|
|
|
language = gettext.translation(DOMAIN, LOCALE_DIRECTORY, [default_locale], fallback=True)
|
|
|
|
language.install()
|
|
|
|
_ = language.gettext
|
|
|
|
|
2020-05-04 21:12:41 +00:00
|
|
|
# these names are consistent for
|
|
|
|
# - driver selection in command line
|
|
|
|
# - driver wrapper file names
|
|
|
|
# - config file keys
|
|
|
|
AVAILABLE_DRIVERS = ['waifu2x_caffe',
|
|
|
|
'waifu2x_converter_cpp',
|
|
|
|
'waifu2x_ncnn_vulkan',
|
|
|
|
'srmd_ncnn_vulkan',
|
|
|
|
'anime4kcpp']
|
2019-08-16 03:57:24 +00:00
|
|
|
|
2018-12-11 20:52:48 +00:00
|
|
|
|
|
|
|
class Upscaler:
|
2019-03-19 17:10:38 +00:00
|
|
|
""" An instance of this class is a upscaler that will
|
2019-04-29 04:06:54 +00:00
|
|
|
upscale all images in the given directory.
|
2019-03-25 01:57:22 +00:00
|
|
|
|
2019-03-19 17:10:38 +00:00
|
|
|
Raises:
|
|
|
|
Exception -- all exceptions
|
|
|
|
ArgumentError -- if argument is not valid
|
|
|
|
"""
|
|
|
|
|
2020-05-03 23:20:23 +00:00
|
|
|
def __init__(self, input_video, output_video, driver_settings, ffmpeg_settings):
|
2019-03-19 17:10:38 +00:00
|
|
|
# mandatory arguments
|
2018-12-11 20:52:48 +00:00
|
|
|
self.input_video = input_video
|
|
|
|
self.output_video = output_video
|
2019-10-20 01:52:11 +00:00
|
|
|
self.driver_settings = driver_settings
|
2019-03-09 17:50:54 +00:00
|
|
|
self.ffmpeg_settings = ffmpeg_settings
|
2018-12-11 20:52:48 +00:00
|
|
|
|
2019-03-19 17:10:38 +00:00
|
|
|
# optional arguments
|
2020-05-03 23:20:23 +00:00
|
|
|
self.driver = 'waifu2x_caffe'
|
2019-03-30 18:01:36 +00:00
|
|
|
self.scale_width = None
|
|
|
|
self.scale_height = None
|
|
|
|
self.scale_ratio = None
|
2020-02-26 10:27:57 +00:00
|
|
|
self.processes = 1
|
2019-07-27 21:29:33 +00:00
|
|
|
self.video2x_cache_directory = pathlib.Path(tempfile.gettempdir()) / 'video2x'
|
2019-03-30 18:01:36 +00:00
|
|
|
self.image_format = 'png'
|
|
|
|
self.preserve_frames = False
|
2018-12-11 20:52:48 +00:00
|
|
|
|
2019-04-29 04:06:54 +00:00
|
|
|
def create_temp_directories(self):
|
|
|
|
"""create temporary directory
|
2019-04-18 18:56:30 +00:00
|
|
|
"""
|
2020-04-04 10:24:40 +00:00
|
|
|
|
|
|
|
# create a new temp directory if the current one is not found
|
|
|
|
if not self.video2x_cache_directory.exists():
|
|
|
|
self.video2x_cache_directory = pathlib.Path(tempfile.gettempdir()) / 'video2x'
|
|
|
|
|
|
|
|
# create temp directories for extracted frames and upscaled frames
|
2019-07-27 21:29:33 +00:00
|
|
|
self.extracted_frames = pathlib.Path(tempfile.mkdtemp(dir=self.video2x_cache_directory))
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.debug_info(_('Extracted frames are being saved to: {}').format(self.extracted_frames))
|
2019-07-27 21:29:33 +00:00
|
|
|
self.upscaled_frames = pathlib.Path(tempfile.mkdtemp(dir=self.video2x_cache_directory))
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.debug_info(_('Upscaled frames are being saved to: {}').format(self.upscaled_frames))
|
2019-02-08 19:04:43 +00:00
|
|
|
|
2019-04-29 04:06:54 +00:00
|
|
|
def cleanup_temp_directories(self):
|
2019-04-18 18:56:30 +00:00
|
|
|
"""delete temp directories when done
|
|
|
|
"""
|
2019-02-08 19:04:43 +00:00
|
|
|
if not self.preserve_frames:
|
2019-12-12 03:20:01 +00:00
|
|
|
for directory in [self.extracted_frames, self.upscaled_frames, self.video2x_cache_directory]:
|
2019-03-25 03:04:50 +00:00
|
|
|
try:
|
2019-04-18 18:56:30 +00:00
|
|
|
# avalon framework cannot be used if python is shutting down
|
|
|
|
# therefore, plain print is used
|
2020-05-05 00:25:12 +00:00
|
|
|
print(_('Cleaning up cache directory: {}').format(directory))
|
2019-03-25 03:04:50 +00:00
|
|
|
shutil.rmtree(directory)
|
|
|
|
except (OSError, FileNotFoundError):
|
2020-05-05 00:25:12 +00:00
|
|
|
print(_('Unable to delete: {}').format(directory))
|
2019-07-27 21:29:33 +00:00
|
|
|
traceback.print_exc()
|
2018-12-11 20:52:48 +00:00
|
|
|
|
|
|
|
def _check_arguments(self):
|
2019-03-19 17:10:38 +00:00
|
|
|
# check if arguments are valid / all necessary argument
|
2018-12-11 20:52:48 +00:00
|
|
|
# values are specified
|
|
|
|
if not self.input_video:
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.error(_('You must specify input video file/directory path'))
|
2020-05-03 23:20:23 +00:00
|
|
|
raise ArgumentError('input video path not specified')
|
|
|
|
if not self.output_video:
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.error(_('You must specify output video file/directory path'))
|
2020-05-03 23:20:23 +00:00
|
|
|
raise ArgumentError('output video path not specified')
|
|
|
|
if (self.driver in ['waifu2x_converter', 'waifu2x_ncnn_vulkan', 'anime4k']) and self.scale_width and self.scale_height:
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.error(_('Selected driver accepts only scaling ratio'))
|
2020-05-03 23:20:23 +00:00
|
|
|
raise ArgumentError('selected driver supports only scaling ratio')
|
|
|
|
if self.driver == 'waifu2x_ncnn_vulkan' and self.scale_ratio is not None and (self.scale_ratio > 2 or not self.scale_ratio.is_integer()):
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.error(_('Scaling ratio must be 1 or 2 for waifu2x_ncnn_vulkan'))
|
2020-05-03 23:20:23 +00:00
|
|
|
raise ArgumentError('scaling ratio must be 1 or 2 for waifu2x_ncnn_vulkan')
|
|
|
|
if self.driver == 'srmd_ncnn_vulkan' and self.scale_ratio is not None and (self.scale_ratio not in [2, 3, 4]):
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.error(_('Scaling ratio must be one of 2, 3 or 4 for srmd_ncnn_vulkan'))
|
2020-05-03 23:20:23 +00:00
|
|
|
raise ArgumentError('scaling ratio must be one of 2, 3 or 4 for srmd_ncnn_vulkan')
|
|
|
|
if (self.scale_width or self.scale_height) and self.scale_ratio:
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.error(_('You can only specify either scaling ratio or output width and height'))
|
2020-05-03 23:20:23 +00:00
|
|
|
raise ArgumentError('both scaling ration and width/height specified')
|
|
|
|
if (self.scale_width and not self.scale_height) or (not self.scale_width and self.scale_height):
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.error(_('You must specify both width and height'))
|
2020-05-03 23:20:23 +00:00
|
|
|
raise ArgumentError('only one of width or height is specified')
|
2018-12-11 20:52:48 +00:00
|
|
|
|
2019-04-29 04:06:54 +00:00
|
|
|
def _progress_bar(self, extracted_frames_directories):
|
2019-03-05 00:34:57 +00:00
|
|
|
""" This method prints a progress bar
|
|
|
|
|
|
|
|
This method prints a progress bar by keeping track
|
2019-04-29 04:06:54 +00:00
|
|
|
of the amount of frames in the input directory
|
|
|
|
and the output directory. This is originally
|
2019-03-05 00:34:57 +00:00
|
|
|
suggested by @ArmandBernard.
|
|
|
|
"""
|
2019-07-28 07:09:40 +00:00
|
|
|
|
2019-03-19 17:10:38 +00:00
|
|
|
# get number of extracted frames
|
2019-07-28 07:09:40 +00:00
|
|
|
self.total_frames = 0
|
2019-04-29 04:06:54 +00:00
|
|
|
for directory in extracted_frames_directories:
|
2020-02-22 07:31:39 +00:00
|
|
|
self.total_frames += len([f for f in directory.iterdir() if str(f).lower().endswith(self.image_format.lower())])
|
2019-03-05 00:34:57 +00:00
|
|
|
|
2020-05-05 00:25:12 +00:00
|
|
|
with tqdm(total=self.total_frames, ascii=True, desc=_('Upscaling Progress')) as progress_bar:
|
2019-03-05 00:34:57 +00:00
|
|
|
|
|
|
|
# tqdm update method adds the value to the progress
|
|
|
|
# bar instead of setting the value. Therefore, a delta
|
|
|
|
# needs to be calculated.
|
|
|
|
previous_cycle_frames = 0
|
|
|
|
while not self.progress_bar_exit_signal:
|
|
|
|
|
2019-08-26 03:03:37 +00:00
|
|
|
with contextlib.suppress(FileNotFoundError):
|
2020-02-22 07:31:39 +00:00
|
|
|
self.total_frames_upscaled = len([f for f in self.upscaled_frames.iterdir() if str(f).lower().endswith(self.image_format.lower())])
|
2019-07-28 07:09:40 +00:00
|
|
|
delta = self.total_frames_upscaled - previous_cycle_frames
|
|
|
|
previous_cycle_frames = self.total_frames_upscaled
|
2019-03-05 00:34:57 +00:00
|
|
|
|
2019-03-19 17:10:38 +00:00
|
|
|
# if upscaling is finished
|
2019-07-28 07:09:40 +00:00
|
|
|
if self.total_frames_upscaled >= self.total_frames:
|
2019-03-05 00:34:57 +00:00
|
|
|
return
|
|
|
|
|
2019-08-04 03:15:19 +00:00
|
|
|
# adds the delta into the progress bar
|
2019-03-05 00:34:57 +00:00
|
|
|
progress_bar.update(delta)
|
|
|
|
|
|
|
|
time.sleep(1)
|
|
|
|
|
2019-06-14 03:49:18 +00:00
|
|
|
def _upscale_frames(self):
|
2018-12-11 20:52:48 +00:00
|
|
|
""" 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
|
|
|
|
"""
|
|
|
|
|
2020-02-26 10:27:57 +00:00
|
|
|
# progress bar process exit signal
|
2019-03-05 00:34:57 +00:00
|
|
|
self.progress_bar_exit_signal = False
|
|
|
|
|
2019-06-14 23:19:12 +00:00
|
|
|
# initialize waifu2x driver
|
2020-05-03 23:20:23 +00:00
|
|
|
if self.driver not in AVAILABLE_DRIVERS:
|
2020-05-05 00:25:12 +00:00
|
|
|
raise UnrecognizedDriverError(_('Unrecognized driver: {}').format(self.driver))
|
2019-03-25 01:57:22 +00:00
|
|
|
|
2020-02-26 10:27:57 +00:00
|
|
|
# create a container for all upscaler processes
|
|
|
|
upscaler_processes = []
|
2019-06-25 23:25:27 +00:00
|
|
|
|
2020-02-26 10:27:57 +00:00
|
|
|
# list all images in the extracted frames
|
|
|
|
frames = [(self.extracted_frames / f) for f in self.extracted_frames.iterdir() if f.is_file]
|
2019-06-25 23:25:27 +00:00
|
|
|
|
2020-02-26 10:27:57 +00:00
|
|
|
# if we have less images than processes,
|
|
|
|
# create only the processes necessary
|
|
|
|
if len(frames) < self.processes:
|
|
|
|
self.processes = len(frames)
|
2019-06-25 23:25:27 +00:00
|
|
|
|
2020-02-26 10:27:57 +00:00
|
|
|
# create a directory for each process and append directory
|
|
|
|
# name into a list
|
|
|
|
process_directories = []
|
|
|
|
for process_id in range(self.processes):
|
|
|
|
process_directory = self.extracted_frames / str(process_id)
|
|
|
|
process_directories.append(process_directory)
|
2019-06-25 23:25:27 +00:00
|
|
|
|
2020-02-26 10:27:57 +00:00
|
|
|
# delete old directories and create new directories
|
|
|
|
if process_directory.is_dir():
|
|
|
|
shutil.rmtree(process_directory)
|
|
|
|
process_directory.mkdir(parents=True, exist_ok=True)
|
2019-06-25 23:25:27 +00:00
|
|
|
|
2020-02-26 10:27:57 +00:00
|
|
|
# waifu2x-converter-cpp will perform multi-threading within its own process
|
2020-05-03 23:20:23 +00:00
|
|
|
if self.driver == 'waifu2x_converter_cpp':
|
2020-02-26 10:27:57 +00:00
|
|
|
process_directories = [self.extracted_frames]
|
2019-06-25 23:25:27 +00:00
|
|
|
|
2020-02-26 10:27:57 +00:00
|
|
|
else:
|
2019-06-25 23:25:27 +00:00
|
|
|
# evenly distribute images into each directory
|
|
|
|
# until there is none left in the directory
|
|
|
|
for image in frames:
|
|
|
|
# move image
|
2020-02-26 10:27:57 +00:00
|
|
|
image.rename(process_directories[0] / image.name)
|
2019-06-25 23:25:27 +00:00
|
|
|
# rotate list
|
2020-02-26 10:27:57 +00:00
|
|
|
process_directories = process_directories[-1:] + process_directories[:-1]
|
|
|
|
|
|
|
|
# create threads and start them
|
|
|
|
for process_directory in process_directories:
|
|
|
|
|
2020-05-04 21:12:41 +00:00
|
|
|
DriverWrapperMain = getattr(importlib.import_module(f'wrappers.{self.driver}'), 'WrapperMain')
|
|
|
|
driver = DriverWrapperMain(copy.deepcopy(self.driver_settings))
|
|
|
|
|
2020-02-26 10:27:57 +00:00
|
|
|
# if the driver being used is waifu2x-caffe
|
2020-05-03 23:20:23 +00:00
|
|
|
if self.driver == 'waifu2x_caffe':
|
2020-05-04 21:12:41 +00:00
|
|
|
upscaler_processes.append(driver.upscale(process_directory,
|
|
|
|
self.upscaled_frames,
|
|
|
|
self.scale_ratio,
|
|
|
|
self.scale_width,
|
|
|
|
self.scale_height,
|
|
|
|
self.image_format,
|
|
|
|
self.bit_depth))
|
2020-02-26 10:27:57 +00:00
|
|
|
|
|
|
|
# if the driver being used is waifu2x-converter-cpp
|
2020-05-03 23:20:23 +00:00
|
|
|
elif self.driver == 'waifu2x_converter_cpp':
|
2020-02-26 10:27:57 +00:00
|
|
|
upscaler_processes.append(driver.upscale(process_directory,
|
|
|
|
self.upscaled_frames,
|
|
|
|
self.scale_ratio,
|
|
|
|
self.processes,
|
|
|
|
self.image_format))
|
|
|
|
|
|
|
|
# if the driver being used is waifu2x-ncnn-vulkan
|
2020-05-03 23:20:23 +00:00
|
|
|
elif self.driver == 'waifu2x_ncnn_vulkan':
|
2020-02-26 10:27:57 +00:00
|
|
|
upscaler_processes.append(driver.upscale(process_directory,
|
|
|
|
self.upscaled_frames,
|
|
|
|
self.scale_ratio))
|
|
|
|
|
2020-04-27 00:01:37 +00:00
|
|
|
# if the driver being used is srmd_ncnn_vulkan
|
2020-05-03 23:20:23 +00:00
|
|
|
elif self.driver == 'srmd_ncnn_vulkan':
|
2020-04-27 00:01:37 +00:00
|
|
|
upscaler_processes.append(driver.upscale(process_directory,
|
|
|
|
self.upscaled_frames,
|
|
|
|
self.scale_ratio))
|
|
|
|
|
2020-02-26 10:27:57 +00:00
|
|
|
# start progress bar in a different thread
|
|
|
|
progress_bar = threading.Thread(target=self._progress_bar, args=(process_directories,))
|
|
|
|
progress_bar.start()
|
|
|
|
|
|
|
|
# create the clearer and start it
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.debug_info(_('Starting upscaled image cleaner'))
|
2020-02-26 10:27:57 +00:00
|
|
|
image_cleaner = ImageCleaner(self.extracted_frames, self.upscaled_frames, len(upscaler_processes))
|
|
|
|
image_cleaner.start()
|
|
|
|
|
|
|
|
# wait for all process to exit
|
|
|
|
try:
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.debug_info(_('Main process waiting for subprocesses to exit'))
|
2020-02-26 10:27:57 +00:00
|
|
|
for process in upscaler_processes:
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.debug_info(_('Subprocess {} exited with code {}').format(process.pid, process.wait()))
|
2020-02-26 10:27:57 +00:00
|
|
|
except (KeyboardInterrupt, SystemExit):
|
|
|
|
Avalon.warning('Exit signal received')
|
|
|
|
Avalon.warning('Killing processes')
|
|
|
|
for process in upscaler_processes:
|
|
|
|
process.terminate()
|
|
|
|
|
|
|
|
# cleanup and exit with exit code 1
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.debug_info(_('Killing upscaled image cleaner'))
|
2019-06-25 23:25:27 +00:00
|
|
|
image_cleaner.stop()
|
|
|
|
self.progress_bar_exit_signal = True
|
2020-02-26 10:27:57 +00:00
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
# if the driver is waifu2x-converter-cpp
|
|
|
|
# images need to be renamed to be recognizable for FFmpeg
|
2020-05-03 23:20:23 +00:00
|
|
|
if self.driver == 'waifu2x_converter_cpp':
|
2020-02-26 10:27:57 +00:00
|
|
|
for image in [f for f in self.upscaled_frames.iterdir() if f.is_file()]:
|
|
|
|
renamed = re.sub(f'_\\[.*\\]\\[x(\\d+(\\.\\d+)?)\\]\\.{self.image_format}', f'.{self.image_format}', str(image.name))
|
|
|
|
(self.upscaled_frames / image).rename(self.upscaled_frames / renamed)
|
|
|
|
|
|
|
|
# upscaling done, kill the clearer
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.debug_info(_('Killing upscaled image cleaner'))
|
2020-02-26 10:27:57 +00:00
|
|
|
image_cleaner.stop()
|
2019-06-25 23:25:27 +00:00
|
|
|
|
2020-02-26 10:27:57 +00:00
|
|
|
# pass exit signal to progress bar thread
|
|
|
|
self.progress_bar_exit_signal = True
|
2018-12-11 20:52:48 +00:00
|
|
|
|
|
|
|
def run(self):
|
2020-02-26 10:27:57 +00:00
|
|
|
""" Main controller for Video2X
|
2018-12-11 20:52:48 +00:00
|
|
|
|
|
|
|
This function controls the flow of video conversion
|
|
|
|
and handles all necessary functions.
|
|
|
|
"""
|
|
|
|
|
2019-03-19 17:10:38 +00:00
|
|
|
# parse arguments for waifu2x
|
|
|
|
# check argument sanity
|
2018-12-11 20:52:48 +00:00
|
|
|
self._check_arguments()
|
|
|
|
|
2019-03-19 17:10:38 +00:00
|
|
|
# convert paths to absolute paths
|
2019-07-27 21:29:33 +00:00
|
|
|
self.input_video = self.input_video.absolute()
|
|
|
|
self.output_video = self.output_video.absolute()
|
2018-12-11 20:52:48 +00:00
|
|
|
|
2020-05-03 23:20:23 +00:00
|
|
|
# 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"]}'
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.info(_('Starting to upscale extracted images'))
|
2020-05-05 05:11:50 +00:00
|
|
|
|
|
|
|
# import and initialize Anime4KCPP wrapper
|
|
|
|
DriverWrapperMain = getattr(importlib.import_module('wrappers.anime4kcpp'), 'WrapperMain')
|
|
|
|
driver = DriverWrapperMain(copy.deepcopy(self.driver_settings))
|
|
|
|
|
|
|
|
# run Anime4KCPP
|
2020-05-03 23:20:23 +00:00
|
|
|
driver.upscale(self.input_video, self.output_video, self.scale_ratio, self.processes).wait()
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.info(_('Upscaling completed'))
|
2018-12-11 20:52:48 +00:00
|
|
|
|
2020-05-03 23:20:23 +00:00
|
|
|
else:
|
|
|
|
self.create_temp_directories()
|
|
|
|
|
|
|
|
# initialize objects for ffmpeg and waifu2x-caffe
|
|
|
|
fm = Ffmpeg(self.ffmpeg_settings, self.image_format)
|
|
|
|
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.info(_('Reading video information'))
|
2020-05-03 23:20:23 +00:00
|
|
|
video_info = fm.get_video_info(self.input_video)
|
|
|
|
# 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
|
|
|
|
for stream in video_info['streams']:
|
|
|
|
if stream['codec_type'] == 'video':
|
|
|
|
video_stream_index = stream['index']
|
|
|
|
break
|
|
|
|
|
|
|
|
# exit if no video stream found
|
|
|
|
if video_stream_index is None:
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.error(_('Aborting: No video stream found'))
|
2020-05-03 23:20:23 +00:00
|
|
|
raise StreamNotFoundError('no video stream found')
|
|
|
|
|
|
|
|
# extract frames from video
|
|
|
|
fm.extract_frames(self.input_video, self.extracted_frames)
|
|
|
|
|
|
|
|
# get average frame rate of video stream
|
|
|
|
framerate = float(Fraction(video_info['streams'][video_stream_index]['avg_frame_rate']))
|
|
|
|
fm.pixel_format = video_info['streams'][video_stream_index]['pix_fmt']
|
|
|
|
|
|
|
|
# get a dict of all pixel formats and corresponding bit depth
|
|
|
|
pixel_formats = fm.get_pixel_formats()
|
|
|
|
|
|
|
|
# try getting pixel format's corresponding bti depth
|
|
|
|
try:
|
|
|
|
self.bit_depth = pixel_formats[fm.pixel_format]
|
|
|
|
except KeyError:
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.error(_('Unsupported pixel format: {}').format(fm.pixel_format))
|
2020-05-03 23:20:23 +00:00
|
|
|
raise UnsupportedPixelError(f'unsupported pixel format {fm.pixel_format}')
|
|
|
|
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.info(_('Framerate: {}').format(framerate))
|
2020-05-03 23:20:23 +00:00
|
|
|
|
|
|
|
# width/height will be coded width/height x upscale factor
|
|
|
|
if self.scale_ratio:
|
|
|
|
original_width = video_info['streams'][video_stream_index]['width']
|
|
|
|
original_height = video_info['streams'][video_stream_index]['height']
|
|
|
|
self.scale_width = int(self.scale_ratio * original_width)
|
|
|
|
self.scale_height = int(self.scale_ratio * original_height)
|
|
|
|
|
|
|
|
# upscale images one by one using waifu2x
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.info(_('Starting to upscale extracted images'))
|
2020-05-03 23:20:23 +00:00
|
|
|
self._upscale_frames()
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.info(_('Upscaling completed'))
|
2020-05-03 23:20:23 +00:00
|
|
|
|
|
|
|
# frames to Video
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.info(_('Converting extracted frames into video'))
|
2020-05-03 23:20:23 +00:00
|
|
|
|
|
|
|
# use user defined output size
|
|
|
|
fm.convert_video(framerate, f'{self.scale_width}x{self.scale_height}', self.upscaled_frames)
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.info(_('Conversion completed'))
|
2020-05-03 23:20:23 +00:00
|
|
|
|
|
|
|
# migrate audio tracks and subtitles
|
2020-05-05 00:25:12 +00:00
|
|
|
Avalon.info(_('Migrating audio tracks and subtitles to upscaled video'))
|
2020-05-03 23:20:23 +00:00
|
|
|
fm.migrate_audio_tracks_subtitles(self.input_video, self.output_video, self.upscaled_frames)
|
|
|
|
|
|
|
|
# destroy temp directories
|
|
|
|
self.cleanup_temp_directories()
|