public LoadRemoverReport(string fileName, LoadResults results, VideoCapture capture) { this._fileName = fileName; this._results = results; this._capture = capture; _templateElements = new Dictionary <string, Func <string> >() { { "title", () => Title }, { "tableHeaders", () => TableHeaders }, { "tableRows", () => TableRows }, { "videoFPS", () => results.FPS.ToString() }, { "startTimeSeconds", () => FrameToTimeString(results.StartingFrame, results.FPS) }, { "startTimeFrames", () => results.StartingFrame.ToString() }, { "endTimeSeconds", () => FrameToTimeString(results.EndingFrame, results.FPS) }, { "endTimeFrames", () => results.EndingFrame.ToString() }, { "totalTimeWithLoadsSeconds", () => FrameToTimeString(results.TotalFramesIncludingLoads, results.FPS) }, { "totalTimeWithLoadsFrames", () => results.TotalFramesIncludingLoads.ToString() }, { "totalLoadTimeSeconds", () => FrameToTimeString(results.TotalLoadingFrames, results.FPS) }, { "totalLoadTimeFrames", () => results.TotalLoadingFrames.ToString() }, { "totalTimeWithoutLoadsSeconds", () => FrameToTimeString(results.TotalFramesWithoutLoads, results.FPS) }, { "totalTimeWithoutLoadsFrames", () => results.TotalFramesWithoutLoads.ToString() }, { "loadCountBackSign", () => results.Loads.Count(l => l.Type == LoadType.BackSign).ToString() }, { "loadCountBoss", () => results.Loads.Count(l => l.Type == LoadType.Boss).ToString() }, { "loadCountDeath", () => results.Loads.Count(l => l.Type == LoadType.Death).ToString() }, { "loadCountEndSign", () => results.Loads.Count(l => l.Type == LoadType.EndSign).ToString() }, { "loadCountOverworld", () => results.Loads.Count(l => l.Type == LoadType.Overworld).ToString() }, }; }
private static string CreateFrameImages(Load load, LoadResults lr, VideoCapture capture, int offsetStart, int offsetEnd) { string result = ""; for (int i = offsetStart; i <= offsetEnd; i++) { capture.Set(VideoCaptureProperties.PosFrames, i); var mat = new Mat(); capture.Read(mat); result += CreateBase64HtmlImage(mat); } return(result); }
public static LoadResults Start(string file, bool partialRun, CropSettings?crop, TrimSettings?trim, LoadType loadTypes, bool resize, Action <ProgressPhase, float> updateProgress) { List <Load> loads = new List <Load>(); updateProgress.Invoke(ProgressPhase.Phase_1_PreprocessVideo, 0); VideoCapture capture = new VideoCapture(file); string processedFile = CropTrimAndResizeVideo(capture, file, crop, /*trim,*/ resize); capture = VideoCapture.FromFile(processedFile); updateProgress.Invoke(ProgressPhase.Phase_2_StartingTime, 0); int startingFrame = 0; int endingFrame = trim.HasValue ? (int)Math.Min((trim.Value.End * capture.Fps) - 1, capture.FrameCount - 1) : capture.FrameCount - 1; if (!partialRun && loadTypes.HasFlag(LoadType.Start)) { var startLoad = Util.CountDarknessFrames(LoadType.Start, capture, trim?.Start ?? 0, (int)(capture.Fps * StartScreenMaxDuration)); if (startLoad.FrameStart == -1) { throw new Exception( "Start screen not detected, make sure the video starts on the \"Start\"/\"Options\" screen"); } loads.Add(startLoad); startingFrame = startLoad.FrameStart; if (loadTypes.HasFlag(LoadType.Overworld)) { var startOverworldLoad = Util.CountFrozenFrames(LoadType.Overworld, capture, startLoad.FrameEnd / capture.Fps, (int)capture.Fps / 5, (int)capture.Fps * 20); if (startOverworldLoad.HasValue) { loads.Add(startOverworldLoad.Value); } } } updateProgress.Invoke(ProgressPhase.Phase_3_VideoScale, 0); float videoScale = LifeCounter.GetLifeCountScale(capture, startingFrame, updateProgress); if (float.IsNaN(videoScale)) { throw new Exception("Video Scale couldn't be determined: " + videoScale); } updateProgress.Invoke(ProgressPhase.Phase_4_EndingTime, 0); if (!partialRun) { var _endingFrame = BossLoads.GetLastFinalBossFrame(capture, videoScale, endingFrame, updateProgress); if (!_endingFrame.HasValue) { throw new Exception( "Final hit not detected, make sure the video doesn't end more than 3 minutes after the final hit."); } endingFrame = _endingFrame.Value; } updateProgress.Invoke(ProgressPhase.Phase_5_EndSignLoads, 0); if (loadTypes.HasFlag(LoadType.EndSign)) { loads.AddRange(EndSignLoads.GetEndSignLoads(capture, videoScale, startingFrame, endingFrame, updateProgress)); } updateProgress.Invoke(ProgressPhase.Phase_6_OverworldLoads, 0); if (loadTypes.HasFlag(LoadType.Overworld) || loadTypes.HasFlag(LoadType.BackSign)) { loads.AddRange(OverworldLoads.GetOverworldLoads(capture, videoScale, startingFrame / (float)capture.Fps, endingFrame / (float)capture.Fps, loadTypes, updateProgress)); } updateProgress.Invoke(ProgressPhase.Phase_7_DeathLoads, 0); if (loadTypes.HasFlag(LoadType.Death)) { loads.AddRange(DeathLoads.GetDeathLoads(capture, videoScale, startingFrame, endingFrame, updateProgress)); } updateProgress.Invoke(ProgressPhase.Phase_8_BossLoads, 0); if (loadTypes.HasFlag(LoadType.Boss)) { loads.AddRange(BossLoads.GetBossLoads(capture, videoScale, startingFrame, endingFrame, updateProgress)); } int phase8Progress = 0; // Remove backsign loads that aren't preceded by an overworld load (ignore death loads for this) var sortedLoads = loads.OrderBy(l => l.FrameStart).ToList(); List <Load> backsignLoadsToRemove = new List <Load>(); for (int i = 0; i < sortedLoads.Count; i++) { if (sortedLoads[i].Type == LoadType.BackSign) { var bsLoad = sortedLoads[i]; for (int j = i - 1; j >= 0; j--) { var checkLoad = sortedLoads[j]; // only consider loads more than 3 seconds before the backsign load if (checkLoad.FrameStart > bsLoad.FrameStart - capture.Fps * 3.0) { continue; } if (checkLoad.Type == LoadType.Death) { continue; } if (checkLoad.Type == LoadType.Overworld) { break; } else { backsignLoadsToRemove.Add(bsLoad); } } } } foreach (var l in backsignLoadsToRemove) { loads.Remove(l); } // Remove unnecessary endsign loads (when they overlap with other loads) foreach (var load in loads.Where(l => l.Type != LoadType.EndSign).ToList()) { loads.RemoveAll(l => l.Type == LoadType.EndSign && l.Overlaps(load, (int)(capture.Fps * 0.5f))); } // Remove unnecessary backsign loads (when they overlap with other loads) foreach (var load in loads.Where(l => l.Type != LoadType.BackSign).ToList()) { loads.RemoveAll(l => l.Type == LoadType.BackSign && l.Overlaps(load, (int)(capture.Fps * 0.5f))); } // Remove all loads that start after the last frame loads.RemoveAll(l => l.FrameStart > endingFrame); updateProgress.Invoke(ProgressPhase.Phase_9_GenerateReport, 0); LoadResults results = new LoadResults(loads, (float)capture.Fps, startingFrame, endingFrame); results.SaveDebugImages(capture, "debugExport", "file"); var report = new LoadRemoverReport(Path.GetFileName(file), results, capture); var reportPath = Path.ChangeExtension(file, null) + "_report.html"; report.GenerateHtml(TemplateFile).Save(reportPath); updateProgress.Invoke(ProgressPhase.Phase_9_GenerateReport, 1); var openReport = MessageBox.Show($"Done! The report file can be found at {Environment.NewLine}{reportPath}{Environment.NewLine}" + $"Do you wish to open the report now?", "Report", MessageBoxButton.YesNo, MessageBoxImage.Question); if (openReport == MessageBoxResult.Yes) { // Open report in default application (hopefully the browser) var psi = new ProcessStartInfo { FileName = reportPath, UseShellExecute = true }; Process.Start(psi); } return(results); }