OpenCV VideoCapture:如何正确获取特定帧?

时间:2015-07-06 08:55:38

标签: c++ opencv video video-capture

我正在尝试使用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文件时,两个找到的帧只是稍微关闭了?

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

2 个答案:

答案 0 :(得分:0)

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

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

作为一种快速解决方法,我建议您将视频解码离线,然后处理图像序列。但是,这增加了所需的存储量,它保证了精确的随机帧访问。你可以像这样使用FFmpeg convert your video file in to a sequence of images

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

答案 1 :(得分:0)

显然,opencv / ffmpeg中仍然存在一个错误。 ffmpeg没有提供所需的帧和/或opencv不处理这个。请参阅herehere

[编辑: 直到修复了这个bug(无论是在ffmpeg中还是(作为opencv中的变通方法)),按编号获得精确帧的唯一方法是快速前进&#34;就像你做的那样。 (关于VLC-player:我怀疑它使用了那个错误的set() - 接口。对于一个玩家来说,寻找帧精确通常并不太重要。但是对于编辑来说它是。)