繁体   English   中英

在cvSetCaptureProperty中使用CV_CAP_PROP_POS_FRAMES获取单个帧

[英]Getting individual frames using CV_CAP_PROP_POS_FRAMES in cvSetCaptureProperty

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

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

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


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

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

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

那么,有没有办法解决这个问题呢? 我不只想要关键帧,而是每个帧。

我不知道这对你的目的是否足够精确,但是我已经成功地通过获取帧速率,将帧数转换为时间,然后前进到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);

由于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)

表现也很棒。 使用get_frame寻找第n帧是O(1),并且如果请求(几乎)连续帧则使用加速。 我已经获得了比同时加载三个720p视频更好的实时结果。

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

我在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()

经过几年的假设,这是一个不可靠的bug,我想我已经想出了一种方法,可以在速度和正确性之间取得良好的平衡。

之前的解决方案建议在读取帧之前使用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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM