OpenCV не сообщает точную частоту кадров/количество
У меня есть 33-секундное видео, которое я пытаюсь обработать с помощью OpenCV. Моя цель - определить, какой экземпляр во времени (относительно начала видео) соответствует каждому кадру. Я делаю это, чтобы иметь возможность сравнивать кадры с видео с той же сцены, которые были записаны с разной частотой кадров.
Что работает:
- FPS правильно сообщается как 59.75. Это согласуется с тем, что сообщает
ffprobe
, поэтому я рад поверить в это правильно.
У меня возникают проблемы:
-
CAP_PROP_POS_MSEC
возвращает неверные значения. К концу видео он достигает 557924 мс (более 9 мин). Для 33-секундного видео это не может быть правильно.
-
CAP_PROP_FRAME_COUNT
также неверен. Он сообщил, что 33371, что на 59,75 кадров в секунду даст более 9 минут. В соответствии с вышеуказанной ошибкой, но все же неверно.
-
CAP_PROP_POS_FRAME
также неверно.
Видео можно найти здесь (примерно 10 МБ).
Любые идеи о том, что может быть неправильным?
ffprobe
вывод:
FFprobe version SVN-r20090707, Copyright (c) 2007-2009 Stefano Sabatini
libavutil 49.15. 0 / 49.15. 0
libavcodec 52.20. 0 / 52.20. 1
libavformat 52.31. 0 / 52.31. 0
built on Jan 20 2010 00:13:01, gcc: 4.4.3 20100116 (prerelease)
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/home/misha/Dropbox/Public/sequence.mp4':
Duration: 00:00:33.37, start: 0.000000, bitrate: 2760 kb/s
Stream #0.0(und): Video: h264, yuv420p, 1920x1080, 59.75 tbr, 1k tbn, 2k tbc
Stream #0.1(und): Audio: aac, 44100 Hz, stereo, s16
Полный код:
#include <iostream>
#include <assert.h>
#include <cv.h>
#include <highgui.h>
#include <cmath>
#include <iostream>
#include <string.h>
#include <stdio.h>
extern "C"
{
#include "options.h"
}
using namespace std;
#define DEBUG 0
static void
print_usage(char *argv0)
{
cerr << "usage: " << argv0 << " video.avi [options]" << endl;
}
int main(int argc, char** argv)
{
if (argc < 2)
{
print_usage(argv[0]);
return -1;
}
int step = 30;
struct Option options[] =
{
{ "step", 1, &step },
{ NULL, 0, NULL }
};
int ret = parse_options(2, argc, argv, options);
if (ret == 0)
{
print_usage(argv[0]);
return -1;
}
CvCapture *capture = cvCaptureFromFile(argv[1]);
int counter = 0;
while (cvGrabFrame(capture))
{
++counter;
IplImage *frame = cvRetrieveFrame(capture);
double millis = cvGetCaptureProperty(capture, CV_CAP_PROP_POS_MSEC);
double current = cvGetCaptureProperty(capture, CV_CAP_PROP_POS_FRAMES);
double total = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_COUNT);
printf("%d %d/%d %f\n", counter, (int)current, (int)total, millis);
int min = (int)(millis/1000/60);
millis -= min*60000;
int sec = (int)(millis/1000);
millis -= sec*1000;
printf("%d %02d:%02d:%f\n", counter, min, sec, millis);
}
cvReleaseCapture(&capture);
return 0;
}
Ответы
Ответ 1
Всегда сообщайте версию программного обеспечения, которое вы используете: какую версию OpenCV вы используете для этого? Ваша версия может быть старой, поэтому обновите ее до последней, если это возможно.
Если ваш видеофайл является частью какого-либо другого более крупного видео, эта информация может быть действительно правильной, поскольку:
CV_CAP_PROP_POS_MSEC - film current position in milliseconds or video capture timestamp
OpenCV может просто читать все эти вещи из заголовка файла, что, очевидно, неверно. Это может произойти, если кто-то использовал топор (или другой средневековый инструмент), чтобы снять эту часть с оригинального видео.
Вы должны попробовать с видеороликами, которые вы создали, и знаете, что они не были подделаны.
В худшем случае вам придется реализовать эти функции самостоятельно. Нет biggie.
EDIT:
@misha Вначале я не заметил, что вы используете:
CvCapture *capture = cvCaptureFromFile(argv[1]);
Замените его для cvCaptureFromAVI(), если можете, и ВСЕГДА проверьте возвращаемое значение вызовов OpenCV:
CvCapture *capture = cvCaptureFromAVI(argv[1]);
if(!capture)
{
printf("!!! cvCaptureFromAVI failed (file not found?)\n");
return -1;
}
Несколько дней назад я поделился кодом, который использует OpenCV для чтения видеофайла, а затем сохраняет кадры в виде изображений JPG на диске. Он также сообщает текущий FPS, используя традиционный cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);
, поэтому вам может быть интересно взглянуть на это. Пойдите, проверьте это.
EDIT:
Вы также должны проверить этот поток о количестве кадров, используя OpenCV;
Кстати, я только что обновил некоторые библиотеки в своей системе Ubuntu и перекомпилировал OpenCV-2.1.0.tar.bz2 (используя cmake). Я изменил исходный код (который использует cvCaptureFromAVI()) для печати материала с использованием метода отладки для каждого кадра. Кажется, он работает:
* Filename: sequence.mp4
* FPS: 59
...
...
...
17 00:00:567.000000
18 601/33371 601.000000
18 00:00:601.000000
19 634/33371 634.000000
19 00:00:634.000000
20 668/33371 668.000000
20 00:00:668.000000
21 701/33371 701.000000
21 00:00:701.000000
22 734/33371 734.000000
22 00:00:734.000000
23 768/33371 768.000000
23 00:00:768.000000
24 801/33371 801.000000
24 00:00:801.000000
25 835/33371 835.000000
25 00:00:835.000000
26 868/33371 868.000000
26 00:00:868.000000
27 901/33371 901.000000
27 00:00:901.000000
28 935/33371 935.000000
...