Opencv VideoCapture设置CV_CAP_PROP_POS_FRAMES无效

16

我正在使用OpenCV从一台采用MPEG压缩的Vivotek摄像机的视频输出中读取帧。 我尝试使用下面显示的函数从特定位置开始播放视频,其中“start”是我想要跳过的帧数。

inputVideo.set(CV_CAP_PROP_POS_FRAMES, start); 

然而,我在这方面遇到了问题,因为捕获的帧是不正确的,它发生在开始帧之前。

我正在使用opencv版本2.4.2。

请问有人能帮忙解决这个问题吗?


你怎么知道帧是不正确的? - herohuyongtao
这里有一个关于它的大讨论帖:https://github.com/opencv/opencv/issues/9053 摘要:OpenCV存在一个bug,无论是设置还是获取CV_CAP_PROP_POS_FRAMES都不能保证给出正确的结果。请使用pyav代替。https://pyav.org - Peter
这里有一个关于此问题的大型讨论帖子:https://github.com/opencv/opencv/issues/9053。总结一下:OpenCV存在一个bug,无论是设置还是获取CV_CAP_PROP_POS_FRAMES都不能保证给出正确的结果。建议使用`pyav`代替。https://pyav.org。 - undefined
4个回答

19

虽然有点晚,但是关于同一主题的搜索(不仅限于Vivotek摄像机,还包括在openCV中出现mpeg问题):

  • 视频被编码
  • OpenCV使用FFMPEG
  • 编码使用关键帧
  • 当跳过一些帧时,编码无法给你想要的确切帧

请参见类似的问题:

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);
    

    8
    正如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,以下两个用例可能对您有所帮助。这些可能适用于早期版本,但我没有尝试过。如果您希望捕获介于帧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变量。有关详细信息,请参见此处。要查找您的版本,您可以执行以下操作:

    (major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.')
    major_ver
    

    -2

    对我来说很奇怪,我在前面加了一个assert

    inputVideo.set(CV_CAP_PROP_POS_FRAMES, start); 
    

    移除那个 assert 就解决了问题。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接