视频读写程序
废话少说,先上一段代码,运行看看效果。
#include <stdio.h>
#include <opencv2/opencv.hpp>
using namespace cv;
int main(int argc, char *argv[])
{
// VideoCapture vidCap(0); // 打开默认摄像头
const char file_in[] = "Wildlife.wmv";
VideoCapture vidCap(file_in); // 打开读取视频文件
if (!vidCap.isOpened()){
// printf("Unabled to open camera!\n");
printf("Unabled to open %s!\n", file_in);
return -1;
}
int w = vidCap.get(CAP_PROP_FRAME_WIDTH);
int h = vidCap.get(CAP_PROP_FRAME_HEIGHT);
const char file_out[] = "1.avi";
double fps = vidCap.get(CAP_PROP_FPS);
// double fps = 25;
VideoWriter vidRec(file_out, CV_FOURCC('M','P','E','G'), fps, Size(w,h));// 打开写入视频文件
if (!vidRec.isOpened()){
printf("video file not opened!\n");
return -2;
}
const char wnd1[] = "Capturing...";
namedWindow(wnd1);
Mat matCap;
double lfFreq = getTickFrequency();
int64 iBase = getTickCount();
for (int iFrameCnt = 0; /*iFrameCnt < vidCap.get(CAP_PROP_FRAME_COUNT)*/; ++iFrameCnt){
int iDelay = iFrameCnt*1000/fps - (getTickCount()-iBase)*1000/lfFreq;// 根据fps计算需要等待时间
if (iDelay > 0){
if (waitKey(iDelay) >= 0)// 这里通过waitKey进行延时等待,如果检测到有按键则退出
break;
}
if (!vidCap.read(matCap))// 需要判断读取帧是否成功
break;
imshow(wnd1, matCap);
vidRec.write(matCap);
}
destroyAllWindows();
return 0;
}
我的台式电脑没有带摄像头,所以这里展示的是直接从本地视频文件读取帧然后显示、写成另一视频文件。运行效果图如下:
如果你的电脑带摄像头,可以使用VideoCapture vidCap(0); // 打开默认摄像头
然后看看效果,这就是一个简单的相机显示和录制程序了。
API讲解
OpenCV中提供了VideoCapture 与VideoWriter 两个类负责视频的读写,位于videoio模块。
VideoCapture
这个类既可以用来打开摄像头,也可以读取视频文件,通过isOpened判断是否打开成功,get方法根据提供的属性宏来获取到对应的属性值,例如上述程序中就是用了:
int w = vidCap.get(CAP_PROP_FRAME_WIDTH);
int h = vidCap.get(CAP_PROP_FRAME_HEIGHT);
获取视频帧宽高信息,此外fps是frame per second每秒的帧数。视频文件头信息中已经包含了这些信息,如果是打开摄像头捕捉帧然后写入视频文件,就需要自己设定一个fps,常见的是25和30。通过read去读取下一帧,成功返回true,失败返回false,例如读取到文件结束后就会返回false。
VideoWriter
这个类用来写视频文件,视频文件可以看成是包含了头信息和视频帧数据的文件。所以需要设定保存的格式、fps、宽高等信息。CV_FOURCC(‘M’,’P’,’E’,’G’)代表一种帧数据压缩方式,更多的可以参考VideoWriter 构造函数头文件中给出的说明。同样需要调用isOpened判断是否支持写这类文件。通过write去写入帧数据,示例中写入了1.avi文件,你可以使用播放器打开这个文件看看录制效果。
getTickCount与getTickFrequency
getTickFrequency用来获取CPU的频率,我电脑上调试返回的是3215439,即3215439/1024/1024=3.07GHZ。getTickCount获取开机到现在的时钟计数,通过如下的代码可以判断一个操作用了多少时间(单位s):
double t = (double)getTickCount();
// do something …
t = ((double)getTickCount() - t)/getTickFrequency();
示例中记录下一个基准时间点int64 iBase = getTickCount();然后通过帧数iFrameCnt 计算出当前需要等待的时间,例如第一帧是00:00:00.000从摄像头捕捉的,按照fps=25,即每隔40ms捕捉一帧,那么第二帧应该是00:00:00.040捕捉。如果当前是00:00:00.005,那么就需要延时等待35ms再捕捉。这样你从摄像头捕捉写下来的视频文件每秒的帧数就是25了。
总结
上节中我们展示了图像文件的读写、这节我们展示了视频文件的读写,这都是为了方便我们去获取到图像数据,接下来的章节就是图像处理的正菜了。