Exemplo n.º 1
0
        public static void Init()
        {
            AddSample(SoundType.EndSign, Path.Combine(SoundsFolder, "endsign.wav"));
            AddSample(SoundType.Boss, Path.Combine(SoundsFolder, "bossdefeat.wav"));

            LifeCounter.Init();
            BossLoads.Init();
        }
Exemplo n.º 2
0
        private static bool FindDeathsRecursive(VideoCapture capture, List <int> deaths, float videoScale, int startFrame, int endFrame, float stepSize = 4)
        {
            capture.Set(VideoCaptureProperties.PosMsec, 0);
            double fps = capture.Get(VideoCaptureProperties.Fps);

            int stepSizeFrames = (int)(stepSize * capture.Fps);

            int currentLives = -1;
            int lastFrame    = 0;

            for (int frame = startFrame; frame < endFrame; frame += stepSizeFrames)
            {
                int lifeCount = LifeCounter.GetLifeCount(capture, frame / fps, videoScale);

                if (currentLives == -1)
                {
                    currentLives = lifeCount;
                }

                if (lifeCount > 0)
                {
                    if (lifeCount < currentLives)
                    {
                        if (stepSizeFrames <= 1)
                        {
                            if (!deaths.Contains(lastFrame))
                            {
                                deaths.Add(lastFrame);
                            }

                            Mat dbgMat = new Mat();
                            capture.Read(dbgMat);

                            return(true);
                        }
                        else
                        {
                            // Try first half, then second half
                            if (!FindDeathsRecursive(capture, deaths, videoScale, lastFrame, frame + stepSizeFrames / 2, stepSize / 2))
                            {
                                FindDeathsRecursive(capture, deaths, videoScale, lastFrame + stepSizeFrames / 2, frame,
                                                    stepSize / 2);
                            }
                        }
                    }

                    currentLives = lifeCount;
                }

                lastFrame = frame;
            }

            return(false);
        }
Exemplo n.º 3
0
        public static List <Load> GetOverworldLoads(VideoCapture capture, float scale, float timeStart, float timeEnd,
                                                    LoadType loadTypes,
                                                    Action <LoadRemover.ProgressPhase, float> updateProgress)
        {
            List <Load> loads           = new List <Load>();
            var         binocularFrames = FindBinocularFrames(capture, timeStart, timeEnd);
            List <int>  startingFrames  = new List <int>();
            List <int>  endingFrames    = new List <int>();
            int         lastFrame       = -2;

            foreach (var frame in binocularFrames)
            {
                if (frame > lastFrame + 1)
                {
                    startingFrames.Add(frame);
                    endingFrames.Add(lastFrame);
                }

                lastFrame = frame;
            }

            float fps      = (float)capture.Fps;
            int   progress = 0;

            if (loadTypes.HasFlag(LoadType.Overworld))
            {
                foreach (var f in startingFrames)
                {
                    updateProgress.Invoke(LoadRemover.ProgressPhase.Phase_6_OverworldLoads,
                                          0.5f + ((progress++) / (float)startingFrames.Count) * 0.25f);

                    // 30 fps -> 10 frozen frames
                    // 60 fps -> 20 frozen frames
                    Rect cropRect   = new Rect(0, 0, (int)(capture.FrameWidth * 0.5f), capture.FrameHeight);
                    var  _loadStart = Util.CountFrozenFrames(LoadType.Overworld, capture, f / fps, (int)(fps / 3),
                                                             (int)fps * 25, cropRect);

                    if (!_loadStart.HasValue)
                    {
                        continue;
                    }

                    var loadStart = _loadStart.Value;

                    // Check when the life counter first appears, skip three seconds
                    for (int i = (int)fps * 3; i < fps * 20; i++)
                    {
                        var lifeCount = LifeCounter.GetLifeCount(capture, (f + i) / fps, scale);
                        if (lifeCount >= 0)
                        {
                            loads.Add(new Load(LoadType.Overworld, loadStart.FrameStart, f + i));
                            break;
                        }
                    }
                }
            }

            updateProgress.Invoke(LoadRemover.ProgressPhase.Phase_6_OverworldLoads, 0.66f);

            progress = 0;


            if (loadTypes.HasFlag(LoadType.BackSign))
            {
                foreach (var f in startingFrames)
                {
                    updateProgress.Invoke(LoadRemover.ProgressPhase.Phase_6_OverworldLoads,
                                          0.75f + ((progress++) / (float)startingFrames.Count) * 0.25f);

                    // Check back sign loads
                    var _backSignLoad = Util.CountFrozenFrames(LoadType.BackSign, capture,
                                                               (f / fps) - backSignLoadMaxDuration, (int)fps * 1, (int)fps * backSignLoadMaxDuration);

                    if (!_backSignLoad.HasValue)
                    {
                        continue;
                    }

                    loads.Add(_backSignLoad.Value);
                }
            }

            return(loads);
        }
Exemplo n.º 4
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);
        }
        public static List <Load> GetEndSignLoads(VideoCapture capture, float scale, int startingFrame, int endingFrame,
                                                  Action <LoadRemover.ProgressPhase, float> updateProgress)
        {
            List <Load> loads           = new List <Load>();
            List <int>  potentialFrames = new List <int>();

            Mat   lastMat        = null;
            float lastBrightness = float.NaN;

            for (int f = startingFrame; f < endingFrame; f += (int)(capture.Fps * EndSignLoadMinDuration * 0.5f))
            {
                updateProgress.Invoke(LoadRemover.ProgressPhase.Phase_5_EndSignLoads, ((float)f / capture.FrameCount) * 0.5f);

                capture.PosFrames = f;
                var m = new Mat();
                capture.Read(m);

                float brightness = Util.GetAverageBrightness(m);
                if (float.IsNaN(lastBrightness))
                {
                    lastBrightness = brightness;
                }

                if (lastMat != null)
                {
                    float brightnessDiff = brightness - lastBrightness;

                    if (Math.Abs(brightnessDiff) < MaxBrightnessDeltaForCheck &&
                        Util.GetMaxDifference(m, lastMat) < Util.MaxDifferenceIdenticalFrames)
                    {
                        // Either have a visible lifecount or the left part of the screen isn't dark (to prevent the musician cutscene from being detected)
                        if (LifeCounter.GetLifeCount(capture, f / capture.Fps, scale) >= 0 ||
                            Util.GetAverageBrightness(m[0, m.Height, 0, (int)(m.Width * (3f / 16))]) > Util.DarknessMaxBrightness)
                        {
                            f += CheckPotentialFrames(capture, f, potentialFrames);
                        }
                    }

                    lastBrightness = brightness;
                }

                lastMat = m;
            }

            int pfProgress = 0;

            foreach (var pf in potentialFrames)
            {
                updateProgress.Invoke(LoadRemover.ProgressPhase.Phase_5_EndSignLoads, 0.5f + ((float)pfProgress / potentialFrames.Count) * 0.5f);

                var load = Util.CountFrozenFrames(LoadType.EndSign, capture, pf / capture.Fps,
                                                  (int)(capture.Fps * EndSignLoadMinDuration), (int)(EndSignLoadMaxDuration * capture.Fps));

                if (load.HasValue)
                {
                    var loadEndMat = new Mat();
                    capture.PosFrames = (int)(load.Value.FrameEnd + capture.Fps * 1.0f); // Check last frame + 1 second
                    capture.Read(loadEndMat);

                    // Not a binocular frame or life count found?
                    //if (!OverworldLoads.IsBinocularFrame(loadEndMat) ||
                    //LifeCounter.GetLifeCount(capture, capture.PosFrames / capture.Fps, scale) >= 0) {
                    loads.Add(load.Value);
                    //}
                }
            }

            return(loads);
        }