在假设这是一个无法挽救的错误几年之后,我想我已经找到了一种在速度和正确性之间取得良好平衡的方法。
以前的解决方案建议CV_CAP_PROP_POS_MSEC
在读取框架之前使用该属性:
cv::VideoCapture sourceVideo("/some/file/name.mpg");
const auto frameRate = sourceVideo.get(CV_CAP_PROP_FPS);
void readFrame(int frameNumber, cv::Mat& image) {
const double frameTime = 1000.0 * frameNumber / frameRate;
sourceVideo.set(CV_CAP_PROP_POS_MSEC, frameTime);
sourceVideo.read(image);
}
它确实返回了预期的帧,但问题是使用CV_CAP_PROP_POS_MSEC
可能非常慢,例如用于视频转换。
注意:为简单起见,使用全局变量。
另一方面,如果您只想顺序读取视频,则完全无需搜索即可读取帧。
for (int frameNumber = 0; frameNumber < nFrames; ++frameNumber) {
sourceVideo.read(image);
}
解决方案来自于将两者结合起来:使用变量来记住最后查询的帧lastFrameNumber
,并且仅在请求的帧不是下一个时才搜索。通过这种方式,可以提高顺序读取的速度,同时在必要时允许随机搜索。
cv::VideoCapture sourceVideo("/some/file/name.mpg");
const auto frameRate = sourceVideo.get(CV_CAP_PROP_FPS);
const int lastFrameNumber = -2; // guarantee seeking the first time
void readFrame(int frameNumber, cv::Mat& image) {
if (lastFrameNumber + 1 != frameNumber) { // not the next frame? seek
const double frameTime = 1000.0 * frameNumber / frameRate;
sourceVideo.set(CV_CAP_PROP_POS_MSEC, frameTime);
}
sourceVideo.read(image);
lastFrameNumber = frameNumber;
}