Support deduplication without frame extraction with VS (WIP)

This commit is contained in:
N00MKRAD
2024-01-09 13:31:02 +01:00
parent f1a91a4a53
commit b6293a1940
5 changed files with 214 additions and 33 deletions

View File

@@ -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();

View File

@@ -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<string, List<string>> frames = new Dictionary<string, List<string>>();
@@ -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<string> ffmpegOutputLines = (await Task.Run(() => OsUtils.GetProcStdOut(ffmpeg, true))).SplitIntoLines().Where(l => l.IsNotEmpty()).ToList();
var frames = new Dictionary<int, List<int>>();
var frameNums = new List<int>();
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<int>();
}
lastKeepFrameNum = frameIdx;
}
else
{
frames[lastKeepFrameNum].Add(frameIdx);
}
}
var inputFrames = new List<int>(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));
}
}
}

View File

@@ -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<string> sceneFrames = new List<string>();
static Dictionary<int, string> frameFileContents = new Dictionary<int, string>();
static List<string> inputFilenames = new List<string>();
static int lastOutFileCount;
private static Stopwatch benchmark = new Stopwatch();
private static FileInfo[] frameFiles;
private static FileInfo[] frameFilesWithoutLast;
private static List<string> sceneFrames = new List<string>();
private static Dictionary<int, string> frameFileContents = new Dictionary<int, string>();
private static List<string> inputFilenames = new List<string>();
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<string, List<string>> dupesDict = new Dictionary<string, List<string>>();
static Dictionary<string, List<string>> dupesDict = new Dictionary<string, List<string>>();
static void LoadDupesFile(string path)
{
dupesDict = JsonConvert.DeserializeObject<Dictionary<string, List<string>>>(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<int> inputFrames = JsonConvert.DeserializeObject<List<int>>(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<Dictionary<int, List<int>>>(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<int>();
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<int> frameNums = new List<int>();
//
// 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<Task> tasks = new List<Task>();
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<int> frameNums = new List<int>();
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)

View File

@@ -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()}");

View File

@@ -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;