mirror of
https://github.com/n00mkrad/flowframes.git
synced 2025-12-16 16:37:48 +01:00
Fix color hue shift when interpolating frames, support high bit depth frames
This commit is contained in:
@@ -20,6 +20,7 @@ namespace Flowframes
|
|||||||
public string outPath;
|
public string outPath;
|
||||||
public string FullOutPath { get; set; } = "";
|
public string FullOutPath { get; set; } = "";
|
||||||
public AI ai;
|
public AI ai;
|
||||||
|
public string inPixFmt = "yuv420p";
|
||||||
public Fraction inFps;
|
public Fraction inFps;
|
||||||
public Fraction inFpsDetected;
|
public Fraction inFpsDetected;
|
||||||
public Fraction outFps;
|
public Fraction outFps;
|
||||||
@@ -54,11 +55,17 @@ namespace Flowframes
|
|||||||
inFpsDetected = inFpsDetectedArg;
|
inFpsDetected = inFpsDetectedArg;
|
||||||
inFps = inFpsArg;
|
inFps = inFpsArg;
|
||||||
interpFactor = interpFactorArg;
|
interpFactor = interpFactorArg;
|
||||||
outFps = inFpsArg * (double)interpFactorArg;
|
|
||||||
outItsScale = itsScale;
|
outItsScale = itsScale;
|
||||||
outSettings = outSettingsArg;
|
outSettings = outSettingsArg;
|
||||||
model = modelArg;
|
model = modelArg;
|
||||||
|
|
||||||
|
InitArgs();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitArgs ()
|
||||||
|
{
|
||||||
|
outFps = inFps * (double)interpFactor;
|
||||||
|
|
||||||
alpha = false;
|
alpha = false;
|
||||||
stepByStep = false;
|
stepByStep = false;
|
||||||
|
|
||||||
@@ -204,7 +211,7 @@ namespace Flowframes
|
|||||||
if (alpha || forceHqChroma) // Force PNG if alpha is enabled, or output is not 4:2:0 subsampled
|
if (alpha || forceHqChroma) // Force PNG if alpha is enabled, or output is not 4:2:0 subsampled
|
||||||
{
|
{
|
||||||
if(type == FrameType.Both || type == FrameType.Import)
|
if(type == FrameType.Both || type == FrameType.Import)
|
||||||
framesExt = ".png";
|
framesExt = ".tiff";
|
||||||
|
|
||||||
if (type == FrameType.Both || type == FrameType.Interp)
|
if (type == FrameType.Both || type == FrameType.Interp)
|
||||||
interpExt = ".png";
|
interpExt = ".png";
|
||||||
@@ -212,7 +219,7 @@ namespace Flowframes
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (type == FrameType.Both || type == FrameType.Import)
|
if (type == FrameType.Both || type == FrameType.Import)
|
||||||
framesExt = (Config.GetBool(Config.Key.jpegFrames) ? ".jpg" : ".png");
|
framesExt = (Config.GetBool(Config.Key.jpegFrames) ? ".jpg" : ".tiff");
|
||||||
|
|
||||||
if (type == FrameType.Both || type == FrameType.Interp)
|
if (type == FrameType.Both || type == FrameType.Interp)
|
||||||
interpExt = (Config.GetBool(Config.Key.jpegInterp) ? ".jpg" : ".png");
|
interpExt = (Config.GetBool(Config.Key.jpegInterp) ? ".jpg" : ".png");
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace Flowframes.Forms.Main
|
|||||||
// Main Tab
|
// Main Tab
|
||||||
UiUtils.InitCombox(interpFactorCombox, 0);
|
UiUtils.InitCombox(interpFactorCombox, 0);
|
||||||
UiUtils.InitCombox(outSpeedCombox, 0);
|
UiUtils.InitCombox(outSpeedCombox, 0);
|
||||||
|
|
||||||
UiUtils.InitCombox(aiModel, 2);
|
UiUtils.InitCombox(aiModel, 2);
|
||||||
// Video Utils
|
// Video Utils
|
||||||
UiUtils.InitCombox(trimCombox, 0);
|
UiUtils.InitCombox(trimCombox, 0);
|
||||||
@@ -89,7 +89,7 @@ namespace Flowframes.Forms.Main
|
|||||||
UpdateOutputUi();
|
UpdateOutputUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ResetOutputUi ()
|
public async void ResetOutputUi()
|
||||||
{
|
{
|
||||||
comboxOutputEncoder.Items.Clear();
|
comboxOutputEncoder.Items.Clear();
|
||||||
Config.Set(Config.Key.PerformedHwEncCheck, false.ToString());
|
Config.Set(Config.Key.PerformedHwEncCheck, false.ToString());
|
||||||
@@ -218,12 +218,23 @@ namespace Flowframes.Forms.Main
|
|||||||
public InterpSettings GetCurrentSettings()
|
public InterpSettings GetCurrentSettings()
|
||||||
{
|
{
|
||||||
SetTab(interpOptsTab.Name);
|
SetTab(interpOptsTab.Name);
|
||||||
string inPath = inputTbox.Text.Trim();
|
|
||||||
string outPath = outputTbox.Text.Trim();
|
|
||||||
AI ai = GetAi();
|
AI ai = GetAi();
|
||||||
float interpFactor = interpFactorCombox.GetFloat();
|
|
||||||
float itsScale = outSpeedCombox.GetInt().Clamp(1, 64);
|
var s = new InterpSettings()
|
||||||
return new InterpSettings(inPath, outPath, ai, currInFpsDetected, currInFps, interpFactor, itsScale, GetOutputSettings(), GetModel(ai));
|
{
|
||||||
|
inPath = inputTbox.Text.Trim(),
|
||||||
|
outPath = outputTbox.Text.Trim(),
|
||||||
|
ai = ai,
|
||||||
|
inFpsDetected = currInFpsDetected,
|
||||||
|
inFps = currInFps,
|
||||||
|
interpFactor = interpFactorCombox.GetFloat(),
|
||||||
|
outItsScale = outSpeedCombox.GetInt().Clamp(1, 64),
|
||||||
|
outSettings = GetOutputSettings(),
|
||||||
|
model = GetModel(ai),
|
||||||
|
};
|
||||||
|
|
||||||
|
s.InitArgs();
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InterpSettings UpdateCurrentSettings(InterpSettings settings)
|
public InterpSettings UpdateCurrentSettings(InterpSettings settings)
|
||||||
@@ -349,7 +360,7 @@ namespace Flowframes.Forms.Main
|
|||||||
return ai.FriendlyName + " - " + ai.Description;
|
return ai.FriendlyName + " - " + ai.Description;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeMainTab ()
|
private void InitializeMainTab()
|
||||||
{
|
{
|
||||||
if (_mainTabInitialized)
|
if (_mainTabInitialized)
|
||||||
return;
|
return;
|
||||||
@@ -416,7 +427,7 @@ namespace Flowframes.Forms.Main
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SaveOutputSettings ()
|
private void SaveOutputSettings()
|
||||||
{
|
{
|
||||||
var strings = new List<string>();
|
var strings = new List<string>();
|
||||||
if (comboxOutputFormat.Visible) strings.Add(comboxOutputFormat.Text);
|
if (comboxOutputFormat.Visible) strings.Add(comboxOutputFormat.Text);
|
||||||
@@ -692,7 +703,7 @@ namespace Flowframes.Forms.Main
|
|||||||
{
|
{
|
||||||
if (!_initialized) return;
|
if (!_initialized) return;
|
||||||
|
|
||||||
if(mainTabControl.SelectedTab == interpOptsTab)
|
if (mainTabControl.SelectedTab == interpOptsTab)
|
||||||
{
|
{
|
||||||
aiCombox_SelectedIndexChanged(null, null);
|
aiCombox_SelectedIndexChanged(null, null);
|
||||||
InitializeMainTab();
|
InitializeMainTab();
|
||||||
|
|||||||
@@ -43,30 +43,56 @@ namespace Flowframes.Media
|
|||||||
|
|
||||||
static string GetImgArgs(string extension, bool includePixFmt = true, bool alpha = false)
|
static string GetImgArgs(string extension, bool includePixFmt = true, bool alpha = false)
|
||||||
{
|
{
|
||||||
extension = extension.ToLowerInvariant().Remove(".").Replace("jpeg", "jpg");
|
extension = extension.Lower().Remove(".").Replace("jpeg", "jpg");
|
||||||
string pixFmt = "-pix_fmt rgb24";
|
|
||||||
|
string pixFmt = "yuv420p";
|
||||||
|
|
||||||
|
if (Interpolate.currentMediaFile != null && Interpolate.currentMediaFile.VideoStreams.Any())
|
||||||
|
{
|
||||||
|
pixFmt = Interpolate.currentMediaFile.VideoStreams.First().PixelFormat.Lower();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inputHighBitDepth = pixFmt.Contains("p10") || pixFmt.Contains("p16");
|
||||||
|
bool outputHighBitDepth = Interpolate.currentSettings.outSettings.PixelFormat.ToString().Lower().Contains("p10");
|
||||||
|
|
||||||
string args = "";
|
string args = "";
|
||||||
|
|
||||||
if (extension.Contains("png"))
|
if (extension == "png")
|
||||||
{
|
{
|
||||||
pixFmt = alpha ? "rgba" : "rgb24";
|
pixFmt = alpha ? "rgba" : "rgb24"; // PNG can't use YUV so we overwrite it with RGB
|
||||||
args = $"{pngCompr}";
|
args = pngCompr;
|
||||||
}
|
}
|
||||||
|
else if (extension == "jpg")
|
||||||
if (extension.Contains("jpg"))
|
|
||||||
{
|
{
|
||||||
pixFmt = "yuv420p";
|
// Fallback to YUV420P if not in list of supported formats
|
||||||
|
if (!new[] { "yuvj420p", "yuvj422p", "yuvj444p", "yuv420p", "yuv422p", "yuv444p" }.Contains(pixFmt))
|
||||||
|
{
|
||||||
|
pixFmt = "yuv420p";
|
||||||
|
}
|
||||||
|
|
||||||
args = $"-q:v 1";
|
args = $"-q:v 1";
|
||||||
}
|
}
|
||||||
|
else if (extension == "tiff")
|
||||||
if (extension.Contains("webp"))
|
|
||||||
{
|
{
|
||||||
pixFmt = "yuv420p";
|
// Fallback to YUV420P if not in list of supported formats
|
||||||
|
if (!new[] { "rgb24", "rgb48le", "pal8", "rgba", "yuv420p", "yuv422p", "yuv440p", "yuv444p" }.Contains(pixFmt))
|
||||||
|
{
|
||||||
|
pixFmt = inputHighBitDepth && outputHighBitDepth ? "rgb48le" : "yuv420p";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (extension == "webp")
|
||||||
|
{
|
||||||
|
// Fallback to YUV420P if not in list of supported formats
|
||||||
|
if (!new[] { "bgra", "yuv420p", "yuva420p" }.Contains(pixFmt))
|
||||||
|
{
|
||||||
|
pixFmt = "yuv420p";
|
||||||
|
}
|
||||||
|
|
||||||
args = $"-q:v 100";
|
args = $"-q:v 100";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includePixFmt)
|
if (includePixFmt)
|
||||||
args += $" -pix_fmt {pixFmt} -color_range full";
|
args += $" -pix_fmt {pixFmt}";
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
@@ -105,7 +131,7 @@ namespace Flowframes.Media
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task CopyImages (string inpath, string outpath, bool showLog)
|
public static async Task CopyImages(string inpath, string outpath, bool showLog)
|
||||||
{
|
{
|
||||||
if (showLog) Logger.Log($"Loading images from {new DirectoryInfo(inpath).Name}...");
|
if (showLog) Logger.Log($"Loading images from {new DirectoryInfo(inpath).Name}...");
|
||||||
Directory.CreateDirectory(outpath);
|
Directory.CreateDirectory(outpath);
|
||||||
@@ -129,14 +155,15 @@ namespace Flowframes.Media
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Log($"Symlink Import disabled, copying input frames...", true);
|
Logger.Log($"Symlink Import disabled, copying input frames...", true);
|
||||||
await Task.Run(async () => {
|
await Task.Run(async () =>
|
||||||
|
{
|
||||||
foreach (KeyValuePair<string, string> moveFromToPair in moveFromTo)
|
foreach (KeyValuePair<string, string> moveFromToPair in moveFromTo)
|
||||||
File.Copy(moveFromToPair.Key, moveFromToPair.Value);
|
File.Copy(moveFromToPair.Key, moveFromToPair.Value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool AreImagesCompatible (string inpath, int maxHeight)
|
static bool AreImagesCompatible(string inpath, int maxHeight)
|
||||||
{
|
{
|
||||||
NmkdStopwatch sw = new NmkdStopwatch();
|
NmkdStopwatch sw = new NmkdStopwatch();
|
||||||
string[] validExtensions = Filetypes.imagesInterpCompat; // = new string[] { ".jpg", ".jpeg", ".png" };
|
string[] validExtensions = Filetypes.imagesInterpCompat; // = new string[] { ".jpg", ".jpeg", ".png" };
|
||||||
@@ -310,7 +337,7 @@ namespace Flowframes.Media
|
|||||||
await RunFfmpeg(args, LogMode.Hidden);
|
await RunFfmpeg(args, LogMode.Hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task GeneratePalette (string inputFile, string outputPath, int colors = 256)
|
public static async Task GeneratePalette(string inputFile, string outputPath, int colors = 256)
|
||||||
{
|
{
|
||||||
string args = $"-i {inputFile.Wrap()} -vf palettegen={colors} {outputPath.Wrap()}";
|
string args = $"-i {inputFile.Wrap()} -vf palettegen={colors} {outputPath.Wrap()}";
|
||||||
await Task.Run(() => AvProcess.RunFfmpegSync(args));
|
await Task.Run(() => AvProcess.RunFfmpegSync(args));
|
||||||
|
|||||||
@@ -58,7 +58,9 @@ namespace Flowframes.Os
|
|||||||
l.Add($"inputPath = r'{inputPath}'");
|
l.Add($"inputPath = r'{inputPath}'");
|
||||||
l.Add($"");
|
l.Add($"");
|
||||||
|
|
||||||
if (s.InterpSettings.inputIsFrames || (s.Dedupe && !s.Realtime))
|
bool loadFrames = s.InterpSettings.inputIsFrames || (s.Dedupe && !s.Realtime);
|
||||||
|
|
||||||
|
if (loadFrames)
|
||||||
{
|
{
|
||||||
FileInfo[] frames = IoUtils.GetFileInfosSorted(s.InterpSettings.framesFolder, false, "*.*");
|
FileInfo[] frames = IoUtils.GetFileInfosSorted(s.InterpSettings.framesFolder, false, "*.*");
|
||||||
string ext = frames.FirstOrDefault().Extension;
|
string ext = frames.FirstOrDefault().Extension;
|
||||||
@@ -85,7 +87,7 @@ namespace Flowframes.Os
|
|||||||
l.Add($"clip = clip + firstFrame"); // Add to end (for seamless loop interpolation)
|
l.Add($"clip = clip + firstFrame"); // Add to end (for seamless loop interpolation)
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Add(GetScaleLines(s));
|
l.Add(GetScaleLines(s, loadFrames));
|
||||||
|
|
||||||
if (sc)
|
if (sc)
|
||||||
l.Add($"clip = core.misc.SCDetect(clip=clip, threshold={s.SceneDetectSensitivity.ToStringDot()})"); // Scene detection
|
l.Add($"clip = core.misc.SCDetect(clip=clip, threshold={s.SceneDetectSensitivity.ToStringDot()})"); // Scene detection
|
||||||
@@ -96,7 +98,7 @@ namespace Flowframes.Os
|
|||||||
if (s.Dedupe && !s.Realtime)
|
if (s.Dedupe && !s.Realtime)
|
||||||
l.Add(GetRedupeLines(s));
|
l.Add(GetRedupeLines(s));
|
||||||
|
|
||||||
l.Add($"clip = vs.core.resize.Bicubic(clip, format=vs.YUV444P16, matrix_s=cMatrix)"); // Convert RGB to YUV
|
l.Add($"clip = vs.core.resize.Bicubic(clip, format=vs.YUV444P16, matrix_s={(loadFrames ? "'470bg'" : "cMatrix")})"); // Convert RGB to YUV. Always use 470bg if input is frames
|
||||||
|
|
||||||
if (!s.Dedupe) // Ignore trimming code when using deduping that that already handles trimming in the frame order file
|
if (!s.Dedupe) // Ignore trimming code when using deduping that that already handles trimming in the frame order file
|
||||||
{
|
{
|
||||||
@@ -131,7 +133,7 @@ namespace Flowframes.Os
|
|||||||
return vpyPath;
|
return vpyPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
static string GetScaleLines(VsSettings settings)
|
static string GetScaleLines(VsSettings settings, bool loadFrames)
|
||||||
{
|
{
|
||||||
InterpSettings interp = settings.InterpSettings;
|
InterpSettings interp = settings.InterpSettings;
|
||||||
bool resize = !interp.ScaledResolution.IsEmpty && interp.ScaledResolution != interp.InputResolution;
|
bool resize = !interp.ScaledResolution.IsEmpty && interp.ScaledResolution != interp.InputResolution;
|
||||||
@@ -141,7 +143,7 @@ namespace Flowframes.Os
|
|||||||
s += $"cMatrix = '709'\n";
|
s += $"cMatrix = '709'\n";
|
||||||
s += $"\n";
|
s += $"\n";
|
||||||
|
|
||||||
if (!interp.inputIsFrames)
|
if (!loadFrames)
|
||||||
{
|
{
|
||||||
s += "try:\n";
|
s += "try:\n";
|
||||||
s += " m = clip.get_frame(0).props._Matrix\n";
|
s += " m = clip.get_frame(0).props._Matrix\n";
|
||||||
|
|||||||
Reference in New Issue
Block a user