Solarian Programmer

My programming ramblings

OpenCV video editing tutorial

Posted on June 4, 2015 by Sol

The code for this post is on GitHub: https://github.com/sol-prog/opencv-video-editing.

I’ve always knew that OpenCV can be used to do some video editing, however when I’ve actually tried to use it to open a video file, a few weeks ago, I was amazed at the quantity of misleading or incomplete tutorials you find on the web. I wrote this tutorial to save some time for others like me that need to do some quick and dirty video editing with C++ and OpenCV.

Simplest thing that you could try is to read frames from your computer’s webcam. OpenCV uses the same function, VideoCapture, for opening a video file or a camera attached to your computer, the only difference is that for a camera you will feed the function a number, while for an actual video file you will use the video path.

If your computer has a camera attached to it, you can access it with the next piece of code (use the escape key to close the program):

 1 #include <iostream>
 2 #include <opencv2/opencv.hpp>
 3 
 4 int main() {
 5     // Open the first camera attached to your computer
 6     cv::VideoCapture cap(0);
 7     if(!cap.isOpened()) {
 8         std::cout << "Unable to open the camera\n";
 9         std::exit(-1);
10     }
11 
12     cv::Mat image;
13     double FPS = 24.0;
14     // Read camera frames (at approx 24 FPS) and show the result
15     while(true) {
16         cap >> image;
17         if(image.empty()) {
18             std::cout << "Can't read frames from your camera\n";
19             break;
20         }
21 
22         cv::imshow("Camera feed", image);
23 
24         // Stop the camera if users presses the "ESC" key
25         if(cv::waitKey(1000.0/FPS) == 27) break;
26     }
27 
28     return 0;
29 }

Now, if you want to save the feed from your camera to a video file, you need use the VideoWriter function, that has the next signature:

1 VideoWriter::VideoWriter(const string& filename, int fourcc, double fps, Size frameSize, bool isColor=true)

from which the second parameter, fourcc - 4-character code of codec used to compress the frames, is the most problematic. Basically, you need to know what video codec is provided by your operating system or what codec you have installed. On OS X, for example, I was able to use the MP4V codec to encode videos. The catch was that you need to write the codec four character code in lower cases if you want to actually save the video. Same codec can be used on Windows.

We can modify the above example in order to save the camera feed in a file, I’ve highlighted the important differences between the original and the modified code:

 1 // OpenCV accessing video camera and saving the result
 2 #include <iostream>
 3 #include <opencv2/opencv.hpp>
 4 
 5 int main() {
 6     // Open the first camera attached to your computer
 7     cv::VideoCapture cap(0);
 8     if(!cap.isOpened()) {
 9         std::cout << "Unable to open the camera\n";
10         std::exit(-1);
11     }
12 
13     cv::Mat image;
14     double FPS = 10.0;
15 
16     // Get the width/height of the camera frames
17     int width = static_cast<int>(cap.get(CV_CAP_PROP_FRAME_WIDTH));
18     int height = static_cast<int>(cap.get(CV_CAP_PROP_FRAME_HEIGHT));
19 
20     // Open a video file for writing (the MP4V codec works on OS X and Windows)
21     cv::VideoWriter out("output.mov", CV_FOURCC('m','p', '4', 'v'), FPS, cv::Size(width, height));
22     if(!out.isOpened()) {
23         std::cout <<"Error! Unable to open video file for output." << std::endl;
24         std::exit(-1);
25     }
26 
27     while(true) {
28         cap >> image;
29         if(image.empty()) {
30             std::cout << "Can't read frames from your camera\n";
31             break;
32         }
33 
34         // Save frame to video
35         out << image;
36 
37         cv::imshow("Camera feed", image);
38 
39         // Stop the camera if the user presses the "ESC" key
40         if(cv::waitKey(1000.0/FPS) == 27) break;
41     }
42 
43     return 0;
44 }

For reading a local video file we’ll use the same syntax, e.g.:

1     cv::VideoCapture cap("movie_test.mp4");

Now, that we can read and write video files, let’s try something more ambitious - modifying an existing video. For the purpose of this tutorial we’ll use an open video like Big Buck Bunny, also we are going to ignore the audio.

When we read a video with OpenCV, we have access to each frame and we can apply any of the typical image processing operations provided by the library to this frame, after that we can save the modified video. Let’s try an experiment: split each frame in three, equal area, vertical rectangles, we leave the left rectangle untouched, we convert to gray the middle rectangle and we apply a Canny edge detector to the right rectangle. Finally, if we copy the modified middle and right rectangles back to the original image, this is what we get:

Please note that the original video used to generate the video included above is (c) copyright 2008, Blender Foundation / www.bigbuckbunny.org.

Here is the code I’ve used to obtain the above video:

 1 // OpenCV editing a video and saving the result
 2 #include <iostream>
 3 #include <opencv2/opencv.hpp>
 4 
 5 int main() {
 6     // Open a video file:
 7     cv::VideoCapture cap("big_buck_bunny_720p_h264.mov");
 8     if(!cap.isOpened()) {
 9         std::cout << "Unable to open the camera\n";
10         std::exit(-1);
11     }
12     
13     // Get the width/height and the FPS of the vide
14     int width = static_cast<int>(cap.get(CV_CAP_PROP_FRAME_WIDTH));
15     int height = static_cast<int>(cap.get(CV_CAP_PROP_FRAME_HEIGHT));
16     double FPS = cap.get(CV_CAP_PROP_FPS);
17 
18     // Open a video file for writing (the MP4V codec works on OS X and Windows)
19     cv::VideoWriter out("output.mov", CV_FOURCC('m','p', '4', 'v'), FPS, cv::Size(width, height));
20     if(!out.isOpened()) {
21         std::cout <<"Error! Unable to open video file for output." << std::endl;
22         std::exit(-1);
23     }
24 
25     cv::Mat image;
26     cv::Mat panel_middle, panel_right;
27     int delta = width/3;
28 
29     while(true) {
30         cap >> image;
31         if(image.empty()) {
32             std::cout << "Can't read frames from your camera\n";
33             break;
34         }
35 
36         // Copy the middle panel from the image and convert it to gray
37         panel_middle = image(cv::Rect(delta, 0, image.cols/3, image.rows));
38         cv::cvtColor(panel_middle, panel_middle, cv::COLOR_BGR2GRAY);
39         cv::cvtColor(panel_middle, panel_middle, cv::COLOR_GRAY2BGR);
40 
41         // Copy the modified middle panel back to the source image
42         panel_middle.copyTo(image(cv::Rect(delta, 0, image.cols/3, image.rows)));
43 
44         // Copy the right panel from the image and convert it to gray
45         panel_right = image(cv::Rect(2*delta, 0, image.cols/3, image.rows));
46         cv::cvtColor(panel_right, panel_right, cv::COLOR_BGR2GRAY);
47 
48         // Apply a Canny edge detector to the right panel, convert the result to BGR
49         cv::Canny(panel_right, panel_right, 100, 300, 3);
50         cv::cvtColor(panel_right, panel_right, cv::COLOR_GRAY2BGR);
51 
52         // Copy the modified right panel back to the source image
53         panel_right.copyTo(image(cv::Rect(2*delta, 0, image.cols/3, image.rows)));
54 
55         // Save frame to video
56         out << image;
57 
58         cv::imshow("Modified video", image);
59 
60         // Stop the camera if the user presses the "ESC" key
61         if(cv::waitKey(1000.0/FPS) == 27) break;
62     }
63 
64     return 0;
65 }

If you want to learn more about OpenCV I would recommend reading OpenCV Essentials by O. D. Suarez:

or Learning Image Processing with OpenCV by G. B. Garcia, O.D. Suarez et all:

comments powered by Disqus