mirror of
https://github.com/n00mkrad/flowframes
synced 2024-09-29 14:38:03 +02:00
mpdecimate dedupe now works with new encoding, fixed autoenc+dedupe
This commit is contained in:
parent
0160ca2240
commit
f57b256a93
@ -28,7 +28,7 @@ namespace Flowframes
|
||||
public static async Task ExtractSceneChanges(string inputFile, string frameFolderPath)
|
||||
{
|
||||
Logger.Log("Extracting scene changes...");
|
||||
await VideoToFrames(inputFile, frameFolderPath, false, false, new Size(320, 180), true, true);
|
||||
await VideoToFrames(inputFile, frameFolderPath, false, false, new Size(320, 180), false, true);
|
||||
bool hiddenLog = Interpolate.currentInputFrameCount <= 50;
|
||||
Logger.Log($"Detected {IOUtils.GetAmountOfFiles(frameFolderPath, false)} scene changes.".Replace(" 0 ", " no "), false, !hiddenLog);
|
||||
}
|
||||
@ -39,7 +39,7 @@ namespace Flowframes
|
||||
}
|
||||
|
||||
//public enum TimecodeMode { None, Consecutive, Realtime }
|
||||
public static async Task VideoToFrames(string inputFile, string frameFolderPath, bool deDupe, bool delSrc, Size size, bool timecodes = true, bool sceneDetect = false)
|
||||
public static async Task VideoToFrames(string inputFile, string frameFolderPath, bool deDupe, bool delSrc, Size size, bool timecodes, bool sceneDetect = false)
|
||||
{
|
||||
if (!sceneDetect) Logger.Log("Extracting video frames from input video...");
|
||||
string sizeStr = (size.Width > 1 && size.Height > 1) ? $"-s {size.Width}x{size.Height}" : "";
|
||||
|
@ -267,7 +267,7 @@
|
||||
<Compile Include="IO\Paths.cs" />
|
||||
<Compile Include="IO\Logger.cs" />
|
||||
<Compile Include="Magick\Converter.cs" />
|
||||
<Compile Include="Magick\MagickDedupe.cs" />
|
||||
<Compile Include="Magick\Dedupe.cs" />
|
||||
<Compile Include="OS\NvApi.cs" />
|
||||
<Compile Include="OS\OSUtils.cs" />
|
||||
<Compile Include="OS\Pytorch.cs" />
|
||||
|
@ -3,12 +3,8 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Flowframes.IO;
|
||||
using Flowframes.Main;
|
||||
using Flowframes.UI;
|
||||
using Microsoft.VisualBasic.Devices;
|
||||
using ImageMagick;
|
||||
using Flowframes.OS;
|
||||
using Flowframes.Data;
|
||||
@ -16,7 +12,7 @@ using System.Drawing;
|
||||
|
||||
namespace Flowframes.Magick
|
||||
{
|
||||
class MagickDedupe
|
||||
class Dedupe
|
||||
{
|
||||
public enum Mode { None, Info, Enabled, Auto }
|
||||
public static Mode currentMode;
|
||||
@ -220,5 +216,27 @@ namespace Flowframes.Magick
|
||||
Logger.Log($"Using magick dedupe buffer size {bufferSize} for frame resolution {res.Width}x{res.Height}", true);
|
||||
return bufferSize;
|
||||
}
|
||||
|
||||
public static async Task CreateDupesFileMpdecimate (string framesPath, int lastFrameNum)
|
||||
{
|
||||
string infoFile = Path.Combine(framesPath.GetParentDir(), $"dupes.ini");
|
||||
string fileContent = "";
|
||||
|
||||
FileInfo[] frameFiles = IOUtils.GetFileInfosSorted(framesPath, false, "*.png");
|
||||
|
||||
for(int i = 0; i < frameFiles.Length; i++)
|
||||
{
|
||||
bool isLastItem = (i + 1) == frameFiles.Length;
|
||||
int frameNum1 = frameFiles[i].Name.GetInt();
|
||||
int frameNum2 = isLastItem ? lastFrameNum : frameFiles[i+1].Name.GetInt();
|
||||
|
||||
int diff = frameNum2 - frameNum1;
|
||||
int dupes = diff - 1;
|
||||
|
||||
fileContent += $"{Path.GetFileNameWithoutExtension(frameFiles[i].Name)}:{dupes}\n";
|
||||
}
|
||||
|
||||
File.WriteAllText(infoFile, fileContent);
|
||||
}
|
||||
}
|
||||
}
|
@ -16,8 +16,9 @@ namespace Flowframes.Main
|
||||
static string videoChunksFolder;
|
||||
public static int chunkSize = 125; // Encode every n frames
|
||||
public static int safetyBufferFrames = 50; // Ignore latest n frames to avoid using images that haven't been fully encoded yet
|
||||
public static List<string> encodedFrames = new List<string>();
|
||||
public static List<string> unencodedFrames = new List<string>();
|
||||
public static string[] interpFramesLines;
|
||||
public static List<int> encodedFrameLines = new List<int>();
|
||||
public static List<int> unencodedFrameLines = new List<int>();
|
||||
|
||||
public static bool busy;
|
||||
|
||||
@ -29,14 +30,16 @@ namespace Flowframes.Main
|
||||
interpFramesFolder = interpFramesPath;
|
||||
videoChunksFolder = Path.Combine(interpFramesPath.GetParentDir(), Paths.chunksDir);
|
||||
|
||||
encodedFrames.Clear();
|
||||
unencodedFrames.Clear();
|
||||
encodedFrameLines.Clear();
|
||||
unencodedFrameLines.Clear();
|
||||
|
||||
chunkSize = GetChunkSize(IOUtils.GetAmountOfFiles(Interpolate.current.framesFolder, false, "*.png") * Interpolate.current.interpFactor);
|
||||
safetyBufferFrames = Interpolate.current.ai.aiName.ToUpper().Contains("NCNN") ? 60 : 30; // Use bigger safety buffer for NCNN
|
||||
Logger.Log($"Starting AutoEncode MainLoop - Chunk Size: {chunkSize} Frames - Safety Buffer: {safetyBufferFrames} Frames", true);
|
||||
|
||||
int videoIndex = 1;
|
||||
string encFile = Path.Combine(interpFramesPath.GetParentDir(), $"vfr-{Interpolate.current.interpFactor}x.ini");
|
||||
interpFramesLines = IOUtils.ReadLines(encFile).Select(x => x.Split('/').Last().Remove("'")).ToArray(); // Array with frame filenames
|
||||
|
||||
while (!Interpolate.canceled && GetInterpFramesAmount() < 2)
|
||||
await Task.Delay(1000);
|
||||
@ -51,36 +54,47 @@ namespace Flowframes.Main
|
||||
continue;
|
||||
}
|
||||
|
||||
IOUtils.ZeroPadDir(Directory.GetFiles(interpFramesFolder, $"*.{InterpolateUtils.GetOutExt()}").ToList(), Padding.interpFrames, encodedFrames);
|
||||
string[] interpFrames = IOUtils.GetFilesSorted(interpFramesFolder, $"*.{InterpolateUtils.GetOutExt()}");
|
||||
unencodedFrames = interpFrames.ToList().Except(encodedFrames).ToList();
|
||||
//IOUtils.ZeroPadDir(Directory.GetFiles(interpFramesFolder, $"*.{InterpolateUtils.GetOutExt()}").ToList(), Padding.interpFrames, encodedFrames);
|
||||
//string[] interpFrames = IOUtils.GetFilesSorted(interpFramesFolder, $"*.{InterpolateUtils.GetOutExt()}");
|
||||
|
||||
//unencodedFrameLines = interpFramesLines.Select(x => x.GetInt()).ToList().Except(encodedFrameLines).ToList();
|
||||
|
||||
unencodedFrameLines.Clear();
|
||||
for(int vfrLine = 0; vfrLine < interpFramesLines.Length; vfrLine++)
|
||||
{
|
||||
if (!encodedFrameLines.Contains(vfrLine))
|
||||
unencodedFrameLines.Add(vfrLine);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(videoChunksFolder);
|
||||
|
||||
bool aiRunning = !AiProcess.currentAiProcess.HasExited;
|
||||
|
||||
if (unencodedFrames.Count >= (chunkSize + safetyBufferFrames) || !aiRunning) // Encode every n frames, or after process has exited
|
||||
if (unencodedFrameLines.Count >= (chunkSize + safetyBufferFrames) || !aiRunning) // Encode every n frames, or after process has exited
|
||||
{
|
||||
busy = true;
|
||||
|
||||
List<string> framesToEncode = aiRunning ? unencodedFrames.Take(chunkSize).ToList() : unencodedFrames; // Take all remaining frames if process is done
|
||||
Logger.Log($"Encoding Chunk #{videoIndex} using {Path.GetFileName(framesToEncode.First())} through {Path.GetFileName(framesToEncode.Last())}", true, false, "ffmpeg");
|
||||
List<int> frameLinesToEncode = aiRunning ? unencodedFrameLines.Take(chunkSize).ToList() : unencodedFrameLines; // Take all remaining frames if process is done
|
||||
Logger.Log($"Encoding Chunk #{videoIndex} using {Path.GetFileName(interpFramesLines[frameLinesToEncode.First()])} through {Path.GetFileName(Path.GetFileName(interpFramesLines[frameLinesToEncode.Last()]))}", true, false, "ffmpeg");
|
||||
|
||||
IOUtils.ZeroPadDir(framesToEncode, Padding.interpFrames); // Zero-pad frames before encoding to make sure filenames match with VFR file
|
||||
//IOUtils.ZeroPadDir(framesToEncode, Padding.interpFrames); // Zero-pad frames before encoding to make sure filenames match with VFR file
|
||||
|
||||
string outpath = Path.Combine(videoChunksFolder, $"{videoIndex.ToString().PadLeft(4, '0')}{FFmpegUtils.GetExt(Interpolate.current.outMode)}");
|
||||
int firstFrameNum = Path.GetFileNameWithoutExtension(framesToEncode[0]).GetInt();
|
||||
await CreateVideo.EncodeChunk(outpath, Interpolate.current.outMode, firstFrameNum - 1, framesToEncode.Count);
|
||||
int firstFrameNum = frameLinesToEncode[0];
|
||||
await CreateVideo.EncodeChunk(outpath, Interpolate.current.outMode, firstFrameNum - 1, frameLinesToEncode.Count);
|
||||
|
||||
if(Interpolate.canceled) return;
|
||||
|
||||
if (Config.GetInt("autoEncMode") == 2)
|
||||
{
|
||||
foreach (string frame in framesToEncode)
|
||||
File.WriteAllText(frame, "THIS IS A DUMMY FILE - DO NOT DELETE ME"); // Overwrite to save space without breaking progress counter
|
||||
foreach (int frame in frameLinesToEncode)
|
||||
{
|
||||
string framePath = Path.Combine(interpFramesPath, interpFramesLines[frame]);
|
||||
File.WriteAllText(framePath, "THIS IS A DUMMY FILE - DO NOT DELETE ME"); // Overwrite to save space without breaking progress counter
|
||||
}
|
||||
}
|
||||
|
||||
encodedFrames.AddRange(framesToEncode);
|
||||
encodedFrameLines.AddRange(frameLinesToEncode);
|
||||
|
||||
Logger.Log("Done Encoding Chunk #" + videoIndex, true, false, "ffmpeg");
|
||||
videoIndex++;
|
||||
@ -105,7 +119,7 @@ namespace Flowframes.Main
|
||||
public static bool HasWorkToDo ()
|
||||
{
|
||||
if (Interpolate.canceled || interpFramesFolder == null) return false;
|
||||
return ((AiProcess.currentAiProcess != null && !AiProcess.currentAiProcess.HasExited) || encodedFrames.Count < GetInterpFramesAmount());
|
||||
return ((AiProcess.currentAiProcess != null && !AiProcess.currentAiProcess.HasExited) || encodedFrameLines.Count < interpFramesLines.Length);
|
||||
}
|
||||
|
||||
static int GetChunkSize(int targetFramesAmount)
|
||||
|
@ -79,14 +79,14 @@ namespace Flowframes.Main
|
||||
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);
|
||||
string outFilename = Path.Combine(copyPath, idx.ToString().PadLeft(Padding.interpFrames, '0')) + Path.GetExtension(framePath);
|
||||
|
||||
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)
|
||||
if (sw.ElapsedMilliseconds >= 500 || idx == vfrLines.Length)
|
||||
{
|
||||
sw.Restart();
|
||||
Logger.Log($"Copying interpolated frames to '{Path.GetFileName(copyPath)}' - {idx}/{vfrLines.Length}", false, true);
|
||||
|
@ -83,7 +83,7 @@ namespace Flowframes
|
||||
}
|
||||
|
||||
Program.mainForm.SetStatus("Extracting frames from video...");
|
||||
await FFmpegCommands.VideoToFrames(inPath, outPath, Config.GetInt("dedupMode") == 2, false, Utils.GetOutputResolution(inPath, true));
|
||||
await FFmpegCommands.VideoToFrames(inPath, outPath, Config.GetInt("dedupMode") == 2, false, Utils.GetOutputResolution(inPath, true), false);
|
||||
Utils.FixConsecutiveSceneFrames(Path.Combine(current.tempFolder, Paths.scenesDir), current.framesFolder);
|
||||
|
||||
if (extractAudio)
|
||||
@ -118,11 +118,14 @@ namespace Flowframes
|
||||
}
|
||||
|
||||
if (Config.GetInt("dedupMode") == 1)
|
||||
await MagickDedupe.Run(current.framesFolder);
|
||||
await Dedupe.Run(current.framesFolder);
|
||||
else
|
||||
MagickDedupe.ClearCache();
|
||||
|
||||
if (canceled) return;
|
||||
Dedupe.ClearCache();
|
||||
|
||||
if (Config.GetInt("dedupMode") == 2)
|
||||
await Dedupe.CreateDupesFileMpdecimate(current.framesFolder, currentInputFrameCount);
|
||||
|
||||
if (canceled) return;
|
||||
|
||||
bool useTimestamps = Config.GetInt("timingMode") == 1; // TODO: Auto-Disable timestamps if input frames are sequential, not timestamped
|
||||
await FrameTiming.CreateTimecodeFiles(current.framesFolder, FrameTiming.Mode.CFR, Config.GetBool("enableLoop"), current.interpFactor, !useTimestamps);
|
||||
|
@ -84,7 +84,7 @@ namespace Flowframes.Main
|
||||
AiProcess.filenameMap.Clear();
|
||||
bool extractAudio = true;
|
||||
Program.mainForm.SetStatus("Extracting frames from video...");
|
||||
await FFmpegCommands.VideoToFrames(current.inPath, current.framesFolder, Config.GetInt("dedupMode") == 2, false, InterpolateUtils.GetOutputResolution(current.inPath, true));
|
||||
await FFmpegCommands.VideoToFrames(current.inPath, current.framesFolder, Config.GetInt("dedupMode") == 2, false, InterpolateUtils.GetOutputResolution(current.inPath, true), false);
|
||||
|
||||
if (extractAudio)
|
||||
{
|
||||
|
@ -45,7 +45,7 @@ namespace Flowframes.UI
|
||||
CheckExistingFolder(path, outputTbox.Text.Trim());
|
||||
await Task.Delay(10);
|
||||
await PrintResolution(path);
|
||||
MagickDedupe.ClearCache();
|
||||
Dedupe.ClearCache();
|
||||
await Task.Delay(10);
|
||||
InterpolateUtils.SetPreviewImg(await GetThumbnail(path));
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ namespace Flowframes.UI
|
||||
Program.mainForm.SetWorking(true);
|
||||
Logger.Log("Running frame de-duplication", true);
|
||||
await Task.Delay(10);
|
||||
await MagickDedupe.Run(framesPath, testRun);
|
||||
await Magick.Dedupe.Run(framesPath, testRun);
|
||||
IOUtils.TryDeleteIfExists(framesPath);
|
||||
Program.mainForm.SetProgress(0);
|
||||
Program.mainForm.SetWorking(false);
|
||||
|
Loading…
Reference in New Issue
Block a user