From db32279e6cc63b57dced9e16f2aac8c4ee75b13a Mon Sep 17 00:00:00 2001 From: Janis Hutz Date: Wed, 24 May 2023 20:57:06 +0200 Subject: [PATCH] completely rewrite CLI & finish plugin support --- bin/__pycache__/handler.cpython-311.pyc | Bin 11186 -> 10017 bytes bin/engines/fsr/config.json | 15 ++ bin/engines/{ => fsr}/fsr.py | 52 ++++--- bin/engines/ss.py | 90 ------------ bin/engines/ss/config.json | 14 ++ bin/engines/ss/ss.py | 38 +++++ bin/handler.py | 96 ++++++------- imagevideoupscaler-cli.py | 182 +++++++++++++----------- 8 files changed, 242 insertions(+), 245 deletions(-) create mode 100644 bin/engines/fsr/config.json rename bin/engines/{ => fsr}/fsr.py (83%) delete mode 100644 bin/engines/ss.py create mode 100644 bin/engines/ss/config.json create mode 100644 bin/engines/ss/ss.py diff --git a/bin/__pycache__/handler.cpython-311.pyc b/bin/__pycache__/handler.cpython-311.pyc index 97e96ea9a68cd19db4a7e0bff47a7f13ac2e50d7..5073f74c124b2237558d4b5d7c5d0e7afc7db21c 100644 GIT binary patch delta 2376 zcmZ`)Z)_Ar6rb6<+uPgQ>-8VB*IwJZE2X_c|AE+2DOj|$8d|_AVEI$-Zl~u$@AjCz zs?^gq#sm^cgp3i?5KCeZf-&e};y<)sOo)-NOU{7z!89>^^Fs-K!VfyL+p84oWaqc@ z-kW(d@6CH}zSwhqsPPkzM+CI(+MAT^RRH|Xh(dEL4DOGeZX9xp3K(&70Kvl&xmjAt zk9d%0rilDdzJh#OTGEX?PlE&PBVIZ~JX~B*A={C6k`l6mp*6k$0k}-N)n-b_wTe(N zawG31$~oCV6*v!wtXnRAqr&m0IsGRs#}Z09#VG_EI?_?1iOzj*<+5W%^z%$DIYLdKqP zWSklPfCrFSh~4$rrtP8_ zcqS2vquq*()E81(EHaL8pe3-oqqU>8T@;&Q;lR4~b?cXzMc#28_c*B+9>BmPw?BYO zNxk@L5%W!+kEzKqC5|O>N!(oN!bOycgG}N!w-}4W(kdpByQ*CnQ^ryHSPJEN%kz1A zLX}b8nSKeQh^%SM@qt3aUheEY=w4(9@thFXg*f@%ea$h?17CIa z4XFqQg^O#hyf)i^eMEmTqQ~O8q8ifCoODz#eA!!`^>Av&eR1Dcs|{&uPTHy$zPnP% zsWw9j<)n}~^wv#eQY8NpW=f|)w9r!vrPrYx>rfQ`;xSv9Ux7#RDx$0Q(uksl*%FyX00}!6+^edH_S% zM^d$Iu!_voenUQ}(;FHmMJ={}Vdf;)7zl^*y!O&WS{cvtIFZH((d>FBu(Bu5saoEV zI3g=J@7+d|sqa-qOKW`?s~FePEig+-T+e`c9J45*x{hgSlpu3~reY6c*y1RS*E4y5 zR0f}(@+ygxiql9QRON|rq~+Z&vl=mMR*1Y!Nv7#JP(pnfg{}O~i%K#zkseJ&Xy%#$ zZ-)L_rm~4am;sA_GZP-oYsOziVRFSlBq@)R%22WMc}i=)I>q%1nl{?XvDHUHeTX>bF$ zv7z^g9qVm3*FlQ9yJKe$(0jyPqRntYibG*Y%U^Z4xrM!IxP?Ij17;C!W55cBmF_J2 zu~`C$8m^yO#I8#(~G0K1%C0jg&mU5F^r4<|DAv(q6}w*jY(8ZC`YX-}1@>VUx9dGajWgA?;cR zBv+gFP8I85;hQThpZ3mqDo#9ia^!4{;c3cwnx@4&Ks;XbX3;5VfU+DY(?QuCFLfz| zNoe78|D9mhj4v1L`l|JMr_ueK{=#0Pd#}DPYIH}9pqvZJw}VG+1&&dP)p)LI!q!+L}-ZS}s{*TnWo__#DB|@hF delta 3281 zcma)8eQevt6~CiIiTa==+m$2Pk@ac$BZ=aC%}txcPU5J|S}U%FERN$;bwpa0B1;NM z*NMQv*$|)wS`6uK*a8Fv++uSwxWj^az>00{ygvryzfr0%8yqqi*aB?%%RvKd$cABe zl;lZ?3+zDtc=z7p?~Z)$-n);YpPuW!!?I?8+x(wqxp*f4e<6cf(^orR?f;-V>eF+O zqcWBWJ+Dt&YbI&7)>~{s%`L5H0NPz_gN1F&!d|m5+9<#q)6J+K?x0)vmc>@0`QV-A zJ_soA3gGlN07oxT04(FX(k5Qus2J!q6h1ZgnQ%k5b#C-a_`{XUlnkK<{Y^LE-dYxZ zg)B^m@2{dX06zi}M91tqSH|`4K}ey$(%-?DWjZj1jREu%LtDf6Uk&43updnr4caQg5`@;Le{buvYT>hGnO3p~${r&q7c(ot%h3OsE zfEV=?dJEK~S7=6K=BrI6EKv=KTy{E@6f9`ne5}hVkRPQQr$r$@CCvz4GO}Hno64l5 zYjKg6q*OME!j|^4Mlpef*`&%$r!u^BEzhe~BI1Qp*<>oqtMqJ+<1?y#PUK^GK9QQr zj9@`5rW!FXKgEe6@sGEz2~Fs##U1LwV)1*}S=YfshTR0few=y?faxUlL_;jWee`q7 z=eiAN`^pt-6O0(|I`0jxzNWk}u1s*s^sJJb$7Tf>q448XYJ@z_P;tV;P7xlASo938 zEV2jSzFYHmnR|OzUoKkDl&xo!+V#-dd~MvXUiq*f~>b!YKu;ZmBXynzuu+Ria zT*)AfMU}?E%UZY!J2@Paw*jYh$Fp12bKzL zMAa5aVQ1nPh>XWizJ7Yk!iklULaW()^ysnCnVgjK&gHcz;Cc-Li7zdJ1d>z)HwKl; ziK;=oCQAHlFRcZe5bWBr3z1$o1`+SlN^A&CL>5GE*xFwmA(~%fxDGZfw)ccJ3{}b=_5<~@I%J@zUgN02fK@m zr_6X1#`Az-6)qV6?hF#Av?A93e zd=10DT`3qw)9t5rYunvCJw2P}lioe4%1$pBQ^~IW7rAg{%Tr$eQ|K3*XsvxObRlEM zuZ9FtLsT6u4k}Ejx|u5p6<$@TSgg=>YHaMxSatK`su1?p*8*=U`!+5X9Qs{{GUP9q z#l&QFssiQr_IBfpFf%8mGO9tCl?0v_$i7jVBe<|cb5bNXm*q|gLQW9&VL4KEy4)m} zRPu|T^t=SLB(op)kjdR+C)-ffC3#8s22py@arf|oH8UkjXGNYH#l=mfGr4(Q5MCmd zCj3!a))Q1p6jh5jm*;DO7O_&lVs$dpsjCS-FQsx>yl8$l_cq^Kshz?$*-QwNCG>y; z7<);#kH9Vhy9w;UuuU~2ssMWGZZ}R4gZB(89i2(o+4Irtk7tXngJsvjd*{op!8O;( zlI!H^@uDkIc12beJ%{1K#?H=n^<~Dj!MfI1Pl@#uSznp;EuJm0eG1!mH?tNVDusuN z;S=TXiPc0Yd|C;gzCXD(5-*L!iz8Fzktu~wmqyacNcw>{aEJYvy(@hpe>T24uZ*5k z&d(^9v&HjSC11c*^xJTl6w?S5C67^Rl3KuZ*7qiAI7-S-ET$+yarGfFyF3|=V*uiVs^?LNisdl(F@1rL{k zhd(>`ny}JzQ*wlv^T-6-HJXj;_CsG2P=#yYz zAwat}K_h$!3}kT^k0|jmtN3YX(or;3*n>WrOd!Zk=XsT)6S(8tp65z9Jnc(}C;MCWAf?bq$`U SApUjR<6}+!Photo upscaled' ); + + def videoScaler ( self, tmppath, threads, scalefactor, sharpening, filetype, mode ): + self.isScaling = True + if ( scalefactor == 0 or scalefactor == None ): + self.isScaling = False + # Locate Images and assemble FSR-Command self.file_list = [] - self.filelist = os.listdir(tmppath) + self.filelist = os.listdir( tmppath ) self.filelist.pop(0) self.filelist.sort() self.number = 0 - if sharpening != '' and sharpening != None: + if sharpening != 0 and sharpening != None: for self.file in self.filelist: self.number += 1 if ( self.os_type == 'win32' ): @@ -57,10 +73,10 @@ class FSRScaler: if ( threads > multiprocessing.cpu_count() ): self.threads = multiprocessing.cpu_count(); - if ( not scaling ): + if ( self.isScaling ): engines = { 'c': 'Cubic', 'hqc': 'High Quality Cubic', 'fsr':'FidelityFX Super Resolution' } print( f'\n\n==> Upscaling using { self.threads } threads <==\n\n' ); - print( f'\n\n==> Upscaling Engine is { engines[ mode.lower() ] } <==\n\n' ); + print( f'\n\n==> Upscaling Engine is FidelityFX_CLI with algorithm { engines[ mode.lower() ] } <==\n\n' ); time.sleep( 2 ); @@ -74,20 +90,20 @@ class FSRScaler: if ( i == self.threads - 1 ): for element in self.file_list: self.files += element; - self.command_list.append( ( self.files, fsrpath, quality_setting, i, self.maxlength, self.os_type, mode ) ) + self.command_list.append( ( self.files, scalefactor, i, self.maxlength, self.os_type, mode ) ) self.pool = multiprocessing.Pool( self.threads ) self.pool.starmap( upscalerEngine, self.command_list ); self.pool.close(); self.pool.join(); - if sharpening != '' and sharpening != None: + if sharpening != 0 and sharpening != None: print( f'\n\n\n==> Sharpening using { self.threads } threads <==\n\n' ); time.sleep( 2 ); self.pathSharpening = tmppath - if ( not scaling ): + if ( self.isScaling ): if ( self.os_type == 'win32' ): self.pathSharpening += 'up\\' elif ( self.os_type == 'linux' ): @@ -128,7 +144,7 @@ class FSRScaler: if ( i == self.threads - 1 ): for element in self.file_list: self.files += element; - self.command_list.append( ( self.files, fsrpath, i, self.maxlength, self.os_type, sharpening, not sharpening ) ) + self.command_list.append( ( self.files, i, self.maxlength, self.os_type, sharpening, not sharpening ) ) self.pool = multiprocessing.Pool( self.threads ) self.pool.starmap( sharpeningEngine, self.command_list ); @@ -137,7 +153,7 @@ class FSRScaler: # Add return values -def upscalerEngine ( files, fsrpath, quality_setting, number, maxlength, os_type, version ): +def upscalerEngine ( files, scalefactor, number, maxlength, os_type, version ): scaler = 'FSR' if ( version.upper() == 'HQC' ): scaler = 'HighQualityCubic' @@ -191,9 +207,9 @@ def upscalerEngine ( files, fsrpath, quality_setting, number, maxlength, os_type while len( fileout ) > 0: files_handle = fileout.pop(0) if os_type == 'linux': - command_us = f'wine {fsrpath} -Mode { scaler } -Scale {quality_setting} {quality_setting} {files_handle}' + command_us = f'wine ./bin/lib/FidelityFX_CLI.exe -Mode { scaler } -Scale {scalefactor}x {scalefactor}x {files_handle}' elif os_type == 'win32': - command_us = f'FidelityFX_CLI -Mode { scaler } -Scale {quality_setting} {quality_setting} {files_handle}' + command_us = f'FidelityFX_CLI -Mode { scaler } -Scale {scalefactor}x {scalefactor}x {files_handle}' else: print( 'OS CURRENTLY UNSUPPORTED!' ) return False @@ -209,7 +225,7 @@ def upscalerEngine ( files, fsrpath, quality_setting, number, maxlength, os_type # ####################### -def sharpeningEngine ( files, fsrpath, number, maxlength, os_type, sharpening, didUpscale ): +def sharpeningEngine ( files, number, maxlength, os_type, sharpening, didUpscale ): files = files; # Refactoring of commands that are longer than 32K characters fileout = []; @@ -265,7 +281,7 @@ def sharpeningEngine ( files, fsrpath, number, maxlength, os_type, sharpening, d files_handle = fileout.pop(0) print( '\n\n\n PROCESS: ', number, '\nRunning sharpening filter\n\n\n' ); if os_type == 'linux': - command_sharpening = f'wine {fsrpath} -Mode CAS -Sharpness {sharpening} {files_handle}' + command_sharpening = f'wine ./bin/lib/FidelityFX_CLI.exe -Mode CAS -Sharpness {sharpening} {files_handle}' elif os_type == 'win32': command_sharpening = f'FidelityFX_CLI -Mode CAS -Sharpness {sharpening} {files_handle}' else: diff --git a/bin/engines/ss.py b/bin/engines/ss.py deleted file mode 100644 index ad767d1..0000000 --- a/bin/engines/ss.py +++ /dev/null @@ -1,90 +0,0 @@ -import os -import subprocess -import multiprocessing -import time -import sys - -class SpecialScaler: - def __init__(self): - self.os_type = sys.platform - self.command = "" - self.tmppath = "" - self.videometa = {} - - def superScaler ( self, tmppath, threads, quality_setting, os_platform, model ): - print( '\n\n==> Preparing to upscale videos <==\n\n==> You will see a lot of numbers flying by showing the progress of the upscaling of each individual image.\n==> This process might take a long time, depending on the length of the video.\n\n') - time.sleep( 2 ); - - try: - os.mkdir( f'{tmppath}sc' ) - except FileExistsError: - pass - if ( os_platform == 'win32' ): - self.command = f'realesrgan-ncnn-vulkan -i {tmppath} -o {tmppath}sc -s {quality_setting} -j {threads}:{threads}:{threads} -n {model}' - elif ( os_platform == 'linux' ): - self.command = f'wine ./bin/lib/realesrgan-ncnn-vulkan.exe -i {tmppath} -o {tmppath}sc -s {quality_setting} -j {threads}:{threads}:{threads} -n {model}' - os.system( self.command ); - - - def specialSuperScaler ( self, tmppath, threads, quality_setting, model ): - self.fileList = os.listdir( tmppath ) - self.fileList.pop( 0 ) - self.fileList.sort() - if ( threads > multiprocessing.cpu_count() * 2 ): - self.threads = multiprocessing.cpu_count() * 2; - else: - self.threads = threads - - self.fileCount = len( self.fileList ) // self.threads - self.spareFiles = len( self.fileList ) % self.threads - - self.cmdList = []; - - for t in range( threads ): - try: - os.mkdir( f'{tmppath}{t}' ) - except FileExistsError: - pass - - self.base = t * self.fileCount; - if ( self.os_type == 'win32' ): - for j in range( self.fileCount ): - os.rename( f'{tmppath}{self.fileList[ self.base + j ] }', f'{tmppath}{ t }\\{self.fileList[ self.base + j ] }' ) - elif ( self.os_type == 'linux' ): - for j in range( self.fileCount ): - os.rename( f'{tmppath}{self.fileList[ self.base + j ] }', f'{tmppath}{ t }/{self.fileList[ self.base + j ] }' ) - - self.cmdList.append( ( tmppath, t, quality_setting, model, self.os_type ) ) - - try: - os.mkdir( f'{tmppath}{self.threads + 1}' ) - except FileExistsError: - pass - - if ( self.os_type == 'win32' ): - for k in range( self.spareFiles ): - os.rename( f'{tmppath}{self.fileList[ self.threads * self.fileCount + k ] }', f'{tmppath}{ t }\\{self.fileList[ self.threads * self.fileCount + k ] }' ) - elif ( self.os_type == 'linux' ): - for k in range( self.spareFiles ): - os.rename( f'{tmppath}{self.fileList[ self.threads * self.fileCount + k ] }', f'{tmppath}{ self.threads + 1 }/{self.fileList[ self.threads * self.fileCount + k ] }' ) - - try: - os.mkdir( f'{tmppath}sc' ) - except FileExistsError: - pass - - self.pool_ss = multiprocessing.Pool( self.threads ) - self.pool_ss.starmap( specialScalerEngine, self.cmdList ); - self.pool_ss.close(); - self.pool_ss.join(); - - specialScalerEngine( tmppath, t, quality_setting, model, self.os_type ) - - -def specialScalerEngine ( tmppath, tNumber, quality_setting, model, os_type ): - if ( os_type == 'win32' ): - command = f'realesrgan-ncnn-vulkan -i {tmppath}{tNumber} -o {tmppath}sc -s {quality_setting} -n {model}' - elif ( os_type == 'linux' ): - command = f'wine ./bin/lib/realesrgan-ncnn-vulkan.exe -i {tmppath}{tNumber} -o {tmppath}sc -s {quality_setting} -n {model}' - sub = subprocess.Popen( command, shell=True ); - sub.wait(); \ No newline at end of file diff --git a/bin/engines/ss/config.json b/bin/engines/ss/config.json new file mode 100644 index 0000000..5564f49 --- /dev/null +++ b/bin/engines/ss/config.json @@ -0,0 +1,14 @@ +{ + "abbr":"SS", + "displayName":"Real-ESGRAN", + "lastUsedFilePath":"sc", + "fileNameBeginning":"ig", + "cliModeOptions": { + "av3":{ "displayName": "realesr-animevideov3", "default": true }, + "x4plus":{ "displayName": "realesrgan-x4plus-anime", "default": false } + }, + "pluginCreator": "Janis Hutz", + "pluginCreatorLink": "https://janishutz.com", + "engineLink": "", + "supports": [ "upscaling" ] +} \ No newline at end of file diff --git a/bin/engines/ss/ss.py b/bin/engines/ss/ss.py new file mode 100644 index 0000000..b90963e --- /dev/null +++ b/bin/engines/ss/ss.py @@ -0,0 +1,38 @@ +import os +import subprocess +import multiprocessing +import time +import sys + +class Scaler: + def __init__(self): + self.os_type = sys.platform + self.command = "" + self.tmppath = "" + self.videometa = {} + + def singleScaler ( self, input_path, output_path, scalefactor, threads, mode ): + if self.os_type == 'linux': + self.command = f'wine ./bin/lib/FidelityFX_CLI.exe -Scale {scalefactor} {scalefactor} {input_path} {output_path} -n { mode }' + elif self.os_type == 'win32': + self.command = f'realesrgan-ncnn-vulkan -i {input_path} -o {output_path} -s {scalefactor} -j {threads}:{threads}:{threads} -n { mode }' + else: + print( 'OS CURRENTLY UNSUPPORTED!' ) + return False + + os.system( self.command ) + print( '\n\n==>Photo upscaled' ); + + def videoScaler ( self, tmppath, threads, scalefactor, sharpening, filetype, mode ): + print( '\n\n==> Preparing to upscale videos <==\n\n==> You will see a lot of numbers flying by showing the progress of the upscaling of each individual image.\n==> This process might take a long time, depending on the length of the video.\n\n') + time.sleep( 2 ); + + try: + os.mkdir( f'{tmppath}sc' ) + except FileExistsError: + pass + if ( self.os_type == 'win32' ): + self.command = f'realesrgan-ncnn-vulkan -i {tmppath} -o {tmppath}sc -s {scalefactor} -j {threads}:{threads}:{threads} -n {mode}' + elif ( self.os_type == 'linux' ): + self.command = f'wine ./bin/lib/realesrgan-ncnn-vulkan.exe -i {tmppath} -o {tmppath}sc -s {scalefactor} -j {threads}:{threads}:{threads} -n {mode}' + os.system( self.command ); \ No newline at end of file diff --git a/bin/handler.py b/bin/handler.py index 10eacbc..487520c 100644 --- a/bin/handler.py +++ b/bin/handler.py @@ -1,11 +1,11 @@ -""" +''' * FSRImageVideoUpscalerFrontend - handler.py * * Created by Janis Hutz 03/14/2023, Licensed under the GPL V3 License * https://janishutz.com, development@janishutz.com * * -""" +''' import os @@ -15,12 +15,14 @@ ffmpeg = bin.probe import configparser import time import shutil -import bin.engines.ss -import bin.engines.fsr +import importlib -fsr = bin.engines.fsr.FSRScaler() -ss = bin.engines.ss.SpecialScaler() +importedModules = {} +engineList = os.listdir( 'bin/engines' ); +engineList.pop( 0 ) +for element in engineList: + importedModules[ element ] = importlib.import_module( 'bin.engines.' + element + '.' + element ).Scaler() # Loading the config file to get user preferred temp path config = configparser.ConfigParser() @@ -30,64 +32,56 @@ config.read('./config/settings.ini') class Handler: def __init__(self): self.os_type = sys.platform - self.command = "" - self.tmppath = "" + self.command = '' + self.tmppath = '' self.videometa = {} # TODO: CHECK if this upscaler is any good: https://github.com/Maximellerbach/Image-Processing-using-AI (looks quite promising) - def handler(self, fsrpath, filepath, quality_setting, output_path, sharpening, scaling, filetype, scalerEngine, model, useSpecialModeSS, threads=4 ): + def handler( self, filepath, scalefactor, output_path, sharpening, filetype, engine, mode, threads=4 ): # Function to be called when using this class as this function automatically determines if file is video or image - print( '\n\nFSRImageVideoUpscalerFrontend - V1.1.0\n\nCopyright 2023 FSRImageVideoUpscalerFrontend contributors\n\n\n\n' ); + print( '\n\n ImageVideoUpscaler - V1.1.0\n\n(c) 2023 ImageVideoUpscaler contributors\n\n\n\n' ); - if self.os_type == "linux": - self.tmppath = config["PathSettings"]["tmpPathLinux"] - elif self.os_type == "win32": - self.tmppath = config["PathSettings"]["tmpPathWindows"] + if self.os_type == 'linux': + self.tmppath = config['PathSettings']['tmpPathLinux'] + elif self.os_type == 'win32': + self.tmppath = config['PathSettings']['tmpPathWindows'] else: - print("OS CURRENTLY UNSUPPORTED!") + print('OS CURRENTLY UNSUPPORTED!') return False if ( self.os_type == 'win32' ): self.tmppath += '\\fsru\\' else: if ( self.tmppath[len(self.tmppath) - 1: ] == '/' ): - self.tmppath += "fsru/" + self.tmppath += 'fsru/' else: self.tmppath += '/fsru/' + # checking for spaces in filepath (for use with terminal commands) - self.filepath = "" + self.filepath = '' for self.letter in filepath: - if self.letter == " ": - self.filepath += "\ " + if self.letter == ' ': + self.filepath += '\ ' else: self.filepath += self.letter # Determining filetype - if str(filepath)[len(filepath) - 4:] == ".mp4" or str(filepath)[len(filepath) - 4:] == ".mkv" or str(filepath)[len(filepath) - 4:] == ".MP4": + if str(filepath)[len(filepath) - 4:] == '.mp4' or str(filepath)[len(filepath) - 4:] == '.mkv' or str(filepath)[len(filepath) - 4:] == '.MP4': print( '\n\n==> Upscaling video' ) - self.video_scaling( fsrpath, filepath, quality_setting, output_path, threads, sharpening, scaling, filetype, scalerEngine, model, useSpecialModeSS ) - elif str(filepath)[len(filepath) - 4:] == ".JPG" or str(filepath)[len(filepath) - 4:] == ".png" or str(filepath)[len(filepath) - 4:] == ".jpg" or str(filepath)[len(filepath) - 5:] == ".jpeg": + self.video_scaling( filepath, output_path, scalefactor, threads, sharpening, filetype, mode, engine ) + elif str(filepath)[len(filepath) - 4:] == '.JPG' or str(filepath)[len(filepath) - 4:] == '.png' or str(filepath)[len(filepath) - 4:] == '.jpg' or str(filepath)[len(filepath) - 5:] == '.jpeg': print( '\n==>upscaling image' ) - self.photo_scaling(fsrpath, filepath, quality_setting, output_path) + self.photo_scaling( scalefactor, output_path, engine ) else: - print("not supported") + print('not supported') return False - def photo_scaling(self, fsrpath, filepath, quality_setting, output_path): + def photo_scaling(self, scalefactor, output_path, engine, mode): # DO NOT CALL THIS! Use Handler().handler() instead! - if self.os_type == "linux": - self.command = f"wine {fsrpath} -Scale {quality_setting} {quality_setting} {self.filepath} {output_path}" - elif self.os_type == "win32": - self.command = f"FidelityFX_CLI -Scale {quality_setting} {quality_setting} {self.filepath} {output_path}" - else: - print("OS CURRENTLY UNSUPPORTED!") - return False - - os.system(self.command) - print( '\n\n==>Photo upscaled' ); + pass - def video_scaling( self, fsrpath, filepath, quality_setting, output_path, threads, sharpening, scaling, filetype, scalerEngine, model, useSpecialModeSS ): + def video_scaling( self, input_path, output_path, scalefactor, threads, sharpening, filetype, mode, engine ): # DO NOT CALL THIS! Use Handler().handler() instead! # Splitting video into frames @@ -103,12 +97,12 @@ class Handler: print( '\n==> Created directory' ) - if self.os_type == "linux": - self.command = f"ffmpeg -i {str(self.filepath)} {self.tmppath}ig%08d.{ filetype }" - elif self.os_type == "win32": - self.command = f"ffmpeg -i {str(self.filepath)} \"{self.tmppath}ig%08d.{ filetype }\"" + if self.os_type == 'linux': + self.command = f'ffmpeg -i {str(self.filepath)} {self.tmppath}ig%08d.{ filetype }' + elif self.os_type == 'win32': + self.command = f'ffmpeg -i {str(self.filepath)} \"{self.tmppath}ig%08d.{ filetype }\"' else: - print("OS CURRENTLY UNSUPPORTED!") + print('OS CURRENTLY UNSUPPORTED!') return False os.system( self.command ) @@ -116,7 +110,7 @@ class Handler: # Retrieving Video metadata self.filelist = os.listdir(self.tmppath) - self.videometa = ffmpeg.probe(str(filepath))["streams"].pop(0) + self.videometa = ffmpeg.probe(str(input_path))['streams'].pop(0) self.duration = self.videometa.get( 'duration' ) self.frames = len( self.filelist ) @@ -134,17 +128,7 @@ class Handler: time.sleep( 2 ); - self.lastUsedPath = '' - - if ( scalerEngine.lower() == 'fsr' or scalerEngine.lower() == 'c' or scalerEngine.lower() == 'hqc' ): - self.lastUsedPath = fsr.fsrScaler( self.tmppath, filepath, threads, fsrpath, quality_setting + 'x', sharpening, scaling, filetype, scalerEngine ) - elif ( scalerEngine.upper() == 'SS' ): - if ( not useSpecialModeSS ): - self.lastUsedPath = ss.superScaler( self.tmppath, threads, quality_setting, self.os_type, model ) - else: - self.lastUsedPath = ss.specialSuperScaler( self.tmppath, threads, quality_setting, model ) - else: - raise Exception( 'ERROR upscaling. scalerEngine invalid' ); + importedModules[ engine ].videoScaler ( self.tmppath, int( threads ), int( scalefactor ), float( sharpening ), filetype, mode ) # get Video's audio print( '\n\n==>Finished Upscaling individual images. \n==>Retrieving Video audio to append\n\n' ) @@ -158,8 +142,8 @@ class Handler: time.sleep( 2 ); try: - os.remove(f"{self.tmppath}audio.aac") - os.remove(f"{output_path}") + os.remove(f'{self.tmppath}audio.aac') + os.remove(f'{output_path}') except FileNotFoundError: pass if self.os_type == 'linux': @@ -176,7 +160,7 @@ class Handler: if self.os_type == 'linux': self.command = f'ffmpeg -framerate {self.framerate} -i {self.tmppath}sc/ig%08d.{filetype} {output_path} -i {self.tmppath}audio.aac' elif self.os_type == 'win32': - self.command = f'ffmpeg -framerate {self.framerate} -i \"{self.tmppath}sc\\ig%08d.{filetype}\" {output_path} -i {self.tmppath}audio.aac' + self.command = f'ffmpeg -framerate {self.framerate} -i \'{self.tmppath}sc\\ig%08d.{filetype}\' {output_path} -i {self.tmppath}audio.aac' else: print( 'OS CURRENTLY UNSUPPORTED!' ); return False diff --git a/imagevideoupscaler-cli.py b/imagevideoupscaler-cli.py index ddff270..2377de4 100644 --- a/imagevideoupscaler-cli.py +++ b/imagevideoupscaler-cli.py @@ -11,97 +11,117 @@ import argparse import bin.handler import os import multiprocessing +import json + +engineList = os.listdir( 'bin/engines' ); +engineList.pop( 0 ) + +engineInfo = {} + +for engine in engineList: + engineInfo[ engine ] = json.load( open( 'bin/engines/' + engine + '/config.json' ) ) allowedFiletypes = [ 'png', 'jpg' ]; +def performChecks ( args, ap ): + if ( args.details == None or args.details == '' ): + if ( not args.printengines ): + # Check if input and output file arguments are available + if ( args.inputfile == None or args.inputfile == '' or args.outputfile == None or args.outputfile == '' ): + print( '\n\n ==> ERROR: Input and output file required! <==\n\n' ) + ap.print_usage(); + return False + + # check if output file exists and if, prompt user if it should be overwritten and remove if, if yes + if ( os.path.exists( args.outputfile ) ): + doReplace = input( '--> File already exists. Do you want to replace it? (Y/n) ' ).lower() + if ( doReplace == 'y' or doReplace == '' ): + os.remove( args.outputfile ); + else: + print( '\n==> Refusing to Upscale video. Please delete the file or specify another filepath! <==' ) + return False + + # check if engine argument is valid + try: + engineInfo[ args.engine ] + except KeyError: + print( '\n==> ERROR: Engine not available. Ensure you have specified a valid engine' ) + return False + + # Check scalefactor argument and also verify that engine supports upscaling + if ( int( args.scalefactor ) > 4 and int( args.scalefactor ) < -4 ): + print( '\n==> ERROR: Invalid scale factor. Value has to be an integer between -4 and 4' ) + return False + else: + if ( not 'upscaling' in engineInfo[ args.engine ][ 'supports' ] ): + print( '\n==> ERROR: This engine does NOT support upscaling' ) + return False + + # Check sharpening argument and also verify that engine supports it + if ( float( args.sharpening ) >= 1 and float( args.sharpening ) <= 0 ): + print( '\n==> ERROR: Invalid value for sharpening. Value has to be between 0 and 1' ) + return False + else: + if ( not 'sharpening' in engineInfo[ args.engine ][ 'supports' ] ): + print( '\n==> ERROR: This engine does NOT support sharpening' ) + return False + + # check if scalefactor and / or sharpening is available + if ( args.scalefactor == 0 and args.sharpening == 0 ): + print( '\n==> ERROR: Either scalefactor or sharpening argument required!' ) + return False + + # Check if filetype argument is valid + if ( not args.filetype in allowedFiletypes ): + print( '\n==> ERROR: Unknown filetype for temp files. Can be png or jpg' ) + return False + + # Check if mode of engine is valid + try: + engineInfo[ args.engine ][ 'cliModeOptions' ][ args.mode ] + except KeyError: + print( '\n==> ERROR: The specified mode is not supported by this engine. Options:' ) + for option in engineInfo[ args.engine ][ 'cliModeOptions' ]: + print( ' --> ' + engineInfo[ args.engine ][ 'cliModeOptions' ][ option ][ 'displayName' ] + ' (' + option + ')' ) + return False + + return True + else: + print( '\n\n==> Available engines <==\n' ) + for entry in engineList: + print( '--> ' + entry ) + print( '\n\n' ) + else: + print( '\n\n ==> INFOS about ' + engineInfo[ args.details ][ 'displayName' ] + '\n' ) + print( ' --> Engine cli option is: ' + engineInfo[ args.details ][ 'abbr' ].lower() ) + print( ' --> CLI mode options are: ' ) + for mode in engineInfo[ args.details ][ 'cliModeOptions' ]: + print( ' -> ' + engineInfo[ args.details ][ 'cliModeOptions' ][ mode ][ 'displayName' ] + ':' ) + print( ' > CLI name: ' + mode ) + print( ' > Is the default: ' + str( engineInfo[ args.details ][ 'cliModeOptions' ][ mode ][ 'default' ] ) ) + print( '\n\n' ) + if __name__ == '__main__': - ap = argparse.ArgumentParser( description='ImageVideoUpscaler - CLI, a CLI application to upscale videos and images using FSR.' ) - ap.add_argument( 'inputfile', help='File path for the video / image to be upscaled' ) - ap.add_argument( 'outputfile', help='File path for the video / image that was upscaled' ) - ap.add_argument( '-s', '--scalefactor', help='Scale factor for the video / image. Can be a integer from 1 - 4' ) - ap.add_argument( '-F', '--filetype', help='Change the file type of the temporary image files. Supports png, jpg. Video quality: png > jpg. PNG is default, if not specified.' ) + ap = argparse.ArgumentParser( description='ImageVideoUpscaler - CLI, a CLI application to upscale videos and images using different upscaling engines.' ) + ap.add_argument( '-i', '--inputfile', help='File path for the video / image to be upscaled' ) + ap.add_argument( '-o', '--outputfile', help='Output file path for the video / image that was upscaled' ) + ap.add_argument( '-s', '--scalefactor', help='Scale factor for the video / image. Can be a integer from -4 to 4' ) ap.add_argument( '-S', '--sharpening', help='Sharpening factor (between 0 and 1 whereas 0 means no sharpening, 1 the most sharpening. Recommendation: Do not exceed 0.25, as it often looks bad)' ) - ap.add_argument( '-t', '--threading', help='Use special threading mode with SS scaler (spawns 16 threads upscaling at one time)', action='store_true' ) ap.add_argument( '-T', '--threads', help='Thread count to use. Cannot exceed CPU thread count. Scaling non-linear (using 2 threads is not exactly 2x the speed of 1 thread). Scales well with FSR, barely with Real-ESRGAN, as it uses mostly the GPU to upscale' ) - ap.add_argument( '-E', '--engine', help='Upscaling engine. Can be fsr, C (for Cubic), HQC (for HighQuality Cubic) or SS (for Real-ESRGAN). FSR tends to be higher, Cubic is quite fast but quite low quality, HighQualityCubic is of higher quality, but slower. Real-ESRGAN is meant for anime and is super slow. Defaults to fsr' ) - ap.add_argument( '-M', '--model', help='Only available if using Real-ESRGAN. Change the ML-Model used to upsample video, can be: realesr-animevideov3 | realesrgan-x4plus-anime , defaults to realesr-animevideov3' ) - ap.add_argument( '-N', '--noscaling', help='Do not upscale video, instead only sharpen. Sharpening argument required!', action='store_true' ) + ap.add_argument( '-E', '--engine', help='Upscaling engine. By default can be fsr or ss. Use the -p option to see all installed engines' ) + ap.add_argument( '-M', '--mode', help='Specify a special mode for a specific engine. Might not be available in every engine. Use the -d option to find out more' ) + ap.add_argument( '-F', '--filetype', help='Change the file type of the temporary image files. Supports png, jpg. Video quality: png > jpg. PNG is default, if not specified.' ) + ap.add_argument( '-d', '--details', help='Get details on usage of a particular engine and exit. Reads the config.json file of that engine and displays it in a HR manner' ) + ap.add_argument( '-p', '--printengines', help='Print all engines and exit', action='store_true' ) + ap.set_defaults( scaling = 0, sharpening = 0, threads = 4, engine = 'fsr', mode = 'fsr', filetype = 'png' ) args = ap.parse_args() handler = bin.handler.Handler() - - go = True; - go2 = True; - go3 = True; - specialMode = False; - engine = 'fsr'; - model = 'realesr-animevideov3'; - availableModels = [ 'realesr-animevideov3', 'realesrgan-x4plus-anime' ]; - + multiprocessing.freeze_support(); - if ( os.path.exists( args.outputfile ) ): - doReplace = input( 'File already exists. Do you want to replace it? (Y/n) ' ).lower() - if ( doReplace == 'y' or doReplace == '' ): - go = True - os.remove( args.outputfile ); - else: - print( '\nRefusing to Upscale video. Please delete the file or specify another filepath!') - go = False - if ( args.engine != None ): - if ( args.engine == 'fsr' or args.engine == 'SS' or args.engine.lower() == 'c' or args.engine.lower() == 'hqc' ): - engine = args.engine; - else: - print( 'Invalid argument for engine' ) - go2 = False; - - if ( engine == 'SS' and args.model != None ): - if ( args.model in availableModels ): - model = args.model; - else: - print( 'Invalid argument for model. Can be: realesr-animevideov3 | realesrgan-x4plus-anime' ) - go2 = False; - - if ( args.noscaling ): - if ( args.sharpening != None ): - if ( float( args.sharpening ) > 0 ): - go2 = True; - else: - go2 = False; - else: - print( 'Missing argument for Sharpening. Please specify that argument and try again!' ) - go2 = False; - - if ( args.sharpening != None ): - if ( float( args.sharpening ) > 1 ): - print( 'Invalid argument for Sharpening, please specify a value between 0 and 1!' ) - go3 = False; - - if ( args.filetype != None ): - if ( args.filetype in allowedFiletypes ): - filetype = args.filetype - else: - go3 = False - print( 'Invalid filetype for temp images specified. Please ensure to only use png or jpg!' ); - else: - filetype = 'png' - - specialMode = args.threading; + if ( performChecks( args, ap ) ): + handler.handler( args.inputfile, args.scalefactor, args.outputfile, args.sharpening, args.filetype, args.engine, args.mode, args.threads ) + print( '\n\n---------------------------------------------------------------------------------\n\nDONE \n\n\n\nImageVideoUpscalerFrontend V1.1.0\n\nCopyright 2023 FSRImageVideoUpscalerFrontend contributors\nThis application comes with absolutely no warranty to the extent permitted by applicable law\n\n\n\nOutput was written to ' + args.outputfile + '\n\n\n' ) - if ( go and go2 and go3 ): - if ( args.scalefactor ): - if ( int( args.scalefactor ) ): - if ( args.threads != None ): - handler.handler( 'bin/lib/FidelityFX_CLI.exe', args.inputfile, args.scalefactor, args.outputfile, args.sharpening, args.noscaling, filetype, engine, model, specialMode, threads=int( args.threads ) ); - else: - handler.handler( 'bin/lib/FidelityFX_CLI.exe', args.inputfile, args.scalefactor, args.outputfile, args.sharpening, args.noscaling, filetype, engine, model, specialMode ); - else: - raise NameError( 'Argument Scale does require to be an integer!' ) - else: - if ( args.threads != None ): - handler.handler( 'bin/lib/FidelityFX_CLI.exe', args.inputfile, '2', args.outputfile, args.sharpening, args.noscaling, filetype, engine, model, specialMode, threads=int( args.threads ) ); - else: - handler.handler( 'bin/lib/FidelityFX_CLI.exe', args.inputfile, '2', args.outputfile, args.sharpening, args.noscaling, filetype, engine, model, specialMode ) - print( '\n\n---------------------------------------------------------------------------------\n\nDONE \n\n\n\nImageVideoUpscalerFrontend V1.1.0\n\nCopyright 2023 FSRImageVideoUpscalerFrontend contributors\nThis application comes with absolutely no warranty to the extent permitted by applicable law\n\n\n\nYour video was saved to ' + args.outputfile + '\n\n\n' ) -