C # Почему моя частота кадров видео медленнее при использовании OpenCV VideoCapture?

#c# #opencv #video-streaming #video-capture #emgucv

#c# #opencv #потоковое видео #видеозахват #emgucv

Вопрос:

Я пытаюсь прочитать видео (.mp4) из файла и показать его в поле изображения в формах. Когда я подсчитываю общее время воспроизведения, это на самом деле больше, чем фактическая длина видео. Например, если видео на самом деле длится 10 секунд, оно воспроизводится в течение 12-13 секунд. Почему это? Частота кадров для моего видео составляет 31 кадр / с

Я попытался убедиться, что переменная fps уже имеет правильную частоту кадров.

 private void Play_Video(string filePath)
{
    VideoCapture cameraCapture = new VideoCapture(filePath);

    var stopwatch = new Stopwatch();
    stopwatch.Start();
    while (true)
    {
        Mat m = new Mat();
        cameraCapture.Read(m);
        if (!m.IsEmpty)
        {
            imageBox1.Image = m;
            double fps = cameraCapture.GetCaptureProperty(CapProp.Fps);
            Thread.Sleep(1000 / Convert.ToInt32(fps));
        }
        else
        {
            break;
        }
    }
    stopwatch.Stop();
    double elapsed_time = stopwatch.ElapsedMilliseconds * 0.001;
    // My elapsed_time here shows about 13 seconds, when the actual length of video is 10 seconds. Why?
}
  

НОВОЕ РЕДАКТИРОВАНИЕ:
Я обновил извлечение кадров в отдельной функции, чтобы не вызывать задержки во время рендеринга, но задержка все еще составляет несколько секунд. Например, 30 секунд видео воспроизводится в течение 32-33 секунд. Мой обновленный код:

 private void Play_Video(List<Mat> frames, double fps, Emgu.CV.UI.ImageBox imageBox)
{
    var stopwatch = new Stopwatch();
    stopwatch.Start();

    for (int i = 0; i < frames.Count; i  )
    {
        imageBox.Image = frames[i];
        Thread.Sleep(1000 / Convert.ToInt32(fps));
    }
    stopwatch.Stop();
    double elapsed_time = stopwatch.ElapsedMilliseconds * 0.001;
}
  

Комментарии:

1. Обычно медиаплееры буферизуют кадры, чтобы не снижать скорость воспроизведения. Вы получаете 1 кадр на каждой итерации.

2. Как я могу это сделать? Есть ли какой-нибудь пример?

3. Во втором примере вы считываете все видео и воспроизводите его на основе таймера. В зависимости от размера кадра это много данных. Возможно ли, что в этом примере вы сжигаете GC, повышающий память?

4. На самом деле видео не воспроизводится по таймеру. Таймер существует только для того, чтобы я мог измерить, сколько времени занял цикл for.

Ответ №1:

Это был единственный способ получить точные кадры в секунду, потому что при использовании thread.sleep для включения и выключения требуется 10-15 мс. У меня была такая же проблема с Task.Задержка. Кажется, это работает отлично, если у кого-нибудь есть лучшее решение, я бы хотел его увидеть.

     private void PlayVideo()
    {
        double framecount = video.Get(Emgu.CV.CvEnum.CapProp.FrameCount);
        double fps = video.Get(Emgu.CV.CvEnum.CapProp.Fps);

        video.Set(Emgu.CV.CvEnum.CapProp.PosFrames, (double)(framecount * (double)(savePercent / 100)));
        int ms = Convert.ToInt32(((double)1000 / fps));

        while (disablePreviewTimer == false)
        {
            DateTime dt = DateTime.Now;
            Emgu.CV.Mat img = video.QueryFrame();
            if (img == null)
            {
                disablePreviewTimer = true;
                break;
            }
            else
            {
                Emgu.CV.Util.VectorOfByte vb = new Emgu.CV.Util.VectorOfByte();
                Emgu.CV.CvInvoke.Resize(img, img, PreviewSize);
                Emgu.CV.CvInvoke.Imencode(".jpg", img, vb);

                pictureBox5.Image = Image.FromStream(new MemoryStream(vb.ToArray()));
                Application.DoEvents();

                while ((DateTime.Now - dt).TotalMilliseconds < ms)
                {
                    Application.DoEvents();
                }

                label6.Text = ((int)(DateTime.Now - dt).TotalMilliseconds).ToString();
            }
        }
    }