Semi-fixed CAIN exe, fixed JPEG frames, finished VFR deduping, more

This commit is contained in:
N00MKRAD
2020-11-25 12:40:17 +01:00
parent b593baf3e3
commit 97dce04f92
17 changed files with 434 additions and 220 deletions

View File

@@ -34,9 +34,9 @@ namespace Flowframes
ffmpeg.StartInfo.FileName = "cmd.exe";
ffmpeg.StartInfo.Arguments = "/C cd /D \"" + GetAvDir() + "\" & ffmpeg.exe -hide_banner -loglevel warning -y -stats " + args;
if(logMode != LogMode.Hidden) Logger.Log("Running ffmpeg...", false);
Logger.Log("cmd.exe " + ffmpeg.StartInfo.Arguments, true);
ffmpeg.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
ffmpeg.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);
Logger.Log("cmd.exe " + ffmpeg.StartInfo.Arguments, true, false, "ffmpeg.txt");
ffmpeg.OutputDataReceived += new DataReceivedEventHandler(FfmpegOutputHandler);
ffmpeg.ErrorDataReceived += new DataReceivedEventHandler(FfmpegOutputHandler);
ffmpeg.Start();
ffmpeg.BeginOutputReadLine();
ffmpeg.BeginErrorReadLine();
@@ -45,12 +45,13 @@ namespace Flowframes
Logger.Log("Done running ffmpeg.", true);
}
static void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
static void FfmpegOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
lastOutputFfmpeg = lastOutputFfmpeg + outLine.Data + "\n";
bool hidden = currentLogMode == LogMode.Hidden;
bool replaceLastLine = currentLogMode == LogMode.OnlyLastLine;
Logger.Log(outLine.Data, hidden, replaceLastLine, "ffmpeg.txt");
string trimmedLine = outLine.Data.Remove("q=-0.0").Remove("size=N/A").Remove("bitrate=N/A").TrimWhitespaces();
Logger.Log(trimmedLine, hidden, replaceLastLine, "ffmpeg.txt");
}
public static string GetFfmpegOutput (string args)
@@ -70,7 +71,7 @@ namespace Flowframes
{
Process ffprobe = OSUtils.NewProcess(true);
ffprobe.StartInfo.Arguments = $"/C cd /D {GetAvDir().Wrap()} & ffprobe.exe {args}";
Logger.Log("cmd.exe " + ffprobe.StartInfo.Arguments, true);
Logger.Log("cmd.exe " + ffprobe.StartInfo.Arguments, true, false, "ffmpeg.txt");
ffprobe.Start();
ffprobe.WaitForExit();
string output = ffprobe.StandardOutput.ReadToEnd();
@@ -87,7 +88,7 @@ namespace Flowframes
lastProcess = gifski;
gifski.StartInfo.Arguments = $"/C cd /D {GetAvDir().Wrap()} & gifski.exe {args}";
Logger.Log("Running gifski...");
Logger.Log("cmd.exe " + gifski.StartInfo.Arguments, true);
Logger.Log("cmd.exe " + gifski.StartInfo.Arguments, true, false, "ffmpeg.txt");
gifski.OutputDataReceived += new DataReceivedEventHandler(OutputHandlerGifski);
gifski.ErrorDataReceived += new DataReceivedEventHandler(OutputHandlerGifski);
gifski.Start();

View File

@@ -13,6 +13,9 @@ namespace Flowframes
static string divisionFilter = "\"crop = trunc(iw / 2) * 2:trunc(ih / 2) * 2\"";
static string pngComprArg = "-compression_level 3";
static string mpDecDef = "\"mpdecimate\"";
static string mpDecAggr = "\"mpdecimate=hi=64*32:lo=64*32:frac=0.1\"";
public static async Task VideoToFrames(string inputFile, string frameFolderPath, bool deDupe, bool delSrc)
{
await VideoToFrames(inputFile, frameFolderPath, deDupe, delSrc, new Size());
@@ -24,8 +27,12 @@ namespace Flowframes
if (size.Width > 1 && size.Height > 1) sizeStr = $"-s {size.Width}x{size.Height}";
if (!Directory.Exists(frameFolderPath))
Directory.CreateDirectory(frameFolderPath);
string args = $"-i {inputFile.Wrap()} {pngComprArg} -vsync 0 -pix_fmt rgb24 -vf {divisionFilter} {sizeStr} \"{frameFolderPath}/%08d.png\"";
if(deDupe) args = $"-i {inputFile.Wrap()} -copyts -r 1000 {pngComprArg} -vsync 0 -frame_pts true -vf mpdecimate,{divisionFilter} {sizeStr} \"{frameFolderPath}/%08d.png\"";
string args = $"-i {inputFile.Wrap()} {pngComprArg} -vsync 0 -pix_fmt rgb24 -copyts -r 1000 -frame_pts true -vf {divisionFilter} {sizeStr} \"{frameFolderPath}/%08d.png\"";
if (deDupe)
{
string mpStr = (Config.GetInt("mpdecimateMode") == 0) ? mpDecDef : mpDecAggr;
args = $"-i {inputFile.Wrap()} -copyts -r 1000 {pngComprArg} -vsync 0 -pix_fmt rgb24 -frame_pts true -vf {mpStr},{divisionFilter} {sizeStr} \"{frameFolderPath}/%08d.png\"";
}
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine);
await Task.Delay(1);
if (delSrc)
@@ -47,8 +54,7 @@ namespace Flowframes
{
Logger.Log($"Encoding MP4 video with CRF {crf}...");
int nums = IOUtils.GetFilenameCounterLength(Directory.GetFiles(inputDir, $"*.{imgFormat}")[0], prefix);
string enc = "libx264";
if (useH265) enc = "libx265";
string enc = useH265 ? "libx265" : "libx264";
string loopStr = "";
if (looptimes > 0) loopStr = $"-stream_loop {looptimes}";
string args = $" {loopStr} -framerate {fps.ToString().Replace(",",".")} -i \"{inputDir}\\{prefix}%0{nums}d.{imgFormat}\" -c:v {enc} -crf {crf} {videoEncArgs} -threads {Config.GetInt("ffEncThreads")} -c:a copy {outPath.Wrap()}";
@@ -57,6 +63,16 @@ namespace Flowframes
DeleteSource(inputDir);
}
public static async Task FramesToMp4Vfr(string framesFile, string outPath, bool useH265, int crf, float fps, int looptimes = -1)
{
Logger.Log($"Encoding MP4 video with CRF {crf}...");
string enc = useH265 ? "libx265" : "libx264";
string loopStr = "";
if (looptimes > 0) loopStr = $"-stream_loop {looptimes}";
string args = $" {loopStr} -vsync 1 -f concat -safe 0 -i {framesFile.Wrap()} -r {fps.ToString().Replace(",", ".")} -c:v {enc} -crf {crf} {videoEncArgs} -threads {Config.GetInt("ffEncThreads")} -c:a copy {outPath.Wrap()}";
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine);
}
public static async Task ConvertFramerate (string inputPath, string outPath, bool useH265, int crf, float newFps, bool delSrc = false)
{
Logger.Log($"Changing video frame rate...");

View File

@@ -10,108 +10,137 @@ using System.Windows.Forms;
namespace Flowframes
{
public static class ExtensionMethods
public static class ExtensionMethods
{
public static string TrimNumbers(this string s, bool allowDotComma = false)
{
if(!allowDotComma)
s = Regex.Replace(s, "[^0-9]", "");
else
s = Regex.Replace(s, "[^.,0-9]", "");
return s.Trim();
}
public static int GetInt(this TextBox textbox)
{
return GetInt(textbox.Text);
}
public static int GetInt(this ComboBox combobox)
{
return GetInt(combobox.Text);
}
public static int GetInt (this string str)
public static string TrimNumbers(this string s, bool allowDotComma = false)
{
if (str.Length < 1 || str == null)
return 0;
try { return int.Parse(str.TrimNumbers()); }
catch (Exception e)
{
Logger.Log("Failed to parse \"" + str + "\" to int: " + e, true);
return 0;
}
}
if (!allowDotComma)
s = Regex.Replace(s, "[^0-9]", "");
else
s = Regex.Replace(s, "[^.,0-9]", "");
return s.Trim();
}
public static float GetFloat(this TextBox textbox)
{
return GetFloat(textbox.Text);
}
public static float GetFloat(this ComboBox combobox)
{
return GetFloat(combobox.Text);
}
public static float GetFloat (this string str)
public static int GetInt(this TextBox textbox)
{
if (str.Length < 1 || str == null)
return 0f;
string num = str.TrimNumbers(true).Replace(",", ".");
float value;
float.TryParse(num, NumberStyles.Any, CultureInfo.InvariantCulture, out value);
return value;
}
return GetInt(textbox.Text);
}
public static string Wrap(this string path, bool addSpaceFront = false, bool addSpaceEnd = false)
{
string s = "\"" + path + "\"";
if (addSpaceFront)
s = " " + s;
if (addSpaceEnd)
s = s + " ";
return s;
}
public static string GetParentDir(this string path)
{
return Directory.GetParent(path).FullName;
}
public static int RoundToInt(this float f)
{
return (int)Math.Round(f);
}
public static int Clamp(this int i, int min, int max)
{
if (i < min)
i = min;
if (i > max)
i = max;
return i;
}
public static string[] SplitIntoLines (this string str)
public static int GetInt(this ComboBox combobox)
{
return Regex.Split(str, "\r\n|\r|\n");
}
return GetInt(combobox.Text);
}
public static string Trunc (this string value, int maxChars)
{
return value.Length <= maxChars ? value : value.Substring(0, maxChars) + "…";
}
public static string StripBadChars (this string str)
public static int GetInt(this string str)
{
string outStr = Regex.Replace(str, @"[^\u0020-\u007E]", string.Empty);
outStr = outStr.Replace("(", "").Replace(")", "").Replace("[", "").Replace("]", "").Replace("{", "").Replace("}", "").Replace("%", "");
return outStr;
}
if (str.Length < 1 || str == null)
return 0;
try { return int.Parse(str.TrimNumbers()); }
catch (Exception e)
{
Logger.Log("Failed to parse \"" + str + "\" to int: " + e, true);
return 0;
}
}
public static string StripNumbers (this string str)
public static float GetFloat(this TextBox textbox)
{
return new string(str.Where(c => c != '-' && (c < '0' || c > '9')).ToArray());
}
}
return GetFloat(textbox.Text);
}
public static float GetFloat(this ComboBox combobox)
{
return GetFloat(combobox.Text);
}
public static float GetFloat(this string str)
{
if (str.Length < 1 || str == null)
return 0f;
string num = str.TrimNumbers(true).Replace(",", ".");
float value;
float.TryParse(num, NumberStyles.Any, CultureInfo.InvariantCulture, out value);
return value;
}
public static string Wrap(this string path, bool addSpaceFront = false, bool addSpaceEnd = false)
{
string s = "\"" + path + "\"";
if (addSpaceFront)
s = " " + s;
if (addSpaceEnd)
s = s + " ";
return s;
}
public static string GetParentDir(this string path)
{
return Directory.GetParent(path).FullName;
}
public static int RoundToInt(this float f)
{
return (int)Math.Round(f);
}
public static int Clamp(this int i, int min, int max)
{
if (i < min)
i = min;
if (i > max)
i = max;
return i;
}
public static string[] SplitIntoLines(this string str)
{
return Regex.Split(str, "\r\n|\r|\n");
}
public static string Trunc(this string value, int maxChars)
{
return value.Length <= maxChars ? value : value.Substring(0, maxChars) + "…";
}
public static string StripBadChars(this string str)
{
string outStr = Regex.Replace(str, @"[^\u0020-\u007E]", string.Empty);
outStr = outStr.Replace("(", "").Replace(")", "").Replace("[", "").Replace("]", "").Replace("{", "").Replace("}", "").Replace("%", "");
return outStr;
}
public static string StripNumbers(this string str)
{
return new string(str.Where(c => c != '-' && (c < '0' || c > '9')).ToArray());
}
public static string Remove(this string str, string stringToRemove)
{
if (str == null || stringToRemove == null)
return str;
return str.Replace(stringToRemove, "");
}
public static string TrimWhitespaces(this string str)
{
if (str == null) return str;
var newString = new StringBuilder();
bool previousIsWhitespace = false;
for (int i = 0; i < str.Length; i++)
{
if (Char.IsWhiteSpace(str[i]))
{
if (previousIsWhitespace)
continue;
previousIsWhitespace = true;
}
else
{
previousIsWhitespace = false;
}
newString.Append(str[i]);
}
return newString.ToString();
}
}
}

View File

@@ -128,7 +128,7 @@ namespace Flowframes
outputTbox.Text = inputTbox.Text.Trim().GetParentDir();
string path = inputTbox.Text.Trim();
Program.lastInputPath = path;
string fpsStr = "Not Found.";
string fpsStr = "Not Found";
float fps = IOUtils.GetFpsFolderOrVideo(path);
if(fps > 0)
{

View File

@@ -47,9 +47,16 @@
this.deleteLogsOnStartup = new System.Windows.Forms.CheckBox();
this.label11 = new System.Windows.Forms.Label();
this.tabListPage2 = new Cyotek.Windows.Forms.TabListPage();
this.mpDedupePanel = new System.Windows.Forms.Panel();
this.panel13 = new System.Windows.Forms.Panel();
this.mpdecimateMode = new System.Windows.Forms.ComboBox();
this.label42 = new System.Windows.Forms.Label();
this.magickDedupePanel = new System.Windows.Forms.Panel();
this.panel3 = new System.Windows.Forms.Panel();
this.dedupThresh = new System.Windows.Forms.ComboBox();
this.label4 = new System.Windows.Forms.Label();
this.timingMode = new System.Windows.Forms.ComboBox();
this.label35 = new System.Windows.Forms.Label();
this.panel3 = new System.Windows.Forms.Panel();
this.label27 = new System.Windows.Forms.Label();
this.jpegInterps = new System.Windows.Forms.CheckBox();
this.label25 = new System.Windows.Forms.Label();
@@ -57,8 +64,6 @@
this.label24 = new System.Windows.Forms.Label();
this.enableLoop = new System.Windows.Forms.CheckBox();
this.label15 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.dedupThresh = new System.Windows.Forms.ComboBox();
this.dedupMode = new System.Windows.Forms.ComboBox();
this.label2 = new System.Windows.Forms.Label();
this.enableAudio = new System.Windows.Forms.CheckBox();
@@ -120,6 +125,8 @@
this.settingsTabList.SuspendLayout();
this.generalTab.SuspendLayout();
this.tabListPage2.SuspendLayout();
this.mpDedupePanel.SuspendLayout();
this.magickDedupePanel.SuspendLayout();
this.aiOptsPage.SuspendLayout();
this.vidExportTab.SuspendLayout();
this.debugTab.SuspendLayout();
@@ -330,9 +337,10 @@
// tabListPage2
//
this.tabListPage2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(48)))));
this.tabListPage2.Controls.Add(this.mpDedupePanel);
this.tabListPage2.Controls.Add(this.magickDedupePanel);
this.tabListPage2.Controls.Add(this.timingMode);
this.tabListPage2.Controls.Add(this.label35);
this.tabListPage2.Controls.Add(this.panel3);
this.tabListPage2.Controls.Add(this.label27);
this.tabListPage2.Controls.Add(this.jpegInterps);
this.tabListPage2.Controls.Add(this.label25);
@@ -340,8 +348,6 @@
this.tabListPage2.Controls.Add(this.label24);
this.tabListPage2.Controls.Add(this.enableLoop);
this.tabListPage2.Controls.Add(this.label15);
this.tabListPage2.Controls.Add(this.label4);
this.tabListPage2.Controls.Add(this.dedupThresh);
this.tabListPage2.Controls.Add(this.dedupMode);
this.tabListPage2.Controls.Add(this.label2);
this.tabListPage2.Controls.Add(this.enableAudio);
@@ -350,6 +356,104 @@
this.tabListPage2.Size = new System.Drawing.Size(762, 419);
this.tabListPage2.Text = "Interpolation";
//
// mpDedupePanel
//
this.mpDedupePanel.Controls.Add(this.panel13);
this.mpDedupePanel.Controls.Add(this.mpdecimateMode);
this.mpDedupePanel.Controls.Add(this.label42);
this.mpDedupePanel.Location = new System.Drawing.Point(536, 67);
this.mpDedupePanel.Margin = new System.Windows.Forms.Padding(0);
this.mpDedupePanel.Name = "mpDedupePanel";
this.mpDedupePanel.Size = new System.Drawing.Size(218, 21);
this.mpDedupePanel.TabIndex = 61;
//
// panel13
//
this.panel13.BackgroundImage = global::Flowframes.Properties.Resources.baseline_create_white_18dp_semiTransparent;
this.panel13.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom;
this.panel13.Location = new System.Drawing.Point(164, 0);
this.panel13.Name = "panel13";
this.panel13.Size = new System.Drawing.Size(21, 21);
this.panel13.TabIndex = 57;
this.toolTip1.SetToolTip(this.panel13, "Allows custom input.");
//
// mpdecimateMode
//
this.mpdecimateMode.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.mpdecimateMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.mpdecimateMode.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.mpdecimateMode.ForeColor = System.Drawing.Color.White;
this.mpdecimateMode.FormattingEnabled = true;
this.mpdecimateMode.Items.AddRange(new object[] {
"Normal",
"Aggressive"});
this.mpdecimateMode.Location = new System.Drawing.Point(46, 0);
this.mpdecimateMode.Margin = new System.Windows.Forms.Padding(3, 3, 8, 3);
this.mpdecimateMode.Name = "mpdecimateMode";
this.mpdecimateMode.Size = new System.Drawing.Size(107, 21);
this.mpdecimateMode.TabIndex = 28;
//
// label42
//
this.label42.AutoSize = true;
this.label42.Location = new System.Drawing.Point(3, 3);
this.label42.Margin = new System.Windows.Forms.Padding(3);
this.label42.Name = "label42";
this.label42.Size = new System.Drawing.Size(37, 13);
this.label42.TabIndex = 29;
this.label42.Text = "Mode:";
//
// magickDedupePanel
//
this.magickDedupePanel.Controls.Add(this.panel3);
this.magickDedupePanel.Controls.Add(this.dedupThresh);
this.magickDedupePanel.Controls.Add(this.label4);
this.magickDedupePanel.Location = new System.Drawing.Point(536, 67);
this.magickDedupePanel.Margin = new System.Windows.Forms.Padding(0);
this.magickDedupePanel.Name = "magickDedupePanel";
this.magickDedupePanel.Size = new System.Drawing.Size(218, 21);
this.magickDedupePanel.TabIndex = 60;
//
// panel3
//
this.panel3.BackgroundImage = global::Flowframes.Properties.Resources.baseline_create_white_18dp_semiTransparent;
this.panel3.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom;
this.panel3.Location = new System.Drawing.Point(164, 0);
this.panel3.Name = "panel3";
this.panel3.Size = new System.Drawing.Size(21, 21);
this.panel3.TabIndex = 57;
this.toolTip1.SetToolTip(this.panel3, "Allows custom input.");
//
// dedupThresh
//
this.dedupThresh.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.dedupThresh.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.dedupThresh.ForeColor = System.Drawing.Color.White;
this.dedupThresh.FormattingEnabled = true;
this.dedupThresh.Items.AddRange(new object[] {
"1%",
"2%",
"3%",
"4%",
"5%",
"10%"});
this.dedupThresh.Location = new System.Drawing.Point(66, 0);
this.dedupThresh.Margin = new System.Windows.Forms.Padding(3, 3, 8, 3);
this.dedupThresh.Name = "dedupThresh";
this.dedupThresh.Size = new System.Drawing.Size(87, 21);
this.dedupThresh.TabIndex = 28;
this.dedupThresh.Leave += new System.EventHandler(this.dedupThresh_Leave);
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(3, 3);
this.label4.Margin = new System.Windows.Forms.Padding(3);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(57, 13);
this.label4.TabIndex = 29;
this.label4.Text = "Threshold:";
//
// timingMode
//
this.timingMode.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
@@ -359,10 +463,10 @@
this.timingMode.FormattingEnabled = true;
this.timingMode.Items.AddRange(new object[] {
"Disabled",
"Re-Duplicate Deleted Frames"});
"Use Timecodes From Source Video"});
this.timingMode.Location = new System.Drawing.Point(280, 97);
this.timingMode.Name = "timingMode";
this.timingMode.Size = new System.Drawing.Size(200, 21);
this.timingMode.Size = new System.Drawing.Size(250, 21);
this.timingMode.TabIndex = 59;
//
// label35
@@ -371,19 +475,9 @@
this.label35.Location = new System.Drawing.Point(10, 100);
this.label35.Margin = new System.Windows.Forms.Padding(10, 10, 10, 7);
this.label35.Name = "label35";
this.label35.Size = new System.Drawing.Size(122, 13);
this.label35.Size = new System.Drawing.Size(197, 13);
this.label35.TabIndex = 58;
this.label35.Text = "Fix Video Timing/Length";
//
// panel3
//
this.panel3.BackgroundImage = global::Flowframes.Properties.Resources.baseline_create_white_18dp_semiTransparent;
this.panel3.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom;
this.panel3.Location = new System.Drawing.Point(647, 67);
this.panel3.Name = "panel3";
this.panel3.Size = new System.Drawing.Size(21, 21);
this.panel3.TabIndex = 57;
this.toolTip1.SetToolTip(this.panel3, "Allows custom input.");
this.label35.Text = "Frame Handling Mode For Deduplication";
//
// label27
//
@@ -457,36 +551,6 @@
this.label15.TabIndex = 30;
this.label15.Text = "Animation Loop (Copy First Frame As Last)";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(486, 70);
this.label4.Margin = new System.Windows.Forms.Padding(3);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(57, 13);
this.label4.TabIndex = 29;
this.label4.Text = "Threshold:";
//
// dedupThresh
//
this.dedupThresh.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.dedupThresh.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.dedupThresh.ForeColor = System.Drawing.Color.White;
this.dedupThresh.FormattingEnabled = true;
this.dedupThresh.Items.AddRange(new object[] {
"1%",
"2%",
"3%",
"4%",
"5%",
"10%"});
this.dedupThresh.Location = new System.Drawing.Point(549, 67);
this.dedupThresh.Margin = new System.Windows.Forms.Padding(3, 3, 8, 3);
this.dedupThresh.Name = "dedupThresh";
this.dedupThresh.Size = new System.Drawing.Size(87, 21);
this.dedupThresh.TabIndex = 28;
this.dedupThresh.Leave += new System.EventHandler(this.dedupThresh_Leave);
//
// dedupMode
//
this.dedupMode.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
@@ -496,12 +560,13 @@
this.dedupMode.FormattingEnabled = true;
this.dedupMode.Items.AddRange(new object[] {
"Disabled",
"Always Enabled",
"Detect Automatically For Each Video"});
"Remove After Extraction - Slow, Accurate",
"Remove During Extraction - Fast, Less Accurate"});
this.dedupMode.Location = new System.Drawing.Point(280, 67);
this.dedupMode.Name = "dedupMode";
this.dedupMode.Size = new System.Drawing.Size(200, 21);
this.dedupMode.Size = new System.Drawing.Size(250, 21);
this.dedupMode.TabIndex = 27;
this.dedupMode.SelectedIndexChanged += new System.EventHandler(this.dedupMode_SelectedIndexChanged);
//
// label2
//
@@ -1210,6 +1275,10 @@
this.generalTab.PerformLayout();
this.tabListPage2.ResumeLayout(false);
this.tabListPage2.PerformLayout();
this.mpDedupePanel.ResumeLayout(false);
this.mpDedupePanel.PerformLayout();
this.magickDedupePanel.ResumeLayout(false);
this.magickDedupePanel.PerformLayout();
this.aiOptsPage.ResumeLayout(false);
this.aiOptsPage.PerformLayout();
this.vidExportTab.ResumeLayout(false);
@@ -1310,5 +1379,10 @@
private System.Windows.Forms.Label label41;
private System.Windows.Forms.CheckBox ffprobeCountFrames;
private System.Windows.Forms.Label label40;
private System.Windows.Forms.Panel mpDedupePanel;
private System.Windows.Forms.Panel panel13;
private System.Windows.Forms.ComboBox mpdecimateMode;
private System.Windows.Forms.Label label42;
private System.Windows.Forms.Panel magickDedupePanel;
}
}

View File

@@ -51,6 +51,7 @@ namespace Flowframes.Forms
// Interpolation
ConfigParser.SaveGuiElement(enableAudio);
ConfigParser.SaveComboxIndex(dedupMode);
ConfigParser.SaveComboxIndex(mpdecimateMode);
ConfigParser.SaveGuiElement(dedupThresh, ConfigParser.StringMode.Float);
ConfigParser.SaveComboxIndex(timingMode);
ConfigParser.SaveGuiElement(enableLoop);
@@ -87,6 +88,7 @@ namespace Flowframes.Forms
// Interpolation
ConfigParser.LoadGuiElement(enableAudio);
ConfigParser.LoadComboxIndex(dedupMode);
ConfigParser.LoadComboxIndex(mpdecimateMode);
ConfigParser.LoadGuiElement(dedupThresh, "%");
ConfigParser.LoadComboxIndex(timingMode);
ConfigParser.LoadGuiElement(enableLoop);
@@ -137,5 +139,11 @@ namespace Flowframes.Forms
if (initialized && cmdDebugMode.SelectedIndex == 2)
MessageBox.Show("If you enable this, you need to close the CMD window manually after the process has finished, otherwise processing will be paused!", "Notice");
}
private void dedupMode_SelectedIndexChanged(object sender, EventArgs e)
{
magickDedupePanel.Visible = dedupMode.SelectedIndex == 1;
mpDedupePanel.Visible = dedupMode.SelectedIndex == 2;
}
}
}

View File

@@ -89,7 +89,7 @@ namespace Flowframes.IO
if (key == "torchGpus") return WriteDefault("torchGpus", "0");
if (key == "keepTempFolder") return WriteDefault("keepTempFolder", "False");
if (key == "deleteLogsOnStartup") return WriteDefault("deleteLogsOnStartup", "True");
if (key == "autoDedupFrames") return WriteDefault("autoDedupFrames", "10");
if (key == "autoDedupFrames") return WriteDefault("autoDedupFrames", "15");
if (key == "minOutVidLength") return WriteDefault("minOutVidLength", "2");
if (key == "mp4Enc") return WriteDefault("mp4Enc", "0");
if (key == "h264Crf") return WriteDefault("h264Crf", "20");

View File

@@ -31,16 +31,6 @@ namespace Flowframes.IO
packages.Add(Packages.licenses);
}
public static FlowPackage GetPkg(string friendlyName)
{
foreach (FlowPackage pkg in packages)
{
if (pkg.friendlyName == friendlyName)
return pkg;
}
return new FlowPackage();
}
static Stopwatch sw = new Stopwatch();
public static async Task DownloadAndInstall(string filename, bool showDialog = true)
{
@@ -112,25 +102,6 @@ namespace Flowframes.IO
}
}
public static bool IsInstalled(FlowPackage pkg)
{
return IsInstalled(pkg.fileName);
}
public static bool IsInstalled(string filename)
{
Logger.Log("PkgInstaller.IsInstalled - Checking for pkg with filename " + filename, true);
string path = Path.Combine(Paths.GetPkgPath(), Path.GetFileNameWithoutExtension(filename));
return Directory.Exists(path);
}
public static bool IsAiAvailable(AI ai, bool msg = true)
{
Logger.Log("PkgInstaller.IsAiAvailable - Checking for AI " + ai.aiName, true);
return IsInstalled(ai.pkg);
}
static void Print(string s, bool replaceLastLine = false)
{
if (installerForm != null)

52
Code/IO/PkgUtils.cs Normal file
View File

@@ -0,0 +1,52 @@
using Flowframes.Data;
using Flowframes.Forms;
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
namespace Flowframes.IO
{
class PkgUtils
{
public static FlowPackage GetPkg(string friendlyNameOrFilename)
{
foreach (FlowPackage pkg in PkgInstaller.packages)
{
if (pkg.fileName == friendlyNameOrFilename)
return pkg;
if (pkg.friendlyName == friendlyNameOrFilename)
return pkg;
}
return new FlowPackage();
}
public static string GetPkgFolder (FlowPackage pkg)
{
return Path.Combine(Paths.GetPkgPath(), Path.GetFileNameWithoutExtension(pkg.fileName));
}
public static bool IsInstalled(FlowPackage pkg)
{
string path = GetPkgFolder(pkg);
return (Directory.Exists(path) && IOUtils.GetAmountOfFiles(path, true) > 0);
}
public static bool IsUpToDate(FlowPackage pkg, int minVersion)
{
return (GetVersion(pkg) >= minVersion);
}
public static int GetVersion (FlowPackage pkg)
{
string versionFilePath = Path.Combine(GetPkgFolder(pkg), "ver.ini");
return IOUtils.ReadLines(versionFilePath)[0].Split('#')[0].GetInt();
}
public static bool IsAiAvailable(AI ai, bool msg = true)
{
Logger.Log("PkgInstaller.IsAiAvailable - Checking for AI " + ai.aiName, true);
return IsInstalled(ai.pkg);
}
}
}

View File

@@ -143,8 +143,9 @@ namespace Flowframes.Magick
Program.mainForm.SetProgress((int)Math.Round(((float)i / framePaths.Length) * 100f));
await Task.Delay(10);
if (Interpolate.cancelled) return;
if(!testRun && skipIfNoDupes && !hasEncounteredAnyDupes && i >= skipAfterNoDupesFrames)
if (!testRun && skipIfNoDupes && !hasEncounteredAnyDupes && i >= skipAfterNoDupesFrames)
{
skipped = true;
break;
@@ -166,8 +167,11 @@ namespace Flowframes.Magick
Logger.Log($"[FrameDedup]{testStr} Done. Kept {statsFramesKept} frames, deleted {statsFramesDeleted} frames.", false, true);
}
RenameCounterDir(path, "png");
ZeroPadDir(path, ext, 8);
if (statsFramesKept <= 0)
Interpolate.Cancel("No frames were left after de-duplication.");
//RenameCounterDir(path, "png");
//ZeroPadDir(path, ext, 8);
}
static Dictionary<int, int> UpdateDupeDict(Dictionary<int, int> dict, int frame, int amount)
@@ -315,7 +319,7 @@ namespace Flowframes.Magick
}
}
static void ZeroPadDir(string path, string ext, int targetLength, bool recursive = false)
public static void ZeroPadDir(string path, string ext, int targetLength, bool recursive = false)
{
FileInfo[] files;
if (recursive)

View File

@@ -7,8 +7,6 @@ using Flowframes.OS;
using Flowframes.UI;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@@ -29,8 +27,8 @@ namespace Flowframes.Main
return;
}
await Task.Delay(10);
if(Config.GetInt("timingMode") == 1)
await MagickDedupe.Reduplicate(path);
//if(Config.GetInt("timingMode") == 1)
// await MagickDedupe.Reduplicate(path);
Program.mainForm.SetStatus("Creating output video from frames...");
try
{
@@ -76,7 +74,15 @@ namespace Flowframes.Main
bool h265 = Config.GetInt("mp4Enc") == 1;
int crf = h265 ? Config.GetInt("h265Crf") : Config.GetInt("h264Crf");
await FFmpegCommands.FramesToMp4(framesPath, outPath, h265, crf, fps, "", false, looptimes, ext);
if (Config.GetInt("timingMode") == 1 && Config.GetInt("dedupMode") != 0)
{
string vfrFile = Path.Combine(framesPath.GetParentDir(), "vfr.ini");
await FFmpegCommands.FramesToMp4Vfr(vfrFile, outPath, h265, crf, fps, looptimes);
}
else
{
await FFmpegCommands.FramesToMp4(framesPath, outPath, h265, crf, fps, "", false, looptimes, ext);
}
await MergeAudio(i.lastInputPath, outPath);
if (changeFps > 0)

View File

@@ -62,9 +62,7 @@ namespace Flowframes
if (cancelled) return;
sw.Restart();
await Task.Delay(10);
if (Config.GetBool("vfrDedupe"))
VfrDedupe.CreateTimecodeFile(framesPath, Config.GetBool("enableLoop"), interpFactor);
await MagickDedupe.Run(framesPath);
await PostProcessFrames();
if (cancelled) return;
string interpFramesDir = Path.Combine(currentTempDir, "frames-interpolated");
string outPath = Path.Combine(outDir, Path.GetFileNameWithoutExtension(inPath) + IOUtils.GetAiSuffix(ai, interpFactor) + Utils.GetExt(outMode));
@@ -96,11 +94,11 @@ namespace Flowframes
float factor = (float)maxHeight / resolution.Height;
int width = (resolution.Width * factor).RoundToInt();
Logger.Log($"Video is bigger than the maximum - Downscaling to {width}x{maxHeight}.");
await FFmpegCommands.VideoToFrames(inPath, outPath, Config.GetBool("vfrDedupe"), false, new Size(width, maxHeight));
await FFmpegCommands.VideoToFrames(inPath, outPath, Config.GetInt("dedupMode") == 2, false, new Size(width, maxHeight));
}
else
{
await FFmpegCommands.VideoToFrames(inPath, outPath, Config.GetBool("vfrDedupe"), false);
await FFmpegCommands.VideoToFrames(inPath, outPath, Config.GetInt("dedupMode") == 2, false);
}
/*
if (AvProcess.lastOutputFfmpeg.ToLower().Contains("invalid"))
@@ -115,17 +113,37 @@ namespace Flowframes
if (audioFile != null && !File.Exists(audioFile))
await FFmpegCommands.ExtractAudio(inPath, audioFile);
}
if (!cancelled && Config.GetBool("enableLoop"))
if (!cancelled && Config.GetBool("enableLoop") && Config.GetInt("timingMode") != 1)
{
string lastFrame = IOUtils.GetHighestFrameNumPath(outPath);
int newNum = Path.GetFileName(lastFrame).GetInt() + 1;
string newFilename = Path.Combine(lastFrame.GetParentDir(), newNum.ToString().PadLeft(8, '0') + ".png");
string firstFrame = Path.Combine(lastFrame.GetParentDir(), 1.ToString().PadLeft(8, '0') + ".png");
string firstFrame = new DirectoryInfo(outPath).GetFiles("*.png")[0].FullName;
File.Copy(firstFrame, newFilename);
Logger.Log("Copied loop frame.");
}
}
public static bool firstFrameFix;
static async Task PostProcessFrames ()
{
if (Config.GetInt("dedupMode") == 1)
await MagickDedupe.Run(framesPath);
//await Task.Delay(10000);
if (Config.GetInt("timingMode") == 1 && Config.GetInt("dedupMode") != 0)
await VfrDedupe.CreateTimecodeFile(framesPath, Config.GetBool("enableLoop"), interpFactor, firstFrameFix);
if (cancelled) return;
MagickDedupe.RenameCounterDir(framesPath, "png");
MagickDedupe.ZeroPadDir(framesPath, "png", 8);
if (firstFrameFix)
IOUtils.TryCopy(new DirectoryInfo(framesPath).GetFiles("*.png")[0].FullName, Path.Combine(framesPath, "00000000.png"), true);
}
static async Task RunAi(string outpath, int targetFrames, int tilesize, AI ai)
{
Directory.CreateDirectory(outpath);

View File

@@ -26,7 +26,7 @@ namespace Flowframes.Main
int percent = (int)Math.Round(((float)frames / target) * 100f);
Program.mainForm.SetProgress(percent);
float generousTime = ((AiProcess.processTime.ElapsedMilliseconds - 1000) / 1000f);
float generousTime = ((AiProcess.processTime.ElapsedMilliseconds - 2000) / 1000f);
float fps = (float)frames / generousTime;
string fpsIn = (fps / Interpolate.interpFactor).ToString("0.00");
string fpsOut = fps.ToString("0.00");

View File

@@ -1,5 +1,8 @@
using System;
using Flowframes.IO;
using Flowframes.UI;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
@@ -9,10 +12,15 @@ namespace Flowframes.Main
{
class VfrDedupe
{
public static void CreateTimecodeFile(string framesPath, bool loopEnabled, int interpFactor)
public static async Task CreateTimecodeFile(string framesPath, bool loopEnabled, int interpFactor, bool firstFrameFix)
{
Logger.Log("Generating timecodes...");
FileInfo[] frameFiles = new DirectoryInfo(framesPath).GetFiles("*.png");
string vfrFile = Path.Combine(framesPath.GetParentDir(), "vfr.ini");
string fileContent = "";
string interpPath = framesPath.Replace(@"\", "/") + "-interpolated";
int lastFrameDuration = 1;
@@ -20,13 +28,15 @@ namespace Flowframes.Main
int totalFileCount = 1;
for (int i = 0; i < (frameFiles.Length - 1); i++)
{
if (Interpolate.cancelled) return;
string filename1 = frameFiles[i].Name;
string filename2 = frameFiles[i + 1].Name;
//Logger.Log("durationTotal = " + Path.GetFileNameWithoutExtension(filename2).GetInt() + " - " + Path.GetFileNameWithoutExtension(filename1).GetInt());
int durationTotal = Path.GetFileNameWithoutExtension(filename2).GetInt() - Path.GetFileNameWithoutExtension(filename1).GetInt();
lastFrameDuration = durationTotal;
float durationPerInterpFrame = (float)durationTotal / interpFactor;
//Logger.Log("durationPerInterpFrame = " + durationPerInterpFrame);
int interpolatedFrameCount = interpFactor;
// If loop is enabled, account for the extra frame added to the end for loop continuity
@@ -36,10 +46,34 @@ namespace Flowframes.Main
// Generate frames file lines
for (int frm = 0; frm < interpolatedFrameCount; frm++)
{
string durationStr = (durationPerInterpFrame / 1000f).ToString("0.00000").Replace(",", ".");
File.AppendAllText(vfrFile, $"{totalFileCount.ToString().PadLeft(8, '0')}.png\nduration {durationStr}\n");
string durationStr = ((durationPerInterpFrame / 1000f) * 1).ToString("0.00000").Replace(",", ".");
fileContent += $"file '{interpPath}/{totalFileCount.ToString().PadLeft(8, '0')}.png'\nduration {durationStr}\n";
totalFileCount++;
}
if((i+1) % 50 == 0)
{
Logger.Log($"Generating timecodes... {i + 1}/{frameFiles.Length}", false, true);
await Task.Delay(1);
}
}
File.WriteAllText(vfrFile, fileContent);
Logger.Log($"Generating timecodes... Done.", false, true);
if (firstFrameFix)
{
string[] lines = IOUtils.ReadLines(vfrFile);
File.WriteAllText(vfrFile, lines[0].Replace("00000001.png", "00000000.png"));
File.AppendAllText(vfrFile, "\n" + lines[1] + "\n");
File.AppendAllLines(vfrFile, lines);
}
if (Config.GetBool("enableLoop"))
{
int lastFileNumber = frameFiles.Last().Name.GetInt();
lastFileNumber += lastFrameDuration;
File.Copy(frameFiles.First().FullName, Path.Combine(frameFiles.First().FullName.GetParentDir(), lastFileNumber + ".png"));
}
}
}

View File

@@ -20,9 +20,10 @@ namespace Flowframes
public static Process currentAiProcess;
public static Stopwatch processTime = new Stopwatch();
static void Init (Process proc, string ext = "png")
static void Init (Process proc, string defaultExt = "png", bool needsFirstFrameFix = false)
{
InterpolateUtils.lastExt = ext;
Interpolate.firstFrameFix = needsFirstFrameFix;
InterpolateUtils.lastExt = defaultExt;
if (Config.GetBool("jpegInterps")) InterpolateUtils.lastExt = "jpg";
processTime.Restart();
currentAiProcess = proc;
@@ -35,6 +36,7 @@ namespace Flowframes
string dainDir = Path.Combine(Paths.GetPkgPath(), Path.GetFileNameWithoutExtension(Packages.dainNcnn.fileName));
Process dain = OSUtils.NewProcess(!OSUtils.ShowHiddenCmd());
Init(dain);
dain.StartInfo.Arguments = $"{OSUtils.GetHiddenCmdArg()} cd /D {dainDir.Wrap()} & dain-ncnn-vulkan.exe {args} -f {InterpolateUtils.lastExt}";
Logger.Log("Running DAIN...", false);
Logger.Log("cmd.exe " + dain.StartInfo.Arguments, true);
@@ -43,7 +45,6 @@ namespace Flowframes
dain.OutputDataReceived += (sender, outLine) => { LogOutput(outLine.Data, "dain-ncnn-log.txt"); };
dain.ErrorDataReceived += (sender, outLine) => { LogOutput("[E] " + outLine.Data, "dain-ncnn-log.txt"); };
}
Init(dain);
dain.Start();
if (!OSUtils.ShowHiddenCmd())
{
@@ -96,6 +97,7 @@ 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());
Init(cain);
cain.StartInfo.Arguments = $"{OSUtils.GetHiddenCmdArg()} cd /D {cainDir.Wrap()} & {cainExe} {args} -f {InterpolateUtils.lastExt}";
Logger.Log("cmd.exe " + cain.StartInfo.Arguments, true);
if (!OSUtils.ShowHiddenCmd())
@@ -103,7 +105,6 @@ namespace Flowframes
cain.OutputDataReceived += (sender, outLine) => { LogOutput(outLine.Data, "cain-ncnn-log.txt"); };
cain.ErrorDataReceived += (sender, outLine) => { LogOutput("[E] " + outLine.Data, "cain-ncnn-log.txt"); };
}
Init(cain);
cain.Start();
if (!OSUtils.ShowHiddenCmd())
{
@@ -117,11 +118,12 @@ namespace Flowframes
public static async Task RunRifeCuda(string framesPath, int interpFactor)
{
string script = "interp-parallel.py";
if(Config.GetInt("rifeMode") == 0 || IOUtils.GetAmountOfFiles(framesPath, false) < 30)
if(Config.GetInt("rifeMode") == 0 || IOUtils.GetAmountOfFiles(framesPath, false) < 10)
script = "interp-basic.py";
string rifeDir = Path.Combine(Paths.GetPkgPath(), Path.GetFileNameWithoutExtension(Packages.rifeCuda.fileName));
Process rifePy = OSUtils.NewProcess(!OSUtils.ShowHiddenCmd());
Init(rifePy, "png", true);
string args = $" --input {framesPath.Wrap()} --times {(int)Math.Log(interpFactor, 2)}";
rifePy.StartInfo.Arguments = $"{OSUtils.GetHiddenCmdArg()} cd /D {rifeDir.Wrap()} & " +
$"set CUDA_VISIBLE_DEVICES={Config.Get("torchGpus")} & {Python.GetPyCmd()} {script} {args} --imgformat {InterpolateUtils.lastExt}";
@@ -132,7 +134,6 @@ namespace Flowframes
rifePy.OutputDataReceived += (sender, outLine) => { LogOutput(outLine.Data, "rife-cuda-log.txt"); };
rifePy.ErrorDataReceived += (sender, outLine) => { LogOutput("[E] " + outLine.Data, "rife-cuda-log.txt"); };
}
Init(rifePy);
rifePy.Start();
if (!OSUtils.ShowHiddenCmd())
{

Binary file not shown.

View File

@@ -100,8 +100,8 @@ def clear_buffer(user_args):
if item is None:
break
if user_args.png:
print('Writing {}/{:0>7d}.png'.format(interp_output_path, cnt))
cv2.imwrite('{}/{:0>7d}.png'.format(interp_output_path, cnt), item[:, :, ::1])
print('Writing {}/{:0>8d}.png'.format(interp_output_path, cnt))
cv2.imwrite('{}/{:0>8d}.png'.format(interp_output_path, cnt), item[:, :, ::1])
cnt += 1
else:
vid_out.write(item[:, :, ::-1])