2021-08-23 16:50:18 +02:00
using Flowframes.Data ;
using Flowframes.IO ;
using Flowframes.MiscUtils ;
2024-01-09 13:31:02 +01:00
using Flowframes.Os ;
using Flowframes.Properties ;
2021-09-28 20:44:08 +02:00
using Newtonsoft.Json ;
2021-08-23 16:50:18 +02:00
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.IO ;
using System.Linq ;
using System.Threading.Tasks ;
namespace Flowframes.Main
{
class FrameOrder
{
2024-01-09 13:31:02 +01:00
private static Stopwatch benchmark = new Stopwatch ( ) ;
private static FileInfo [ ] frameFiles ;
private static FileInfo [ ] frameFilesWithoutLast ;
private static List < string > sceneFrames = new List < string > ( ) ;
private static Dictionary < int , string > frameFileContents = new Dictionary < int , string > ( ) ;
private static List < string > inputFilenames = new List < string > ( ) ;
private static int lastOutFileCount ;
public static async Task CreateFrameOrderFile ( string tempFolder , bool loopEnabled , float interpFactor )
2021-08-23 16:50:18 +02:00
{
Logger . Log ( "Generating frame order information..." ) ;
try
{
2024-01-09 13:31:02 +01:00
foreach ( FileInfo file in IoUtils . GetFileInfosSorted ( tempFolder , false , $"{Paths.frameOrderPrefix}*.*" ) )
2021-08-23 16:50:18 +02:00
file . Delete ( ) ;
benchmark . Restart ( ) ;
2025-07-24 18:34:57 +02:00
bool vs = Interpolate . currentSettings . ai . NameInternal = = Implementations . rifeNcnnVs . NameInternal ;
2024-01-09 13:31:02 +01:00
2025-07-24 18:34:57 +02:00
if ( vs ) // && !Interpolate.currentSettings.inputIsFrames)
2024-01-09 13:31:02 +01:00
CreateFramesFileVid ( Interpolate . currentSettings . inPath , Interpolate . currentSettings . tempFolder , loopEnabled , interpFactor ) ;
else
await CreateFramesFileImgSeq ( tempFolder , loopEnabled , interpFactor ) ;
2021-08-23 16:50:18 +02:00
Logger . Log ( $"Generating frame order information... Done." , false , true ) ;
Logger . Log ( $"Generated frame order info file in {benchmark.ElapsedMilliseconds} ms" , true ) ;
}
catch ( Exception e )
{
Logger . Log ( $"Error generating frame order information: {e.Message}\n{e.StackTrace}" ) ;
}
2024-01-09 13:31:02 +01:00
}
2021-08-23 16:50:18 +02:00
2024-01-09 13:31:02 +01:00
static Dictionary < string , List < string > > dupesDict = new Dictionary < string , List < string > > ( ) ;
2021-08-23 16:50:18 +02:00
static void LoadDupesFile ( string path )
{
2022-05-26 18:25:02 +02:00
dupesDict = JsonConvert . DeserializeObject < Dictionary < string , List < string > > > ( File . ReadAllText ( path ) ) ;
2021-08-23 16:50:18 +02:00
}
2024-01-09 13:31:02 +01:00
public static void CreateFramesFileVid ( string vidPath , string tempFolder , bool loop , float interpFactor )
{
if ( Interpolate . canceled ) return ;
Logger . Log ( $"Generating frame order information for {interpFactor}x..." , false , true ) ;
// frameFileContents.Clear();
int lastOutFileCount = 0 ;
string inputJsonPath = Path . Combine ( tempFolder , "input.json" ) ;
List < int > inputFrames = JsonConvert . DeserializeObject < List < int > > ( File . ReadAllText ( inputJsonPath ) ) ;
int frameCount = Interpolate . currentMediaFile . FrameCount ;
frameCount = inputFrames . Count ;
// if (loop)
// {
// frameCount++;
// }
int frameCountWithoutLast = frameCount - 1 ;
string dupesFile = Path . Combine ( tempFolder , "dupes.test.json" ) ;
var dupes = JsonConvert . DeserializeObject < Dictionary < int , List < int > > > ( File . ReadAllText ( dupesFile ) ) ;
2025-06-28 23:16:14 +02:00
bool debug = Debugger . IsAttached | | Config . GetBool ( "frameOrderDebug" , false ) ;
2024-01-09 13:31:02 +01:00
int targetFrameCount = ( frameCount * interpFactor ) . RoundToInt ( ) - InterpolateUtils . GetRoundedInterpFramesPerInputFrame ( interpFactor ) ;
Fraction step = new Fraction ( frameCount , targetFrameCount + InterpolateUtils . GetRoundedInterpFramesPerInputFrame ( interpFactor ) ) ;
var framesList = new List < int > ( ) ;
2025-06-28 23:19:58 +02:00
void DebugLog ( string message )
{
if ( debug )
Console . WriteLine ( message ) ;
}
2024-01-09 13:31:02 +01:00
for ( int i = 0 ; i < targetFrameCount ; i + + )
{
2024-10-13 16:58:06 +02:00
float currentFrameTime = 1 + ( step * i ) . Float ;
2024-01-09 13:31:02 +01:00
int sourceFrameIdx = ( int ) Math . Floor ( currentFrameTime ) - 1 ;
framesList . Add ( i ) ;
2025-06-28 23:19:58 +02:00
DebugLog ( $"Frame: #{i} - Idx: {sourceFrameIdx} - [Time: {currentFrameTime}]" ) ;
2024-01-09 13:31:02 +01:00
if ( sourceFrameIdx < dupes . Count )
{
bool last = i = = lastOutFileCount ;
if ( last & & loop )
continue ;
for ( int dupeNum = 0 ; dupeNum < dupes . ElementAt ( sourceFrameIdx ) . Value . Count ; dupeNum + + )
{
framesList . Add ( framesList . Last ( ) ) ;
2025-06-28 23:19:58 +02:00
DebugLog ( $"Frame: #{i} - Idx: {sourceFrameIdx} - (Dupe {dupeNum + 1}/{dupes.ElementAt(sourceFrameIdx).Value.Count})" ) ;
2024-01-09 13:31:02 +01:00
}
}
}
// if (loop)
// {
// framesList.Add(framesList.First());
// }
//for (int x = 0; x < frameFileContents.Count; x++)
// fileContent += frameFileContents[x];
lastOutFileCount + + ;
if ( Config . GetBool ( Config . Key . fixOutputDuration ) ) // Match input duration by padding duping last frame until interp frames == (inputframes * factor)
{
int neededFrames = ( frameCount * interpFactor ) . RoundToInt ( ) - framesList . Count ;
for ( int i = 0 ; i < neededFrames ; i + + )
framesList . Add ( framesList . Last ( ) ) ;
}
if ( loop )
framesList . RemoveAt ( framesList . Count ( ) - 1 ) ;
string framesFileVs = Path . Combine ( tempFolder , "frames.vs.json" ) ;
// List<int> frameNums = new List<int>();
//
// foreach (string line in fileContent.SplitIntoLines().Where(x => x.StartsWith("file ")))
// frameNums.Add(line.Split('/')[1].Split('.')[0].GetInt() - 1); // Convert filename to 0-indexed number
File . WriteAllText ( framesFileVs , JsonConvert . SerializeObject ( framesList , Formatting . Indented ) ) ;
}
public static async Task CreateFramesFileImgSeq ( string tempFolder , bool loop , float interpFactor )
2021-08-23 16:50:18 +02:00
{
2024-01-09 13:31:02 +01:00
// await CreateFramesFileVideo(Interpolate.currentSettings.inPath, loop, interpFactor);
2021-08-23 16:50:18 +02:00
if ( Interpolate . canceled ) return ;
Logger . Log ( $"Generating frame order information for {interpFactor}x..." , false , true ) ;
bool sceneDetection = true ;
2022-07-20 18:10:31 +02:00
string ext = Interpolate . currentSettings . interpExt ;
2021-08-23 16:50:18 +02:00
frameFileContents . Clear ( ) ;
lastOutFileCount = 0 ;
2024-01-09 13:31:02 +01:00
string framesDir = Path . Combine ( tempFolder , Paths . framesDir ) ;
frameFiles = new DirectoryInfo ( framesDir ) . GetFiles ( "*" + Interpolate . currentSettings . framesExt ) ;
2021-08-23 16:50:18 +02:00
frameFilesWithoutLast = frameFiles ;
Array . Resize ( ref frameFilesWithoutLast , frameFilesWithoutLast . Length - 1 ) ;
2024-01-09 13:31:02 +01:00
string framesFile = Path . Combine ( tempFolder , Paths . GetFrameOrderFilename ( interpFactor ) ) ;
2021-08-23 16:50:18 +02:00
string fileContent = "" ;
2024-12-27 19:14:43 +01:00
// string dupesFile = Path.Combine(tempFolder, "dupes.json");
// LoadDupesFile(dupesFile);
2021-08-23 16:50:18 +02:00
2024-01-09 13:31:02 +01:00
string scnFramesPath = Path . Combine ( tempFolder , Paths . scenesDir ) ;
2021-08-23 16:50:18 +02:00
sceneFrames . Clear ( ) ;
if ( Directory . Exists ( scnFramesPath ) )
sceneFrames = Directory . GetFiles ( scnFramesPath ) . Select ( file = > GetNameNoExt ( file ) ) . ToList ( ) ;
2021-09-28 20:44:08 +02:00
inputFilenames . Clear ( ) ;
2024-01-09 13:31:02 +01:00
bool debug = true ; // Config.GetBool("frameOrderDebug", false);
2021-08-23 16:50:18 +02:00
List < Task > tasks = new List < Task > ( ) ;
2022-04-07 14:20:28 +02:00
int linesPerTask = ( 400 / interpFactor ) . RoundToInt ( ) ;
2021-08-23 16:50:18 +02:00
int num = 0 ;
2022-04-07 14:20:28 +02:00
int targetFrameCount = ( frameFiles . Length * interpFactor ) . RoundToInt ( ) - InterpolateUtils . GetRoundedInterpFramesPerInputFrame ( interpFactor ) ;
2022-05-26 18:50:21 +02:00
if ( interpFactor = = ( int ) interpFactor ) // Use old multi-threaded code if factor is not fractional
2022-04-07 14:20:28 +02:00
{
for ( int i = 0 ; i < frameFilesWithoutLast . Length ; i + = linesPerTask )
{
tasks . Add ( GenerateFrameLines ( num , i , linesPerTask , ( int ) interpFactor , sceneDetection , debug ) ) ;
num + + ;
}
}
else
2021-08-23 16:50:18 +02:00
{
2022-04-22 14:22:28 +02:00
await GenerateFrameLinesFloat ( frameFiles . Length , targetFrameCount , interpFactor , sceneDetection , debug ) ;
2021-08-23 16:50:18 +02:00
}
await Task . WhenAll ( tasks ) ;
for ( int x = 0 ; x < frameFileContents . Count ; x + + )
fileContent + = frameFileContents [ x ] ;
lastOutFileCount + + ;
2022-04-07 14:20:28 +02:00
if ( Config . GetBool ( Config . Key . fixOutputDuration ) ) // Match input duration by padding duping last frame until interp frames == (inputframes * factor)
2021-09-28 20:44:08 +02:00
{
2022-04-07 14:20:28 +02:00
int neededFrames = ( frameFiles . Length * interpFactor ) . RoundToInt ( ) - fileContent . SplitIntoLines ( ) . Where ( x = > x . StartsWith ( "'file " ) ) . Count ( ) ;
2024-01-09 13:31:02 +01:00
2022-04-07 14:20:28 +02:00
for ( int i = 0 ; i < neededFrames ; i + + )
fileContent + = fileContent . SplitIntoLines ( ) . Where ( x = > x . StartsWith ( "'file " ) ) . Last ( ) ;
2021-09-28 20:44:08 +02:00
}
2022-04-07 14:20:28 +02:00
2021-08-23 16:50:18 +02:00
if ( loop )
fileContent = fileContent . Remove ( fileContent . LastIndexOf ( "\n" ) ) ;
File . WriteAllText ( framesFile , fileContent ) ;
2021-09-28 20:44:08 +02:00
File . WriteAllText ( framesFile + ".inputframes.json" , JsonConvert . SerializeObject ( inputFilenames , Formatting . Indented ) ) ;
2022-06-06 07:03:27 +02:00
2024-01-09 13:31:02 +01:00
string framesFileVs = Path . Combine ( tempFolder , "frames.vs.json" ) ;
2022-06-06 07:03:27 +02:00
List < int > frameNums = new List < int > ( ) ;
foreach ( string line in fileContent . SplitIntoLines ( ) . Where ( x = > x . StartsWith ( "file " ) ) )
frameNums . Add ( line . Split ( '/' ) [ 1 ] . Split ( '.' ) [ 0 ] . GetInt ( ) - 1 ) ; // Convert filename to 0-indexed number
File . WriteAllText ( framesFileVs , JsonConvert . SerializeObject ( frameNums , Formatting . Indented ) ) ;
2021-08-23 16:50:18 +02:00
}
2022-05-26 09:37:25 +02:00
class FrameFileLine
{
public string OutFileName { get ; set ; } = "" ;
public string InFileNameFrom { get ; set ; } = "" ;
public string InFileNameTo { get ; set ; } = "" ;
public string InFileNameFromNext { get ; set ; } = "" ;
public string InFileNameToNext { get ; set ; } = "" ;
public float Timestep { get ; set ; } = - 1 ;
public bool Discard { get ; set ; } = false ;
public bool DiscardNext { get ; set ; } = false ;
public int DiscardedFrames { get ; set ; } = 0 ;
2022-05-26 18:50:21 +02:00
public FrameFileLine ( string outFileName , string inFilenameFrom , string inFilenameTo , string inFilenameToNext , float timestep , bool discard = false , bool discardNext = false , int discardedFrames = 0 )
2022-05-26 09:37:25 +02:00
{
OutFileName = outFileName ;
InFileNameFrom = inFilenameFrom ;
InFileNameTo = inFilenameTo ;
InFileNameFromNext = inFilenameTo ;
InFileNameToNext = inFilenameToNext ;
Timestep = timestep ;
Discard = discard ;
DiscardNext = discardNext ;
DiscardedFrames = discardedFrames ;
}
public override string ToString ( )
{
List < string > strings = new List < string > ( ) ;
if ( ! string . IsNullOrWhiteSpace ( InFileNameTo ) ) strings . Add ( $"to {InFileNameTo}" ) ;
if ( Timestep > = 0f ) strings . Add ( $"@ {Timestep.ToString(" 0.000000 ").Split('.').Last()}" ) ;
if ( Discard ) strings . Add ( "[Discard]" ) ;
if ( DiscardNext ) strings . Add ( $"SCN:{InFileNameFromNext}>{InFileNameToNext}>{DiscardedFrames}" ) ;
return $"file '{OutFileName}' # => {InFileNameFrom} {string.Join(" ", strings)}\n" ;
}
}
2022-04-22 14:22:28 +02:00
static async Task GenerateFrameLinesFloat ( int sourceFrameCount , int targetFrameCount , float factor , bool sceneDetection , bool debug )
2022-04-07 14:20:28 +02:00
{
int totalFileCount = 0 ;
2022-05-26 09:37:25 +02:00
bool blendSceneChances = Config . GetInt ( Config . Key . sceneChangeFillMode ) > 0 ;
2022-07-20 18:10:31 +02:00
string ext = Interpolate . currentSettings . interpExt ;
2022-04-22 14:22:28 +02:00
Fraction step = new Fraction ( sourceFrameCount , targetFrameCount + InterpolateUtils . GetRoundedInterpFramesPerInputFrame ( factor ) ) ;
2022-04-07 14:20:28 +02:00
2022-05-26 09:37:25 +02:00
List < FrameFileLine > lines = new List < FrameFileLine > ( ) ;
2022-04-07 14:20:28 +02:00
2022-05-26 08:16:25 +02:00
string lastUndiscardFrame = "" ;
2022-04-07 14:20:28 +02:00
for ( int i = 0 ; i < targetFrameCount ; i + + )
{
if ( Interpolate . canceled ) return ;
2024-10-13 16:58:06 +02:00
float currentFrameTime = 1 + ( step * i ) . Float ;
2022-04-22 14:22:28 +02:00
int sourceFrameIdx = ( int ) Math . Floor ( currentFrameTime ) - 1 ;
2022-05-26 08:16:25 +02:00
float timestep = ( currentFrameTime - ( int ) Math . Floor ( currentFrameTime ) ) ;
2022-05-26 09:37:25 +02:00
bool sceneChange = ( sceneDetection & & ( sourceFrameIdx + 1 ) < FrameRename . importFilenames . Length & & sceneFrames . Contains ( GetNameNoExt ( FrameRename . importFilenames [ sourceFrameIdx + 1 ] ) ) ) ;
2022-05-26 08:16:25 +02:00
string filename = $"{Paths.interpDir}/{(i + 1).ToString().PadLeft(Padding.interpFrames, '0')}{ext}" ;
if ( string . IsNullOrWhiteSpace ( lastUndiscardFrame ) )
lastUndiscardFrame = filename ;
2022-05-26 09:37:25 +02:00
if ( ! sceneChange )
2022-05-26 08:16:25 +02:00
lastUndiscardFrame = filename ;
2022-04-22 14:22:28 +02:00
2022-05-26 09:37:25 +02:00
string inputFilenameFrom = frameFiles [ sourceFrameIdx ] . Name ;
string inputFilenameTo = ( sourceFrameIdx + 1 > = frameFiles . Length ) ? "" : frameFiles [ sourceFrameIdx + 1 ] . Name ;
string inputFilenameToNext = ( sourceFrameIdx + 2 > = frameFiles . Length ) ? "" : frameFiles [ sourceFrameIdx + 2 ] . Name ;
2024-01-09 13:31:02 +01:00
Console . WriteLine ( $"Frame: Idx {sourceFrameIdx} - {(sceneChange && !blendSceneChances ? lastUndiscardFrame : filename)}" ) ;
2022-05-26 09:37:25 +02:00
lines . Add ( new FrameFileLine ( sceneChange & & ! blendSceneChances ? lastUndiscardFrame : filename , inputFilenameFrom , inputFilenameTo , inputFilenameToNext , timestep , sceneChange ) ) ;
2022-05-26 18:50:21 +02:00
string inputFilenameNoExtRenamed = Path . GetFileNameWithoutExtension ( FrameRename . importFilenames [ sourceFrameIdx ] ) ;
if ( ! dupesDict . ContainsKey ( inputFilenameNoExtRenamed ) )
continue ;
foreach ( string s in dupesDict [ inputFilenameNoExtRenamed ] )
2024-01-09 13:31:02 +01:00
{
string fname = sceneChange & & ! blendSceneChances ? lastUndiscardFrame : filename ;
Console . WriteLine ( $"Frame: Idx {sourceFrameIdx} - Dupe {dupesDict[inputFilenameNoExtRenamed].IndexOf(s)}/{dupesDict[inputFilenameNoExtRenamed].Count} {fname}" ) ;
lines . Add ( new FrameFileLine ( fname , inputFilenameFrom , inputFilenameTo , inputFilenameToNext , timestep , sceneChange ) ) ;
}
2022-04-07 14:20:28 +02:00
}
if ( totalFileCount > lastOutFileCount )
lastOutFileCount = totalFileCount ;
2022-05-26 18:50:21 +02:00
for ( int lineIdx = 0 ; lineIdx < lines . Count ; lineIdx + + )
2022-05-26 09:37:25 +02:00
{
bool discardNext = lineIdx > 0 & & ( lineIdx + 1 ) < lines . Count & & ! lines . ElementAt ( lineIdx ) . Discard & & lines . ElementAt ( lineIdx + 1 ) . Discard ;
int discardedFramesCount = 0 ;
if ( discardNext )
{
for ( int idx = lineIdx + 1 ; idx < lines . Count ; idx + + )
{
if ( lines . ElementAt ( idx ) . Discard )
discardedFramesCount + + ;
else
break ;
}
}
lines . ElementAt ( lineIdx ) . DiscardNext = discardNext ;
lines . ElementAt ( lineIdx ) . DiscardedFrames = discardedFramesCount ;
}
frameFileContents [ 0 ] = String . Join ( "" , lines ) ;
2022-04-07 14:20:28 +02:00
}
static async Task GenerateFrameLines ( int number , int startIndex , int count , int factor , bool sceneDetection , bool debug )
2021-08-23 16:50:18 +02:00
{
int totalFileCount = ( startIndex ) * factor ;
int interpFramesAmount = factor ;
2022-07-20 18:10:31 +02:00
string ext = Interpolate . currentSettings . interpExt ;
2021-08-23 16:50:18 +02:00
string fileContent = "" ;
for ( int i = startIndex ; i < ( startIndex + count ) ; i + + )
{
if ( Interpolate . canceled ) return ;
if ( i > = frameFilesWithoutLast . Length ) break ;
string frameName = GetNameNoExt ( frameFilesWithoutLast [ i ] . Name ) ;
string frameNameImport = GetNameNoExt ( FrameRename . importFilenames [ i ] ) ;
2022-05-26 18:25:02 +02:00
int dupesAmount = dupesDict . ContainsKey ( frameNameImport ) ? dupesDict [ frameNameImport ] . Count : 0 ;
2022-05-26 09:41:12 +02:00
bool discardThisFrame = ( sceneDetection & & i < frameFilesWithoutLast . Length & & sceneFrames . Contains ( GetNameNoExt ( FrameRename . importFilenames [ i + 1 ] ) ) ) ; // i+2 is in scene detection folder, means i+1 is ugly interp frame
2021-08-23 16:50:18 +02:00
for ( int frm = 0 ; frm < interpFramesAmount ; frm + + ) // Generate frames file lines
{
if ( discardThisFrame ) // If frame is scene cut frame
{
string frameBeforeScn = i . ToString ( ) . PadLeft ( Padding . inputFramesRenamed , '0' ) + Path . GetExtension ( FrameRename . importFilenames [ i ] ) ;
string frameAfterScn = ( i + 1 ) . ToString ( ) . PadLeft ( Padding . inputFramesRenamed , '0' ) + Path . GetExtension ( FrameRename . importFilenames [ i + 1 ] ) ;
string scnChangeNote = $"SCN:{frameBeforeScn}>{frameAfterScn}" ;
totalFileCount + + ;
fileContent = WriteFrameWithDupes ( dupesAmount , fileContent , totalFileCount , ext , debug , $"[In: {frameName}] [{((frm == 0) ? " Source " : $" Interp { frm } ")}]" , scnChangeNote ) ;
if ( Config . GetInt ( Config . Key . sceneChangeFillMode ) = = 0 ) // Duplicate last frame
{
int lastNum = totalFileCount ;
for ( int dupeCount = 1 ; dupeCount < interpFramesAmount ; dupeCount + + )
{
totalFileCount + + ;
fileContent = WriteFrameWithDupes ( dupesAmount , fileContent , lastNum , ext , debug , $"[In: {frameName}] [DISCARDED]" ) ;
}
}
else
{
for ( int dupeCount = 1 ; dupeCount < interpFramesAmount ; dupeCount + + )
{
totalFileCount + + ;
fileContent = WriteFrameWithDupes ( dupesAmount , fileContent , totalFileCount , ext , debug , $"[In: {frameName}] [BLEND FRAME]" ) ;
}
}
frm = interpFramesAmount ;
}
else
{
totalFileCount + + ;
fileContent = WriteFrameWithDupes ( dupesAmount , fileContent , totalFileCount , ext , debug , $"[In: {frameName}] [{((frm == 0) ? " Source " : $" Interp { frm } ")}]" ) ;
}
2021-09-28 20:44:08 +02:00
inputFilenames . Add ( frameFilesWithoutLast [ i ] . Name ) ;
2021-08-23 16:50:18 +02:00
}
}
if ( totalFileCount > lastOutFileCount )
lastOutFileCount = totalFileCount ;
frameFileContents [ number ] = fileContent ;
}
static string WriteFrameWithDupes ( int dupesAmount , string fileContent , int frameNum , string ext , bool debug , string debugNote = "" , string forcedNote = "" )
{
for ( int writtenDupes = - 1 ; writtenDupes < dupesAmount ; writtenDupes + + ) // Write duplicates
fileContent + = $"file '{Paths.interpDir}/{frameNum.ToString().PadLeft(Padding.interpFrames, '0')}{ext}' # {(debug ? ($" Dupe { ( writtenDupes + 1 ) . ToString ( "000" ) } { debugNote } ").Replace(" Dupe 000 ", " ") : " ")}{forcedNote}\n" ;
return fileContent ;
}
2022-04-07 14:20:28 +02:00
static string GetNameNoExt ( string path )
2021-09-28 20:44:08 +02:00
{
return Path . GetFileNameWithoutExtension ( path ) ;
}
2021-08-23 16:50:18 +02:00
}
}