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);
        }
Esempio n. 3
0
        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);
        }