OpenCV视频捕获:如何正确获取特定帧

OpenCV VideoCapture: Howto get specific frame correctly?

本文关键字:获取 何正确 视频 OpenCV      更新时间:2023-10-16

我正试图使用OpenCV 2.4.11从视频文件中获取特定帧。我试着按照文档和在线教程正确操作,现在已经测试了两种方法:

1( 第一种方法是使用video.grab((暴力读取每一帧,直到我到达我想要的特定帧(时间戳(。如果特定帧在视频序列中较晚,则此方法会很慢!

string videoFile(videoFilename);
VideoCapture video(videoFile);
double videoTimestamp = video.get(CV_CAP_PROP_POS_MSEC);
int videoFrameNumber = static_cast<int>(video.get(CV_CAP_PROP_POS_FRAMES));
while (videoTimestamp < targetTimestamp)
{
    videoTimestamp = video.get(CV_CAP_PROP_POS_MSEC);
    videoFrameNumber = static_cast<int>(video.get(CV_CAP_PROP_POS_FRAMES));
    // Grabe frame (but don't decode the frame as we are only "Fast forwarding")
    video.grab();
}
// Get and save frame
if (video.retrieve(frame))
{
    char txtBuffer[100];
    sprintf(txtBuffer, "Video1Frame_Target_%f_TS_%f_FN_%d.png", targetTimestamp, videoTimestamp, videoFrameNumber);
    string imgName = txtBuffer;
    imwrite(imgName, frame);
}

2( 第二种方法我使用video.set(…(。这种方法更快,如果特定帧在视频序列中较晚,似乎不会更慢。

string videoFile(videoFilename);
VideoCapture video2(videoFile);
videoTimestamp = video2.get(CV_CAP_PROP_POS_MSEC);
videoFrameNumber = static_cast<int>(video2.get(CV_CAP_PROP_POS_FRAMES));
video2.set(CV_CAP_PROP_POS_MSEC, targetTimestamp);
while (videoTimestamp < targetTimestamp)
{
    videoTimestamp = video2.get(CV_CAP_PROP_POS_MSEC);
    videoFrameNumber = (int)video2.get(CV_CAP_PROP_POS_FRAMES);
    // Grabe frame (but don't decode the frame as we are only "Fast forwarding")
    video2.grab();
}
// Get and save frame
if (video2.retrieve(frame))
{
    char txtBuffer[100];
    sprintf(txtBuffer, "Video2Frame_Target_%f_TS_%f_FN_%d.png", targetTimestamp, videoTimestamp, videoFrameNumber);
    string imgName = txtBuffer;
    imwrite(imgName, frame);
}

问题(现在的问题是,使用这两种方法确实会导致相同帧数的目标图像的内容帧数不相等?!?

我很想得出结论,方法1是正确的,OpenCV video.set(…(方法有问题。但是,如果我使用VLC播放器找到近似的目标帧位置,那么实际上是方法2最接近"正确"的结果?

作为一些额外的信息:我测试了相同的视频序列,但在两个不同的视频文件中分别用"avc1"MPG4和"wmv3"WMV编解码器编码。

使用WMV文件,找到的两个帧相差甚远?

使用MPG4文件,找到的两个帧只是略有偏离?

有没有人对此有一定的经验,可以解释我的发现,并告诉我从视频文件中获取特定帧的正确方法?

显然opencv/ffmpeg中仍然存在一个错误。ffmpeg无法提供所需的帧,和/或opencv无法处理此问题。看看这里和这里。

[编辑:在这个错误被修复之前(无论是在ffmpeg中,还是在opencv中(,按数字获取确切帧的唯一方法就是像以前一样"快进"。(关于VLC播放器:我怀疑它使用了那个bug集((-接口。对于玩家来说,寻找准确的帧通常并不太重要。但对于编辑来说,这是(。]

我认为OpenCV使用FFmpeg进行视频解码。

我们曾经遇到过类似的问题,但直接使用了FFmpeg。默认情况下,不能保证随机(但精确(帧访问。WMV解码器特别模糊。FFmpeg的较新版本允许您访问较低级别的例程,这些例程可用于构建帧的检索功能。这个解决方案有点复杂,我现在记不清了。我稍后会设法找到更多的细节。

作为一种快速解决方案,我建议离线解码视频,然后处理图像序列。尽管这增加了所需的存储量,但它保证了精确的随机帧访问。您可以使用FFmpeg将您的视频文件转换为如下图像序列:

ffmpeg -i "input.mov" -an -f image2 "output_%05d.png"