mirror of
https://github.com/n00mkrad/flowframes.git
synced 2025-12-16 16:37:48 +01:00
Pad/crop system for improved model support, VS improvements
This commit is contained in:
@@ -1,11 +1,7 @@
|
||||
using Flowframes.IO;
|
||||
using Flowframes.MiscUtils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flowframes.Data
|
||||
{
|
||||
@@ -26,7 +22,7 @@ namespace Flowframes.Data
|
||||
|
||||
public string LogFilename { get { return PkgDir + "-log"; } }
|
||||
|
||||
public AiInfo () { }
|
||||
public AiInfo() { }
|
||||
|
||||
public AiInfo(AiBackend backend, string aiName, string longName, InterpFactorSupport factorSupport = InterpFactorSupport.Fixed, int[] supportedFactors = null)
|
||||
{
|
||||
@@ -37,7 +33,7 @@ namespace Flowframes.Data
|
||||
FactorSupport = factorSupport;
|
||||
}
|
||||
|
||||
public string GetVerboseInfo ()
|
||||
public string GetVerboseInfo()
|
||||
{
|
||||
return $"Name:\n{NameShort}\n\n" +
|
||||
$"Full Name:\n{NameLong}\n\n" +
|
||||
@@ -48,12 +44,12 @@ namespace Flowframes.Data
|
||||
$"Package Directory/Size:\n{PkgDir} ({FormatUtils.Bytes(IoUtils.GetDirSize(Path.Combine(Paths.GetPkgPath(), PkgDir), true))})";
|
||||
}
|
||||
|
||||
private string GetImplemString (AiBackend backend)
|
||||
private string GetImplemString(AiBackend backend)
|
||||
{
|
||||
if (backend == AiBackend.Pytorch)
|
||||
return $"CUDA/Pytorch Implementation";
|
||||
|
||||
if(backend == AiBackend.Ncnn)
|
||||
if (backend == AiBackend.Ncnn)
|
||||
return $"Vulkan/NCNN{(Piped ? "/VapourSynth" : "")} Implementation";
|
||||
|
||||
if (backend == AiBackend.Tensorflow)
|
||||
@@ -76,7 +72,7 @@ namespace Flowframes.Data
|
||||
return "Custom";
|
||||
}
|
||||
|
||||
private string GetHwAccelString (AiBackend backend)
|
||||
private string GetHwAccelString(AiBackend backend)
|
||||
{
|
||||
if (Backend == AiBackend.Pytorch)
|
||||
return $"GPU (Nvidia CUDA)";
|
||||
@@ -87,7 +83,7 @@ namespace Flowframes.Data
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
private string GetFactorsString (InterpFactorSupport factorSupport)
|
||||
private string GetFactorsString(InterpFactorSupport factorSupport)
|
||||
{
|
||||
if (factorSupport == InterpFactorSupport.Fixed)
|
||||
return $"{string.Join(", ", SupportedFactors.Select(x => $"{x}x"))}";
|
||||
@@ -103,5 +99,33 @@ namespace Flowframes.Data
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || GetType() != obj.GetType())
|
||||
return false;
|
||||
|
||||
var other = (AiInfo)obj;
|
||||
return Backend == other.Backend && NameInternal == other.NameInternal;
|
||||
}
|
||||
|
||||
// Combine hash codes of properties (using a simple hash approach for .NET Framework)
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 23 + Backend.GetHashCode();
|
||||
hash = hash * 23 + (NameInternal?.GetHashCode() ?? 0);
|
||||
return hash;
|
||||
}
|
||||
|
||||
public static bool operator ==(AiInfo left, AiInfo right)
|
||||
{
|
||||
if (left is null)
|
||||
return ReferenceEquals(right, null);
|
||||
else
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(AiInfo left, AiInfo right) => !(left == right);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flowframes.Data
|
||||
namespace Flowframes.Data
|
||||
{
|
||||
public class OutputSettings
|
||||
{
|
||||
|
||||
@@ -3,13 +3,13 @@ using Flowframes.Data;
|
||||
using Flowframes.IO;
|
||||
using Flowframes.Main;
|
||||
using Flowframes.MiscUtils;
|
||||
using Flowframes.Ui;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Flowframes
|
||||
{
|
||||
@@ -33,10 +33,49 @@ namespace Flowframes
|
||||
public string interpFolder;
|
||||
public bool inputIsFrames;
|
||||
|
||||
private Size _inputResolution;
|
||||
public Size InputResolution { get { RefreshInputRes(); return _inputResolution; } }
|
||||
public Size ScaledResolution { get { return InterpolateUtils.GetOutputResolution(InputResolution, false); } }
|
||||
public Size ScaledPaddedResolution { get { return InterpolateUtils.GetOutputResolution(InputResolution, true); } }
|
||||
private Size _inputResolution = new Size();
|
||||
public Size InputResolution
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_inputResolution.IsEmpty)
|
||||
_inputResolution = GetMediaResolutionCached.GetSizeAsync(inPath).Result;
|
||||
return _inputResolution;
|
||||
}
|
||||
}
|
||||
|
||||
private Size _outputResolution = new Size();
|
||||
public Size OutputResolution
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_outputResolution.IsEmpty)
|
||||
_outputResolution = InterpolateUtils.GetInterpolationResolution(FfmpegCommands.ModuloMode.ForEncoding, InputResolution);
|
||||
return _outputResolution;
|
||||
}
|
||||
}
|
||||
|
||||
private Size _scaledResolution = new Size();
|
||||
public Size ScaledResolution
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_scaledResolution.IsEmpty)
|
||||
_scaledResolution = InterpolateUtils.GetInterpolationResolution(FfmpegCommands.ModuloMode.Disabled, InputResolution);
|
||||
return _scaledResolution;
|
||||
}
|
||||
}
|
||||
|
||||
private Size _interpResolution = new Size();
|
||||
public Size InterpResolution
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_interpResolution.IsEmpty)
|
||||
_interpResolution = InterpolateUtils.GetInterpolationResolution(FfmpegCommands.ModuloMode.ForInterpolation, InputResolution);
|
||||
return _interpResolution;
|
||||
}
|
||||
}
|
||||
|
||||
public bool alpha;
|
||||
public bool stepByStep;
|
||||
@@ -169,12 +208,6 @@ namespace Flowframes
|
||||
inputIsFrames = IoUtils.IsPathDirectory(inPath);
|
||||
}
|
||||
|
||||
async Task RefreshInputRes ()
|
||||
{
|
||||
if (_inputResolution.IsEmpty)
|
||||
_inputResolution = await GetMediaResolutionCached.GetSizeAsync(inPath);
|
||||
}
|
||||
|
||||
public void RefreshAlpha ()
|
||||
{
|
||||
try
|
||||
@@ -239,7 +272,7 @@ namespace Flowframes
|
||||
s += $"OUTMODE|{outSettings.Format}\n";
|
||||
s += $"MODEL|{model.Name}\n";
|
||||
s += $"INPUTRES|{InputResolution.Width}x{InputResolution.Height}\n";
|
||||
s += $"OUTPUTRES|{ScaledResolution.Width}x{ScaledResolution.Height}\n";
|
||||
s += $"OUTPUTRES|{OutputResolution.Width}x{OutputResolution.Height}\n";
|
||||
s += $"ALPHA|{alpha}\n";
|
||||
s += $"STEPBYSTEP|{stepByStep}\n";
|
||||
s += $"FRAMESEXT|{framesExt}\n";
|
||||
|
||||
@@ -74,8 +74,8 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\RelGui</OutputPath>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<Optimize>false</Optimize>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
|
||||
@@ -601,7 +601,7 @@ namespace Flowframes.IO
|
||||
Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat());
|
||||
float fps = fpsLimit ? maxFps.Float : curr.outFps.Float;
|
||||
|
||||
Size outRes = await InterpolateUtils.GetOutputResolution(curr.inPath, true);
|
||||
Size outRes = curr.OutputResolution; // TODO: Replace with EncodeResolution once implemented?
|
||||
string pattern = Config.Get(Config.Key.exportNamePattern);
|
||||
string inName = Interpolate.currentSettings.inputIsFrames ? Path.GetFileName(curr.inPath) : Path.GetFileNameWithoutExtension(curr.inPath);
|
||||
bool encodeBoth = Config.GetInt(Config.Key.maxFpsMode) == 0;
|
||||
|
||||
@@ -19,8 +19,8 @@ namespace Flowframes.Main
|
||||
{
|
||||
class Export
|
||||
{
|
||||
private static string MaxFps = Config.Get(Config.Key.maxFps);
|
||||
private static Fraction MaxFpsFrac = new Fraction(MaxFps);
|
||||
private static string MaxFps => Config.Get(Config.Key.maxFps);
|
||||
private static Fraction MaxFpsFrac => new Fraction(MaxFps);
|
||||
|
||||
public static async Task ExportFrames(string path, string outFolder, OutputSettings exportSettings, bool stepByStep)
|
||||
{
|
||||
@@ -75,7 +75,7 @@ namespace Flowframes.Main
|
||||
public static async Task<string> GetPipedFfmpegCmd(bool ffplay = false)
|
||||
{
|
||||
InterpSettings s = I.currentSettings;
|
||||
string encArgs = FfmpegUtils.GetEncArgs(s.outSettings, (s.ScaledResolution.IsEmpty ? s.InputResolution : s.ScaledResolution), s.outFps.Float, true).FirstOrDefault();
|
||||
string encArgs = FfmpegUtils.GetEncArgs(s.outSettings, (s.OutputResolution.IsEmpty ? s.InputResolution : s.OutputResolution), s.outFps.Float, true).FirstOrDefault();
|
||||
bool fpsLimit = MaxFpsFrac.Float > 0f && s.outFps.Float > MaxFpsFrac.Float;
|
||||
bool gifInput = I.currentMediaFile.Format.Upper() == "GIF"; // If input is GIF, we don't need to check the color space etc
|
||||
VidExtraData extraData = gifInput ? new VidExtraData() : await FfmpegCommands.GetVidExtraInfo(s.inPath);
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace Flowframes
|
||||
if (!currentSettings.inputIsFrames) // Extract if input is video, import if image sequence
|
||||
await ExtractFrames(currentSettings.inPath, currentSettings.framesFolder, currentSettings.alpha);
|
||||
else
|
||||
await FfmpegExtract.ImportImagesCheckCompat(currentSettings.inPath, currentSettings.framesFolder, currentSettings.alpha, currentSettings.ScaledResolution, true, currentSettings.framesExt);
|
||||
await FfmpegExtract.ImportImagesCheckCompat(currentSettings.inPath, currentSettings.framesFolder, currentSettings.alpha, currentSettings.OutputResolution, true, currentSettings.framesExt);
|
||||
}
|
||||
|
||||
public static async Task ExtractFrames(string inPath, string outPath, bool alpha)
|
||||
@@ -137,7 +137,7 @@ namespace Flowframes
|
||||
Program.mainForm.SetStatus("Extracting frames from video...");
|
||||
currentSettings.RefreshExtensions(InterpSettings.FrameType.Import);
|
||||
bool mpdecimate = Config.GetInt(Config.Key.dedupMode) == 2;
|
||||
Size res = await Utils.GetOutputResolution(inPath, true, true);
|
||||
Size res = await Utils.GetOutputResolution(FfmpegCommands.ModuloMode.ForEncoding, inPath, print: true);
|
||||
await FfmpegExtract.VideoToFrames(inPath, outPath, alpha, currentSettings.inFpsDetected, mpdecimate, false, res, currentSettings.framesExt);
|
||||
|
||||
if (mpdecimate)
|
||||
|
||||
@@ -12,6 +12,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using I = Flowframes.Interpolate;
|
||||
using Padding = Flowframes.Data.Padding;
|
||||
using Flowframes.MiscUtils;
|
||||
|
||||
namespace Flowframes.Main
|
||||
{
|
||||
@@ -252,42 +253,42 @@ namespace Flowframes.Main
|
||||
|
||||
public static bool IsVideoValid(string videoPath)
|
||||
{
|
||||
if (videoPath == null || !IoUtils.IsFileValid(videoPath))
|
||||
return false;
|
||||
return true;
|
||||
return videoPath != null && IoUtils.IsFileValid(videoPath);
|
||||
}
|
||||
|
||||
public static async Task<Size> GetOutputResolution(string inputPath, bool pad, bool print = false)
|
||||
public static async Task<Size> GetOutputResolution(FfmpegCommands.ModuloMode moduloMode, string inputPath, bool print = false)
|
||||
{
|
||||
Size resolution = await GetMediaResolutionCached.GetSizeAsync(inputPath);
|
||||
return GetOutputResolution(resolution, pad, print);
|
||||
return GetInterpolationResolution(moduloMode, resolution, print);
|
||||
}
|
||||
|
||||
public static Size GetOutputResolution(Size inputRes, bool pad, bool print = false)
|
||||
public static Size GetInterpolationResolution(FfmpegCommands.ModuloMode moduloMode, Size inputRes, bool onlyRoundUp = true, bool print = false)
|
||||
{
|
||||
Size res = new Size(inputRes.Width, inputRes.Height);
|
||||
int maxHeight = Config.GetInt(Config.Key.maxVidHeight);
|
||||
int mod = pad ? FfmpegCommands.GetModulo() : 1;
|
||||
int modulo = FfmpegCommands.GetModulo(moduloMode);
|
||||
float factor = res.Height > maxHeight ? (float)maxHeight / res.Height : 1f; // Calculate downscale factor if bigger than max, otherwise just use 1x
|
||||
Logger.Log($"Un-rounded downscaled size: {(res.Width * factor).ToString("0.###")}x{(res.Height * factor).ToString("0.###")}", true);
|
||||
int width = RoundDivisibleBy((res.Width * factor).RoundToInt(), mod);
|
||||
int height = RoundDivisibleBy((res.Height * factor).RoundToInt(), mod);
|
||||
int width = RoundDivisibleBy((res.Width * factor).RoundToInt(), modulo, onlyRoundUp);
|
||||
int height = RoundDivisibleBy((res.Height * factor).RoundToInt(), modulo, onlyRoundUp);
|
||||
res = new Size(width, height);
|
||||
|
||||
if (print && factor < 1f)
|
||||
Logger.Log($"Video is bigger than the maximum - Downscaling to {width}x{height}.");
|
||||
|
||||
if (res != inputRes)
|
||||
Logger.Log($"Scaled {inputRes.Width}x{inputRes.Height} to {res.Width}x{res.Height}", true);
|
||||
|
||||
Logger.Log($"Scaled input res {inputRes.Width}x{inputRes.Height} to {res.Width}x{res.Height} ({moduloMode})", true);
|
||||
return res;
|
||||
}
|
||||
|
||||
public static int RoundDivisibleBy(int number, int divisibleBy) // Round to a number that's divisible by 2 (for h264 etc)
|
||||
public static int RoundDivisibleBy(float number, int divisibleBy, bool onlyRoundUp = false)
|
||||
{
|
||||
int a = (number / divisibleBy) * divisibleBy; // Smaller multiple
|
||||
int b = a + divisibleBy; // Larger multiple
|
||||
return (number - a > b - number) ? b : a; // Return of closest of two
|
||||
int numberInt = number.RoundToInt();
|
||||
|
||||
if (divisibleBy == 0)
|
||||
return numberInt;
|
||||
|
||||
return onlyRoundUp
|
||||
? (int)Math.Ceiling((double)number / divisibleBy) * divisibleBy
|
||||
: (int)Math.Round((double)number / divisibleBy) * divisibleBy;
|
||||
}
|
||||
|
||||
public static bool CanUseAutoEnc(bool stepByStep, InterpSettings current)
|
||||
@@ -334,9 +335,9 @@ namespace Flowframes.Main
|
||||
return true;
|
||||
}
|
||||
|
||||
public static async Task<bool> UseUhd()
|
||||
public static bool UseUhd()
|
||||
{
|
||||
return UseUhd(await GetOutputResolution(I.currentSettings.inPath, false));
|
||||
return UseUhd(I.currentSettings.OutputResolution);
|
||||
}
|
||||
|
||||
public static bool UseUhd(Size outputRes)
|
||||
@@ -358,7 +359,7 @@ namespace Flowframes.Main
|
||||
if (sceneFramesToDelete.Contains(scnFrame))
|
||||
continue;
|
||||
|
||||
int sourceIndexForScnFrame = sourceFrames.IndexOf(scnFrame); // Get source index of scene frame
|
||||
int sourceIndexForScnFrame = sourceFrames.IndexOf(scnFrame); // Get source index of scene frame
|
||||
if ((sourceIndexForScnFrame + 1) == sourceFrames.Count)
|
||||
continue;
|
||||
string followingFrame = sourceFrames[sourceIndexForScnFrame + 1]; // Get filename/timestamp of the next source frame
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Flowframes.Media;
|
||||
using Flowframes.Data;
|
||||
using Flowframes.IO;
|
||||
using Flowframes.MiscUtils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
@@ -9,7 +8,6 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static Flowframes.AvProcess;
|
||||
using System.Drawing.Imaging;
|
||||
|
||||
namespace Flowframes
|
||||
{
|
||||
@@ -34,21 +32,38 @@ namespace Flowframes
|
||||
return wrap ? mpd.Wrap() : mpd;
|
||||
}
|
||||
|
||||
public static int GetModulo()
|
||||
{
|
||||
if (Interpolate.currentSettings.ai.NameInternal == Implementations.flavrCuda.NameInternal)
|
||||
return 8;
|
||||
public enum ModuloMode { Disabled, ForInterpolation, ForEncoding }
|
||||
|
||||
return Interpolate.currentSettings.outSettings.Encoder.GetInfo().Modulo;
|
||||
public static int GetModulo(ModuloMode mode)
|
||||
{
|
||||
if (mode == ModuloMode.ForEncoding)
|
||||
{
|
||||
return Interpolate.currentSettings.outSettings.Encoder.GetInfo().Modulo;
|
||||
}
|
||||
else if (mode == ModuloMode.ForInterpolation)
|
||||
{
|
||||
bool RifeNeedsPadding(string ver) => ver.Split('.').Last().GetInt() >= 25; // e.g. "RIFE 4.25" needs padding
|
||||
|
||||
if (Interpolate.currentSettings.model.Name.Contains("RIFE") && RifeNeedsPadding(Interpolate.currentSettings.model.Name))
|
||||
return 64;
|
||||
|
||||
if (Interpolate.currentSettings.ai == Implementations.flavrCuda)
|
||||
return 8;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static string GetPadFilter()
|
||||
public static string GetPadFilter(int width = -1, int height = -1)
|
||||
{
|
||||
int mod = GetModulo();
|
||||
int mod = GetModulo(ModuloMode.ForEncoding);
|
||||
|
||||
if (mod < 2)
|
||||
return "";
|
||||
|
||||
if (width > 0 && width % mod == 0 && height > 0 && height % mod == 0)
|
||||
return "";
|
||||
|
||||
return $"pad=width=ceil(iw/{mod})*{mod}:height=ceil(ih/{mod})*{mod}:color=black@0";
|
||||
}
|
||||
|
||||
@@ -154,14 +169,14 @@ namespace Flowframes
|
||||
}
|
||||
}
|
||||
|
||||
public static void CheckVfr (string inputFile, MediaFile mediaFile, List<string> outputLinesPackets = null)
|
||||
public static void CheckVfr(string inputFile, MediaFile mediaFile, List<string> outputLinesPackets = null)
|
||||
{
|
||||
if (mediaFile.InputTimestamps.Any())
|
||||
return;
|
||||
|
||||
Logger.Log($"Checking frame timing...", true, false, "ffmpeg");
|
||||
|
||||
if(outputLinesPackets == null)
|
||||
if (outputLinesPackets == null)
|
||||
{
|
||||
string argsPackets = $"ffprobe -v error -select_streams v:0 -show_packets -show_entries packet=pts_time -read_intervals \"%+120\" -of csv=p=0 {inputFile.Wrap()}";
|
||||
outputLinesPackets = NUtilsTemp.OsUtils.RunCommand($"cd /D {GetAvDir().Wrap()} && {argsPackets}").SplitIntoLines().Where(l => l.IsNotEmpty()).ToList();
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Flowframes.Media
|
||||
|
||||
IoUtils.RenameExistingFileOrDir(outPath);
|
||||
Directory.CreateDirectory(outPath.GetParentDir());
|
||||
string[] encArgs = Utils.GetEncArgs(settings, (Interpolate.currentSettings.ScaledResolution.IsEmpty ? Interpolate.currentSettings.InputResolution : Interpolate.currentSettings.ScaledResolution), Interpolate.currentSettings.outFps.Float);
|
||||
string[] encArgs = Utils.GetEncArgs(settings, (Interpolate.currentSettings.OutputResolution.IsEmpty ? Interpolate.currentSettings.InputResolution : Interpolate.currentSettings.OutputResolution), Interpolate.currentSettings.outFps.Float);
|
||||
|
||||
string inArg = $"-f concat -i {Path.GetFileName(framesFile)}";
|
||||
string linksDir = Path.Combine(framesFile + Paths.symlinksSuffix);
|
||||
@@ -102,12 +102,7 @@ namespace Flowframes.Media
|
||||
filters.Add($"zscale=transfer=linear,format={settings.PixelFormat.ToString().Lower()}".Wrap());
|
||||
}
|
||||
|
||||
// Only if encoder is not GIF and width and height are not divisible by 2
|
||||
if (settings.Encoder != Enums.Encoding.Encoder.Gif && (mf.VideoStreams[0].Resolution.Width % 2 != 0 || mf.VideoStreams[0].Resolution.Height % 2 != 0))
|
||||
{
|
||||
filters.Add(GetPadFilter());
|
||||
}
|
||||
|
||||
filters.Add(GetPadFilter(Interpolate.currentSettings.ScaledResolution.Width, Interpolate.currentSettings.ScaledResolution.Height));
|
||||
filters = filters.Where(f => f.IsNotEmpty()).ToList();
|
||||
|
||||
return filters.Count > 0 ?
|
||||
|
||||
@@ -204,14 +204,16 @@ namespace Flowframes.Media
|
||||
return false;
|
||||
}
|
||||
|
||||
int div = GetModulo();
|
||||
bool allDivBy2 = randomSamples.All(i => (i.Width % div == 0) && (i.Height % div == 0));
|
||||
Logger.Log($"---> TODO: Modulo check for images? Or just check later and apply padding before interpolation if needed...", hidden: true);
|
||||
|
||||
if (!allDivBy2)
|
||||
{
|
||||
Logger.Log($"Sequence not compatible: Not all image dimensions are divisible by {div}.", true);
|
||||
return false;
|
||||
}
|
||||
// int div = GetModulo();
|
||||
// bool allDivBy2 = randomSamples.All(i => (i.Width % div == 0) && (i.Height % div == 0));
|
||||
//
|
||||
// if (!allDivBy2)
|
||||
// {
|
||||
// Logger.Log($"Sequence not compatible: Not all image dimensions are divisible by {div}.", true);
|
||||
// return false;
|
||||
// }
|
||||
|
||||
bool allSmallEnough = randomSamples.All(i => (i.Height <= maxHeight));
|
||||
|
||||
|
||||
@@ -211,5 +211,16 @@ namespace Flowframes.MiscUtils
|
||||
string t = showTildeForApprox && !isPrecise ? "~" : "";
|
||||
return $"{f} ({t}{decimalStr})";
|
||||
}
|
||||
|
||||
public static string GetCallStack(int maxLevels = 10)
|
||||
{
|
||||
var trace = new System.Diagnostics.StackTrace().GetFrames()
|
||||
.Skip(1) // Skip the current method
|
||||
.Select(f => f.GetMethod().Name)
|
||||
.Take(maxLevels)
|
||||
.ToList();
|
||||
return string.Join(", ", trace);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,12 @@ namespace Flowframes.Os
|
||||
if (!Interpolate.currentSettings.ai.Piped)
|
||||
InterpolationProgress.UpdateInterpProgress(interpFramesCount, InterpolationProgress.targetFrames);
|
||||
|
||||
string logStr = $"Done running {aiName} - Interpolation took {FormatUtils.Time(processTime.Elapsed)}. Output FPS: {InterpolationProgress.LastFps.ToString("0.0")}";
|
||||
string logStr = $"Done running {aiName} - Interpolation took {FormatUtils.Time(processTime.Elapsed)}.";
|
||||
|
||||
if(InterpolationProgress.LastFps > 0.0001)
|
||||
{
|
||||
logStr += $" Output FPS: {InterpolationProgress.LastFps.ToString("0.0")}";
|
||||
}
|
||||
|
||||
if (Interpolate.currentlyUsingAutoEnc && AutoEncode.HasWorkToDo())
|
||||
{
|
||||
@@ -198,7 +203,7 @@ namespace Flowframes.Os
|
||||
{
|
||||
string outPath = Path.Combine(inPath.GetParentDir(), outDir);
|
||||
Directory.CreateDirectory(outPath);
|
||||
string uhdStr = await InterpolateUtils.UseUhd() ? "--UHD" : "";
|
||||
string uhdStr = InterpolateUtils.UseUhd() ? "--UHD" : "";
|
||||
string wthreads = $"--wthreads {2 * (int)interpFactor}";
|
||||
string rbuffer = $"--rbuffer {Config.GetInt(Config.Key.rifeCudaBufferSize, 200)}";
|
||||
//string scale = $"--scale {Config.GetFloat("rifeCudaScale", 1.0f).ToStringDot()}";
|
||||
@@ -210,7 +215,7 @@ namespace Flowframes.Os
|
||||
SetProgressCheck(Path.Combine(Interpolate.currentSettings.tempFolder, outDir), interpFactor);
|
||||
rifePy.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {Path.Combine(Paths.GetPkgPath(), Implementations.rifeCuda.PkgDir).Wrap()} & " +
|
||||
$"set CUDA_VISIBLE_DEVICES={Config.Get(Config.Key.torchGpus)} & {Python.GetPyCmd()} {script} {args}";
|
||||
Logger.Log($"Running RIFE (CUDA){(await InterpolateUtils.UseUhd() ? " (UHD Mode)" : "")}...", false);
|
||||
Logger.Log($"Running RIFE (CUDA){(InterpolateUtils.UseUhd() ? " (UHD Mode)" : "")}...", false);
|
||||
Logger.Log("cmd.exe " + rifePy.StartInfo.Arguments, true);
|
||||
|
||||
if (!OsUtils.ShowHiddenCmd())
|
||||
@@ -297,7 +302,7 @@ namespace Flowframes.Os
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Log($"Running RIFE (NCNN){(await InterpolateUtils.UseUhd() ? " (UHD Mode)" : "")}...", false);
|
||||
Logger.Log($"Running RIFE (NCNN){(InterpolateUtils.UseUhd() ? " (UHD Mode)" : "")}...", false);
|
||||
|
||||
await RunRifeNcnnProcess(framesPath, factor, outPath, mdl);
|
||||
await NcnnUtils.DeleteNcnnDupes(outPath, factor);
|
||||
@@ -321,7 +326,7 @@ namespace Flowframes.Os
|
||||
int targetFrames = ((IoUtils.GetAmountOfFiles(lastInPath, false, "*.*") * factor).RoundToInt()); // TODO: Maybe won't work with fractional factors ??
|
||||
|
||||
string frames = mdl.Contains("v4") ? $"-n {targetFrames}" : "";
|
||||
string uhdStr = await InterpolateUtils.UseUhd() ? "-u" : "";
|
||||
string uhdStr = InterpolateUtils.UseUhd() ? "-u" : "";
|
||||
string ttaStr = Config.GetBool(Config.Key.rifeNcnnUseTta, false) ? "-x" : "";
|
||||
|
||||
rifeNcnn.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {Path.Combine(Paths.GetPkgPath(), Implementations.rifeNcnn.PkgDir).Wrap()} & rife-ncnn-vulkan.exe " +
|
||||
@@ -355,10 +360,8 @@ namespace Flowframes.Os
|
||||
|
||||
try
|
||||
{
|
||||
Size scaledSize = await InterpolateUtils.GetOutputResolution(Interpolate.currentSettings.inPath, false, false);
|
||||
Logger.Log($"Running RIFE (NCNN-VS){(InterpolateUtils.UseUhd(scaledSize) ? " (UHD Mode)" : "")}...", false);
|
||||
|
||||
await RunRifeNcnnVsProcess(framesPath, factor, outPath, mdl, scaledSize, rt);
|
||||
Logger.Log($"Running RIFE (NCNN-VS){(InterpolateUtils.UseUhd(Interpolate.currentSettings.OutputResolution) ? " (UHD Mode)" : "")}...", false);
|
||||
await RunRifeNcnnVsProcess(framesPath, factor, outPath, mdl, Interpolate.currentSettings.OutputResolution, rt);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -406,7 +409,7 @@ namespace Flowframes.Os
|
||||
AiStarted(rifeNcnnVs, 1000, inPath);
|
||||
}
|
||||
|
||||
rifeNcnnVs.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {pkgDir.Wrap()} & vspipe {VapourSynthUtils.CreateScript(vsSettings).Wrap()} -c y4m - | {pipedTargetArgs}";
|
||||
rifeNcnnVs.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {pkgDir.Wrap()} & vspipe {VapourSynthUtils.CreateScript(vsSettings).Wrap()} {VapourSynthUtils.GetVsPipeArgs(vsSettings)} -c y4m - | {pipedTargetArgs}";
|
||||
|
||||
Logger.Log("cmd.exe " + rifeNcnnVs.StartInfo.Arguments, true);
|
||||
|
||||
@@ -554,7 +557,7 @@ namespace Flowframes.Os
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Log($"Running IFRNet (NCNN){(await InterpolateUtils.UseUhd() ? " (UHD Mode)" : "")}...", false);
|
||||
Logger.Log($"Running IFRNet (NCNN){(InterpolateUtils.UseUhd() ? " (UHD Mode)" : "")}...", false);
|
||||
|
||||
await RunIfrnetNcnnProcess(framesPath, factor, outPath, mdl);
|
||||
await NcnnUtils.DeleteNcnnDupes(outPath, factor);
|
||||
@@ -576,7 +579,7 @@ namespace Flowframes.Os
|
||||
SetProgressCheck(outPath, factor);
|
||||
//int targetFrames = ((IoUtils.GetAmountOfFiles(lastInPath, false, "*.*") * factor).RoundToInt()); // TODO: Maybe won't work with fractional factors ??
|
||||
//string frames = mdl.Contains("v4") ? $"-n {targetFrames}" : "";
|
||||
string uhdStr = ""; // await InterpolateUtils.UseUhd() ? "-u" : "";
|
||||
string uhdStr = ""; // InterpolateUtils.UseUhd() ? "-u" : "";
|
||||
string ttaStr = ""; // Config.GetBool(Config.Key.rifeNcnnUseTta, false) ? "-x" : "";
|
||||
|
||||
ifrnetNcnn.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {Path.Combine(Paths.GetPkgPath(), Implementations.ifrnetNcnn.PkgDir).Wrap()} & ifrnet-ncnn-vulkan.exe " +
|
||||
|
||||
@@ -22,21 +22,35 @@ namespace Flowframes.Os
|
||||
public bool Uhd { get; set; } = false;
|
||||
public float SceneDetectSensitivity { get; set; } = 0.15f;
|
||||
public int GpuId { get; set; } = 0;
|
||||
public int GpuThreads { get; set; } = 3;
|
||||
public int GpuThreads { get; set; } = 2;
|
||||
public bool Tta { get; set; } = false;
|
||||
public bool Loop { get; set; } = false;
|
||||
public bool MatchDuration { get; set; } = false;
|
||||
public bool Dedupe { get; set; } = false;
|
||||
public bool Realtime { get; set; } = false;
|
||||
public bool Osd { get; set; } = true;
|
||||
public int PadX { get; set; } = 0;
|
||||
public int PadY { get; set; } = 0;
|
||||
}
|
||||
|
||||
public static string CreateScript(VsSettings s)
|
||||
public const string VarInputPath = "inputPath";
|
||||
public const string VarTempDirPath = "tempDirPath";
|
||||
public const string VarLwiPath = "cacheLwiPath";
|
||||
|
||||
public static string GetVsPipeArgs(VsSettings s)
|
||||
{
|
||||
return $"--arg {VarInputPath}={s.InterpSettings.inPath.Wrap()} --arg {VarTempDirPath}={s.InterpSettings.tempFolder.Wrap()}";
|
||||
}
|
||||
|
||||
public static string CreateScript(VsSettings s, bool alwaysPreferFactorOverFps = true)
|
||||
{
|
||||
Logger.Log($"Creating RIFE VS script. Model: {s.ModelDir}, Factor: {s.Factor}, Res: {s.Res.Width}x{s.Res.Height}, UHD: {s.Uhd}, SC Sens: {s.SceneDetectSensitivity}, " +
|
||||
$"GPU ID: {s.GpuId}, GPU Threads: {s.GpuThreads}, TTA: {s.Tta}, Loop: {s.Loop}, Match Duration: {s.MatchDuration}, Dedupe: {s.Dedupe}, RT: {s.Realtime}{(s.Osd ? $", OSD: {s.Osd}" : "")}", true);
|
||||
|
||||
string inputPath = s.InterpSettings.inPath;
|
||||
s.PadX = s.InterpSettings.InterpResolution.Width - s.InterpSettings.ScaledResolution.Width;
|
||||
s.PadY = s.InterpSettings.InterpResolution.Height - s.InterpSettings.ScaledResolution.Height;
|
||||
|
||||
int txtScale = (s.InterpSettings.ScaledResolution.Width / 1000f).RoundToInt().Clamp(1, 4);
|
||||
string mdlPath = Path.Combine(Paths.GetPkgPath(), Implementations.rifeNcnnVs.PkgDir, s.ModelDir).Replace(@"\", "/").Wrap();
|
||||
|
||||
bool sc = s.SceneDetectSensitivity >= 0.01f;
|
||||
@@ -53,14 +67,21 @@ namespace Flowframes.Os
|
||||
int targetFrameCountMatchDuration = (frameCount * s.Factor).RoundToInt(); // Target frame count to match original duration (and for loops)
|
||||
int targetFrameCountTrue = targetFrameCountMatchDuration - endDupeCount; // Target frame count without dupes at the end (only in-between frames added)
|
||||
|
||||
List<string> l = new List<string> { "import sys", "import os", "import json", "import time", "import functools", "import vapoursynth as vs", "core = vs.core", "" }; // Imports
|
||||
l.Add($"inputPath = r'{inputPath}'");
|
||||
List<string> imports = new List<string> { "sys", "os", "json", "time", "functools", "vapoursynth as vs" }; // Imports
|
||||
|
||||
var l = new List<string>() { "# Generated by Flowframes. Will be overwritten on the next run!", "" };
|
||||
l.AddRange(imports.Select(i => $"import {i}"));
|
||||
l.Add("core = vs.core");
|
||||
l.Add($"");
|
||||
l.Add($"inputPath = globals()[{VarInputPath.Wrap()}]");
|
||||
l.Add($"tempDirPath = globals()[{VarTempDirPath.Wrap()}]");
|
||||
l.Add($"");
|
||||
|
||||
bool loadFrames = s.InterpSettings.inputIsFrames;
|
||||
|
||||
if (loadFrames)
|
||||
{
|
||||
l.Add($"# Load frames");
|
||||
FileInfo[] frames = IoUtils.GetFileInfosSorted(s.InterpSettings.framesFolder, false, "*.*");
|
||||
string ext = frames.FirstOrDefault().Extension;
|
||||
string first = Path.GetFileNameWithoutExtension(frames.FirstOrDefault().FullName);
|
||||
@@ -69,29 +90,42 @@ namespace Flowframes.Os
|
||||
}
|
||||
else
|
||||
{
|
||||
l.Add("indexFilePath = f'{inputPath}.cache.lwi'");
|
||||
l.Add($"if os.path.isdir(r'{s.InterpSettings.tempFolder}'):");
|
||||
l.Add($" indexFilePath = r'{Path.Combine(s.InterpSettings.tempFolder, "cache.lwi")}'");
|
||||
l.Add($"# Load video");
|
||||
l.Add("indexFilePath = os.path.join(tempDirPath, 'index.lwi') if os.path.isdir(tempDirPath) else f'{inputPath}.index.lwi'");
|
||||
l.Add($"clip = core.lsmas.LWLibavSource(inputPath, cachefile=indexFilePath)"); // Load video with lsmash
|
||||
l.Add(Debugger.IsAttached ? "clip = core.text.FrameNum(clip, alignment=7)" : "");
|
||||
l.Add(GetDedupeLines(s));
|
||||
}
|
||||
|
||||
if (trim)
|
||||
l.Add($"clip = clip.std.Trim({srcTrimStartFrame}, {srcTrimEndFrame}) # Trim");
|
||||
|
||||
l.Add($"");
|
||||
l.Add($"srcFrames = len(clip)");
|
||||
l.Add(Debugger.IsAttached ? $"clip = core.text.FrameNum(clip, alignment=7, scale={txtScale}) # Input frame counter" : "");
|
||||
l.Add(GetDedupeLines(s));
|
||||
l.Add($"");
|
||||
|
||||
if (trim)
|
||||
{
|
||||
l.Add($"# Trim");
|
||||
l.Add($"clip = clip.std.Trim({srcTrimStartFrame}, {srcTrimEndFrame}) # Trim");
|
||||
l.Add($"");
|
||||
}
|
||||
|
||||
if (s.Loop && !s.InterpSettings.inputIsFrames)
|
||||
{
|
||||
l.Add($"# Loop: Copy first frame to end of clip");
|
||||
l.Add($"firstFrame = clip[0]"); // Grab first frame
|
||||
l.Add($"clip = clip + firstFrame"); // Add to end (for seamless loop interpolation)
|
||||
l.Add($"");
|
||||
}
|
||||
|
||||
l.Add($"firstFrameProps = clip.get_frame(0).props");
|
||||
l.Add(GetScaleLines(s, loadFrames));
|
||||
l.Add($"");
|
||||
|
||||
if (sc)
|
||||
l.Add($"clip = core.misc.SCDetect(clip=clip, threshold={s.SceneDetectSensitivity.ToStringDot()})"); // Scene detection
|
||||
{
|
||||
l.Add($"# Scene detection");
|
||||
l.Add($"clip = core.misc.SCDetect(clip=clip, threshold={s.SceneDetectSensitivity.ToString("0.0#####")})"); // Scene detection
|
||||
l.Add($"");
|
||||
}
|
||||
|
||||
Fraction outFps = s.InterpSettings.inFps * s.Factor;
|
||||
|
||||
@@ -100,16 +134,21 @@ namespace Flowframes.Os
|
||||
outFps = Interpolate.currentMediaFile.VideoStreams.First().FpsInfo.SpecifiedFps * s.Factor;
|
||||
}
|
||||
|
||||
Fraction factorFrac = new Fraction(s.Factor);
|
||||
string interpStr = Interpolate.currentMediaFile.IsVfr ? $"factor_num={factorFrac.Numerator}, factor_den={factorFrac.Denominator}" : $"fps_num={outFps.Numerator}, fps_den={outFps.Denominator}";
|
||||
l.Add($"preInterpFrames = len(clip)");
|
||||
|
||||
Fraction factor = new Fraction(s.Factor);
|
||||
string interpStr = alwaysPreferFactorOverFps || Interpolate.currentMediaFile.IsVfr ? $"factor_num={factor.Numerator}, factor_den={factor.Denominator}" : $"fps_num={outFps.Numerator}, fps_den={outFps.Denominator}";
|
||||
l.Add($"clip = core.rife.RIFE(clip, {interpStr}, model_path={mdlPath}, gpu_id={s.GpuId}, gpu_thread={s.GpuThreads}, tta={s.Tta}, uhd={s.Uhd}, sc={sc})"); // Interpolate
|
||||
|
||||
if (s.Dedupe && !s.Realtime)
|
||||
{
|
||||
l.Add(GetRedupeLines(s));
|
||||
l.Add($"");
|
||||
}
|
||||
|
||||
Console.WriteLine($"In Format: {Interpolate.currentMediaFile.Format.Upper()}");
|
||||
bool use470bg = loadFrames && !new[] { "GIF", "EXR" }.Contains(Interpolate.currentMediaFile.Format.Upper());
|
||||
l.Add($"clip = vs.core.resize.Bicubic(clip, format=vs.YUV444P16, matrix_s={(use470bg ? "'470bg'" : "cMatrix")})"); // Convert RGB to YUV. Always use 470bg if input is YUV frames
|
||||
l.Add($"clip = core.std.Crop(clip, right={s.PadX}, bottom={s.PadY})");
|
||||
|
||||
if (!s.Dedupe) // Ignore trimming code when using deduping that that already handles trimming in the frame order file
|
||||
{
|
||||
@@ -130,13 +169,14 @@ namespace Flowframes.Os
|
||||
if (s.Realtime && s.Osd)
|
||||
l.Add(GetOsdLines());
|
||||
|
||||
l.Add(Debugger.IsAttached ? "clip = core.text.Text(clip, str(len(clip)), alignment=4)" : "");
|
||||
l.Add(Debugger.IsAttached ? "clip = core.text.FrameNum(clip, alignment=1)" : "");
|
||||
l.Add(Debugger.IsAttached ? $"clip = core.text.FrameNum(clip, alignment=9, scale={txtScale}) # Output frame counter" : "");
|
||||
l.Add(Debugger.IsAttached ? $"clip = core.text.Text(clip, f\"Frames: {{srcFrames}}/{{preInterpFrames}} -> {{len(clip)}} [{factor.GetString()}x]\", alignment=8, scale={txtScale})" : "");
|
||||
l.Add(Debugger.IsAttached ? $"clip = core.text.Text(clip, f\"targetMatchDuration: {targetFrameCountMatchDuration} - targetTrue: {targetFrameCountTrue} - endDupeCount: {endDupeCount}\", alignment=2, scale={txtScale})" : "");
|
||||
l.Add($"clip.set_output()"); // Set output
|
||||
l.Add("");
|
||||
|
||||
l.Add($"if os.path.isfile(r'{inputPath}.cache.lwi'):");
|
||||
l.Add($" os.remove(r'{inputPath}.cache.lwi')");
|
||||
l.Add("if os.path.isfile(r'{inputPath}.index.lwi'):");
|
||||
l.Add(" os.remove(r'{inputPath}.index.lwi')");
|
||||
|
||||
string pkgPath = Path.Combine(Paths.GetPkgPath(), Implementations.rifeNcnnVs.PkgDir);
|
||||
string vpyPath = Path.Combine(pkgPath, "rife.vpy");
|
||||
@@ -150,16 +190,19 @@ namespace Flowframes.Os
|
||||
{
|
||||
InterpSettings interp = settings.InterpSettings;
|
||||
bool resize = !interp.ScaledResolution.IsEmpty && interp.ScaledResolution != interp.InputResolution;
|
||||
|
||||
string s = "";
|
||||
|
||||
s += $"\n";
|
||||
s += $"cMatrix = '709'\n";
|
||||
s += $"\n";
|
||||
s += $"# Scaled Res: {interp.ScaledResolution.Width}x{interp.ScaledResolution.Height} - Interpolation Res: {interp.InterpResolution.Width}x{interp.InterpResolution.Height} - Output Res: {interp.OutputResolution.Width}x{interp.OutputResolution.Height}\n";
|
||||
s += $"\n";
|
||||
|
||||
if (!loadFrames)
|
||||
{
|
||||
s += "try:\n";
|
||||
s += " m = clip.get_frame(0).props._Matrix\n";
|
||||
s += " m = firstFrameProps._Matrix\n";
|
||||
s += " if m == 0: cMatrix = 'rgb'\n";
|
||||
s += " elif m == 4: cMatrix = 'fcc'\n";
|
||||
s += " elif m == 5: cMatrix = '470bg'\n";
|
||||
@@ -174,12 +217,7 @@ namespace Flowframes.Os
|
||||
s += $"except:\n";
|
||||
s += $" cMatrix = '709'\n";
|
||||
s += $"\n";
|
||||
s += $"colRange = 'limited'\n";
|
||||
s += $"\n";
|
||||
s += $"try:\n";
|
||||
s += $" if clip.get_frame(0).props._ColorRange == 0: colRange = 'full'\n";
|
||||
s += $"except:\n";
|
||||
s += $" colRange = 'limited'\n";
|
||||
s += $"colRange = 'full' if firstFrameProps.get('_ColorRange') == 0 else 'limited'\n";
|
||||
s += $"\n";
|
||||
}
|
||||
|
||||
@@ -190,6 +228,13 @@ namespace Flowframes.Os
|
||||
s += $" clip = core.resize.Bicubic(clip=clip, format=vs.RGBS{(resize ? $", width={interp.ScaledResolution.Width}, height={interp.ScaledResolution.Height}" : "")})\n";
|
||||
s += $"\n";
|
||||
|
||||
if (settings.PadX > 0 || settings.PadY > 0)
|
||||
{
|
||||
s += "# Padding to achieve a compatible resolution\n";
|
||||
s += $"clip = core.std.AddBorders(clip, right={settings.PadX}, bottom={settings.PadY})\n";
|
||||
s += $"\n";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -211,8 +256,7 @@ namespace Flowframes.Os
|
||||
s += " reorderedClip = reorderedClip + clip[i]\n";
|
||||
s += "\n";
|
||||
s += "clip = reorderedClip.std.Trim(1, reorderedClip.num_frames - 1) # Dedupe trim\n";
|
||||
s += Debugger.IsAttached ? "clip = core.text.FrameNum(clip, alignment=4)\n" : "";
|
||||
s += "\n";
|
||||
//s += Debugger.IsAttached ? "clip = core.text.FrameNum(clip, alignment=9)\n" : "";
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -231,8 +275,7 @@ namespace Flowframes.Os
|
||||
s += " reorderedClip = reorderedClip + clip[i]\n";
|
||||
s += "\n";
|
||||
s += "clip = reorderedClip.std.Trim(1, reorderedClip.num_frames - 1) # Redupe trim\n";
|
||||
s += Debugger.IsAttached ? "clip = core.text.FrameNum(clip, alignment=1)\n" : "";
|
||||
s += "\n";
|
||||
//s += Debugger.IsAttached ? "clip = core.text.FrameNum(clip, alignment=1)\n" : "";
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -270,7 +313,6 @@ namespace Flowframes.Os
|
||||
s += $" return clip \n";
|
||||
s += $" \n";
|
||||
s += $"clip = core.std.FrameEval(clip, functools.partial(onFrame, clip=clip)) \n";
|
||||
s += $" \n";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user