diff --git a/Code/IO/IoUtils.cs b/Code/IO/IoUtils.cs index a0fe6b4..2dfc3f7 100644 --- a/Code/IO/IoUtils.cs +++ b/Code/IO/IoUtils.cs @@ -848,6 +848,9 @@ namespace Flowframes.IO public static FileInfo[] GetFileInfosSorted(string path, bool recursive = false, string pattern = "*") { + if(!Directory.Exists(path)) + return new FileInfo[0]; + SearchOption opt = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; DirectoryInfo dir = new DirectoryInfo(path); return dir.GetFiles(pattern, opt).OrderBy(x => x.Name).ToArray(); diff --git a/Code/Magick/Dedupe.cs b/Code/Magick/Dedupe.cs index 34b7f8c..e501032 100644 --- a/Code/Magick/Dedupe.cs +++ b/Code/Magick/Dedupe.cs @@ -8,6 +8,8 @@ using System.Threading; using Flowframes.IO; using ImageMagick; using Newtonsoft.Json; +using Flowframes.Os; +using System.Windows.Controls; namespace Flowframes.Magick { @@ -224,14 +226,14 @@ namespace Flowframes.Magick return GetDifference(GetImage(img1Path), GetImage(img2Path)); } - public static async Task CreateDupesFile(string framesPath, int lastFrameNum, string ext) + public static async Task CreateDupesFile(string framesPath, string ext) { bool debug = Config.GetBool("dupeScanDebug", false); FileInfo[] frameFiles = IoUtils.GetFileInfosSorted(framesPath, false, "*" + ext); if (debug) - Logger.Log($"Running CreateDupesFile for '{framesPath}' ({frameFiles.Length} files), lastFrameNum = {lastFrameNum}, ext = {ext}.", true, false, "dupes"); + Logger.Log($"Running CreateDupesFile for '{framesPath}' ({frameFiles.Length} files), ext = {ext}.", true, false, "dupes"); Dictionary> frames = new Dictionary>(); @@ -257,7 +259,55 @@ namespace Flowframes.Magick } } - File.WriteAllText(Path.Combine(framesPath.GetParentDir(), "dupes.json"), JsonConvert.SerializeObject(frames, Formatting.Indented)); + File.WriteAllText(Path.Combine(framesPath.GetParentDir(), "dupes.json"), frames.ToJson(true)); + } + + public static async Task CreateFramesFileVideo(string videoPath, bool loop) + { + if (!Directory.Exists(Interpolate.currentSettings.tempFolder)) + Directory.CreateDirectory(Interpolate.currentSettings.tempFolder); + + Process ffmpeg = OsUtils.NewProcess(true); + string baseCmd = $"/C cd /D {Path.Combine(IO.Paths.GetPkgPath(), IO.Paths.audioVideoDir).Wrap()}"; + string mpDec = FfmpegCommands.GetMpdecimate((int)FfmpegCommands.MpDecSensitivity.Normal, false); + ffmpeg.StartInfo.Arguments = $"{baseCmd} & ffmpeg -loglevel debug -y -i {videoPath.Wrap()} -fps_mode vfr -vf {mpDec} -f null NUL 2>&1 | findstr keep_count:"; + List ffmpegOutputLines = (await Task.Run(() => OsUtils.GetProcStdOut(ffmpeg, true))).SplitIntoLines().Where(l => l.IsNotEmpty()).ToList(); + + var frames = new Dictionary>(); + var frameNums = new List(); + int lastKeepFrameNum = 0; + + for (int frameIdx = 0; frameIdx < ffmpegOutputLines.Count; frameIdx++) + { + string line = ffmpegOutputLines[frameIdx]; + bool drop = frameIdx != 0 && line.Contains(" drop ") && !line.Contains(" keep "); + // Console.WriteLine($"[Frame {frameIdx.ToString().PadLeft(6, '0')}] {(drop ? "DROP" : "KEEP")}"); + // frameNums.Add(lastKeepFrameNum); + + if (!drop) + { + if (!frames.ContainsKey(frameIdx) || frames[frameIdx] == null) + { + frames[frameIdx] = new List(); + } + + lastKeepFrameNum = frameIdx; + } + else + { + frames[lastKeepFrameNum].Add(frameIdx); + } + } + + var inputFrames = new List(frames.Keys); + + if (loop) + { + inputFrames.Add(inputFrames.First()); + } + + File.WriteAllText(Path.Combine(Interpolate.currentSettings.tempFolder, "input.json"), inputFrames.ToJson(true)); + File.WriteAllText(Path.Combine(Interpolate.currentSettings.tempFolder, "dupes.test.json"), frames.ToJson(true)); } } } diff --git a/Code/Main/FrameOrder.cs b/Code/Main/FrameOrder.cs index cde2a4e..e7e3564 100644 --- a/Code/Main/FrameOrder.cs +++ b/Code/Main/FrameOrder.cs @@ -1,6 +1,8 @@ using Flowframes.Data; using Flowframes.IO; using Flowframes.MiscUtils; +using Flowframes.Os; +using Flowframes.Properties; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -13,25 +15,30 @@ namespace Flowframes.Main { class FrameOrder { - static Stopwatch benchmark = new Stopwatch(); - static FileInfo[] frameFiles; - static FileInfo[] frameFilesWithoutLast; - static List sceneFrames = new List(); - static Dictionary frameFileContents = new Dictionary(); - static List inputFilenames = new List(); - static int lastOutFileCount; + private static Stopwatch benchmark = new Stopwatch(); + private static FileInfo[] frameFiles; + private static FileInfo[] frameFilesWithoutLast; + private static List sceneFrames = new List(); + private static Dictionary frameFileContents = new Dictionary(); + private static List inputFilenames = new List(); + private static int lastOutFileCount; - public static async Task CreateFrameOrderFile(string framesPath, bool loopEnabled, float times) + public static async Task CreateFrameOrderFile(string tempFolder, bool loopEnabled, float interpFactor) { Logger.Log("Generating frame order information..."); try { - foreach (FileInfo file in IoUtils.GetFileInfosSorted(framesPath.GetParentDir(), false, $"{Paths.frameOrderPrefix}*.*")) + foreach (FileInfo file in IoUtils.GetFileInfosSorted(tempFolder, false, $"{Paths.frameOrderPrefix}*.*")) file.Delete(); benchmark.Restart(); - await CreateEncFile(framesPath, loopEnabled, times); + + if (Interpolate.currentSettings.ai.NameInternal == Implementations.rifeNcnnVs.NameInternal) + CreateFramesFileVid(Interpolate.currentSettings.inPath, Interpolate.currentSettings.tempFolder, loopEnabled, interpFactor); + else + await CreateFramesFileImgSeq(tempFolder, loopEnabled, interpFactor); + Logger.Log($"Generating frame order information... Done.", false, true); Logger.Log($"Generated frame order info file in {benchmark.ElapsedMilliseconds} ms", true); } @@ -39,36 +46,122 @@ namespace Flowframes.Main { Logger.Log($"Error generating frame order information: {e.Message}\n{e.StackTrace}"); } - } + } - static Dictionary> dupesDict = new Dictionary>(); + static Dictionary> dupesDict = new Dictionary>(); static void LoadDupesFile(string path) { dupesDict = JsonConvert.DeserializeObject>>(File.ReadAllText(path)); } - public static async Task CreateEncFile(string framesPath, bool loopEnabled, float interpFactor) + public static void CreateFramesFileVid(string vidPath, string tempFolder, bool loop, float interpFactor) { if (Interpolate.canceled) return; Logger.Log($"Generating frame order information for {interpFactor}x...", false, true); - bool loop = Config.GetBool(Config.Key.enableLoop); + // frameFileContents.Clear(); + int lastOutFileCount = 0; + + string inputJsonPath = Path.Combine(tempFolder, "input.json"); + List inputFrames = JsonConvert.DeserializeObject>(File.ReadAllText(inputJsonPath)); + + int frameCount = Interpolate.currentMediaFile.FrameCount; + frameCount = inputFrames.Count; + + // if (loop) + // { + // frameCount++; + // } + + int frameCountWithoutLast = frameCount - 1; + string dupesFile = Path.Combine(tempFolder, "dupes.test.json"); + var dupes = JsonConvert.DeserializeObject>>(File.ReadAllText(dupesFile)); + + bool debug = Config.GetBool("frameOrderDebug", false); + + int targetFrameCount = (frameCount * interpFactor).RoundToInt() - InterpolateUtils.GetRoundedInterpFramesPerInputFrame(interpFactor); + + Fraction step = new Fraction(frameCount, targetFrameCount + InterpolateUtils.GetRoundedInterpFramesPerInputFrame(interpFactor)); + + var framesList = new List(); + + for (int i = 0; i < targetFrameCount; i++) + { + float currentFrameTime = 1 + (step * i).GetFloat(); + int sourceFrameIdx = (int)Math.Floor(currentFrameTime) - 1; + + framesList.Add(i); + Console.WriteLine($"Frame: #{i} - Idx: {sourceFrameIdx} - [Time: {currentFrameTime}]"); + + if (sourceFrameIdx < dupes.Count) + { + bool last = i == lastOutFileCount; + + if (last && loop) + continue; + + for (int dupeNum = 0; dupeNum < dupes.ElementAt(sourceFrameIdx).Value.Count; dupeNum++) + { + framesList.Add(framesList.Last()); + Console.WriteLine($"Frame: #{i} - Idx: {sourceFrameIdx} - (Dupe {dupeNum + 1}/{dupes.ElementAt(sourceFrameIdx).Value.Count})"); + } + } + } + + // if (loop) + // { + // framesList.Add(framesList.First()); + // } + + //for (int x = 0; x < frameFileContents.Count; x++) + // fileContent += frameFileContents[x]; + + lastOutFileCount++; + + if (Config.GetBool(Config.Key.fixOutputDuration)) // Match input duration by padding duping last frame until interp frames == (inputframes * factor) + { + int neededFrames = (frameCount * interpFactor).RoundToInt() - framesList.Count; + + for (int i = 0; i < neededFrames; i++) + framesList.Add(framesList.Last()); + } + + if (loop) + framesList.RemoveAt(framesList.Count() - 1); + + string framesFileVs = Path.Combine(tempFolder, "frames.vs.json"); + // List frameNums = new List(); + // + // foreach (string line in fileContent.SplitIntoLines().Where(x => x.StartsWith("file "))) + // frameNums.Add(line.Split('/')[1].Split('.')[0].GetInt() - 1); // Convert filename to 0-indexed number + + File.WriteAllText(framesFileVs, JsonConvert.SerializeObject(framesList, Formatting.Indented)); + } + + public static async Task CreateFramesFileImgSeq(string tempFolder, bool loop, float interpFactor) + { + // await CreateFramesFileVideo(Interpolate.currentSettings.inPath, loop, interpFactor); + + if (Interpolate.canceled) return; + Logger.Log($"Generating frame order information for {interpFactor}x...", false, true); + bool sceneDetection = true; string ext = Interpolate.currentSettings.interpExt; frameFileContents.Clear(); lastOutFileCount = 0; - frameFiles = new DirectoryInfo(framesPath).GetFiles("*" + Interpolate.currentSettings.framesExt); + string framesDir = Path.Combine(tempFolder, Paths.framesDir); + frameFiles = new DirectoryInfo(framesDir).GetFiles("*" + Interpolate.currentSettings.framesExt); frameFilesWithoutLast = frameFiles; Array.Resize(ref frameFilesWithoutLast, frameFilesWithoutLast.Length - 1); - string framesFile = Path.Combine(framesPath.GetParentDir(), Paths.GetFrameOrderFilename(interpFactor)); + string framesFile = Path.Combine(tempFolder, Paths.GetFrameOrderFilename(interpFactor)); string fileContent = ""; - string dupesFile = Path.Combine(framesPath.GetParentDir(), "dupes.json"); + string dupesFile = Path.Combine(tempFolder, "dupes.json"); LoadDupesFile(dupesFile); - string scnFramesPath = Path.Combine(framesPath.GetParentDir(), Paths.scenesDir); + string scnFramesPath = Path.Combine(tempFolder, Paths.scenesDir); sceneFrames.Clear(); @@ -76,7 +169,7 @@ namespace Flowframes.Main sceneFrames = Directory.GetFiles(scnFramesPath).Select(file => GetNameNoExt(file)).ToList(); inputFilenames.Clear(); - bool debug = Config.GetBool("frameOrderDebug", false); + bool debug = true; // Config.GetBool("frameOrderDebug", false); List tasks = new List(); int linesPerTask = (400 / interpFactor).RoundToInt(); int num = 0; @@ -106,7 +199,7 @@ namespace Flowframes.Main if (Config.GetBool(Config.Key.fixOutputDuration)) // Match input duration by padding duping last frame until interp frames == (inputframes * factor) { int neededFrames = (frameFiles.Length * interpFactor).RoundToInt() - fileContent.SplitIntoLines().Where(x => x.StartsWith("'file ")).Count(); - + for (int i = 0; i < neededFrames; i++) fileContent += fileContent.SplitIntoLines().Where(x => x.StartsWith("'file ")).Last(); } @@ -117,7 +210,7 @@ namespace Flowframes.Main File.WriteAllText(framesFile, fileContent); File.WriteAllText(framesFile + ".inputframes.json", JsonConvert.SerializeObject(inputFilenames, Formatting.Indented)); - string framesFileVs = Path.Combine(framesPath.GetParentDir(), "frames.vs.json"); + string framesFileVs = Path.Combine(tempFolder, "frames.vs.json"); List frameNums = new List(); foreach (string line in fileContent.SplitIntoLines().Where(x => x.StartsWith("file "))) @@ -194,6 +287,8 @@ namespace Flowframes.Main string inputFilenameFrom = frameFiles[sourceFrameIdx].Name; string inputFilenameTo = (sourceFrameIdx + 1 >= frameFiles.Length) ? "" : frameFiles[sourceFrameIdx + 1].Name; string inputFilenameToNext = (sourceFrameIdx + 2 >= frameFiles.Length) ? "" : frameFiles[sourceFrameIdx + 2].Name; + + Console.WriteLine($"Frame: Idx {sourceFrameIdx} - {(sceneChange && !blendSceneChances ? lastUndiscardFrame : filename)}"); lines.Add(new FrameFileLine(sceneChange && !blendSceneChances ? lastUndiscardFrame : filename, inputFilenameFrom, inputFilenameTo, inputFilenameToNext, timestep, sceneChange)); string inputFilenameNoExtRenamed = Path.GetFileNameWithoutExtension(FrameRename.importFilenames[sourceFrameIdx]); @@ -202,7 +297,11 @@ namespace Flowframes.Main continue; foreach (string s in dupesDict[inputFilenameNoExtRenamed]) - lines.Add(new FrameFileLine(sceneChange && !blendSceneChances ? lastUndiscardFrame : filename, inputFilenameFrom, inputFilenameTo, inputFilenameToNext, timestep, sceneChange)); + { + string fname = sceneChange && !blendSceneChances ? lastUndiscardFrame : filename; + Console.WriteLine($"Frame: Idx {sourceFrameIdx} - Dupe {dupesDict[inputFilenameNoExtRenamed].IndexOf(s)}/{dupesDict[inputFilenameNoExtRenamed].Count} {fname}"); + lines.Add(new FrameFileLine(fname, inputFilenameFrom, inputFilenameTo, inputFilenameToNext, timestep, sceneChange)); + } } if (totalFileCount > lastOutFileCount) diff --git a/Code/Main/Interpolate.cs b/Code/Main/Interpolate.cs index a28f9ae..5db68e7 100644 --- a/Code/Main/Interpolate.cs +++ b/Code/Main/Interpolate.cs @@ -45,7 +45,7 @@ namespace Flowframes Program.mainForm.SetStatus("Starting..."); sw.Restart(); - if (!AutoEncodeResume.resumeNextRun && !(currentSettings.ai.Piped && !currentSettings.inputIsFrames && Config.GetInt(Config.Key.dedupMode) == 0)) + if (!AutoEncodeResume.resumeNextRun && !(currentSettings.ai.Piped && !currentSettings.inputIsFrames /* && Config.GetInt(Config.Key.dedupMode) == 0) */)) { await GetFrames(); if (canceled) return; @@ -180,7 +180,7 @@ namespace Flowframes if (!Config.GetBool(Config.Key.enableLoop)) { - await Utils.CopyLastFrame(currentMediaFile.FrameCount); + // await Utils.CopyLastFrame(currentMediaFile.FrameCount); } else { @@ -199,14 +199,18 @@ namespace Flowframes bool dedupe = Config.GetInt(Config.Key.dedupMode) != 0; - if (!ai.Piped || (ai.Piped && currentSettings.inputIsFrames) || (ai.Piped && dedupe)) + if (!ai.Piped || (ai.Piped && currentSettings.inputIsFrames)) { - await Task.Run(async () => { await Dedupe.CreateDupesFile(currentSettings.framesFolder, currentMediaFile.FrameCount, currentSettings.framesExt); }); + await Task.Run(async () => { await Dedupe.CreateDupesFile(currentSettings.framesFolder, currentSettings.framesExt); }); await Task.Run(async () => { await FrameRename.Rename(); }); } + else if (ai.Piped && dedupe) + { + await Task.Run(async () => { await Dedupe.CreateFramesFileVideo(currentSettings.inPath, Config.GetBool(Config.Key.enableLoop)); }); + } if (!ai.Piped || (ai.Piped && dedupe)) - await Task.Run(async () => { await FrameOrder.CreateFrameOrderFile(currentSettings.framesFolder, Config.GetBool(Config.Key.enableLoop), currentSettings.interpFactor); }); + await Task.Run(async () => { await FrameOrder.CreateFrameOrderFile(currentSettings.tempFolder, Config.GetBool(Config.Key.enableLoop), currentSettings.interpFactor); }); if (currentSettings.model.FixedFactors.Count() > 0 && (currentSettings.interpFactor != (int)currentSettings.interpFactor || !currentSettings.model.FixedFactors.Contains(currentSettings.interpFactor.RoundToInt()))) Cancel($"The selected model does not support {currentSettings.interpFactor}x interpolation.\n\nSupported Factors: {currentSettings.model.GetFactorsString()}"); diff --git a/Code/Os/VapourSynthUtils.cs b/Code/Os/VapourSynthUtils.cs index 929d85f..7b17d73 100644 --- a/Code/Os/VapourSynthUtils.cs +++ b/Code/Os/VapourSynthUtils.cs @@ -7,8 +7,6 @@ using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Flowframes.Os { @@ -58,7 +56,7 @@ namespace Flowframes.Os l.Add($"inputPath = r'{inputPath}'"); l.Add($""); - bool loadFrames = s.InterpSettings.inputIsFrames || (s.Dedupe && !s.Realtime); + bool loadFrames = s.InterpSettings.inputIsFrames; if (loadFrames) { @@ -74,6 +72,8 @@ namespace Flowframes.Os l.Add($"if os.path.isdir(r'{s.InterpSettings.tempFolder}'):"); l.Add($" indexFilePath = r'{Path.Combine(s.InterpSettings.tempFolder, "cache.lwi")}'"); l.Add($"clip = core.lsmas.LWLibavSource(inputPath, cachefile=indexFilePath)"); // Load video with lsmash + l.Add("clip = core.text.FrameNum(clip, alignment=7)"); + l.Add(GetDedupeLines(s)); } if (trim) @@ -187,6 +187,30 @@ namespace Flowframes.Os return s; } + static string GetDedupeLines(VsSettings settings) + { + string s = ""; + + string inputJsonPath = Path.Combine(settings.InterpSettings.tempFolder, "input.json"); + + if (!File.Exists(inputJsonPath)) + return s; + + s += "reorderedClip = clip[0]\n"; + s += "\n"; + s += $"with open(r'{inputJsonPath}') as json_file:\n"; + s += " frameList = json.load(json_file)\n"; + s += " \n"; + s += " for i in frameList:\n"; + s += " reorderedClip = reorderedClip + clip[i]\n"; + s += "\n"; + s += "clip = reorderedClip.std.Trim(1, reorderedClip.num_frames - 1)\n"; + s += "clip = core.text.FrameNum(clip, alignment=4)\n"; + s += "\n"; + + return s; + } + static string GetRedupeLines(VsSettings settings) { string s = ""; @@ -201,6 +225,7 @@ namespace Flowframes.Os s += " reorderedClip = reorderedClip + clip[i]\n"; s += "\n"; s += "clip = reorderedClip.std.Trim(1, reorderedClip.num_frames - 1)\n"; + s += "clip = core.text.FrameNum(clip, alignment=1)\n"; s += "\n"; return s;