在Robomaster比赛中,读取视频或者图像是极其重要的。因为我们需要在热身赛的时候用摄像头保存一段视频或者图片。之后根据视频中的光线来调节摄像头曝光。此外,也可能需要将视频分解成图片来做数据集,或者说将多个图片合成一段视频。所以这一节的内容就是为这个做准备的。
读于仕琪《open cv入门教程》的前四章,读毛星云的书:《open cv3编程入门》P34-P40,P65-P70
了解基本概念。(回复opencv2可收到这两本书链接)
之后学习两个例程:打开摄像头保存视频和打开摄像头保存图片
#include
#include "opencv2/opencv.hpp"
/*opencv.hpp包含了Opencv各模块的头文件,如高层GUI图形用户界面模块头文件highgui.hpp,图像处理模块头文件imgprogc.hpp等。所以,用“#include”即可,达到精简代码的作用。*/
using namespace std;
using namespace cv;
int main(int argc, char** argv)//或者char* argv[]
{
//0表示打开笔记本上第一个摄像头
VideoCapture cap(0);
// VideoCapture cap(0);
//检查是否成功打开
if(!cap.isOpened())
{
cerr << "Can not open a camera or file." << endl;
//此处用了cerr,而不是cout。cerr对象又叫标准错误,通常用来输出警告和错误信息给程序的使用者。无 //缓存
return -1;
}
//定义视频的宽度和高度
Size s(640, 480);//宽在前,高在后
//创建 writer,并指定 FOURCC 及 FPS 等参数
VideoWriter writer = VideoWriter("myvideo.avi",CV_FOURCC('M','J','P','G'), 25, s);
//检查是否成功创建
if(!writer.isOpened())
{
cerr << "Can not create video file.\n" << endl;
return -1;
}
//视频帧
Mat frame(s, CV_8UC3);
for(;;)//等价于while(1)
{
//从 cap 中读一帧,存到 frame
cap >> frame;
//如果未读到图像
if(frame.empty())
break;
//显示结果
imshow("pictures", frame);
writer << frame;
//等待 30ms,如果按键则退出循环
if(waitKey(30) >= 0)
break;
}
//cap.release();
//writer.release();以前需要释放类,但是现在不需要release,会自动释放的
//退出时会自动释放 cap 中占用资源
return 0;
}
上面的代码实现了打开笔记本自带的摄像头。将摄像头的内容显示出来,并且保存为一个视频。
代码的执行过程参照OpenCV(一)测试open cv时候的操作,使用g++指令。
执行代码之后,在命令行输入
xdg-open myvideo.avi
可以查看生成的myvideo.avi视频,xdg-open是Ubuntu自带的打开命令,除视频外还可以打开图片,文档等等
注意:
这个函数是opencv里的,和imshow()配合,使用waitKey(30)代表在屏幕上显示imshow()的图像30毫秒。
30ms后自动关闭显示。用在循环里,也就是每一个循环处理一帧图像,每一帧图像显示30毫秒。
同时,该函数返回值为当前键盘按下的值(是int),没有按键时返回-1。
所以,代码中一旦有键盘上的键按下,那么waitkey(30)这个函数返回的数值就是这个英文字母代表的ASCII码值。满足条件,break跳出循环。
最高帧率由设备决定,实际帧率由程序决定
最高帧率:摄像头设备打开后会不断地刷新当前图像,这个刷新的频率即是最高帧率,由设备的硬件限定。
实际帧率:假设设备的最高帧率为25帧,那么每帧的时间间隔为40ms,当程序读取处理每帧耗费的时间超过
40ms时,帧率就会低于25帧;假设程序读取一帧并进行处理耗费100ms,则实际帧率为10。
关于实际帧率的计算,必须从获取一个帧开始,到获取下一帧结束。如果需要对图像进行复杂处理,而又
不希望影响到帧率,可以先把图像保存在内存中,另开线程进行处理。经验证,显示图像及保存视频文件函数
耗时较少,对帧率基本没有影响,保存图像文件函数耗时较多,会对帧率有影响。
拓展阅读1
对于帧率的分析很有帮助,大家看一下
VideoCapture cap(0);()中的数字是摄像头的编号,该编号和摄像头的PID有关, PID越小,编号则越小,编号从0开始。笔记本会内置的摄像头设备编号是最小的,为0。 PID是硬件设备都有的编号。
#include
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
int d=0;
char ad[300]={0};
VideoCapture cap(0);
if(!cap.isOpened())
{
cerr << "Can not open a camera or file." << endl;
return -1;
}
Size s(640, 480);
Mat frame(s, CV_8UC3);
for(;;)
{
cap >> frame;
if(frame.empty())
break;
imshow("pictures", frame);
int k=waitKey(30);//延时30ms
if(k=='e')
break;
else if(k=='c')
{
sprintf(ad, "./%d.png", ++d);
imwrite(ad, frame );
cout<<"get "<<d<<" picture "<<endl;
}
}
//退出时会自动释放 cap 中占用资源
return 0;
}
上面的代码实现了打开摄像头,显示摄像头拍到的内容,并且按照数字的顺序将,视频中的内容保存为图片。按c则拍照片,按e则退出。
和上面的代码框架大体相同,只不过没有把从摄像头得到的图像真写入到视频里,而是把每一帧写成图像。得到的。打开摄像头,从摄像头读取一帧图像,将这帧图像保存为PNG图片。(png更好,一基本概念教程里有说)所不同的是我们要保存多个图片,图片的命名。有一定方法。同时,这里还用到了waitkey()返回值。
imwrite(ad, frame );
教程中介绍imwrite函数( ,),两个参数,第一个用字符串表示,此处用了数组表示的字符串,代码图片要保存到的路径,路径中包含图片名字。第二个参数是mat类的对象。
每一次,imwrite()函数保存图片的路径都不同。这都源于sprintf()函数。
sprintf()最常见的应用之一莫过于把整数打印到字符串中,通过sprintf()函数,每一次保存图片都可以令图片名字不同,实现了保存多个图片的目的。
比如说,ad [] = “./%d.png” ./表示当前路径,%d这里的位置会被后边的int变量d替换,所以ad实际上是
./1.png ./2.png ./3.png
sprintf函数详解
PS:任务的代码将在下一次更新时发布。
matlab2016将图片合并为多个视频以及将视频拆分为多个图片
%将图片转化为视频
clear all;
clc;
srcDic = uigetdir('G:\smc\ljw');%此处更改路径,注意文件名前的不是'\'而是'/'
cd(srcDic);
allnames = struct2cell(dir('*.jpg'));%找到所有jpg文件
[k,len]=size(allnames);
aviobj = VideoWriter('example.avi');%在图片目录下会生成avi文件
aviobj.FrameRate = 2;%更改帧率
open(aviobj)
for i = 1:len
name = allnames{1,i};
frame = imread(name);
writeVideo(aviobj,frame);
end
close(aviobj)
%将视频转化成图片
v1=VideoReader('example.avi');%path
for i=1:v1.numberofframes
temp=read(v1,i);
%figure(i)
%imshow(temp);
str = strcat('output',int2str(i),'.jpg')
imwrite(temp,str);
end
%将图片转化为视频
clear all;
clc;
srcDic = uigetdir('C:\Users\UMECJF\Desktop\jpgc');
cd(srcDic);
allnames = struct2cell(dir('*.jpg'));
[k,len]=size(allnames);
aviobj = VideoWriter('example.avi');
aviobj.FrameRate = 10;
open(aviobj)
for i = 1:len
name = allnames{1,i};
frame = imread(name);
writeVideo(aviobj,frame);
end
close(aviobj)
欢迎大家关注我的个人公众号,现阶段主要总结Robomaster相关的计算机视觉知识。
公众号名称:三丰杂货铺