Basic support for RIFE-NCNN-VS

This commit is contained in:
n00mkrad
2022-05-31 22:17:22 +02:00
parent 241e92043d
commit 9ea9ec0875
25 changed files with 317 additions and 121 deletions

View File

@@ -7,29 +7,30 @@ using System.Threading.Tasks;
namespace Flowframes.Data
{
public struct AI
public class AI
{
public enum Backend { Pytorch, Ncnn, Tensorflow, Other }
public Backend backend;
public string aiName;
public string aiNameShort;
public string friendlyName;
public string description;
public string pkgDir;
public enum FactorSupport { Fixed, AnyPowerOfTwo, AnyInteger, AnyFloat }
public FactorSupport factorSupport;
public int[] supportedFactors;
public enum AiBackend { Pytorch, Ncnn, Tensorflow, Other }
public AiBackend Backend { get; set; } = AiBackend.Pytorch;
public string AiName { get; set; } = "";
public string AiNameShort { get; set; } = "";
public string FriendlyName { get; set; } = "";
public string Description { get; set; } = "";
public string PkgDir { get; set; } = "";
public enum InterpFactorSupport { Fixed, AnyPowerOfTwo, AnyInteger, AnyFloat }
public InterpFactorSupport FactorSupport { get; set; } = InterpFactorSupport.Fixed;
public int[] SupportedFactors { get; set; } = new int[0];
public bool Piped { get; set; } = false;
public AI(Backend backend, string aiName, string friendlyName, string desc, string pkgDir, FactorSupport factorSupport = FactorSupport.Fixed, int[] factors = null)
public AI(AiBackend backend, string aiName, string friendlyName, string desc, string pkgDir, InterpFactorSupport factorSupport = InterpFactorSupport.Fixed, int[] supportedFactors = null)
{
this.backend = backend;
this.aiName = aiName;
this.aiNameShort = aiName.Split(' ')[0].Split('_')[0];
this.friendlyName = friendlyName;
this.description = desc;
this.pkgDir = pkgDir;
this.supportedFactors = factors;
this.factorSupport = factorSupport;
Backend = backend;
AiName = aiName;
AiNameShort = aiName.Split(' ')[0].Split('_')[0];
FriendlyName = friendlyName;
Description = desc;
PkgDir = pkgDir;
SupportedFactors = supportedFactors;
FactorSupport = factorSupport;
}
}
}

View File

@@ -4,28 +4,32 @@ namespace Flowframes.Data
{
class Implementations
{
public static AI rifeCuda = new AI(AI.Backend.Pytorch, "RIFE_CUDA", "RIFE",
"CUDA/Pytorch Implementation of RIFE (Nvidia Only!)", "rife-cuda", AI.FactorSupport.AnyInteger, new int[] { 2, 3, 4, 5, 6, 7, 8, 9, 10 });
public static AI rifeCuda = new AI(AI.AiBackend.Pytorch, "RIFE_CUDA", "RIFE",
"CUDA/Pytorch Implementation of RIFE (Nvidia Only!)", "rife-cuda", AI.InterpFactorSupport.AnyInteger, new int[] { 2, 3, 4, 5, 6, 7, 8, 9, 10 });
public static AI rifeNcnn = new AI(AI.Backend.Ncnn, "RIFE_NCNN", "RIFE (NCNN)",
"Vulkan/NCNN Implementation of RIFE", "rife-ncnn", AI.FactorSupport.AnyFloat, new int[] { 2, 3, 4, 5, 6, 7, 8, 9, 10 });
public static AI rifeNcnn = new AI(AI.AiBackend.Ncnn, "RIFE_NCNN", "RIFE (NCNN)",
"Vulkan/NCNN Implementation of RIFE", "rife-ncnn", AI.InterpFactorSupport.AnyFloat, new int[] { 2, 3, 4, 5, 6, 7, 8, 9, 10 });
public static AI flavrCuda = new AI(AI.Backend.Pytorch, "FLAVR_CUDA", "FLAVR",
"Experimental Pytorch Implementation of FLAVR (Nvidia Only!)", "flavr-cuda", AI.FactorSupport.Fixed, new int[] { 2, 4, 8 });
public static AI rifeNcnnVs = new AI(AI.AiBackend.Ncnn, "RIFE_NCNN_VS", "RIFE (NCNN/VS)",
"Vulkan/NCNN Implementation of RIFE in VS", "rife-ncnn-vs", AI.InterpFactorSupport.AnyFloat, new int[] { 2, 3, 4, 5, 6, 7, 8, 9, 10 })
{ Piped = true };
public static AI dainNcnn = new AI(AI.Backend.Ncnn, "DAIN_NCNN", "DAIN (NCNN)",
"Vulkan/NCNN Implementation of DAIN", "dain-ncnn", AI.FactorSupport.AnyFloat, new int[] { 2, 3, 4, 5, 6, 7, 8 });
public static AI flavrCuda = new AI(AI.AiBackend.Pytorch, "FLAVR_CUDA", "FLAVR",
"Experimental Pytorch Implementation of FLAVR (Nvidia Only!)", "flavr-cuda", AI.InterpFactorSupport.Fixed, new int[] { 2, 4, 8 });
public static AI xvfiCuda = new AI(AI.Backend.Pytorch, "XVFI_CUDA", "XVFI",
"CUDA/Pytorch Implementation of XVFI (Nvidia Only!)", "xvfi-cuda", AI.FactorSupport.AnyInteger, new int[] { 2, 3, 4, 5, 6, 7, 8, 9, 10 });
public static AI dainNcnn = new AI(AI.AiBackend.Ncnn, "DAIN_NCNN", "DAIN (NCNN)",
"Vulkan/NCNN Implementation of DAIN", "dain-ncnn", AI.InterpFactorSupport.AnyFloat, new int[] { 2, 3, 4, 5, 6, 7, 8 });
public static List<AI> networks = new List<AI> { rifeCuda, rifeNcnn, flavrCuda, dainNcnn, xvfiCuda };
public static AI xvfiCuda = new AI(AI.AiBackend.Pytorch, "XVFI_CUDA", "XVFI",
"CUDA/Pytorch Implementation of XVFI (Nvidia Only!)", "xvfi-cuda", AI.InterpFactorSupport.AnyInteger, new int[] { 2, 3, 4, 5, 6, 7, 8, 9, 10 });
public static List<AI> networks = new List<AI> { rifeCuda, rifeNcnn, rifeNcnnVs, flavrCuda, dainNcnn, xvfiCuda };
public static AI GetAi (string aiName)
{
foreach(AI ai in networks)
{
if (ai.aiName == aiName)
if (ai.AiName == aiName)
return ai;
}

View File

@@ -18,6 +18,7 @@ namespace Flowframes
{
public string inPath;
public string outPath;
public string FullOutPath { get; set; } = "";
public AI ai;
public Fraction inFps;
public Fraction inFpsDetected;
@@ -233,7 +234,7 @@ namespace Flowframes
{
string s = $"INPATH|{inPath}\n";
s += $"OUTPATH|{outPath}\n";
s += $"AI|{ai.aiName}\n";
s += $"AI|{ai.AiName}\n";
s += $"INFPSDETECTED|{inFpsDetected}\n";
s += $"INFPS|{inFps}\n";
s += $"OUTFPS|{outFps}\n";

View File

@@ -222,7 +222,7 @@ namespace Flowframes
public static string[] Split(this string str, string trimStr)
{
return str.Split(new string[] { trimStr }, StringSplitOptions.None);
return str?.Split(new string[] { trimStr }, StringSplitOptions.None);
}
public static bool MatchesWildcard(this string str, string wildcard)

View File

@@ -441,6 +441,7 @@
<Compile Include="Os\Python.cs" />
<Compile Include="Os\StartupChecks.cs" />
<Compile Include="Os\Updater.cs" />
<Compile Include="Os\VapourSynthUtils.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="MiscUtils\FormatUtils.cs" />

View File

@@ -1297,7 +1297,7 @@
this.scnDetectValue.DecimalPlaces = 2;
this.scnDetectValue.ForeColor = System.Drawing.Color.White;
this.scnDetectValue.Increment = new decimal(new int[] {
5,
1,
0,
0,
131072});
@@ -1308,7 +1308,7 @@
0,
65536});
this.scnDetectValue.Minimum = new decimal(new int[] {
5,
2,
0,
0,
131072});

View File

@@ -191,7 +191,7 @@ namespace Flowframes
inputTbox.Text = entry.inPath;
MainUiFunctions.SetOutPath(outputTbox, entry.outPath);
interpFactorCombox.Text = entry.interpFactor.ToString();
aiCombox.SelectedIndex = Implementations.networks.IndexOf(Implementations.networks.Where(x => x.aiName == entry.ai.aiName).FirstOrDefault());
aiCombox.SelectedIndex = Implementations.networks.IndexOf(Implementations.networks.Where(x => x.AiName == entry.ai.AiName).FirstOrDefault());
SetOutMode(entry.outMode);
}
@@ -273,20 +273,20 @@ namespace Flowframes
foreach (AI ai in Implementations.networks)
{
if (ai.backend == AI.Backend.Pytorch && !pytorchAvailable)
if (ai.Backend == AI.AiBackend.Pytorch && !pytorchAvailable)
{
Logger.Log($"AI implementation {ai.friendlyName} ({ai.backend}) has not been loaded because Pytorch was not found.", true);
Logger.Log($"AI implementation {ai.FriendlyName} ({ai.Backend}) has not been loaded because Pytorch was not found.", true);
continue;
}
aiCombox.Items.Add(ai.friendlyName + " - " + ai.description);
aiCombox.Items.Add(ai.FriendlyName + " - " + ai.Description);
}
string lastUsedAiName = Config.Get(Config.Key.lastUsedAiName);
aiCombox.SelectedIndex = Implementations.networks.IndexOf(Implementations.networks.Where(x => x.aiName == lastUsedAiName).FirstOrDefault());
aiCombox.SelectedIndex = Implementations.networks.IndexOf(Implementations.networks.Where(x => x.AiName == lastUsedAiName).FirstOrDefault());
if (aiCombox.SelectedIndex < 0) aiCombox.SelectedIndex = 0;
Config.Set(Config.Key.lastUsedAiName, GetAi().aiName);
Config.Set(Config.Key.lastUsedAiName, GetAi().AiName);
ConfigParser.LoadComboxIndex(outModeCombox);
}
@@ -466,16 +466,16 @@ namespace Flowframes
interpFactorCombox.Items.Clear();
foreach (int factor in GetAi().supportedFactors)
foreach (int factor in GetAi().SupportedFactors)
interpFactorCombox.Items.Add($"x{factor}");
interpFactorCombox.SelectedIndex = 0;
if (initialized)
Config.Set(Config.Key.lastUsedAiName, GetAi().aiName);
Config.Set(Config.Key.lastUsedAiName, GetAi().AiName);
interpFactorCombox_SelectedIndexChanged(null, null);
fpsOutTbox.ReadOnly = GetAi().factorSupport != AI.FactorSupport.AnyFloat;
fpsOutTbox.ReadOnly = GetAi().FactorSupport != AI.InterpFactorSupport.AnyFloat;
}
public void UpdateAiModelCombox()

View File

@@ -34,7 +34,7 @@ namespace Flowframes.Forms
InterpSettings entry = Program.batchQueue.ElementAt(i);
string niceOutMode = entry.outMode.ToString().ToUpper().Remove("VID").Remove("IMG");
string str = $"#{i+1}: {Path.GetFileName(entry.inPath).Trunc(40)} - {entry.inFps.GetFloat()} FPS => " +
$"{entry.interpFactor}x {entry.ai.aiNameShort} ({entry.model.name}) => {niceOutMode}";
$"{entry.interpFactor}x {entry.ai.AiNameShort} ({entry.model.name}) => {niceOutMode}";
taskList.Items.Add(str);
}
}

View File

@@ -578,7 +578,7 @@ namespace Flowframes.IO
filename = filename.Replace("[NAME]", inName);
filename = filename.Replace("[FULLNAME]", Path.GetFileName(curr.inPath));
filename = filename.Replace("[FACTOR]", curr.interpFactor.ToStringDot());
filename = filename.Replace("[AI]", curr.ai.aiNameShort.ToUpper());
filename = filename.Replace("[AI]", curr.ai.AiNameShort.ToUpper());
filename = filename.Replace("[MODEL]", curr.model.name.Remove(" "));
filename = filename.Replace("[FPS]", fps.ToStringDot());
filename = filename.Replace("[ROUNDFPS]", fps.RoundToInt().ToString());

View File

@@ -134,8 +134,8 @@ namespace Flowframes.IO
public static async Task DownloadModelFiles (AI ai, string modelDir, bool log = true)
{
string aiDir = ai.pkgDir;
Logger.Log($"DownloadModelFiles(string ai = {ai.aiName}, string model = {modelDir}, bool log = {log})", true);
string aiDir = ai.PkgDir;
Logger.Log($"DownloadModelFiles(string ai = {ai.AiName}, string model = {modelDir}, bool log = {log})", true);
try
{
@@ -198,7 +198,7 @@ namespace Flowframes.IO
foreach (AI ai in Implementations.networks)
{
string aiPkgFolder = Path.Combine(Paths.GetPkgPath(), ai.pkgDir);
string aiPkgFolder = Path.Combine(Paths.GetPkgPath(), ai.PkgDir);
ModelCollection aiModels = AiModels.GetModels(ai);
foreach(ModelCollection.ModelInfo model in aiModels.models)

View File

@@ -14,12 +14,12 @@ namespace Flowframes.Main
{
public static ModelCollection GetModels (AI ai)
{
string pkgPath = Path.Combine(Paths.GetPkgPath(), ai.pkgDir);
string pkgPath = Path.Combine(Paths.GetPkgPath(), ai.PkgDir);
string modelsFile = Path.Combine(pkgPath, "models.json");
if (!File.Exists(modelsFile))
{
Logger.Log($"Error: File models.json is missing for {ai.aiName}, can't load AI models for this implementation!");
Logger.Log($"Error: File models.json is missing for {ai.AiName}, can't load AI models for this implementation!");
return new ModelCollection(ai);
}
@@ -37,7 +37,7 @@ namespace Flowframes.Main
public static List<string> GetCustomModels(AI ai)
{
string pkgPath = Path.Combine(Paths.GetPkgPath(), ai.pkgDir);
string pkgPath = Path.Combine(Paths.GetPkgPath(), ai.PkgDir);
List<string> custModels = new List<string>();
foreach (DirectoryInfo dir in new DirectoryInfo(pkgPath).GetDirectories())

View File

@@ -36,10 +36,10 @@ namespace Flowframes.Main
safetyBufferFrames = 90;
if (Interpolate.current.ai.backend == AI.Backend.Ncnn)
if (Interpolate.current.ai.Backend == AI.AiBackend.Ncnn)
safetyBufferFrames = Config.GetInt(Config.Key.autoEncSafeBufferNcnn, 150);
if (Interpolate.current.ai.backend == AI.Backend.Pytorch)
if (Interpolate.current.ai.Backend == AI.AiBackend.Pytorch)
safetyBufferFrames = Config.GetInt(Config.Key.autoEncSafeBufferCuda, 90);
}

View File

@@ -81,7 +81,7 @@ namespace Flowframes.Main
string fname = Path.GetFileName(entry.inPath);
if (IoUtils.IsPathDirectory(entry.inPath)) fname = Path.GetDirectoryName(entry.inPath);
Logger.Log($"Queue: Processing {fname} ({entry.interpFactor}x {entry.ai.aiNameShort}).");
Logger.Log($"Queue: Processing {fname} ({entry.interpFactor}x {entry.ai.AiNameShort}).");
Program.mainForm.LoadBatchEntry(entry); // Load entry into GUI
Interpolate.current = entry;
@@ -92,7 +92,7 @@ namespace Flowframes.Main
Program.batchQueue.Dequeue();
Program.mainForm.SetWorking(false);
Logger.Log($"Queue: Done processing {fname} ({entry.interpFactor}x {entry.ai.aiNameShort}).");
Logger.Log($"Queue: Done processing {fname} ({entry.interpFactor}x {entry.ai.AiNameShort}).");
}
static void SetBusy(bool state)
@@ -120,7 +120,7 @@ namespace Flowframes.Main
return false;
}
if (IoUtils.GetAmountOfFiles(Path.Combine(Paths.GetPkgPath(), entry.ai.pkgDir), true) < 1)
if (IoUtils.GetAmountOfFiles(Path.Combine(Paths.GetPkgPath(), entry.ai.PkgDir), true) < 1)
{
Logger.Log("Queue: Can't process queue entry: Selected AI is not available.");
return false;

View File

@@ -189,6 +189,12 @@ namespace Flowframes.Main
}
}
public static async Task MuxPipedVideo (string inputVideo, string outputPath)
{
await MuxOutputVideo(inputVideo, Path.Combine(outputPath, outputPath));
await Loop(outputPath, await GetLoopTimes());
}
public static async Task ChunksToVideo(string tempFolder, string chunksFolder, string baseOutPath, bool isBackup = false)
{
if (IoUtils.GetAmountOfFiles(chunksFolder, true, "*" + FfmpegUtils.GetExt(I.current.outMode)) < 1)

View File

@@ -47,7 +47,7 @@ namespace Flowframes
Program.mainForm.SetStatus("Starting...");
sw.Restart();
if (!AutoEncodeResume.resumeNextRun)
if (!AutoEncodeResume.resumeNextRun && !current.ai.Piped)
{
await GetFrames();
if (canceled) return;
@@ -57,13 +57,17 @@ namespace Flowframes
if (canceled) return;
bool skip = await AutoEncodeResume.PrepareResumedRun();
if (skip || canceled) return;
//Task.Run(() => Utils.DeleteInterpolatedInputFrames());
await RunAi(current.interpFolder, current.ai);
if (canceled) return;
Program.mainForm.SetProgress(100);
if(!currentlyUsingAutoEnc)
await Export.ExportFrames(current.interpFolder, current.outPath, current.outMode, false);
if (!currentlyUsingAutoEnc)
{
if (current.ai.Piped)
await Export.MuxPipedVideo(current.inPath, current.FullOutPath);
else
await Export.ExportFrames(current.interpFolder, current.outPath, current.outMode, false);
}
if (!AutoEncodeResume.resumeNextRun && Config.GetBool(Config.Key.keepTempFolder))
await Task.Run(async () => { await FrameRename.Unrename(); });
@@ -71,7 +75,7 @@ namespace Flowframes
await Done();
}
public static async Task Done ()
public static async Task Done()
{
await Cleanup();
Program.mainForm.SetWorking(false);
@@ -84,7 +88,7 @@ namespace Flowframes
Program.mainForm.InterpolationDone();
}
public static async Task<int> GetCurrentInputFrameCount ()
public static async Task<int> GetCurrentInputFrameCount()
{
if (currentInputFrameCount < 2)
currentInputFrameCount = await GetFrameCountCached.GetFrameCountAsync(current.inPath);
@@ -92,7 +96,7 @@ namespace Flowframes
return currentInputFrameCount;
}
public static async Task GetFrames ()
public static async Task GetFrames()
{
current.RefreshAlpha();
current.RefreshExtensions(InterpSettings.FrameType.Import);
@@ -125,17 +129,17 @@ namespace Flowframes
float percentDeleted = ((float)framesDeleted / currentInputFrameCount) * 100f;
string keptPercent = $"{(100f - percentDeleted).ToString("0.0")}%";
if(QuickSettingsTab.trimEnabled)
if (QuickSettingsTab.trimEnabled)
Logger.Log($"Deduplication: Kept {framesLeft} frames.");
else
Logger.Log($"Deduplication: Kept {framesLeft} ({keptPercent}) frames, deleted {framesDeleted} frames.");
}
if(!Config.GetBool("allowConsecutiveSceneChanges", true))
if (!Config.GetBool("allowConsecutiveSceneChanges", true))
Utils.FixConsecutiveSceneFrames(Path.Combine(current.tempFolder, Paths.scenesDir), current.framesFolder);
}
public static async Task PostProcessFrames (bool stepByStep)
public static async Task PostProcessFrames(bool stepByStep)
{
if (canceled) return;
@@ -145,11 +149,11 @@ namespace Flowframes
if (!Directory.Exists(current.framesFolder) || currentInputFrameCount <= 0 || extractedFrames < 2)
{
if(extractedFrames == 1)
if (extractedFrames == 1)
Cancel("Only a single frame was extracted from your input file!\n\nPossibly your input is an image, not a video?");
else
Cancel($"Frame extraction failed!\nExtracted {extractedFrames} frames - current.framesFolder exists: {Directory.Exists(current.framesFolder)} - currentInputFrameCount = {currentInputFrameCount} - extractedFrames = {extractedFrames}.\n\nYour input file might be incompatible.");
}
}
if (Config.GetInt(Config.Key.dedupMode) == 1)
await Dedupe.Run(current.framesFolder);
@@ -175,12 +179,15 @@ namespace Flowframes
{
if (canceled) return;
await Task.Run(async () => { await Dedupe.CreateDupesFile(current.framesFolder, currentInputFrameCount, current.framesExt); });
await Task.Run(async () => { await FrameRename.Rename(); });
await Task.Run(async () => { await FrameOrder.CreateFrameOrderFile(current.framesFolder, Config.GetBool(Config.Key.enableLoop), current.interpFactor); });
if (!ai.Piped)
{
await Task.Run(async () => { await Dedupe.CreateDupesFile(current.framesFolder, currentInputFrameCount, current.framesExt); });
await Task.Run(async () => { await FrameRename.Rename(); });
await Task.Run(async () => { await FrameOrder.CreateFrameOrderFile(current.framesFolder, Config.GetBool(Config.Key.enableLoop), current.interpFactor); });
}
Program.mainForm.SetStatus("Downloading models...");
await ModelDownloader.DownloadModelFiles(ai, current.model.dir);
//await ModelDownloader.DownloadModelFiles(ai, current.model.dir);
if (canceled) return;
currentlyUsingAutoEnc = Utils.CanUseAutoEnc(stepByStep, current);
@@ -189,19 +196,22 @@ namespace Flowframes
List<Task> tasks = new List<Task>();
if (ai.aiName == Implementations.rifeCuda.aiName)
if (ai.AiName == Implementations.rifeCuda.AiName)
tasks.Add(AiProcess.RunRifeCuda(current.framesFolder, current.interpFactor, current.model.dir));
if (ai.aiName == Implementations.rifeNcnn.aiName)
if (ai.AiName == Implementations.rifeNcnn.AiName)
tasks.Add(AiProcess.RunRifeNcnn(current.framesFolder, outpath, current.interpFactor, current.model.dir));
if (ai.aiName == Implementations.flavrCuda.aiName)
if (ai.AiName == Implementations.rifeNcnnVs.AiName)
tasks.Add(AiProcess.RunRifeNcnnVs(current.framesFolder, outpath, current.interpFactor, current.model.dir));
if (ai.AiName == Implementations.flavrCuda.AiName)
tasks.Add(AiProcess.RunFlavrCuda(current.framesFolder, current.interpFactor, current.model.dir));
if (ai.aiName == Implementations.dainNcnn.aiName)
if (ai.AiName == Implementations.dainNcnn.AiName)
tasks.Add(AiProcess.RunDainNcnn(current.framesFolder, outpath, current.interpFactor, current.model.dir, Config.GetInt(Config.Key.dainNcnnTilesize, 512)));
if (ai.aiName == Implementations.xvfiCuda.aiName)
if (ai.AiName == Implementations.xvfiCuda.AiName)
tasks.Add(AiProcess.RunXvfiCuda(current.framesFolder, current.interpFactor, current.model.dir));
if (currentlyUsingAutoEnc)
@@ -227,10 +237,10 @@ namespace Flowframes
if (!current.stepByStep && !Config.GetBool(Config.Key.keepTempFolder))
{
if(!BatchProcessing.busy && IoUtils.GetAmountOfFiles(Path.Combine(current.tempFolder, Paths.resumeDir), true) > 0)
if (!BatchProcessing.busy && IoUtils.GetAmountOfFiles(Path.Combine(current.tempFolder, Paths.resumeDir), true) > 0)
{
DialogResult dialogResult = MessageBox.Show($"Delete the temp folder (Yes) or keep it for resuming later (No)?", "Delete temporary files?", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
Task.Run(async () => { await IoUtils.TryDeleteIfExistsAsync(current.tempFolder); });
}
@@ -261,7 +271,7 @@ namespace Flowframes
catch (Exception e)
{
Logger.Log("Cleanup Error: " + e.Message, true);
if(retriesLeft > 0)
if (retriesLeft > 0)
{
await Task.Delay(1000);
await Cleanup(ignoreKeepSetting, retriesLeft - 1, true);

View File

@@ -148,7 +148,7 @@ namespace Flowframes.Main
public static bool CheckAiAvailable(AI ai, ModelCollection.ModelInfo model)
{
if (IoUtils.GetAmountOfFiles(Path.Combine(Paths.GetPkgPath(), ai.pkgDir), true) < 1)
if (IoUtils.GetAmountOfFiles(Path.Combine(Paths.GetPkgPath(), ai.PkgDir), true) < 1)
{
UiUtils.ShowMessageBox("The selected AI is not installed!", UiUtils.MessageType.Error);
I.Cancel("Selected AI not available.", true);
@@ -162,7 +162,7 @@ namespace Flowframes.Main
return false;
}
if (I.current.ai.aiName.ToUpper().Contains("CUDA") && NvApi.gpuList.Count < 1)
if (I.current.ai.AiName.ToUpper().Contains("CUDA") && NvApi.gpuList.Count < 1)
{
UiUtils.ShowMessageBox("Warning: No Nvidia GPU was detected. CUDA might fall back to CPU!\n\nTry an NCNN implementation instead if you don't have an Nvidia GPU.", UiUtils.MessageType.Error);
@@ -286,6 +286,12 @@ namespace Flowframes.Main
{
AutoEncode.UpdateChunkAndBufferSizes();
if (current.ai.Piped)
{
Logger.Log($"Not Using AutoEnc: Using piped encoding.", true);
return false;
}
if (Config.GetInt(Config.Key.cmdDebugMode) > 0)
{
Logger.Log($"Not Using AutoEnc: CMD window is shown (cmdDebugMode > 0)", true);

View File

@@ -25,7 +25,7 @@ namespace Flowframes
public static int GetPadding ()
{
return (Interpolate.current.ai.aiName == Implementations.flavrCuda.aiName) ? 8 : 2; // FLAVR input needs to be divisible by 8
return (Interpolate.current.ai.AiName == Implementations.flavrCuda.AiName) ? 8 : 2; // FLAVR input needs to be divisible by 8
}
public static string GetPadFilter ()

View File

@@ -50,6 +50,7 @@ namespace Flowframes.Media
case Codec.ProRes: return "prores_ks";
case Codec.AviRaw: return Config.Get(Config.Key.aviCodec);
}
return "libx264";
}

View File

@@ -54,7 +54,7 @@ namespace Flowframes.MiscUtils
return;
ModelCollection.ModelInfo modelInfo = modelCollection.models[i];
form.SetStatus($"Downloading files for {modelInfo.ai.aiName.Replace("_", "-")}...");
form.SetStatus($"Downloading files for {modelInfo.ai.AiName.Replace("_", "-")}...");
await ModelDownloader.DownloadModelFiles(ai, modelInfo.dir, false);
taskCounter++;
UpdateProgressBar();

View File

@@ -11,6 +11,8 @@ using Flowframes.MiscUtils;
using System.Collections.Generic;
using ImageMagick;
using Paths = Flowframes.IO.Paths;
using Flowframes.Media;
using System.Drawing;
namespace Flowframes.Os
{
@@ -63,6 +65,18 @@ namespace Flowframes.Os
InterpolationProgress.GetProgressByFrameAmount(interpPath, target);
}
static void SetProgressCheck(int sourceFrames, float factor, string logFile)
{
int target = ((sourceFrames * factor) - (factor - 1)).RoundToInt();
InterpolationProgress.progressPaused = false;
InterpolationProgress.currentFactor = factor;
if (InterpolationProgress.progCheckRunning)
InterpolationProgress.targetFrames = target;
else
InterpolationProgress.GetProgressFromFfmpegLog(logFile, target);
}
static async Task AiFinished(string aiName)
{
if (Interpolate.canceled) return;
@@ -82,7 +96,7 @@ namespace Flowframes.Os
Logger.Log(logStr);
processTime.Stop();
if (interpFramesCount < 3)
if (!Interpolate.current.ai.Piped && interpFramesCount < 3)
{
string[] logLines = File.ReadAllLines(Path.Combine(Paths.GetLogPath(), lastLogName + ".txt"));
string log = string.Join("\n", logLines.Reverse().Take(10).Reverse().Select(x => x.Split("]: ").Last()).ToList());
@@ -120,7 +134,7 @@ namespace Flowframes.Os
try
{
string rifeDir = Path.Combine(Paths.GetPkgPath(), Implementations.rifeCuda.pkgDir);
string rifeDir = Path.Combine(Paths.GetPkgPath(), Implementations.rifeCuda.PkgDir);
string script = "rife.py";
if (!File.Exists(Path.Combine(rifeDir, script)))
@@ -164,7 +178,7 @@ namespace Flowframes.Os
Process rifePy = OsUtils.NewProcess(!OsUtils.ShowHiddenCmd());
AiStarted(rifePy, 3500);
SetProgressCheck(Path.Combine(Interpolate.current.tempFolder, outDir), interpFactor);
rifePy.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {Path.Combine(Paths.GetPkgPath(), Implementations.rifeCuda.pkgDir).Wrap()} & " +
rifePy.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {Path.Combine(Paths.GetPkgPath(), Implementations.rifeCuda.PkgDir).Wrap()} & " +
$"set CUDA_VISIBLE_DEVICES={Config.Get(Config.Key.torchGpus)} & {Python.GetPyCmd()} {script} {args}";
Logger.Log($"Running RIFE (CUDA){(await InterpolateUtils.UseUhd() ? " (UHD Mode)" : "")}...", false);
Logger.Log("cmd.exe " + rifePy.StartInfo.Arguments, true);
@@ -193,7 +207,7 @@ namespace Flowframes.Os
try
{
string flavDir = Path.Combine(Paths.GetPkgPath(), Implementations.flavrCuda.pkgDir);
string flavDir = Path.Combine(Paths.GetPkgPath(), Implementations.flavrCuda.PkgDir);
string script = "flavr.py";
if (!File.Exists(Path.Combine(flavDir, script)))
@@ -222,7 +236,7 @@ namespace Flowframes.Os
Process flavrPy = OsUtils.NewProcess(!OsUtils.ShowHiddenCmd());
AiStarted(flavrPy, 4000);
SetProgressCheck(Path.Combine(Interpolate.current.tempFolder, outDir), interpFactor);
flavrPy.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {Path.Combine(Paths.GetPkgPath(), Implementations.flavrCuda.pkgDir).Wrap()} & " +
flavrPy.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {Path.Combine(Paths.GetPkgPath(), Implementations.flavrCuda.PkgDir).Wrap()} & " +
$"set CUDA_VISIBLE_DEVICES={Config.Get(Config.Key.torchGpus)} & {Python.GetPyCmd()} {script} {args}";
Logger.Log($"Running FLAVR (CUDA)...", false);
Logger.Log("cmd.exe " + flavrPy.StartInfo.Arguments, true);
@@ -268,6 +282,7 @@ namespace Flowframes.Os
static async Task RunRifeNcnnProcess(string inPath, float factor, string outPath, string mdl)
{
Directory.CreateDirectory(outPath);
string logFileName = "rife-ncnn-log";
Process rifeNcnn = OsUtils.NewProcess(!OsUtils.ShowHiddenCmd());
AiStarted(rifeNcnn, 1500, inPath);
SetProgressCheck(outPath, factor);
@@ -277,15 +292,15 @@ namespace Flowframes.Os
string uhdStr = await InterpolateUtils.UseUhd() ? "-u" : "";
string ttaStr = Config.GetBool(Config.Key.rifeNcnnUseTta, false) ? "-x" : "";
rifeNcnn.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {Path.Combine(Paths.GetPkgPath(), Implementations.rifeNcnn.pkgDir).Wrap()} & rife-ncnn-vulkan.exe " +
rifeNcnn.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {Path.Combine(Paths.GetPkgPath(), Implementations.rifeNcnn.PkgDir).Wrap()} & rife-ncnn-vulkan.exe " +
$" -v -i {inPath.Wrap()} -o {outPath.Wrap()} {frames} -m {mdl.ToLower()} {ttaStr} {uhdStr} -g {Config.Get(Config.Key.ncnnGpus)} -f {GetNcnnPattern()} -j {GetNcnnThreads()}";
Logger.Log("cmd.exe " + rifeNcnn.StartInfo.Arguments, true);
if (!OsUtils.ShowHiddenCmd())
{
rifeNcnn.OutputDataReceived += (sender, outLine) => { LogOutput("[O] " + outLine.Data, "rife-ncnn-log"); };
rifeNcnn.ErrorDataReceived += (sender, outLine) => { LogOutput("[E] " + outLine.Data, "rife-ncnn-log", true); };
rifeNcnn.OutputDataReceived += (sender, outLine) => { LogOutput("[O] " + outLine.Data, logFileName); };
rifeNcnn.ErrorDataReceived += (sender, outLine) => { LogOutput("[E] " + outLine.Data, logFileName, true); };
}
rifeNcnn.Start();
@@ -299,6 +314,64 @@ namespace Flowframes.Os
while (!rifeNcnn.HasExited) await Task.Delay(1);
}
public static async Task RunRifeNcnnVs(string framesPath, string outPath, float factor, string mdl)
{
processTimeMulti.Restart();
try
{
bool uhd = await InterpolateUtils.UseUhd();
Logger.Log($"Running RIFE (NCNN-VS){(uhd ? " (UHD Mode)" : "")}...", false);
await RunRifeNcnnVsProcess(framesPath, factor, outPath, mdl, uhd);
//await DeleteNcnnDupes(outPath, factor);
}
catch (Exception e)
{
Logger.Log("Error running RIFE-NCNN-VS: " + e.Message);
Logger.Log("Stack Trace: " + e.StackTrace, true);
}
await AiFinished("RIFE");
}
static async Task RunRifeNcnnVsProcess(string inPath, float factor, string outPath, string mdl, bool uhd)
{
Directory.CreateDirectory(outPath);
string logFileName = "rife-ncnn-vs-log";
Process rifeNcnnVs = OsUtils.NewProcess(!OsUtils.ShowHiddenCmd());
AiStarted(rifeNcnnVs, 1500, inPath);
SetProgressCheck(Interpolate.currentInputFrameCount, factor, logFileName);
Interpolate.current.FullOutPath = Path.Combine(Interpolate.current.outPath, await IoUtils.GetCurrentExportFilename(false, true));
string encArgs = FfmpegUtils.GetEncArgs(FfmpegUtils.GetCodec(Interpolate.current.outMode), (Interpolate.current.ScaledResolution.IsEmpty ? Interpolate.current.InputResolution : Interpolate.current.ScaledResolution), Interpolate.current.outFps.GetFloat()).FirstOrDefault();
string ffmpegArgs = $"ffmpeg -y -i pipe: {encArgs} {Interpolate.current.FullOutPath.Wrap()}";
float scn = Config.GetBool(Config.Key.scnDetect) ? Config.GetFloat(Config.Key.scnDetectValue) : 0f;
Size res = InterpolateUtils.GetOutputResolution(Interpolate.current.InputResolution, true, true);
rifeNcnnVs.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {Path.Combine(Paths.GetPkgPath(), Implementations.rifeNcnnVs.PkgDir).Wrap()} & " +
$" vspipe {VapourSynthUtils.CreateScript(Interpolate.current, mdl, factor, res, uhd, scn).Wrap()} -c y4m - | {ffmpegArgs}";
Logger.Log("cmd.exe " + rifeNcnnVs.StartInfo.Arguments, true);
if (!OsUtils.ShowHiddenCmd())
{
rifeNcnnVs.OutputDataReceived += (sender, outLine) => { LogOutput("[O] " + outLine.Data, logFileName); };
rifeNcnnVs.ErrorDataReceived += (sender, outLine) => { LogOutput("[E] " + outLine.Data, logFileName, true); };
}
rifeNcnnVs.Start();
if (!OsUtils.ShowHiddenCmd())
{
rifeNcnnVs.BeginOutputReadLine();
rifeNcnnVs.BeginErrorReadLine();
}
while (!rifeNcnnVs.HasExited) await Task.Delay(1);
}
public static async Task RunDainNcnn(string framesPath, string outPath, float factor, string mdl, int tilesize)
{
if (Interpolate.currentlyUsingAutoEnc) // Ensure AutoEnc is not paused
@@ -320,7 +393,7 @@ namespace Flowframes.Os
public static async Task RunDainNcnnProcess(string framesPath, string outPath, float factor, string mdl, int tilesize)
{
string dainDir = Path.Combine(Paths.GetPkgPath(), Implementations.dainNcnn.pkgDir);
string dainDir = Path.Combine(Paths.GetPkgPath(), Implementations.dainNcnn.PkgDir);
Directory.CreateDirectory(outPath);
Process dain = OsUtils.NewProcess(!OsUtils.ShowHiddenCmd());
AiStarted(dain, 1500);
@@ -359,7 +432,7 @@ namespace Flowframes.Os
try
{
string xvfiDir = Path.Combine(Paths.GetPkgPath(), Implementations.xvfiCuda.pkgDir);
string xvfiDir = Path.Combine(Paths.GetPkgPath(), Implementations.xvfiCuda.PkgDir);
string script = "main.py";
if (!File.Exists(Path.Combine(xvfiDir, script)))
@@ -381,7 +454,7 @@ namespace Flowframes.Os
public static async Task RunXvfiCudaProcess(string inPath, string outDir, string script, float interpFactor, string mdlDir)
{
string pkgPath = Path.Combine(Paths.GetPkgPath(), Implementations.xvfiCuda.pkgDir);
string pkgPath = Path.Combine(Paths.GetPkgPath(), Implementations.xvfiCuda.PkgDir);
string basePath = inPath.GetParentDir();
string outPath = Path.Combine(basePath, outDir);
Directory.CreateDirectory(outPath);
@@ -474,7 +547,7 @@ namespace Flowframes.Os
if (!hasShownError && line.ToLower().Contains("error(s) in loading state_dict"))
{
hasShownError = true;
string msg = (Interpolate.current.ai.aiName == Implementations.flavrCuda.aiName) ? "\n\nFor FLAVR, you need to select the correct model for each scale!" : "";
string msg = (Interpolate.current.ai.AiName == Implementations.flavrCuda.AiName) ? "\n\nFor FLAVR, you need to select the correct model for each scale!" : "";
UiUtils.ShowMessageBox($"Error loading the AI model!\n\n{line}{msg}", UiUtils.MessageType.Error);
}
@@ -499,7 +572,7 @@ namespace Flowframes.Os
if (!hasShownError && err && line.Contains("vkAllocateMemory failed"))
{
hasShownError = true;
bool usingDain = (Interpolate.current.ai.aiName == Implementations.dainNcnn.aiName);
bool usingDain = (Interpolate.current.ai.AiName == Implementations.dainNcnn.AiName);
string msg = usingDain ? "\n\nTry reducing the tile size in the AI settings." : "\n\nTry a lower resolution (Settings -> Max Video Size).";
UiUtils.ShowMessageBox($"Vulkan ran out of memory!\n\n{line}{msg}", UiUtils.MessageType.Error);
}
@@ -581,10 +654,5 @@ namespace Flowframes.Os
}
}
}
static double Compare(string referenceImg, string compareImg)
{
return new MagickImage(referenceImg).Compare(new MagickImage(compareImg), ErrorMetric.PeakSignalToNoiseRatio);
}
}
}

View File

@@ -157,14 +157,14 @@ namespace Flowframes.Os
try
{
var client = new WebClient();
string aiName = ai.pkgDir;
string aiName = ai.PkgDir;
string url = $"https://raw.githubusercontent.com/n00mkrad/flowframes/main/Pkgs/{aiName}/models.txt";
string movePath = Path.Combine(Paths.GetPkgPath(), aiName, "models.txt");
string savePath = movePath + ".tmp";
if (!Directory.Exists(savePath.GetParentDir()))
{
Logger.Log($"Skipping {ai.pkgDir} models file download as '{savePath.GetParentDir()}' does not exist!", true);
Logger.Log($"Skipping {ai.PkgDir} models file download as '{savePath.GetParentDir()}' does not exist!", true);
continue;
}
@@ -185,7 +185,7 @@ namespace Flowframes.Os
}
catch (Exception e)
{
Logger.Log($"Failed to fetch models file for {ai.friendlyName}. Ignore this if you are not connected to the internet.");
Logger.Log($"Failed to fetch models file for {ai.FriendlyName}. Ignore this if you are not connected to the internet.");
Logger.Log($"{e.Message}\n{e.StackTrace}", true);
}
}

View File

@@ -0,0 +1,59 @@
using Flowframes.Data;
using Flowframes.IO;
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
{
public static string CreateScript(InterpSettings set, string modelDir, float factor, Size res, bool uhd, float sceneDetectSens = 0.15f, int gpuId = 0, int gpuThreads = 3, bool tta = false)
{
string inputPath = set.inPath;
bool resize = !set.ScaledResolution.IsEmpty && set.ScaledResolution != set.InputResolution;
bool sc = sceneDetectSens >= 0.01f;
string text = ""
+ $"import sys\n"
+ $"import vapoursynth as vs\n"
+ $"core = vs.core\n"
+ $"clip = core.ffms2.Source(source=r'{inputPath}')\n"
+ $"clip = core.resize.Bicubic(clip=clip, format=vs.RGBS, matrix_in_s=\"709\", range_s=\"limited\"{(resize ? $", width={set.ScaledResolution.Width}, height={set.ScaledResolution.Height}" : "")})\n"
+ $"{(sc ? $"clip = core.misc.SCDetect(clip=clip,threshold={sceneDetectSens.ToStringDot()})" : "# Scene detection disabled")}\n"
+ $"clip = core.rife.RIFE(clip, {GetModelNum(modelDir)}, {factor.ToStringDot()}, {gpuId}, {gpuThreads}, {tta}, {uhd}, {sc})\n"
+ $"clip = vs.core.resize.Bicubic(clip, format=vs.YUV444P16, matrix_s=\"709\")\n"
+ $"clip.set_output()\n";
string pkgPath = Path.Combine(Paths.GetPkgPath(), Implementations.rifeNcnnVs.PkgDir);
string vpyPath = Path.Combine(pkgPath, "rife.vpy");
File.WriteAllText(vpyPath, text);
return vpyPath;
}
private static int GetModelNum(string modelDir)
{
switch (modelDir)
{
case "rife": return 0;
case "rife-HD": return 1;
case "rife-UHD": return 2;
case "rife-anime": return 3;
case "rife-v2": return 4;
case "rife-v2.3": return 5;
case "rife-v2.4": return 6;
case "rife-v3.0": return 7;
case "rife-v3.1": return 8;
case "rife-v4": return 9;
}
return 9;
}
}
}

View File

@@ -28,17 +28,22 @@ namespace Flowframes.Ui
public static PictureBox preview;
public static BigPreviewForm bigPreviewForm;
public static async void GetProgressByFrameAmount(string outdir, int target)
public static void Restart ()
{
progCheckRunning = true;
deletedFramesCount = 0;
lastFrame = 0;
peakFpsOut = 0f;
Program.mainForm.SetProgress(0);
}
public static async void GetProgressByFrameAmount(string outdir, int target)
{
targetFrames = target;
currentOutdir = outdir;
Logger.Log($"Starting GetProgressByFrameAmount() loop for outdir '{currentOutdir}', target is {target} frames", true);
bool firstProgUpd = true;
Program.mainForm.SetProgress(0);
deletedFramesCount = 0;
lastFrame = 0;
peakFpsOut = 0f;
Restart();
while (Program.busy)
{
@@ -74,7 +79,7 @@ namespace Flowframes.Ui
{
try
{
string ncnnStr = I.current.ai.aiName.Contains("NCNN") ? " done" : "";
string ncnnStr = I.current.ai.AiName.Contains("NCNN") ? " done" : "";
Regex frameRegex = new Regex($@"(?<=.)\d*(?={I.current.interpExt}{ncnnStr})");
if (!frameRegex.IsMatch(output)) return;
lastFrame = Math.Max(int.Parse(frameRegex.Match(output).Value), lastFrame);
@@ -85,6 +90,40 @@ namespace Flowframes.Ui
}
}
public static async void GetProgressFromFfmpegLog(string logFile, int target)
{
targetFrames = target;
Logger.Log($"Starting GetProgressFromFfmpegLog() loop for log '{logFile}', target is {target} frames", true);
Restart();
while (Program.busy)
{
if (!progressPaused && AiProcess.processTime.IsRunning)
{
string lastLogLine = Logger.GetSessionLogLastLines(logFile, 3).Where(x => x.Contains("frame=")).LastOrDefault();
int num = lastLogLine == null ? 0 : lastLogLine.Split("frame=")[1].Split("fps=")[0].GetInt();
if(num > 0)
UpdateInterpProgress(num, targetFrames);
await Task.Delay(500);
if (num >= targetFrames)
break;
}
else
{
await Task.Delay(100);
}
}
progCheckRunning = false;
if (I.canceled)
Program.mainForm.SetProgress(0);
}
public static int interpolatedInputFramesCount;
public static float peakFpsOut;

View File

@@ -157,31 +157,31 @@ namespace Flowframes.Ui
{
AI ai = Program.mainForm.GetAi();
if (ai.aiName == Implementations.rifeNcnn.aiName && !Program.mainForm.GetModel(ai).dir.Contains("v4"))
if (ai.AiName == Implementations.rifeNcnn.AiName && !Program.mainForm.GetModel(ai).dir.Contains("v4"))
{
if (factor != 2)
Logger.Log($"{ai.friendlyName} models before 4.0 only support 2x interpolation!");
Logger.Log($"{ai.FriendlyName} models before 4.0 only support 2x interpolation!");
return 2;
}
if (ai.factorSupport == AI.FactorSupport.Fixed)
if (ai.FactorSupport == AI.InterpFactorSupport.Fixed)
{
int closest = ai.supportedFactors.Min(i => (Math.Abs(factor.RoundToInt() - i), i)).i;
int closest = ai.SupportedFactors.Min(i => (Math.Abs(factor.RoundToInt() - i), i)).i;
return (float)closest;
}
if(ai.factorSupport == AI.FactorSupport.AnyPowerOfTwo)
if(ai.FactorSupport == AI.InterpFactorSupport.AnyPowerOfTwo)
{
return ToNearestPow2(factor.RoundToInt()).Clamp(2, 128);
}
if(ai.factorSupport == AI.FactorSupport.AnyInteger)
if(ai.FactorSupport == AI.InterpFactorSupport.AnyInteger)
{
return factor.RoundToInt().Clamp(2, 128);
}
if(ai.factorSupport == AI.FactorSupport.AnyFloat)
if(ai.FactorSupport == AI.InterpFactorSupport.AnyFloat)
{
return factor.Clamp(2, 128);
}

View File

@@ -60,7 +60,7 @@ namespace Flowframes.Ui
}
catch (Exception e)
{
Logger.Log($"Failed to load available AI models for {ai.aiName}! {e.Message}");
Logger.Log($"Failed to load available AI models for {ai.AiName}! {e.Message}");
Logger.Log($"Stack Trace: {e.StackTrace}", true);
}