Stworzyłem aplikację, aby wyodrębnić każdą ramkę z filmu. Nie jest jeszcze idealny i ma z nim wiele problemów. Jednym z problemów jest jak długo trwa. Wyodrębniono pełne 2 godziny, aby wyodrębnić ramki o 3 minuty wideo, a oryginalny film był cztery minuty, ale dał mi błąd pamięci po prawie zakończeniu wideo:

"OpenCV: nie udało się przydzielić 2764800 bajtów"

Wykonanie zadania zajmuje dużo czasu i ma kilka błędów, używam EMGU.CV i Mediatoolkit

Oto mój kod:

  using Emgu.CV;
  using MediaToolkit;
  using MediaToolkit.Model;



    string file;

    private void CreateFrames()
    {
        int i = 0;

        VideoCapture _video = new VideoCapture(file);
        var videoInfo = new MediaFile { Filename = file };
        using (var engine = new Engine()) { engine.GetMetadata(videoInfo); }

        while (i <= videoInfo.Metadata.Duration.Milliseconds * videoInfo.Metadata.VideoData.Fps)
        {
            Mat m = new Mat();
            _video.Read(m);
            _video.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.PosFrames, i);
            preview.Image = _video.QueryFrame().ToBitmap();
            Bitmap bitmap = _video.QueryFrame().ToBitmap();
            bitmap.Save(String.Format(@"C:\\frames\\videoframe-{0}.png", i), 
    ImageFormat.Png);
            i++;
        }
    }

    private async void browse_Click(object sender, EventArgs e)
    {
        DialogResult result = openFileDialog.ShowDialog();
        if (result == DialogResult.OK)
        {
            file = openFileDialog.FileName;
            var videoInfo = new MediaFile { Filename = file };
            using (var engine = new Engine()) { engine.GetMetadata(videoInfo); }
            label1.Text = videoInfo.Metadata.VideoData.Fps + "  \n" + 
         videoInfo.Metadata.Duration.ToString();

            // must be run on a second thread else it will not work and it will freeze
            Task getframes = new Task(CreateFrames);
            getframes.Start();
        }
    }

Byłbym wdzięczny, jeśli ktoś ma rozwiązanie, aby to szybciej i poprawić problem z pamięcią.

1
user12581835 11 marzec 2020, 14:40

2 odpowiedzi

Najlepsza odpowiedź

Przede wszystkim sprawdź, czy są idisposable obiektów bez "przy użyciu". Na przykład, bitmapa dziedziczy od obrazu, który realizuje się idisposable. Tak więc, gdy używasz bimap, kod powinien być taki:

using (Bitmap bitmap = _video.QueryFrame().ToBitmap())
{
    bitmap.Save(String.Format(@"C:\\frames\\videoframe-{0}.png", i), ImageFormat.Png);
}

Sprawdź także, czy klasa maty wdraża IDSPObable. Jeśli tak jest, inny użycie może pomóc w problemie pamięci.

W przypadku problemu z wydajnością naprawdę nie mogę pomóc, ale 3 minuty wideo, w 60fps, wynoszą 10800 klatek. 2 godziny to 7200 sekund. Tak 0,6 sekundy na ramkę. Czy to naprawdę powolne? Po prostu pytam, nie mam pojęcia ...

1
Pepelui360 11 marzec 2020, 11:59

Oto implementacja przy użyciu tylko Emgu.CV (Nie znam MediaToolKit).

using (var video = new VideoCapture(file))
using (var img = new Mat())
{
    while (video.Grab())
    {
        video.Retrieve(img);
        var filename = Path.Combine(outputDirectory, $"{i}.png");
        CvInvoke.Imwrite(filename, img);
        i++;
    }
}

Przetestowałem to na wideo FullHD {X0}}. Nie znam dokładnego kodeka. Zajmuje to około 65 ms na ramkę z moim procesorem (I7-8700K). Ale rozważ, jeśli chcesz użyć formatu png. Jeśli nie chcesz żadnych błędów kompresji, a twardy jest szybki i wystarczająco duży dla bmp, zajmuje tylko 13 ms na ramkę. jpg zajmuje 40 ms.

2
Anders 7 kwiecień 2021, 06:51