Fix color hue shift when interpolating frames, support high bit depth frames

This commit is contained in:
N00MKRAD
2023-12-22 05:20:22 +01:00
parent 4eb1eac09c
commit 35b8d888d4
4 changed files with 81 additions and 34 deletions

View File

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

View File

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

View File

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

View File

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