代码
#include <iostream> // for standard I/O
#include <string> // for strings
#include <opencv2/core/core.hpp> // Basic OpenCV structures (cv::Mat)
#include <opencv2/highgui/highgui.hpp> // Video write
using namespace std;
using namespace cv;
int main(int argc, char *argv[], char *window_name)
{
if (argc != 4)
{
cout << "Not enough parameters" << endl;
return -1;
}
const string source = argv[1]; // the source file name
const bool askOutputType = argv[3][0] =='Y'; // If false it will use the inputs codec type
VideoCapture inputVideo(source); // Open input
if ( !inputVideo.isOpened())
{
cout << "Could not open the input video." << source << endl;
return -1;
}
string::size_type pAt = source.find_last_of('.'); // Find extension point
const string NAME = source.substr(0, pAt) + argv[2][0] + ".avi"; // Form the new name with container
int ex = static_cast<int>(inputVideo.get(CV_CAP_PROP_FOURCC)); // Get Codec Type- Int form
// Transform from int to char via Bitwise operators
char EXT[] = {ex & 0XFF , (ex & 0XFF00) >> 8,(ex & 0XFF0000) >> 16,(ex & 0XFF000000) >> 24, 0};
Size S = Size((int) inputVideo.get(CV_CAP_PROP_FRAME_WIDTH), //Acquire input size
(int) inputVideo.get(CV_CAP_PROP_FRAME_HEIGHT));
VideoWriter outputVideo; // Open the output
if (askOutputType)
outputVideo.open(NAME , ex=-1, inputVideo.get(CV_CAP_PROP_FPS),S, true);
else
outputVideo.open(NAME , ex, inputVideo.get(CV_CAP_PROP_FPS),S, true);
if (!outputVideo.isOpened())
{
cout << "Could not open the output video for write: " << source << endl;
return -1;
}
union { int v; char c[5];} uEx ;
uEx.v = ex; // From Int to char via union
uEx.c[4]='\0';
cout << "Input frame resolution: Width=" << S.width << " Height=" << S.height
<< " of nr#: " << inputVideo.get(CV_CAP_PROP_FRAME_COUNT) << endl;
cout << "Input codec type: " << EXT << endl;
int channel = 2; // Select the channel to save
switch(argv[2][0])
{
case 'R' : {channel = 2; break;}
case 'G' : {channel = 1; break;}
case 'B' : {channel = 0; break;}
}
Mat src,res;
vector<Mat> spl;
while( true) //Show the image captured in the window and repeat
{
inputVideo >> src; // read
if( src.empty()) break; // check if at end
split(src, spl); // process - extract only the correct channel
for( int i =0; i < 3; ++i)
if (i != channel)
spl[i] = Mat::zeros(S, spl[0].type());
merge(spl, res);
//outputVideo.write(res); //save or
outputVideo << res;
}
cout << "Finished writing" << endl;
return 0;
}
解释
-
首先,你需要知道一个视频文件是什么样子的。每一个视频文件本质上都是一个容器,文件的扩展名只是表示容器格式(例如 avi , mov ,或者 mkv )而不是视频和音频的压缩格式。容器里可能会有很多元素,例如视频流,音频流和一些字幕流等等。这些流的储存方式是由每一个流对应的编解码器(codec)决定的。通常来说,视频流很可能使用 mp3 或 aac 格式来储存。而视频格式就更多些,通常是 XVID , DIVX , H264 或 LAGS(Lagarith Lossless Codec)等等。具体你能够使用的编码器种类可以在操作系统的编解码器列表里找到。
-
OpenCV只是个计算机视觉库而不是一个视频处理编码库。所以开发者们试图将这个部分尽可能地精简,结果就是OpenCV能够处理的视频只剩下 avi 扩展名的了。另外一个限制就是你不能创建超过2GB的单个视频,还有就是每个文件里只能支持一个视频流,不能将音频流和字幕流等其他数据放在里面。尽管如此,任何系统支持的编解码器在这里应该都能工作。
VideoWriter 类
- 输出的文件名中包含了容器的类型,当然在现在仅仅支持 avi 格式。在这个例子中我们会使用输入文件名+通道名+avi来创建输出文件名。
- 然后决定使用的编解码器,现在所有的视频编解码器都使用最多四个字节的名称来标识,例如 XVID, DIVX, 和 H264 等。这个被称作FourCC(four character code)。你可以通过 get 函数来向视频询问这个编码, get 函数会返回一个double数,仅仅是因为double包含了64位数据而已,由于FourCC编码只占据了其中低位的4个字节,所以可以直接通过强制转换成int型来扔掉高位的四个字节。
- OpenCV内部使用这个int数来当作第二个参数,这里会使用两种方法来将这个整型数转换为字符串:位操作符和联合体。前者可以用&操作符并进行移位操作,以便从int里面释放出字符:
- 也可以使用 联合体 来做到
- :
- 如果传入的参数是一个负数的话,就会在执行时弹出一个对话框,在里面包括了所有已安装的编码器类型,你可以在里面选择一个并使用之。
- 输出视频的帧率,也就是每秒需要绘制的图像数,在这里我让输出视频的帧率与输入视频相同,输入视频的帧率可以由 get 函数来获得。
- 输出视频的尺寸,在这里我同样保持和输入视频一样的大小,这个大小同样也可以由 get 函数来获得。
- 最后一个参数是一个可选参数。默认下它是true,来表示输出的视频是彩色的(所以你需要传给它三通道的图像),如果想创建一个灰度视频就传入false。
- 具体实现请看下面的例子:
- 最好使用 isOpened() 函数来检查是不是成功打开。在成功打开时视频后,就可以用 write 函数向这个对象按照序列发送一些图像帧了,另外使用重载操作符 << 也可以完成这些操作。最后,视频会在 VideoWriter 对象析构时自动关闭。
- 要“释放”出某个通道,又要保持视频为彩色,实际上也就意味着要把未选择的通道都设置为全0。这个操作既可以通过手工扫描整幅图像来完成,又可以通过分离通道然后再合并来做到,具体操作时先分离三通道图像为三个不同的单通道图像,然后再将选定的通道与另外两张大小和类型都相同的黑色图像合并起来。