现在找一个能拍摄视频的设备真是太容易了。结果大家都用视频来代替以前的序列图像。视频可能由两种形式得到,一个是像网络摄像头那样实时视频流,或者由其他设备产生的压缩编码后的视频文件。幸运的是,OpenCV可以使用相同的C++类、用同一种方式处理这些视频信息。在接下来的教程里你将学习如何使用摄像头或者视频文件。
- 如何打开和读取视频流
- 两种检查相似度的方法:PSNR和SSIM
如何读取一个视频流(摄像头或者视频文件)?
总的来说,视频捕获需要的所有函数都集成在 VideoCapture C++ 类里面。虽然它底层依赖另一个FFmpeg开源库,但是它已经被集成在OpenCV里所以你不需要额外地关注它的具体实现方法。你只需要知道一个视频由一系列图像构成,我们用一个专业点儿的词汇来称呼这些构成视频的图像:“帧”(frame)。此外在视频文件里还有个参数叫做“帧率”(frame rate)的,用来表示两帧之间的间隔时间,帧率的单位是(帧/秒)。这个参数只和视频的播放速度有关,对于单独的一帧图像来说没有任何用途。
你需要先定义一个 VideoCapture 类的对象来打开和读取视频流。具体可以通过 constructor 或者通过 open 函数来完成。如果使用整型数当参数的话,就可以将这个对象绑定到一个摄像机,将系统指派的ID号当作参数传入即可。例如你可以传入0来打开第一个摄像机,传入1打开第二个摄像机,以此类推。如果使用字符串当参数,就会打开一个由这个字符串(文件名)指定的视频文件。
CvCapture 是一个结构体,用来保存图像捕获所需要的信息。 opencv提供两种方式从外部捕获图像:
#include<opencv2/opencv.hpp> using namespace cv; void main() { VideoCapture cap; cap.open("E:\\VS2015Opencv\\vs2015\\project\\video\\01.avi"); //打开视频,以上两句等价于VideoCapture cap("E://01.avi"); //cap.open("http://www.laganiere.name/bike.avi");//也可以直接从网页中获取图片,前提是网页有视频,以及网速够快 if (!cap.isOpened())//如果视频不能正常打开则返回 return ; Mat frame; while (1) { cap >> frame;//等价于cap.read(frame); if (frame.empty())//如果某帧为空则退出循环 break; imshow("video", frame); waitKey(20);//每帧延时20毫秒 } cap.release();//释放资源 }
#include<opencv2/opencv.hpp> #include<iostream> using namespace cv; using namespace std; void main() { VideoCapture cap; cap.open("E:\\VS2015Opencv\\vs2015\\project\\video\\01.avi"); if (!cap.isOpened()) return; int width = cap.get(CV_CAP_PROP_FRAME_WIDTH); //帧宽度 int height = cap.get(CV_CAP_PROP_FRAME_HEIGHT); //帧高度 int frameRate = cap.get(CV_CAP_PROP_FPS); //帧率 x frames/s int totalFrames = cap.get(CV_CAP_PROP_FRAME_COUNT); //总帧数 cout << "视频宽度=" << width << endl; cout << "视频高度=" << height << endl; cout << "视频总帧数=" << totalFrames << endl; cout << "帧率=" << frameRate << endl; Mat frame; while (1) { cap >> frame;//等价于cap.read(frame); if (frame.empty()) break; imshow("video", frame); if (waitKey(33) != 255) break; } cap.release(); }
waitkey(33) 如果不按键的时候是返回 oxff,这个无符号就是255,有符号就是-1
windows vs 的环境默认了这个为非符号数 即255,而opencv的新手书中,往往作者环境会认为是-1
解决方案:把原始代码中循环读取帧的
if (waitKey(33)>=0)break;
改为
if (waitKey(33) != 255)break;
或者把waitkey的返回值用有符号数去读取。