13

我试图通过设置属性跳到特定的框架CV_CAP_PROP_POS_FRAMES,然后像这样读取框架:

cvSetCaptureProperty( input_video, CV_CAP_PROP_POS_FRAMES, current_frame );
frame = cvQueryFrame( input_video );

我面临的问题是,OpenCV 2.1 为 12 个连续值返回相同的帧,current_frame而我想读取每个单独的帧,而不仅仅是关键帧。谁能告诉我怎么了?


我做了一些研究,发现问题是由解压缩算法引起的。

类似MPEG的算法(包括HD等)并不单独压缩每一帧,而是时不时保存一个关键帧,然后只保存最后一帧和后续帧的差异。

您报告的问题是由于当您选择一帧时,解码器(可能是ffmpeg)自动前进到下一个关键帧。

那么,有没有办法解决这个问题?我不仅想要关键帧,还想要每个单独的帧。

4

5 回答 5

5

我不知道这对于您的目的是否足够精确,但我已经成功地通过抓取帧速率,将帧数转换为时间,然后推进到 MPEG 视频中的特定点时间。像这样:

cv::VideoCapture sourceVideo("/some/file/name.mpg");
double frameRate = sourceVideo.get(CV_CAP_PROP_FPS);
double frameTime = 1000.0 * frameNumber / frameRate;
sourceVideo.set(CV_CAP_PROP_POS_MSEC, frameTime);
于 2013-05-31T01:33:05.413 回答
3

CV_CAP_PROP_POS_FRAMES 跳转到关键帧。我遇到了同样的问题并使用这个(python-)代码解决了它。它可能不是完全有效,但完成工作:

def seekTo(cap, position):
  positiontoset = position
  pos = -1
  cap.set(cv.CV_CAP_PROP_POS_FRAMES, position)
  while pos < position:
    ret, image = cap.read()
    pos = cap.get(cv.CV_CAP_PROP_POS_FRAMES)
    if pos == position:
      return image
    elif pos > position:
      positiontoset -= 1
      cap.set(cv.CV_CAP_PROP_POS_FRAMES, positiontoset)
      pos = -1
于 2013-03-07T15:47:03.417 回答
3

由于 OpenCV 的这种限制,明智的做法是改用 FFMPEG。Moviepy是一个不错的包装库。

# Get nth frame from a video
from moviepy.video.io.ffmpeg_reader import FFMPEG_VideoReader
cap = FFMPEG_VideoReader("movie.mov",True)
cap.initialize()
cap.get_frame(n/FPS)

性能也很棒。寻找第 n 帧的get_frame时间为 O(1),如果请求(几乎)连续的帧,则使用加速。同时加载三个 720p 视频时,我得到了比实时更好的结果。

于 2015-08-20T13:54:09.853 回答
0

我在 OpenCV 3 / Python 3 上成功使用了以下内容:

 # Skip to 150 frame then read the 151th frame
 cap.set(cv2.CAP_PROP_POS_FRAMES, 150))
 ret, frame = cap.read()
于 2016-11-07T01:25:40.433 回答
0

在假设这是一个无法挽救的错误几年之后,我想我已经找到了一种在速度和正确性之间取得良好平衡的方法。

以前的解决方案建议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;
}
于 2017-05-25T15:25:14.673 回答