Scene Detect works with img seq output, better progress UI for chained interps

This commit is contained in:
N00MKRAD 2020-12-20 21:25:34 +01:00
parent 1cfb0bc866
commit 3a77cf422f
11 changed files with 138 additions and 76 deletions

View File

@ -224,7 +224,7 @@ namespace Flowframes
{
UpdateOutputFPS();
int guiInterpFactor = interpFactorCombox.GetInt();
if (guiInterpFactor > 2 && !GetAi().supportsAnyExp && Config.GetInt("autoEncMode") > 0)
if (!Program.busy && guiInterpFactor > 2 && !GetAi().supportsAnyExp && Config.GetInt("autoEncMode") > 0)
Logger.Log($"Warning: {GetAi().aiName.Replace("_", "-")} doesn't natively support 4x/8x and will run multiple times for {guiInterpFactor}x. Auto-Encode will only work on the last run.");
}

View File

@ -500,6 +500,25 @@ namespace Flowframes.IO
}
}
public static bool MoveTo(string file, string targetFolder, bool overwrite = true)
{
string targetPath = Path.Combine(targetFolder, Path.GetFileName(file));
try
{
if (!Directory.Exists(targetFolder))
Directory.CreateDirectory(targetFolder);
if (overwrite)
DeleteIfExists(targetPath);
File.Move(file, targetPath);
return true;
}
catch (Exception e)
{
Logger.Log($"Failed to move {file} to {targetFolder}: {e.Message}");
return false;
}
}
public enum Hash { MD5, CRC32 }
public static string GetHash (string filename, Hash hashType)
{
@ -539,7 +558,7 @@ namespace Flowframes.IO
ZeroPadDir(files.Select(x => x.FullName).ToList(), targetLength);
}
public static void ZeroPadDir(List<string> files, int targetLength, List<string> exclude = null)
public static void ZeroPadDir(List<string> files, int targetLength, List<string> exclude = null, bool noLog = true)
{
if(exclude != null)
files = files.Except(exclude).ToList();
@ -555,7 +574,8 @@ namespace Flowframes.IO
}
catch (Exception e)
{
Logger.Log($"Failed to zero-pad {file} => {targetFilename}: {e.Message}", true);
if(!noLog)
Logger.Log($"Failed to zero-pad {file} => {targetFilename}: {e.Message}", true);
}
}
}

View File

@ -214,7 +214,7 @@ namespace Flowframes.Magick
if (currentMode == Mode.None)
return;
string ext = InterpolateUtils.GetExt();
string ext = InterpolateUtils.GetOutExt();
string dupeInfoFile = Path.Combine(Interpolate.current.tempFolder, "dupes.ini");
if (!File.Exists(dupeInfoFile)) return;

View File

@ -50,8 +50,8 @@ namespace Flowframes.Main
continue;
}
IOUtils.ZeroPadDir(Directory.GetFiles(interpFramesFolder, $"*.{InterpolateUtils.GetExt()}").ToList(), Padding.interpFrames, encodedFrames);
string[] interpFrames = Directory.GetFiles(interpFramesFolder, $"*.{InterpolateUtils.GetExt()}");
IOUtils.ZeroPadDir(Directory.GetFiles(interpFramesFolder, $"*.{InterpolateUtils.GetOutExt()}").ToList(), Padding.interpFrames, encodedFrames);
string[] interpFrames = Directory.GetFiles(interpFramesFolder, $"*.{InterpolateUtils.GetOutExt()}");
unencodedFrames = interpFrames.ToList().Except(encodedFrames).ToList();
Directory.CreateDirectory(videoChunksFolder);
@ -117,7 +117,7 @@ namespace Flowframes.Main
static int GetInterpFramesAmount()
{
return IOUtils.GetAmountOfFiles(interpFramesFolder, false, $"*.{InterpolateUtils.GetExt()}");
return IOUtils.GetAmountOfFiles(interpFramesFolder, false, $"*.{InterpolateUtils.GetOutExt()}");
}
}
}

View File

@ -13,6 +13,7 @@ using System.Threading.Tasks;
using System.Windows.Forms;
using Padding = Flowframes.Data.Padding;
using i = Flowframes.Interpolate;
using System.Diagnostics;
namespace Flowframes.Main
{
@ -26,11 +27,7 @@ namespace Flowframes.Main
{
try
{
Logger.Log("Moving interpolated frames out of temp folder...");
string copyPath = Path.Combine(i.current.tempFolder.ReplaceLast("-temp", "-interpolated"));
Logger.Log($"{path} -> {copyPath}");
IOUtils.CreateDir(copyPath);
IOUtils.Copy(path, copyPath, true);
await CopyOutputFrames(path, Path.GetFileNameWithoutExtension(outPath));
}
catch(Exception e)
{
@ -39,7 +36,7 @@ namespace Flowframes.Main
return;
}
if (IOUtils.GetAmountOfFiles(path, false, $"*.{InterpolateUtils.GetExt()}") <= 1)
if (IOUtils.GetAmountOfFiles(path, false, $"*.{InterpolateUtils.GetOutExt()}") <= 1)
{
i.Cancel("Output folder does not contain frames - An error must have occured during interpolation!", AiProcess.hasShownError);
return;
@ -63,6 +60,44 @@ namespace Flowframes.Main
}
}
static async Task CopyOutputFrames (string framesPath, string folderName)
{
Program.mainForm.SetStatus("Copying output frames...");
string copyPath = Path.Combine(i.current.outPath, folderName);
Logger.Log($"Copying interpolated frames to '{copyPath}'");
IOUtils.TryDeleteIfExists(copyPath);
IOUtils.CreateDir(copyPath);
Stopwatch sw = new Stopwatch();
sw.Restart();
string vfrFile = Path.Combine(framesPath.GetParentDir(), $"vfr-{i.current.interpFactor}x.ini");
string[] vfrLines = IOUtils.ReadLines(vfrFile);
string currentInterpExt = $".{InterpolateUtils.GetOutExt()}";
vfrLines = vfrLines.Where(x => x.Contains(currentInterpExt)).ToArray(); // Remove duration lines, leaving only filename lines
for (int idx = 1; idx <= vfrLines.Length; idx++)
{
string line = vfrLines[idx-1];
string inFilename = line.Split('/').Last().Remove("'");
string framePath = Path.Combine(framesPath, inFilename);
string outFilename = Path.Combine(copyPath, idx.ToString().PadLeft(Padding.interpFrames, '0')) + InterpolateUtils.GetOutExt(true);
if ((idx < vfrLines.Length) && vfrLines[idx].Contains(inFilename)) // If file is re-used in the next line, copy instead of move
File.Copy(framePath, outFilename);
else
File.Move(framePath, outFilename);
if (sw.ElapsedMilliseconds >= 1000 || idx == vfrLines.Length)
{
sw.Restart();
Logger.Log($"Copying interpolated frames to '{Path.GetFileName(copyPath)}' - {idx}/{vfrLines.Length}", false, true);
await Task.Delay(1);
}
}
//IOUtils.ZeroPadDir(copyPath, "*", Padding.interpFrames);
}
static async Task Encode(i.OutMode mode, string framesPath, string outPath, float fps, float changeFps = -1, bool keepOriginalFpsVid = true)
{
currentOutFile = outPath;

View File

@ -30,7 +30,7 @@ namespace Flowframes.Main
Logger.Log("Timestamps are disabled, using static frame rate.");
bool sceneDetection = true;
string ext = InterpolateUtils.GetExt();
string ext = InterpolateUtils.GetOutExt();
FileInfo[] frameFiles = new DirectoryInfo(framesPath).GetFiles($"*.png");
string vfrFile = Path.Combine(framesPath.GetParentDir(), $"vfr-{interpFactor}x.ini");

View File

@ -53,19 +53,18 @@ namespace Flowframes
await Task.Delay(10);
await PostProcessFrames();
if (canceled) return;
string interpFramesDir = Path.Combine(current.tempFolder, Paths.interpDir);
int frames = IOUtils.GetAmountOfFiles(current.framesFolder, false, "*.png");
int targetFrameCount = frames * current.interpFactor;
GetProgressByFrameAmount(interpFramesDir, targetFrameCount);
Utils.GetProgressByFrameAmount(current.interpFolder, targetFrameCount);
if (canceled) return;
Program.mainForm.SetStatus("Running AI...");
await RunAi(interpFramesDir, targetFrameCount, current.tilesize, current.ai);
await RunAi(current.interpFolder, targetFrameCount, current.tilesize, current.ai);
if (canceled) return;
Program.mainForm.SetProgress(100);
if(!currentlyUsingAutoEnc)
await CreateVideo.Export(interpFramesDir, current.outFilename, current.outMode);
await CreateVideo.Export(current.interpFolder, current.outFilename, current.outMode);
IOUtils.ReverseRenaming(AiProcess.filenameMap, true); // Get timestamps back
Cleanup(interpFramesDir);
Cleanup(current.interpFolder);
Program.mainForm.SetWorking(false);
Logger.Log("Total processing time: " + FormatUtils.Time(sw.Elapsed));
sw.Stop();
@ -161,30 +160,6 @@ namespace Flowframes
await Task.WhenAll(tasks);
}
public static async void GetProgressByFrameAmount(string outdir, int target)
{
bool firstProgUpd = true;
Program.mainForm.SetProgress(0);
while (Program.busy)
{
if (AiProcess.processTime.IsRunning && Directory.Exists(outdir))
{
if (firstProgUpd && Program.mainForm.IsInFocus())
Program.mainForm.SetTab("preview");
firstProgUpd = false;
string[] frames = Directory.GetFiles(outdir, $"*.{Utils.GetExt()}");
if (frames.Length > 1)
Utils.UpdateInterpProgress(frames.Length, target, frames[frames.Length - 1]);
await Task.Delay(Utils.GetProgressWaitTime(frames.Length));
}
else
{
await Task.Delay(200);
}
}
Program.mainForm.SetProgress(-1);
}
public static void Cancel(string reason = "", bool noMsgBox = false)
{
if (AiProcess.currentAiProcess != null && !AiProcess.currentAiProcess.HasExited)
@ -196,6 +171,7 @@ namespace Flowframes
Program.mainForm.SetProgress(0);
if (Config.GetInt("processingMode") == 0 && !Config.GetBool("keepTempFolder"))
IOUtils.TryDeleteIfExists(current.tempFolder);
AutoEncode.busy = false;
Program.mainForm.SetWorking(false);
Program.mainForm.SetTab("interpolation");
if(!Logger.GetLastLine().Contains("Canceled interpolation."))

View File

@ -131,7 +131,7 @@ namespace Flowframes.Main
int frames = IOUtils.GetAmountOfFiles(current.framesFolder, false, "*.png");
int targetFrameCount = frames * current.interpFactor;
GetProgressByFrameAmount(current.interpFolder, targetFrameCount);
InterpolateUtils.GetProgressByFrameAmount(current.interpFolder, targetFrameCount);
if (canceled) return;
Program.mainForm.SetStatus("Running AI...");
int tilesize = current.ai.supportsTiling ? Config.GetInt($"tilesize_{current.ai.aiName}") : 512;
@ -141,7 +141,7 @@ namespace Flowframes.Main
public static async Task CreateOutputVid()
{
string[] outFrames = Directory.GetFiles(current.interpFolder, $"*.{InterpolateUtils.GetExt()}");
string[] outFrames = Directory.GetFiles(current.interpFolder, $"*.{InterpolateUtils.GetOutExt()}");
if (outFrames.Length > 0 && !IOUtils.CheckImageValid(outFrames[0]))
{
InterpolateUtils.ShowMessage("Invalid frame files detected!\n\nIf you used Auto-Encode, this is normal, and you don't need to run " +

View File

@ -9,6 +9,7 @@ using System.Drawing;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using i = Flowframes.Interpolate;
@ -19,27 +20,56 @@ namespace Flowframes.Main
public static PictureBox preview;
public static BigPreviewForm bigPreviewForm;
public static string GetExt ()
public static string GetOutExt (bool withDot = false)
{
string dotStr = withDot ? "." : "";
if (Config.GetBool("jpegInterps"))
return "jpg";
return "png";
return dotStr + "jpg";
return dotStr + "png";
}
public static int targetFrames;
public static int currentFactor;
public static async void GetProgressByFrameAmount(string outdir, int target)
{
bool firstProgUpd = true;
Program.mainForm.SetProgress(0);
targetFrames = target;
while (Program.busy)
{
if (AiProcess.processTime.IsRunning && Directory.Exists(outdir))
{
if (firstProgUpd && Program.mainForm.IsInFocus())
Program.mainForm.SetTab("preview");
firstProgUpd = false;
string[] frames = Directory.GetFiles(outdir, $"*.{GetOutExt()}");
if (frames.Length > 1)
UpdateInterpProgress(frames.Length, targetFrames, frames[frames.Length - 1]);
await Task.Delay(GetProgressWaitTime(frames.Length));
}
else
{
await Task.Delay(200);
}
}
Program.mainForm.SetProgress(-1);
}
public static void UpdateInterpProgress(int frames, int target, string latestFramePath = "")
{
frames = frames.Clamp(0, target);
int percent = (int)Math.Round(((float)frames / target) * 100f);
Program.mainForm.SetProgress(percent);
float generousTime = ((AiProcess.processTime.ElapsedMilliseconds - AiProcess.lastStartupTimeMs) / 1000f);
float fps = (float)frames / generousTime;
string fpsIn = (fps / i.current.interpFactor).ToString("0.00");
string fpsIn = (fps / currentFactor).ToString("0.00");
string fpsOut = fps.ToString("0.00");
float secondsPerFrame = generousTime / (float)frames;
int framesLeft = target - frames;
float eta = framesLeft * secondsPerFrame;
string etaStr = FormatUtils.Time(new TimeSpan(0, 0, eta.RoundToInt()));
string etaStr = FormatUtils.Time(new TimeSpan(0, 0, eta.RoundToInt()), false);
bool replaceLine = Regex.Split(Logger.textbox.Text, "\r\n|\r|\n").Last().Contains("Average Speed: ");
@ -50,7 +80,7 @@ namespace Flowframes.Main
try
{
if (!string.IsNullOrWhiteSpace(latestFramePath) && frames > i.current.interpFactor)
if (!string.IsNullOrWhiteSpace(latestFramePath) && frames > currentFactor)
{
if (bigPreviewForm == null && !preview.Visible /* ||Program.mainForm.WindowState != FormWindowState.Minimized */ /* || !Program.mainForm.IsInFocus()*/) return; // Skip if the preview is not visible or the form is not in focus
Image img = IOUtils.GetImage(latestFramePath);

View File

@ -23,20 +23,26 @@ namespace Flowframes
public static Stopwatch processTimeMulti = new Stopwatch();
public static int lastStartupTimeMs = 1000;
static string lastInPath;
public static Dictionary<string, string> filenameMap = new Dictionary<string, string>(); // TODO: Store on disk instead for crashes?
static void AiStarted (Process proc, int startupTimeMs)
static void AiStarted (Process proc, int startupTimeMs, int factor, string inPath = "")
{
lastStartupTimeMs = startupTimeMs;
processTime.Restart();
currentAiProcess = proc;
lastInPath = string.IsNullOrWhiteSpace(inPath) ? Interpolate.current.framesFolder : inPath;
int frames = IOUtils.GetAmountOfFiles(lastInPath, false, "*.png");
InterpolateUtils.currentFactor = factor;
InterpolateUtils.targetFrames = (frames * factor) - (factor - 1);
hasShownError = false;
}
static void AiFinished (string aiName)
{
Program.mainForm.SetProgress(100);
InterpolateUtils.UpdateInterpProgress(IOUtils.GetAmountOfFiles(Interpolate.current.interpFolder, false, "*.png"), InterpolateUtils.targetFrames);
string logStr = $"Done running {aiName} - Interpolation took {FormatUtils.Time(processTime.Elapsed)}";
if (Interpolate.currentlyUsingAutoEnc && AutoEncode.HasWorkToDo())
logStr += " - Waiting for encoding to finish...";
@ -50,8 +56,8 @@ namespace Flowframes
string dainDir = Path.Combine(Paths.GetPkgPath(), Path.GetFileNameWithoutExtension(Packages.dainNcnn.fileName));
Process dain = OSUtils.NewProcess(!OSUtils.ShowHiddenCmd());
AiStarted(dain, 1500);
dain.StartInfo.Arguments = $"{OSUtils.GetCmdArg()} cd /D {dainDir.Wrap()} & dain-ncnn-vulkan.exe {args} -f {InterpolateUtils.GetExt()} -j {GetNcnnThreads()}";
AiStarted(dain, 1500, Interpolate.current.interpFactor);
dain.StartInfo.Arguments = $"{OSUtils.GetCmdArg()} cd /D {dainDir.Wrap()} & dain-ncnn-vulkan.exe {args} -f {InterpolateUtils.GetOutExt()} -j {GetNcnnThreads()}";
Logger.Log("Running DAIN...", false);
Logger.Log("cmd.exe " + dain.StartInfo.Arguments, true);
if (!OSUtils.ShowHiddenCmd())
@ -71,7 +77,7 @@ namespace Flowframes
if (Interpolate.canceled) return;
if (!Interpolate.currentlyUsingAutoEnc)
IOUtils.ZeroPadDir(outPath, InterpolateUtils.GetExt(), Padding.interpFrames);
IOUtils.ZeroPadDir(outPath, InterpolateUtils.GetOutExt(), Padding.interpFrames);
AiFinished("DAIN");
}
@ -113,7 +119,7 @@ namespace Flowframes
if (Interpolate.canceled) return;
if (!Interpolate.currentlyUsingAutoEnc)
IOUtils.ZeroPadDir(outPath, InterpolateUtils.GetExt(), Padding.interpFrames);
IOUtils.ZeroPadDir(outPath, InterpolateUtils.GetOutExt(), Padding.interpFrames);
AiFinished("CAIN");
}
@ -123,8 +129,8 @@ namespace Flowframes
string cainDir = Path.Combine(Paths.GetPkgPath(), Path.GetFileNameWithoutExtension(Packages.cainNcnn.fileName));
string cainExe = "cain-ncnn-vulkan.exe";
Process cain = OSUtils.NewProcess(!OSUtils.ShowHiddenCmd());
AiStarted(cain, 1500);
cain.StartInfo.Arguments = $"{OSUtils.GetCmdArg()} cd /D {cainDir.Wrap()} & {cainExe} {args} -f {InterpolateUtils.GetExt()} -j {GetNcnnThreads()}";
AiStarted(cain, 1500, 2);
cain.StartInfo.Arguments = $"{OSUtils.GetCmdArg()} cd /D {cainDir.Wrap()} & {cainExe} {args} -f {InterpolateUtils.GetOutExt()} -j {GetNcnnThreads()}";
Logger.Log("cmd.exe " + cain.StartInfo.Arguments, true);
if (!OSUtils.ShowHiddenCmd())
{
@ -146,7 +152,7 @@ namespace Flowframes
string script = "inference_video.py";
bool uhd = IOUtils.GetVideoRes(Interpolate.current.inPath).Height >= Config.GetInt("uhdThresh");
string uhdStr = uhd ? "--UHD" : "";
string args = $" --img {framesPath.Wrap()} --exp {(int)Math.Log(interpFactor, 2)} {uhdStr} --imgformat {InterpolateUtils.GetExt()} --output {Paths.interpDir}";
string args = $" --img {framesPath.Wrap()} --exp {(int)Math.Log(interpFactor, 2)} {uhdStr} --imgformat {InterpolateUtils.GetOutExt()} --output {Paths.interpDir}";
if (!File.Exists(Path.Combine(rifeDir, script)))
{
@ -155,7 +161,7 @@ namespace Flowframes
}
Process rifePy = OSUtils.NewProcess(!OSUtils.ShowHiddenCmd());
AiStarted(rifePy, 3500);
AiStarted(rifePy, 3500, Interpolate.current.interpFactor);
rifePy.StartInfo.Arguments = $"{OSUtils.GetCmdArg()} cd /D {PkgUtils.GetPkgFolder(Packages.rifeCuda).Wrap()} & " +
$"set CUDA_VISIBLE_DEVICES={Config.Get("torchGpus")} & {Pytorch.GetPyCmd()} {script} {args}";
Logger.Log($"Running RIFE {(uhd ? "(UHD Mode)" : "")} ({script})...".TrimWhitespaces(), false);
@ -184,8 +190,7 @@ namespace Flowframes
if(times > 2)
AutoEncode.paused = true; // Disable autoenc until the last iteration
string args = $" -v -i {framesPath.Wrap()} -o {outPath.Wrap()}";
await RunRifePartial(args);
await RunRifePartial(framesPath, outPath);
if (times == 4 || times == 8) // #2
{
@ -197,8 +202,7 @@ namespace Flowframes
Directory.CreateDirectory(outPath);
if (useAutoEnc && times == 4)
AutoEncode.paused = false;
args = $" -v -i {run1ResultsPath.Wrap()} -o {outPath.Wrap()}";
await RunRifePartial(args);
await RunRifePartial(run1ResultsPath, outPath);
IOUtils.TryDeleteIfExists(run1ResultsPath);
}
@ -212,27 +216,24 @@ namespace Flowframes
Directory.CreateDirectory(outPath);
if (useAutoEnc && times == 8)
AutoEncode.paused = false;
args = $" -v -i {run2ResultsPath.Wrap()} -o {outPath.Wrap()}";
await RunRifePartial(args);
await RunRifePartial(run2ResultsPath, outPath);
IOUtils.TryDeleteIfExists(run2ResultsPath);
}
if (Interpolate.canceled) return;
if (!Interpolate.currentlyUsingAutoEnc)
{
Logger.Log($"zero padding {outPath} with ext \"{InterpolateUtils.GetExt()}\" to length {Padding.interpFrames}");
IOUtils.ZeroPadDir(outPath, InterpolateUtils.GetExt(), Padding.interpFrames);
}
IOUtils.ZeroPadDir(outPath, InterpolateUtils.GetOutExt(), Padding.interpFrames);
AiFinished("RIFE");
}
static async Task RunRifePartial(string args)
static async Task RunRifePartial(string inPath, string outPath)
{
Process rifeNcnn = OSUtils.NewProcess(!OSUtils.ShowHiddenCmd());
AiStarted(rifeNcnn, 1500);
rifeNcnn.StartInfo.Arguments = $"{OSUtils.GetCmdArg()} cd /D {PkgUtils.GetPkgFolder(Packages.rifeNcnn).Wrap()} & rife-ncnn-vulkan.exe {args} -g {Config.Get("ncnnGpus")} -f {InterpolateUtils.GetExt()} -j {GetNcnnThreads()}";
AiStarted(rifeNcnn, 1500, 2, inPath);
rifeNcnn.StartInfo.Arguments = $"{OSUtils.GetCmdArg()} cd /D {PkgUtils.GetPkgFolder(Packages.rifeNcnn).Wrap()} & rife-ncnn-vulkan.exe " +
$" -v -i {inPath.Wrap()} -o {outPath.Wrap()} -g {Config.Get("ncnnGpus")} -f {InterpolateUtils.GetOutExt()} -j {GetNcnnThreads()}";
Logger.Log("cmd.exe " + rifeNcnn.StartInfo.Arguments, true);
if (!OSUtils.ShowHiddenCmd())
{

View File

@ -34,7 +34,7 @@ namespace Flowframes.UI
return secs.ToString("0.00") + "s";
}
public static string Time (TimeSpan span)
public static string Time (TimeSpan span, bool allowMs = true)
{
if(span.TotalHours >= 1f)
return span.ToString(@"hh\:mm\:ss");
@ -42,10 +42,10 @@ namespace Flowframes.UI
if (span.TotalMinutes >= 1f)
return span.ToString(@"mm\:ss");
if (span.TotalSeconds >= 2f)
if (span.TotalSeconds >= 1f || !allowMs)
return span.ToString(@"ss") + "s";
return span.Milliseconds + "ms";
return span.ToString(@"fff") + "ms";
}
public static string TimeSw(Stopwatch sw)