【问题标题】:Opencv VideoCapture set CV_CAP_PROP_POS_FRAMES not workingOpencv VideoCapture 设置 CV_CAP_PROP_POS_FRAMES 不起作用
【发布时间】:2013-10-24 14:47:37
【问题描述】:

我正在使用 opencv 从使用 mpeg 压缩的 Vivotek 相机的视频输出中读取帧。我正在尝试使用该功能从特定位置开始播放视频。如下所示,其中 start 是我要跳过的帧数。

inputVideo.set(CV_CAP_PROP_POS_FRAMES, start); 

但是我遇到了一个问题,因为在开始帧之前捕获了不正确的帧。

我使用的是opencv 2.4.2版

有人可以帮忙解决这个问题吗?

【问题讨论】:

  • 你怎么知道框架不正确?

标签: opencv


【解决方案1】:

有点太晚了,但搜索相同的主题(不是特定于 Vivotek 相机,而是更多关于 openCV 的 mpeg 问题):

  • 视频已编码
  • OpenCV 使用 FFMPEG
  • 编码使用关键帧
  • 跳过某些帧编码无法为您提供所需的确切帧

查看类似问题:

  • Getting individual frames using CV_CAP_PROP_POS_FRAMES in cvSetCaptureProperty

  • How can I get one single frame from a video file?

  • Problem with CV_CAP_PROP_POS_FRAMES setting next frame number

    desired position        key frame (this is where the cursor will stop)
     |   |                       |                    |
     |   >                       |                    |
     |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
    

    使用带有轨迹栏的 openCV 2.4.8 / VS2013 的示例代码:

  • 使用 AVI 格式 [MPEG4 视频 (H264)] 测试:设置帧位置工作正常
  • 使用 MPG 格式 [MPEG1/MPEG2] 测试:设置帧位置工作正常

    double currentPos = capture.get(CV_CAP_PROP_POS_FRAMES);
    std::cout << "CV_CAP_PROP_POS_FRAMES = " << currentPos << std::endl;
    
    // position_slider 0 - 100
    double noFrame = position_slider*nbFrames / 100;
    
    // solution 1
    bool success = capture.set(CV_CAP_PROP_POS_FRAMES, noFrame);
    // solution 2
    double frameRate = capture.get(CV_CAP_PROP_FPS);
    double frameTime = 1000.0 * noFrame / frameRate;
    bool success = capture.set(CV_CAP_PROP_POS_MSEC, frameTime);
    
    if (!success) {
        std::cout << "Cannot set frame position from video file at " << noFrame << std::endl;       
        return;
    }
    
    currentPos = capture.get(CV_CAP_PROP_POS_FRAMES);
    if (currentPos != noFrame) {
        std::cout << "Requesting frame " << noFrame << " but current position == " << currentPos << std::endl;
    }
    
    success = capture.read(frame_aux);
    if (!success) {
        std::cout << "Cannot get frame from video file " << std::endl;
        return;
    }
    imshow("test", frame_aux);
    

【讨论】:

    【解决方案2】:

    正如 BlouBlou 所说,当您使用 FFMPEG 读取一些高压缩视频格式时,它使用“关键帧”技术来解码视频。所以设置CV_CAP_PROP_POS_FRAMES 属性将无法正常工作。

    我测试了 5 个 mpeg 视频以从中读取所有帧。使用long totalFrameNumber = capture.get(CV_CAP_PROP_FRAME_COUNT);获取总帧数

    然后我把每个视频都看了两遍,第一次没有设置CV_CAP_PROP_POS_FRAMES为0,第二次设置了。

    当您将CV_CAP_PROP_POS_FRAMES 设置为 0 时,它确实丢了一些帧,每个视频大约 10 帧。我猜是因为关键帧不在视频的第一帧,所以FFMPEG会跳过一些帧到第一个关键帧。但有些视频格式如.avi 就不会出现这样的问题。我希望我的真实经历可以为其他人节省很多时间(我花了很长时间才找到这个页面,但已经足够了)。

    无论如何,强烈建议不要在面对 mpeg 格式视频时使用CV_CAP_PROP_POS_FRAMES 来获取特定帧。设置CV_CAP_PROP_POS_MSEC是一个明智的选择:)

    【讨论】:

      【解决方案3】:

      如果您愿意升级到 OpenCV 3.0,以下 2 个用例可能会对您有所帮助。这可能适用于早期版本,但我没有尝试过。 如果您希望它捕获帧 a 和帧 b 之间的所有帧,请参考第二个用例并将“desired_frames”替换为:

      desired_frames = range(a,b)
      

      首先,导入一些包

      import cv2
      import math
      import numpy as np
      

      每 n 秒捕获一次(这里,n = 5)

      #################### Setting up the file ################
      videoFile = "Jumanji.mp4"
      vidcap = cv2.VideoCapture(videoFile)
      success,image = vidcap.read()
      
      #################### Setting up parameters ################
      
      seconds = 5
      fps = vidcap.get(cv2.CAP_PROP_FPS) # Gets the frames per second
      multiplier = fps * seconds
      
      #################### Initiate Process ################
      
      while success:
          frameId = int(round(vidcap.get(1))) #current frame number, rounded b/c sometimes you get frame intervals which aren't integers...this adds a little imprecision but is likely good enough
          success, image = vidcap.read()
      
          if frameId % multiplier == 0:
              cv2.imwrite("FolderSeconds/frame%d.jpg" % frameId, image)
      
      vidcap.release()
      print "Complete"
      

      或者,每 n 帧捕获一次(这里,n = 10)

      #################### Setting up the file ################
      videoFile = "Jumanji.mp4"
      vidcap = cv2.VideoCapture(videoFile)
      success,image = vidcap.read()
      
      #################### Setting up parameters ################
      
      #OpenCV is notorious for not being able to good to 
      # predict how many frames are in a video. The point here is just to 
      # populate the "desired_frames" list for all the individual frames
      # you'd like to capture. 
      
      fps = vidcap.get(cv2.CAP_PROP_FPS)
      est_video_length_minutes = 3         # Round up if not sure.
      est_tot_frames = est_video_length_minutes * 60 * fps  # Sets an upper bound # of frames in video clip
      
      n = 5                             # Desired interval of frames to include
      desired_frames = n * np.arange(est_tot_frames) 
      
      
      #################### Initiate Process ################
      
      for i in desired_frames:
          vidcap.set(1,i-1)                      
          success,image = vidcap.read(1)         # image is an array of array of [R,G,B] values
          frameId = vidcap.get(1)                # The 0th frame is often a throw-away
          cv2.imwrite("FolderFrames/frame%d.jpg" % frameId, image)
      
      vidcap.release()
      print "Complete"
      

      差不多就是这样。


      一些不幸的警告...根据您的 opencv 版本(这是为 opencv V3 构建的),您可能需要以不同的方式设置 fps 变量。有关详细信息,请参阅here。要找出您的版本,您可以执行以下操作:
      (major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.')
      major_ver
      

      【讨论】:

        【解决方案4】:

        奇怪的是,我前面有一个assert

        inputVideo.set(CV_CAP_PROP_POS_FRAMES, start); 
        

        并删除 assert 解决了问题

        【讨论】:

          猜你喜欢
          • 2021-03-08
          • 2018-08-23
          • 2012-07-11
          • 2012-07-26
          • 1970-01-01
          • 2016-11-28
          • 2015-11-24
          相关资源
          最近更新 更多