我正在尝试使用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。默认情况下,不保证随机(但精确)帧访问。 WMV解码器特别模糊。
较新版本的FFmpeg允许您访问较低级的例程,这些例程可用于构建帧的检索功能。这个解决方案有点复杂,现在我想不起来了。我稍后会尝试找到更多详细信息。
作为快速解决方法,我建议离线解码视频,然后处理图像序列。虽然,这增加了所需的存储量,但可以保证精确的随机帧访问。您可以像这样使用FFmpeg来convert your video file in to a sequence of images:
ffmpeg -i "input.mov" -an -f image2 "output_%05d.png"
关于c++ - OpenCV VideoCapture:如何正确获取特定帧?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31241552/