Какой самый быстрый способ извлечь все кадры видео в изображения в C#? - PullRequest
0 голосов
/ 11 марта 2020

Я создал приложение для извлечения каждого кадра из видео. Это еще не идеально, и с ним много проблем. Одна из проблем заключается в том, сколько времени это займет. Извлечение кадров 3-минутного видео заняло целых 2 часа, а исходное видео заняло четыре минуты, но после того, как видео было почти закончено, возникла ошибка памяти:

'OpenCV: не удалось выделить 2764800 байт '

Итак, выполнение задачи занимает много времени, и у нее есть несколько ошибок. Я использую Emgu.CV и MediaToolkit

Вот мой код:

  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();
        }
    }

Я был бы признателен, если бы у кого-нибудь было решение сделать это быстрее и, возможно, решить проблему с памятью.

Ответы [ 2 ]

0 голосов
/ 13 марта 2020

Вот реализация, использующая только Emgu.CV (я не знаком с 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++;
    }
}

Я проверил это на видео FullHD mp4. Не знаю точного кода c. Это занимает примерно 65 мс на кадр с моим процессором (i7-8700k). Но подумайте, нужно ли вам использовать формат png. Если вам не нужны какие-либо ошибки сжатия, и ваш жесткий диск достаточно быстрый и достаточно большой для bmp, он занимает всего 13 мс на кадр. jpg занимает 40 мс.

0 голосов
/ 11 марта 2020

Прежде всего, проверьте наличие IDisposable объектов без «использования». Например, Bitmap наследуется от Image, который реализует IDisposable. Таким образом, при использовании bimap ваш код должен выглядеть следующим образом:

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

Также проверьте, реализует ли класс Mat класс IDisposable. Если это так, то другое использование может помочь вам с проблемой памяти.

Что касается производительности, я действительно не могу помочь, но 3-минутное видео со скоростью 60 кадров в секунду составляет 10800 кадров. 2 часа - 7200 секунд. Так что 0,6 секунды на кадр. Это действительно медленно? Просто спрашиваю, понятия не имею ...

...