2022-05-31 22:17:22 +02:00
using Flowframes.Data ;
using Flowframes.IO ;
2022-07-18 20:38:40 +02:00
using Flowframes.MiscUtils ;
using Flowframes.Ui ;
2022-05-31 22:17:22 +02:00
using System ;
using System.Collections.Generic ;
using System.Drawing ;
using System.IO ;
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
namespace Flowframes.Os
{
class VapourSynthUtils
{
2022-05-31 23:47:18 +02:00
public class VsSettings
2022-05-31 22:17:22 +02:00
{
2022-05-31 23:47:18 +02:00
public InterpSettings InterpSettings { get ; set ; }
public string ModelDir { get ; set ; } = "" ;
public float Factor { get ; set ; } = 2.0f ;
public Size Res { get ; set ; } = new Size ( ) ;
public bool Uhd { get ; set ; } = false ;
public float SceneDetectSensitivity { get ; set ; } = 0.15f ;
public int GpuId { get ; set ; } = 0 ;
public int GpuThreads { get ; set ; } = 3 ;
public bool Tta { get ; set ; } = false ;
public bool Loop { get ; set ; } = false ;
public bool MatchDuration { get ; set ; } = false ;
2022-06-06 07:03:27 +02:00
public bool Dedupe { get ; set ; } = false ;
2022-06-04 12:43:48 +02:00
public bool Realtime { get ; set ; } = false ;
2022-06-09 00:30:58 +02:00
public bool Osd { get ; set ; } = true ;
2022-05-31 23:47:18 +02:00
}
public static string CreateScript ( VsSettings s )
{
2022-07-20 18:29:36 +02:00
Logger . Log ( $"Creating RIFE VS script. Model: {s.ModelDir}, Factor: {s.Factor}, Res: {s.Res.Width}x{s.Res.Height}, UHD: {s.Uhd}, SC Sens: {s.SceneDetectSensitivity}, " +
$"GPU ID: {s.GpuId}, GPU Threads: {s.GpuThreads}, TTA: {s.Tta}, Loop: {s.Loop}, Match Duration: {s.MatchDuration}, Dedupe: {s.Dedupe}, RT: {s.Realtime}{(s.Osd ? $" , OSD : { s . Osd } " : " ")}" , true ) ;
2022-05-31 23:47:18 +02:00
string inputPath = s . InterpSettings . inPath ;
2022-06-04 12:43:48 +02:00
string mdlPath = Path . Combine ( Paths . GetPkgPath ( ) , Implementations . rifeNcnnVs . PkgDir , s . ModelDir ) . Replace ( @"\" , "/" ) . Wrap ( ) ;
2022-05-31 23:47:18 +02:00
bool sc = s . SceneDetectSensitivity > = 0.01f ;
2022-07-20 18:10:31 +02:00
long frameCount = ( long ) Interpolate . currentMediaFile . FrameCount ;
2022-07-18 20:38:40 +02:00
bool trim = QuickSettingsTab . trimEnabled ;
long srcTrimStartFrame = trim ? ( long ) ( Math . Round ( FormatUtils . TimestampToMs ( QuickSettingsTab . trimStart ) / 1000f * s . InterpSettings . inFps . GetFloat ( ) ) ) : 0 ;
long srcTrimEndFrame = trim & & QuickSettingsTab . doTrimEnd ? ( long ) ( Math . Round ( FormatUtils . TimestampToMs ( QuickSettingsTab . trimEnd ) / 1000f * s . InterpSettings . inFps . GetFloat ( ) ) ) - 1 : frameCount - 1 ;
if ( trim )
frameCount = srcTrimEndFrame - srcTrimStartFrame ;
2022-05-31 23:47:18 +02:00
int endDupeCount = s . Factor . RoundToInt ( ) - 1 ;
2022-07-18 20:38:40 +02:00
int targetFrameCountMatchDuration = ( frameCount * s . Factor ) . RoundToInt ( ) ; // Target frame count to match original duration (and for loops)
2022-06-01 16:23:00 +02:00
int targetFrameCountTrue = targetFrameCountMatchDuration - endDupeCount ; // Target frame count without dupes at the end (only in-between frames added)
2022-05-31 23:47:18 +02:00
2022-06-09 00:30:58 +02:00
List < string > l = new List < string > { "import sys" , "import os" , "import json" , "import time" , "import functools" , "import vapoursynth as vs" , "core = vs.core" , "" } ; // Imports
l . Add ( $"inputPath = r'{inputPath}'" ) ;
l . Add ( $"" ) ;
2022-06-01 10:27:02 +02:00
2023-12-22 05:20:22 +01:00
bool loadFrames = s . InterpSettings . inputIsFrames | | ( s . Dedupe & & ! s . Realtime ) ;
if ( loadFrames )
2022-06-01 15:46:34 +02:00
{
2023-02-15 18:25:41 +01:00
FileInfo [ ] frames = IoUtils . GetFileInfosSorted ( s . InterpSettings . framesFolder , false , "*.*" ) ;
string ext = frames . FirstOrDefault ( ) . Extension ;
string first = Path . GetFileNameWithoutExtension ( frames . FirstOrDefault ( ) . FullName ) ;
l . Add ( $"clip = core.imwri.Read(r'{Path.Combine(s.InterpSettings.framesFolder, $" % 0 { first . Length } d { ext } ")}', firstnum={first.GetInt()})" ) ; // Load image sequence with imwri
2022-06-01 15:46:34 +02:00
l . Add ( $"clip = core.std.AssumeFPS(clip, fpsnum={s.InterpSettings.inFps.Numerator}, fpsden={s.InterpSettings.inFps.Denominator})" ) ; // Set frame rate for img seq
}
2022-06-01 10:27:02 +02:00
else
2022-06-01 15:46:34 +02:00
{
2022-06-09 00:30:58 +02:00
l . Add ( "indexFilePath = f'{inputPath}.cache.lwi'" ) ;
2022-06-04 12:43:48 +02:00
l . Add ( $"if os.path.isdir(r'{s.InterpSettings.tempFolder}'):" ) ;
2023-01-08 02:28:15 +01:00
l . Add ( $" indexFilePath = r'{Path.Combine(s.InterpSettings.tempFolder, " cache . lwi ")}'" ) ;
2022-06-09 00:30:58 +02:00
l . Add ( $"clip = core.lsmas.LWLibavSource(inputPath, cachefile=indexFilePath)" ) ; // Load video with lsmash
2022-06-01 15:46:34 +02:00
}
2022-05-31 23:47:18 +02:00
2022-07-18 20:38:40 +02:00
if ( trim )
l . Add ( $"clip = clip.std.Trim({srcTrimStartFrame}, {srcTrimEndFrame})" ) ;
2022-06-09 00:30:58 +02:00
l . Add ( $"" ) ;
2022-06-01 16:23:00 +02:00
if ( s . Loop & & ! s . InterpSettings . inputIsFrames )
2022-05-31 23:47:18 +02:00
{
l . Add ( $"firstFrame = clip[0]" ) ; // Grab first frame
l . Add ( $"clip = clip + firstFrame" ) ; // Add to end (for seamless loop interpolation)
}
2023-12-22 05:20:22 +01:00
l . Add ( GetScaleLines ( s , loadFrames ) ) ;
2022-05-31 23:47:18 +02:00
if ( sc )
2022-06-09 00:30:58 +02:00
l . Add ( $"clip = core.misc.SCDetect(clip=clip, threshold={s.SceneDetectSensitivity.ToStringDot()})" ) ; // Scene detection
2022-05-31 23:47:18 +02:00
2023-10-27 09:54:34 +02:00
Fraction outFps = s . InterpSettings . inFps * s . Factor ;
l . Add ( $"clip = core.rife.RIFE(clip, fps_num={outFps.Numerator}, fps_den={outFps.Denominator}, model_path={mdlPath}, gpu_id={s.GpuId}, gpu_thread={s.GpuThreads}, tta={s.Tta}, uhd={s.Uhd}, sc={sc})" ) ; // Interpolate
2022-06-06 07:03:27 +02:00
2022-06-06 20:54:16 +02:00
if ( s . Dedupe & & ! s . Realtime )
2023-01-08 02:28:15 +01:00
l . Add ( GetRedupeLines ( s ) ) ;
2022-06-06 07:03:27 +02:00
2023-12-22 05:20:22 +01:00
l . Add ( $"clip = vs.core.resize.Bicubic(clip, format=vs.YUV444P16, matrix_s={(loadFrames ? " ' 470 bg ' " : " cMatrix ")})" ) ; // Convert RGB to YUV. Always use 470bg if input is frames
2022-05-31 23:47:18 +02:00
2022-06-06 20:54:16 +02:00
if ( ! s . Dedupe ) // Ignore trimming code when using deduping that that already handles trimming in the frame order file
2022-05-31 23:47:18 +02:00
{
2022-06-06 20:54:16 +02:00
if ( s . Loop )
{
l . Add ( $"clip = clip.std.Trim(0, {targetFrameCountMatchDuration - 1})" ) ; // -1 because we use index, not count
}
else
{
if ( ! s . MatchDuration )
l . Add ( $"clip = clip.std.Trim(0, {targetFrameCountTrue - 1})" ) ; // -1 because we use index, not count
}
2022-05-31 23:47:18 +02:00
}
2022-06-04 12:43:48 +02:00
if ( s . Realtime & & s . Loop )
2022-06-09 00:30:58 +02:00
l . AddRange ( new List < string > { $"clip = clip.std.Loop(0)" , "" } ) ; // Can't loop piped video so we loop it before piping it to ffplay
if ( s . Realtime & & s . Osd )
2023-01-08 02:28:15 +01:00
l . Add ( GetOsdLines ( ) ) ;
2022-06-04 12:43:48 +02:00
2022-05-31 23:47:18 +02:00
l . Add ( $"clip.set_output()" ) ; // Set output
2022-06-09 00:30:58 +02:00
l . Add ( "" ) ;
2022-05-31 22:17:22 +02:00
2022-06-04 12:43:48 +02:00
l . Add ( $"if os.path.isfile(r'{inputPath}.cache.lwi'):" ) ;
2023-01-08 02:28:15 +01:00
l . Add ( $" os.remove(r'{inputPath}.cache.lwi')" ) ;
2022-06-04 12:43:48 +02:00
2022-05-31 22:17:22 +02:00
string pkgPath = Path . Combine ( Paths . GetPkgPath ( ) , Implementations . rifeNcnnVs . PkgDir ) ;
string vpyPath = Path . Combine ( pkgPath , "rife.vpy" ) ;
2022-05-31 23:47:18 +02:00
File . WriteAllText ( vpyPath , string . Join ( "\n" , l ) ) ;
2022-05-31 22:17:22 +02:00
return vpyPath ;
}
2023-12-22 05:20:22 +01:00
static string GetScaleLines ( VsSettings settings , bool loadFrames )
2022-06-01 15:46:34 +02:00
{
2023-01-08 02:28:15 +01:00
InterpSettings interp = settings . InterpSettings ;
bool resize = ! interp . ScaledResolution . IsEmpty & & interp . ScaledResolution ! = interp . InputResolution ;
string s = "" ;
2022-06-01 15:46:34 +02:00
2023-01-08 02:28:15 +01:00
s + = $"\n" ;
s + = $"cMatrix = '709'\n" ;
s + = $"\n" ;
2022-06-06 07:03:27 +02:00
2023-12-22 05:20:22 +01:00
if ( ! loadFrames )
2022-06-06 07:03:27 +02:00
{
2023-01-08 02:28:15 +01:00
s + = "try:\n" ;
s + = " m = clip.get_frame(0).props._Matrix\n" ;
s + = " if m == 0: cMatrix = 'rgb'\n" ;
s + = " elif m == 4: cMatrix = 'fcc'\n" ;
s + = " elif m == 5: cMatrix = '470bg'\n" ;
s + = " elif m == 6: cMatrix = '170m'\n" ;
s + = " elif m == 7: cMatrix = '240m'\n" ;
s + = " elif m == 8: cMatrix = 'ycgco'\n" ;
s + = " elif m == 9: cMatrix = '2020ncl'\n" ;
s + = " elif m == 10: cMatrix = '2020cl'\n" ;
s + = " elif m == 12: cMatrix = 'chromancl'\n" ;
s + = " elif m == 13: cMatrix = 'chromacl'\n" ;
s + = " elif m == 14: cMatrix = 'ictcp'\n" ;
s + = $"except:\n" ;
s + = $" cMatrix = '709'\n" ;
s + = $"\n" ;
s + = $"colRange = 'limited'\n" ;
s + = $"\n" ;
s + = $"try:\n" ;
s + = $" if clip.get_frame(0).props._ColorRange == 0: colRange = 'full'\n" ;
s + = $"except:\n" ;
s + = $" colRange = 'limited'\n" ;
s + = $"\n" ;
2022-06-06 07:03:27 +02:00
}
2023-01-08 02:28:15 +01:00
s + = $"if clip.format.color_family == vs.YUV:\n" ;
s + = $" clip = core.resize.Bicubic(clip=clip, format=vs.RGBS, matrix_in_s=cMatrix, range_s=colRange{(resize ? $" , width = { interp . ScaledResolution . Width } , height = { interp . ScaledResolution . Height } " : " ")})\n" ;
s + = $"\n" ;
s + = $"if clip.format.color_family == vs.RGB:\n" ;
s + = $" clip = core.resize.Bicubic(clip=clip, format=vs.RGBS{(resize ? $" , width = { interp . ScaledResolution . Width } , height = { interp . ScaledResolution . Height } " : " ")})\n" ;
s + = $"\n" ;
2022-06-01 15:46:34 +02:00
2023-01-08 02:28:15 +01:00
return s ;
2022-06-01 15:46:34 +02:00
}
2023-01-08 02:28:15 +01:00
static string GetRedupeLines ( VsSettings settings )
2022-05-31 22:17:22 +02:00
{
2023-01-08 02:28:15 +01:00
string s = "" ;
2023-01-08 21:53:32 +01:00
s + = "reorderedClip = clip[0]\n" ;
2023-01-08 02:28:15 +01:00
s + = "\n" ;
2023-01-08 02:28:54 +01:00
s + = $"with open(r'{Path.Combine(settings.InterpSettings.tempFolder, " frames . vs . json ")}') as json_file:\n" ;
2023-01-08 02:28:15 +01:00
s + = " frameList = json.load(json_file)\n" ;
s + = " \n" ;
s + = " for i in frameList:\n" ;
s + = " if(i < clip.num_frames):\n" ;
s + = " reorderedClip = reorderedClip + clip[i]\n" ;
s + = "\n" ;
s + = "clip = reorderedClip.std.Trim(1, reorderedClip.num_frames - 1)\n" ;
s + = "\n" ;
return s ;
2022-05-31 22:17:22 +02:00
}
2022-06-09 00:30:58 +02:00
2023-01-08 02:28:15 +01:00
static string GetOsdLines ( )
2022-06-09 00:30:58 +02:00
{
2023-01-08 02:28:15 +01:00
string s = "" ;
s + = $"framesProducedPrevious = 0 \n" ;
s + = $"framesProducedCurrent = 0 \n" ;
s + = $"lastFpsUpdateTime = time.time() \n" ;
s + = $"startTime = time.time() \n" ;
s + = $" \n" ;
s + = $"def onFrame(n, clip): \n" ;
s + = $" global startTime \n" ;
s + = $" fpsAvgTime = 1 \n" ;
s + = $" \n" ;
s + = $" if time.time() - startTime > fpsAvgTime: \n" ;
s + = $" global framesProducedPrevious \n" ;
s + = $" global framesProducedCurrent \n" ;
s + = $" global lastFpsUpdateTime \n" ;
s + = $" \n" ;
s + = $" fpsFloat = (clip.fps.numerator / clip.fps.denominator) \n" ;
s + = $" videoTimeFloat = (1 / fpsFloat) * n \n" ;
s + = $" framesProducedCurrent+=1 \n" ;
s + = $" \n" ;
s + = $" if time.time() - lastFpsUpdateTime > fpsAvgTime: \n" ;
s + = $" lastFpsUpdateTime = time.time() \n" ;
s + = $" framesProducedPrevious = framesProducedCurrent / fpsAvgTime \n" ;
s + = $" framesProducedCurrent = 0 \n" ;
s + = $" \n" ;
s + = $" speed = (framesProducedPrevious / fpsFloat) * 100 \n" ;
s + = $" osdString = f\" Time : { { time . strftime ( \ ' % H : % M : % S \ ' , time . gmtime ( videoTimeFloat ) ) } } - FPS : { { framesProducedPrevious : . 2f } } / { { fpsFloat : . 2f } } ( { { speed : . 0f } } % ) { { \ ' [ ! ] \ ' if speed < 95 else \ ' \ ' } } \ " \n" ;
s + = $" clip = core.text.Text(clip, text=osdString, alignment=7, scale=1) \n" ;
s + = $" return clip \n" ;
s + = $" \n" ;
s + = $"clip = core.std.FrameEval(clip, functools.partial(onFrame, clip=clip)) \n" ;
s + = $" \n" ;
return s ;
2022-06-09 00:30:58 +02:00
}
public static int GetSeekSeconds ( long videoLengthSeconds )
{
int seekStep = 10 ;
if ( videoLengthSeconds > 2 * 60 ) seekStep = 20 ;
if ( videoLengthSeconds > 5 * 60 ) seekStep = 30 ;
if ( videoLengthSeconds > 15 * 60 ) seekStep = 60 ;
return seekStep ;
}
2022-05-31 22:17:22 +02:00
}
}