Files
flowframes/Code/Magick/Blend.cs

174 lines
6.6 KiB
C#
Raw Normal View History

using Flowframes.Data;
using Flowframes.IO;
using Flowframes.MiscUtils;
using ImageMagick;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.Magick
{
class Blend
{
2021-03-19 19:48:16 +01:00
public static async Task BlendSceneChanges(string framesFilePath, bool setStatus = true)
{
Stopwatch sw = new Stopwatch();
sw.Restart();
int totalFrames = 0;
2021-03-19 19:48:16 +01:00
string oldStatus = Program.mainForm.GetStatus();
if(setStatus)
Program.mainForm.SetStatus("Blending scene transitions...");
string keyword = "SCN:";
string[] framesLines = IOUtils.ReadLines(framesFilePath); // Array with frame filenames
int amountOfBlendFrames = (int)Interpolate.current.interpFactor - 1;
List<Task> runningTasks = new List<Task>();
int maxThreads = Environment.ProcessorCount * 2;
foreach (string line in framesLines)
{
try
{
if (line.Contains(keyword))
{
string trimmedLine = line.Split(keyword).Last();
string[] inputFrameNames = trimmedLine.Split('>');
string img1 = Path.Combine(Interpolate.current.framesFolder, inputFrameNames[0]);
string img2 = Path.Combine(Interpolate.current.framesFolder, inputFrameNames[1]);
string firstOutputFrameName = line.Split('/').Last().Remove("'").Split('#').First();
string ext = Path.GetExtension(firstOutputFrameName);
int firstOutputFrameNum = firstOutputFrameName.GetInt();
List<string> outputFilenames = new List<string>();
for (int blendFrameNum = 1; blendFrameNum <= amountOfBlendFrames; blendFrameNum++)
{
int outputNum = firstOutputFrameNum + blendFrameNum + 1;
string outputPath = Path.Combine(Interpolate.current.interpFolder, outputNum.ToString().PadLeft(Padding.interpFrames, '0'));
outputPath = Path.ChangeExtension(outputPath, ext);
outputFilenames.Add(outputPath);
}
if (runningTasks.Count >= maxThreads)
{
do
{
await Task.Delay(10);
RemoveCompletedTasks(runningTasks);
} while (runningTasks.Count >= maxThreads);
}
Logger.Log($"Starting task for transition {inputFrameNames[0]} > {inputFrameNames[1]} ({runningTasks.Count}/{maxThreads} running)", true);
Task newTask = Task.Run(() => BlendImages(img1, img2, outputFilenames.ToArray()));
runningTasks.Add(newTask);
totalFrames += outputFilenames.Count;
await Task.Delay(1);
}
}
catch (Exception e)
{
Logger.Log("Failed to blend scene changes: " + e.Message, true);
}
}
while (true)
{
RemoveCompletedTasks(runningTasks);
if (runningTasks.Count < 1)
break;
await Task.Delay(10);
}
Logger.Log($"Created {totalFrames} blend frames in {FormatUtils.TimeSw(sw)} ({(totalFrames / (sw.ElapsedMilliseconds / 1000f)).ToString("0.00")} FPS)", true);
2021-03-19 19:48:16 +01:00
if (setStatus)
Program.mainForm.SetStatus(oldStatus);
}
2021-03-19 19:48:16 +01:00
static void RemoveCompletedTasks (List<Task> runningTasks)
{
foreach (Task task in new List<Task>(runningTasks))
{
if (task.IsCompleted)
runningTasks.Remove(task);
}
}
public static void BlendImages(string img1Path, string img2Path, string imgOutPath)
{
MagickImage img1 = new MagickImage(img1Path);
MagickImage img2 = new MagickImage(img2Path);
img2.Alpha(AlphaOption.Opaque);
img2.Evaluate(Channels.Alpha, EvaluateOperator.Set, new Percentage(50));
img1.Composite(img2, Gravity.Center, CompositeOperator.Over);
img1.Format = MagickFormat.Png24;
img1.Quality = 10;
img1.Write(imgOutPath);
}
public static async Task BlendImages (string img1Path, string img2Path, string[] imgOutPaths)
{
try
{
MagickImage img1 = new MagickImage(img1Path);
MagickImage img2 = new MagickImage(img2Path);
int alphaFraction = (100f / (imgOutPaths.Length + 1)).RoundToInt(); // Alpha percentage per image
int currentAlpha = alphaFraction;
foreach (string imgOutPath in imgOutPaths)
{
string outPath = imgOutPath.Trim();
MagickImage img1Inst = new MagickImage(img1);
MagickImage img2Inst = new MagickImage(img2);
img2Inst.Alpha(AlphaOption.Opaque);
img2Inst.Evaluate(Channels.Alpha, EvaluateOperator.Set, new Percentage(currentAlpha));
currentAlpha += alphaFraction;
img1Inst.Composite(img2Inst, Gravity.Center, CompositeOperator.Over);
img1Inst.Format = MagickFormat.Png24;
img1Inst.Quality = 10;
img1Inst.Write(outPath);
//await WriteImageSafe(img1Inst, outPath);
await Task.Delay(1);
}
}
catch (Exception e)
{
Logger.Log("BlendImages Error: " + e.Message);
}
}
static async Task WriteImageSafe (MagickImage img, string path)
{
img.Write(path);
try
{
MagickImage imgRead = new MagickImage(path);
Size res = new Size(imgRead.Width, imgRead.Height);
}
catch(Exception e)
{
Logger.Log($"Failed to write '{path}' correctly, will retry. {e.Message}", true);
await Task.Delay(100);
await WriteImageSafe(img, path);
}
}
}
}