Opencv VideoCapture set CV_CAP_PROP_POS_FRAMES not working

后端 未结 4 1829
醉酒成梦
醉酒成梦 2020-12-09 12:41

I am using opencv to read frames from a video output from a Vivotek Camera using the mpeg compression. I am trying to use the function to start the video from a particular

相关标签:
4条回答
  • 2020-12-09 12:42

    Strangely to me, I had an assert in front of

    inputVideo.set(CV_CAP_PROP_POS_FRAMES, start); 
    

    and removing that assert solved the problem

    0 讨论(0)
  • 2020-12-09 12:57

    Just as BlouBlou said, when you read some high compression video format with FFMPEG, it used "key frame" technology to decode video. So set CV_CAP_PROP_POS_FRAMES property will not work as you want.

    I tested 5 mpeg videos to read all frames from them. Get total frame count using long totalFrameNumber = capture.get(CV_CAP_PROP_FRAME_COUNT);

    Then I read each video twice, first time not set CV_CAP_PROP_POS_FRAMES to 0, second time set it.

    It did drop some frames when you set CV_CAP_PROP_POS_FRAMES to 0, about 10 frames each video. I guess because the key frame is not in the first frame of the video, so FFMPEG skip some frames to the first key frame. But some video format such as .avi won't appear such problem. I hope my real experience will save other people much time (well I spent to find this page not too long but enough).

    Anyway, highly recommend not to using CV_CAP_PROP_POS_FRAMES to get a specific frame when you face mpeg format video. It is a smart choice to set CV_CAP_PROP_POS_MSEC:)

    0 讨论(0)
  • 2020-12-09 13:08

    A bit too late but searching on same topic (not specific to Vivotek Camera but more on mpeg problem with openCV) :

    • Video is encoded
    • OpenCV uses FFMPEG
    • Encoding use key frames
    • When skipping some frames encoding can't give you the exact frame you want

    see similar question :

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

      Sample code using openCV 2.4.8 / VS2013 with trackbar :

    • tested with AVI format [MPEG4 Video (H264)] : setting frame position works fine
    • tested with MPG format [MPEG1/MPEG2] : setting frame position works fine

      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);
      
    0 讨论(0)
  • 2020-12-09 13:08

    If you're willing to upgrade to OpenCV 3.0 the following 2 use cases may be help you. This may work for earlier versions, but I haven't tried. If you want it to capture all the frames between frame a and frame b, refer to the 2nd use case and replace "desired_frames" with:

    desired_frames = range(a,b)
    

    First, import some packages

    import cv2
    import math
    import numpy as np
    

    Capture every n seconds (here, 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"
    

    Alternatively, capture every n frames (here, 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"
    

    That's pretty much it.


    Some unfortunante caveats...depending on your version of opencv (this is built for opencv V3), you may need to set the fps variable differently. See here for details. To find out your version, you can do the following:

    (major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.')
    major_ver
    
    0 讨论(0)
提交回复
热议问题