Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reverse video playback in OpenCV

Is it possible to play videos backwards in OpenCV? Either by an API call or by buffering video frames and reversing the order into a new video file.

Thanks

like image 243
darasan Avatar asked Jun 29 '12 10:06

darasan


People also ask

How do I reverse a video in OpenCV?

Take a video as input and play it in a reverse mode by breaking the video into frame by frame and simultaneously store that frame in the list. After getting list of frames we perform iteration over the frames. For playing video in reverse mode, we need only to iterate reverse in the list of frames.

How do I view video in OpenCV?

To read a video with OpenCV, we can use the cv2. VideoCapture(filename, apiPreference) class. In the case where you want to read a video from a file, the first argument is the path to the video file (eg: my_videos/test_video. mp4).

What is OpenCV used for?

OpenCV (Open Source Computer Vision Library) is an open source computer vision and machine learning software library. OpenCV was built to provide a common infrastructure for computer vision applications and to accelerate the use of machine perception in the commercial products.


2 Answers

The only practical way to do it is to manually extract frames, buffer them ( on memory or files) then reload them in reverse order.

The problem is that video compressors are all exploiting time redundancy - the fact that two consecutive frames are most of the time very similar. So, they encode a full frame from time to time (usually every few hundred frames) then only send differences between the previous and current.

Now, for decoding to work, it must be done in the same order - decode a keyframe (a full one), then for each new frame, add differences to obtain the current image.

This strategy makes it very hard to reverse play in video. There are several techniques, but all involve buffering.

Now, you may have seen the CV_CAP_PROP_POS_FRAMES parameters, described by Astor. It seems ok, but because of the issues described above, OpenCV is not able to correctly jump to a specific frame (there are multiple bugs open on these issues). They (OpenCV devs) are working on some solutions, but even those will be very slow (they involve going back to previous keyframe, then decoding back to the selected one). If you use this technique, each frame must be decoded hundreds of times on average, making it very slow. And yet, it doesn't work.

Edit If you pursue the buffer way to reverse it, keep in mind that a decoded video will quickly eat off the memory resources of a regular computer. A usual 720p video one minute long needs 4.7GB of memory when decompressed! Storing frames as individual files on disk is a practical solution to this problem.

like image 191
Sam Avatar answered Sep 22 '22 05:09

Sam


Another solution, similar to ArtemStorozhuk's answer is to use the FPS to move from the frame domain into the time domain, then seek backwards using CV_CAP_PROP_POS_MSEC (which doesn't hammer the CPU like CV_CAP_PROP_POS_FRAMES can). Here's my sample code, which works perfectly on an .mpg, using only about 50% CPU.

#include <opencv2/opencv.hpp>

int main (int argc, char* argv[])
{
  cv::VideoCapture cap(argv[1]);

  double frame_rate = cap.get(CV_CAP_PROP_FPS);

  // Calculate number of msec per frame.
  // (msec/sec / frames/sec = msec/frame)
  double frame_msec = 1000 / frame_rate;

  // Seek to the end of the video.
  cap.set(CV_CAP_PROP_POS_AVI_RATIO, 1);

  // Get video length (because we're at the end).
  double video_time = cap.get(CV_CAP_PROP_POS_MSEC);

  cv::Mat frame;
  cv::namedWindow("window");

  while (video_time > 0)
  {
    // Decrease video time by number of msec in one frame
    // and seek to the new time.
    video_time -= frame_msec;
    cap.set(CV_CAP_PROP_POS_MSEC, video_time);

    // Grab the frame and display it.
    cap >> frame;
    cv::imshow("window", frame);

    // Necessary for opencv's event loop to work.
    // Wait for the length of one frame before
    // continuing the loop. Exit if the user presses
    // any key. If you want the video to play faster
    // or slower, adjust the parameter accordingly.    
    if (cv::waitKey(frame_msec) >= 0)
      break;
  }
}
like image 33
Trebor Rude Avatar answered Sep 22 '22 05:09

Trebor Rude