Getting individual frames using CV_CAP_PROP_POS_FRAMES in cvSetCaptureProperty

前端 未结 5 943
天命终不由人
天命终不由人 2020-12-15 09:00

I am trying to jump to a specific frame by setting the CV_CAP_PROP_POS_FRAMES property and then reading the frame like this:

cvSetCapturePropert         


        
相关标签:
5条回答
  • 2020-12-15 09:06

    I don't know whether or not this would be precise enough for your purpose, but I've had success getting to a particular point in an MPEG video by grabbing the frame rate, converting the frame number to a time, then advancing to the time. Like so:

    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);
    
    0 讨论(0)
  • Due to this limitation in OpenCV, it may be wise to to use FFMPEG instead. Moviepy is a nice wrapper library.

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

    Performance is great too. Seeking to the nth frame with get_frame is O(1), and a speed-up is used if (nearly) consecutive frames are requested. I've gotten better-than-realtime results loading three 720p videos simultaneously.

    0 讨论(0)
  • 2020-12-15 09:16

    I've successfully used the following on 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()
    
    0 讨论(0)
  • 2020-12-15 09:21

    CV_CAP_PROP_POS_FRAMES jumps to a key frame. I had the same issue and worked around it using this (python-)code. It's probably not totally efficient, but get's the job done:

    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
    
    0 讨论(0)
  • 2020-12-15 09:21

    After some years assuming this as a unsavable bug, I think I've figured out a way to use with a good balance between speed and correctness.

    A previous solution suggested to use the CV_CAP_PROP_POS_MSEC property before reading the frame:

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

    It does return the expected frame, but the problem is that using CV_CAP_PROP_POS_MSEC may be very slow, for example for a video conversion.

    Note: using global variables for simplicity.


    On the other hand, if you just want to read the video sequentially, it is enough to read frame without seeking at all.

    for (int frameNumber = 0; frameNumber < nFrames; ++frameNumber) {
      sourceVideo.read(image);
    }
    

    The solution comes from combining both: using a variable to remember the last queried frame, lastFrameNumber, and only seek when requested frame is not the next one. In this way it is possible to increase the speed in a sequential reading while allowing random seek if necessary.

    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;
    }
    
    0 讨论(0)
提交回复
热议问题