public static async Task Convert(string dir, MagickFormat format, int quality, string ext = "", bool print = true, bool setProgress = true) { var files = IoUtils.GetFilesSorted(dir); if (print) { Logger.Log($"Converting {files.Length} files in {dir}"); } int counter = 0; foreach (string file in files) { if (print) { Logger.Log("Converting " + Path.GetFileName(file) + " to " + format.ToString().StripNumbers().ToUpper(), false, true); } MagickImage img = new MagickImage(file); img.Format = format; img.Quality = quality; string outpath = file; if (!string.IsNullOrWhiteSpace(ext)) { outpath = Path.ChangeExtension(outpath, ext); } img.Write(outpath); counter++; if (setProgress) { Program.mainForm.SetProgress((int)Math.Round(((float)counter / files.Length) * 100f)); } await Task.Delay(1); } }
public static async Task CopyLastFrame(int lastFrameNum) { if (I.canceled) { return; } try { lastFrameNum--; // We have to do this as extracted frames start at 0, not 1 bool frameFolderInput = IoUtils.IsPathDirectory(I.current.inPath); string targetPath = Path.Combine(I.current.framesFolder, lastFrameNum.ToString().PadLeft(Padding.inputFrames, '0') + I.current.framesExt); if (File.Exists(targetPath)) { return; } Size res = IoUtils.GetImage(IoUtils.GetFilesSorted(I.current.framesFolder, false).First()).Size; if (frameFolderInput) { string lastFramePath = IoUtils.GetFilesSorted(I.current.inPath, false).Last(); await FfmpegExtract.ExtractLastFrame(lastFramePath, targetPath, res); } else { await FfmpegExtract.ExtractLastFrame(I.current.inPath, targetPath, res); } } catch (Exception e) { Logger.Log("CopyLastFrame Error: " + e.Message); } }
public static async Task DeleteInterpolatedInputFrames() { interpolatedInputFramesCount = 0; string[] inputFrames = IoUtils.GetFilesSorted(I.current.framesFolder); for (int i = 0; i < inputFrames.Length; i++) { while (Program.busy && (i + 10) > interpolatedInputFramesCount) { await Task.Delay(1000); } if (!Program.busy) { break; } if (i != 0 && i != inputFrames.Length - 1) { IoUtils.OverwriteFileWithText(inputFrames[i]); } if (i % 10 == 0) { await Task.Delay(10); } } }
public static async Task CreateOutputVid() { if (IoUtils.GetAmountOfFiles(current.interpFolder, false) < 2) { if (Config.GetBool(Config.Key.sbsRunPreviousStepIfNeeded)) { Logger.Log($"There are no interpolated frames to export - Running interpolation step first..."); await InterpolateStep(); } if (IoUtils.GetAmountOfFiles(current.interpFolder, false) < 2) { Cancel($"There are no interpolated frames to encode!\n\nDid you delete the folder?"); return; } } if (!(await InterpolateUtils.CheckEncoderValid())) { return; } string[] outFrames = IoUtils.GetFilesSorted(current.interpFolder, current.interpExt); if (outFrames.Length > 0 && !IoUtils.CheckImageValid(outFrames[0])) { UiUtils.ShowMessageBox("Invalid frame files detected!\n\nIf you used Auto-Encode, this is normal, and you don't need to run " + "this step as the video was already created in the \"Interpolate\" step.", UiUtils.MessageType.Error); return; } await Export.ExportFrames(current.interpFolder, current.outPath, current.outMode, true); }
public static string[] importFilenames; // index=renamed, value=original TODO: Store on disk instead for crashes? public static async Task Rename() { importFilenames = IoUtils.GetFilesSorted(Interpolate.current.framesFolder).Select(x => Path.GetFileName(x)).ToArray(); await IoUtils.RenameCounterDir(Interpolate.current.framesFolder, 0, Padding.inputFramesRenamed); framesAreRenamed = true; }
public static async Task ExtractAlpha(string inputDir, string outputDir, bool print = true, bool setProgress = true, bool removeInputAlpha = true) { try { var files = IoUtils.GetFilesSorted(inputDir); if (print) { Logger.Log($"Extracting alpha channel from images..."); } Directory.CreateDirectory(outputDir); Stopwatch sw = new Stopwatch(); sw.Restart(); int counter = 0; foreach (string file in files) { MagickImage alphaImg = new MagickImage(file); if (removeInputAlpha) { MagickImage rgbImg = alphaImg; rgbImg.Format = MagickFormat.Png24; rgbImg.Quality = 10; MagickImage bg = new MagickImage(MagickColors.Black, rgbImg.Width, rgbImg.Height); bg.Composite(rgbImg, CompositeOperator.Over); rgbImg = bg; rgbImg.Write(file); } alphaImg.Format = MagickFormat.Png24; alphaImg.Quality = 10; alphaImg.FloodFill(MagickColors.None, 0, 0); // Fill the image with a transparent background alphaImg.InverseOpaque(MagickColors.None, MagickColors.White); // Change all the pixels that are not transparent to white. alphaImg.ColorAlpha(MagickColors.Black); // Change the transparent pixels to black. string outPath = Path.Combine(outputDir, Path.GetFileName(file)); alphaImg.Write(outPath); counter++; if (sw.ElapsedMilliseconds > 250) { if (setProgress) { Program.mainForm.SetProgress((int)Math.Round(((float)counter / files.Length) * 100f)); } await Task.Delay(1); sw.Restart(); } } } catch (Exception e) { Logger.Log("ExtractAlpha Error: " + e.Message); } }
public static async Task ChunksToVideo(string tempFolder, string chunksFolder, string baseOutPath, bool isBackup = false) { if (IoUtils.GetAmountOfFiles(chunksFolder, true, "*" + FfmpegUtils.GetExt(I.current.outMode)) < 1) { I.Cancel("No video chunks found - An error must have occured during chunk encoding!", AiProcess.hasShownError); return; } NmkdStopwatch sw = new NmkdStopwatch(); if (!isBackup) { Program.mainForm.SetStatus("Merging video chunks..."); } try { DirectoryInfo chunksDir = new DirectoryInfo(chunksFolder); foreach (DirectoryInfo dir in chunksDir.GetDirectories()) { string suffix = dir.Name.Replace("chunks", ""); string tempConcatFile = Path.Combine(tempFolder, $"chunks-concat{suffix}.ini"); string concatFileContent = ""; foreach (string vid in IoUtils.GetFilesSorted(dir.FullName)) { concatFileContent += $"file '{Paths.chunksDir}/{dir.Name}/{Path.GetFileName(vid)}'\n"; } File.WriteAllText(tempConcatFile, concatFileContent); Logger.Log($"CreateVideo: Running MergeChunks() for frames file '{Path.GetFileName(tempConcatFile)}'", true); bool fpsLimit = dir.Name.Contains(Paths.fpsLimitSuffix); string outPath = Path.Combine(baseOutPath, await IoUtils.GetCurrentExportFilename(fpsLimit, true)); await MergeChunks(tempConcatFile, outPath, isBackup); if (!isBackup) { Task.Run(async() => { await IoUtils.TryDeleteIfExistsAsync(IoUtils.FilenameSuffix(outPath, Paths.backupSuffix)); }); } } } catch (Exception e) { Logger.Log("ChunksToVideo Error: " + e.Message, isBackup); if (!isBackup) { MessageBox.Show("An error occured while trying to merge the video chunks.\nCheck the log for details."); } } Logger.Log($"Merged video chunks in {sw}", true); }
public void RefreshAlpha() { try { bool alphaModel = model.supportsAlpha; bool png = outMode == Interpolate.OutMode.ImgPng; bool gif = outMode == Interpolate.OutMode.VidGif; bool proResAlpha = outMode == Interpolate.OutMode.VidProRes && Config.GetInt(Config.Key.proResProfile) > 3; bool outputSupportsAlpha = png || gif || proResAlpha; string ext = inputIsFrames ? Path.GetExtension(IoUtils.GetFilesSorted(inPath).First()).ToLower() : Path.GetExtension(inPath).ToLower(); alpha = (alphaModel && outputSupportsAlpha && (ext == ".gif" || ext == ".png" || ext == ".apng" || ext == ".mov")); Logger.Log($"RefreshAlpha: model.supportsAlpha = {alphaModel} - outputSupportsAlpha = {outputSupportsAlpha} - " + $"input ext: {ext} => alpha = {alpha}", true); } catch (Exception e) { Logger.Log("RefreshAlpha Error: " + e.Message, true); alpha = false; } }
public static async Task MakeBinary(string inputDir, string outputDir, bool print = true, bool setProgress = true) { try { var files = IoUtils.GetFilesSorted(inputDir); if (print) { Logger.Log($"Processing alpha channel..."); } Directory.CreateDirectory(outputDir); Stopwatch sw = new Stopwatch(); sw.Restart(); int counter = 0; foreach (string file in files) { MagickImage img = new MagickImage(file); img.Format = MagickFormat.Png24; img.Quality = 10; img.Threshold(new Percentage(75)); string outPath = Path.Combine(outputDir, Path.GetFileName(file)); img.Write(outPath); counter++; if (sw.ElapsedMilliseconds > 250) { if (setProgress) { Program.mainForm.SetProgress((int)Math.Round(((float)counter / files.Length) * 100f)); } await Task.Delay(1); sw.Restart(); } } } catch (Exception e) { Logger.Log("MakeBinary Error: " + e.Message); } }
static async Task ExportImageSequence(string framesPath, bool stepByStep) { Program.mainForm.SetStatus("Copying output frames..."); string desiredFormat = Config.Get(Config.Key.imgSeqFormat).ToUpper(); string availableFormat = Path.GetExtension(IoUtils.GetFilesSorted(framesPath)[0]).Remove(".").ToUpper(); string max = Config.Get(Config.Key.maxFps); Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat()); bool fpsLimit = maxFps.GetFloat() > 0f && I.current.outFps.GetFloat() > maxFps.GetFloat(); bool dontEncodeFullFpsSeq = fpsLimit && Config.GetInt(Config.Key.maxFpsMode) == 0; string framesFile = Path.Combine(framesPath.GetParentDir(), Paths.GetFrameOrderFilename(I.current.interpFactor)); if (!dontEncodeFullFpsSeq) { string outputFolderPath = Path.Combine(I.current.outPath, await IoUtils.GetCurrentExportFilename(false, false)); IoUtils.RenameExistingFolder(outputFolderPath); Logger.Log($"Exporting {desiredFormat.ToUpper()} frames to '{Path.GetFileName(outputFolderPath)}'..."); if (desiredFormat.ToUpper() == availableFormat.ToUpper()) // Move if frames are already in the desired format { await CopyOutputFrames(framesPath, framesFile, outputFolderPath, 1, fpsLimit, false); } else // Encode if frames are not in desired format { await FfmpegEncode.FramesToFrames(framesFile, outputFolderPath, 1, I.current.outFps, new Fraction(), desiredFormat, GetImgSeqQ(desiredFormat)); } } if (fpsLimit) { string outputFolderPath = Path.Combine(I.current.outPath, await IoUtils.GetCurrentExportFilename(true, false)); Logger.Log($"Exporting {desiredFormat.ToUpper()} frames to '{Path.GetFileName(outputFolderPath)}' (Resampled to {maxFps} FPS)..."); await FfmpegEncode.FramesToFrames(framesFile, outputFolderPath, 1, I.current.outFps, maxFps, desiredFormat, GetImgSeqQ(desiredFormat)); } if (!stepByStep) { await IoUtils.DeleteContentsOfDirAsync(I.current.interpFolder); } }
public static async Task Unrename() { Stopwatch sw = new Stopwatch(); sw.Restart(); string[] files = IoUtils.GetFilesSorted(Interpolate.current.framesFolder); for (int i = 0; i < files.Length; i++) { string movePath = Path.Combine(Interpolate.current.framesFolder, importFilenames[i]); File.Move(files[i], movePath); if (sw.ElapsedMilliseconds > 100) { await Task.Delay(1); sw.Restart(); } } framesAreRenamed = false; }
public static async Task <Image> GetThumbnail(string path) { string imgOnDisk = Path.Combine(Paths.GetDataPath(), "thumb-temp.jpg"); try { if (!IoUtils.IsPathDirectory(path)) // If path is video - Extract first frame { await FfmpegExtract.ExtractSingleFrame(path, imgOnDisk, 1); return(IoUtils.GetImage(imgOnDisk)); } else // Path is frame folder - Get first frame { return(IoUtils.GetImage(IoUtils.GetFilesSorted(path)[0])); } } catch (Exception e) { Logger.Log("GetThumbnail Error: " + e.Message, true); return(null); } }
public static void FixConsecutiveSceneFrames(string sceneFramesPath, string sourceFramesPath) { if (!Directory.Exists(sceneFramesPath) || IoUtils.GetAmountOfFiles(sceneFramesPath, false) < 1) { return; } List <string> sceneFrames = IoUtils.GetFilesSorted(sceneFramesPath).Select(x => Path.GetFileNameWithoutExtension(x)).ToList(); List <string> sourceFrames = IoUtils.GetFilesSorted(sourceFramesPath).Select(x => Path.GetFileNameWithoutExtension(x)).ToList(); List <string> sceneFramesToDelete = new List <string>(); foreach (string scnFrame in sceneFrames) { if (sceneFramesToDelete.Contains(scnFrame)) { continue; } 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 if (sceneFrames.Contains(followingFrame)) // If next source frame is in scene folder, add to deletion list { sceneFramesToDelete.Add(followingFrame); } } foreach (string frame in sceneFramesToDelete) { IoUtils.TryDeleteIfExists(Path.Combine(sceneFramesPath, frame + I.current.framesExt)); } }
public static async Task Preprocess(string dir, bool setProgress = true) { var files = IoUtils.GetFilesSorted(dir); Logger.Log($"Preprocessing {files} files in {dir}"); int counter = 0; foreach (string file in files) { //Logger.Log("Converting " + Path.GetFileName(file) + " to " + format, false, true); MagickImage img = new MagickImage(file); //img.Format = MagickFormat.Bmp; //img.Write(file); //img = new MagickImage(file); img.Format = MagickFormat.Png24; img.Quality = 10; counter++; if (setProgress) { Program.mainForm.SetProgress((int)Math.Round(((float)counter / files.Length) * 100f)); } await Task.Delay(1); } }
public static async Task BlendSceneChanges(string framesFilePath, bool setStatus = true) { Stopwatch sw = new Stopwatch(); sw.Restart(); int totalFrames = 0; string keyword = "SCN:"; string fileContent = File.ReadAllText(framesFilePath); if (!fileContent.Contains(keyword)) { Logger.Log("Skipping BlendSceneChanges as there are no scene changes in this frames file.", true); return; } string[] framesLines = fileContent.SplitIntoLines(); // Array with frame filenames string oldStatus = Program.mainForm.GetStatus(); if (setStatus) { Program.mainForm.SetStatus("Blending scene transitions..."); } int amountOfBlendFrames = (int)Interpolate.current.interpFactor - 1; string[] frames = FrameRename.framesAreRenamed ? new string[0] : IoUtils.GetFilesSorted(Interpolate.current.framesFolder); 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 frameFrom = FrameRename.framesAreRenamed ? inputFrameNames[0] : frames[inputFrameNames[0].GetInt()]; string frameTo = FrameRename.framesAreRenamed ? inputFrameNames[1] : frames[inputFrameNames[1].GetInt()]; string img1 = Path.Combine(Interpolate.current.framesFolder, frameFrom); string img2 = Path.Combine(Interpolate.current.framesFolder, frameTo); 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; 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); if (setStatus) { Program.mainForm.SetStatus(oldStatus); } }
public static async Task EncodeChunk(string outPath, string interpDir, int chunkNo, I.OutMode mode, int firstFrameNum, int framesAmount) { string framesFileFull = Path.Combine(I.current.tempFolder, Paths.GetFrameOrderFilename(I.current.interpFactor)); string concatFile = Path.Combine(I.current.tempFolder, Paths.GetFrameOrderFilenameChunk(firstFrameNum, firstFrameNum + framesAmount)); File.WriteAllLines(concatFile, IoUtils.ReadLines(framesFileFull).Skip(firstFrameNum).Take(framesAmount)); List <string> inputFrames = JsonConvert.DeserializeObject <List <string> >(File.ReadAllText(framesFileFull + ".inputframes.json")).Skip(firstFrameNum).Take(framesAmount).ToList(); if (Config.GetInt(Config.Key.sceneChangeFillMode) == 1) { await Blend.BlendSceneChanges(concatFile, false); } string max = Config.Get(Config.Key.maxFps); Fraction maxFps = max.Contains("/") ? new Fraction(max) : new Fraction(max.GetFloat()); bool fpsLimit = maxFps.GetFloat() != 0 && I.current.outFps.GetFloat() > maxFps.GetFloat(); VidExtraData extraData = await FfmpegCommands.GetVidExtraInfo(I.current.inPath); bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt(Config.Key.maxFpsMode) == 0; if (mode.ToString().ToLower().StartsWith("img")) // Image Sequence output mode, not video { string desiredFormat = Config.Get(Config.Key.imgSeqFormat); string availableFormat = Path.GetExtension(IoUtils.GetFilesSorted(interpDir)[0]).Remove(".").ToUpper(); if (!dontEncodeFullFpsVid) { string outFolderPath = Path.Combine(I.current.outPath, await IoUtils.GetCurrentExportFilename(false, false)); int startNo = IoUtils.GetAmountOfFiles(outFolderPath, false) + 1; if (chunkNo == 1) // Only check for existing folder on first chunk, otherwise each chunk makes a new folder { IoUtils.RenameExistingFolder(outFolderPath); } if (desiredFormat.ToUpper() == availableFormat.ToUpper()) // Move if frames are already in the desired format { await CopyOutputFrames(interpDir, concatFile, outFolderPath, startNo, fpsLimit, true); } else // Encode if frames are not in desired format { await FfmpegEncode.FramesToFrames(concatFile, outFolderPath, startNo, I.current.outFps, new Fraction(), desiredFormat, GetImgSeqQ(desiredFormat), AvProcess.LogMode.Hidden); } } if (fpsLimit) { string outputFolderPath = Path.Combine(I.current.outPath, await IoUtils.GetCurrentExportFilename(true, false)); int startNumber = IoUtils.GetAmountOfFiles(outputFolderPath, false) + 1; await FfmpegEncode.FramesToFrames(concatFile, outputFolderPath, startNumber, I.current.outFps, maxFps, desiredFormat, GetImgSeqQ(desiredFormat), AvProcess.LogMode.Hidden); } } else { if (!dontEncodeFullFpsVid) { await FfmpegEncode.FramesToVideo(concatFile, outPath, mode, I.current.outFps, new Fraction(), I.current.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode } if (fpsLimit) { string filename = Path.GetFileName(outPath); string newParentDir = outPath.GetParentDir() + Paths.fpsLimitSuffix; outPath = Path.Combine(newParentDir, filename); await FfmpegEncode.FramesToVideo(concatFile, outPath, mode, I.current.outFps, maxFps, I.current.outItsScale, extraData, AvProcess.LogMode.Hidden, true); // Encode with limited fps } } AutoEncodeResume.encodedChunks += 1; AutoEncodeResume.encodedFrames += framesAmount; AutoEncodeResume.processedInputFrames.AddRange(inputFrames); }