RoboMaster视觉教程OpenCV(二)读取视频图像与转换

RoboMaster视觉教程OpenCV(二)读取视频图像与转换

在Robomaster比赛中,读取视频或者图像是极其重要的。因为我们需要在热身赛的时候用摄像头保存一段视频或者图片。之后根据视频中的光线来调节摄像头曝光。此外,也可能需要将视频分解成图片来做数据集,或者说将多个图片合成一段视频。所以这一节的内容就是为这个做准备的。

文章目录

  • RoboMaster视觉教程OpenCV(二)读取视频图像与转换
    • 一 基本概念
    • 二 打开摄像头保存视频例程
      • 2.1 waitkey()
      • 2.2 帧率
      • 2.3 VideoCapture()
    • 三 打开摄像头保存图片例程
      • 3.1 代码框架
      • 3.2 图片命名
    • 四 任务
      • 4.1 任务参考代码
    • 微信公众号

一 基本概念

读于仕琪《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自带的打开命令,除视频外还可以打开图片,文档等等

注意:

  1. Size s(640, 480);Mat类,VideoWrite类和摄像头硬件这三者的分辨率一定要一致。否则视频只有6kb,无法打开。(Mat默认)

2.1 waitkey()

这个函数是opencv里的,和imshow()配合,使用waitKey(30)代表在屏幕上显示imshow()的图像30毫秒。

30ms后自动关闭显示。用在循环里,也就是每一个循环处理一帧图像,每一帧图像显示30毫秒。

同时,该函数返回值为当前键盘按下的值(是int),没有按键时返回-1。
所以,代码中一旦有键盘上的键按下,那么waitkey(30)这个函数返回的数值就是这个英文字母代表的ASCII码值。满足条件,break跳出循环。

2.2 帧率

最高帧率由设备决定,实际帧率由程序决定

最高帧率:摄像头设备打开后会不断地刷新当前图像,这个刷新的频率即是最高帧率,由设备的硬件限定。

实际帧率:假设设备的最高帧率为25帧,那么每帧的时间间隔为40ms,当程序读取处理每帧耗费的时间超过
40ms时,帧率就会低于25帧;假设程序读取一帧并进行处理耗费100ms,则实际帧率为10。

关于实际帧率的计算,必须从获取一个帧开始,到获取下一帧结束。如果需要对图像进行复杂处理,而又
不希望影响到帧率,可以先把图像保存在内存中,另开线程进行处理。经验证,显示图像及保存视频文件函数
耗时较少,对帧率基本没有影响,保存图像文件函数耗时较多,会对帧率有影响。

拓展阅读1

对于帧率的分析很有帮助,大家看一下

2.3 VideoCapture()

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则退出。

3.1 代码框架

和上面的代码框架大体相同,只不过没有把从摄像头得到的图像真写入到视频里,而是把每一帧写成图像。得到的。打开摄像头,从摄像头读取一帧图像,将这帧图像保存为PNG图片。(png更好,一基本概念教程里有说)所不同的是我们要保存多个图片,图片的命名。有一定方法。同时,这里还用到了waitkey()返回值。

3.2 图片命名

imwrite(ad, frame );

教程中介绍imwrite函数( ,),两个参数,第一个用字符串表示,此处用了数组表示的字符串,代码图片要保存到的路径,路径中包含图片名字。第二个参数是mat类的对象。

每一次,imwrite()函数保存图片的路径都不同。这都源于sprintf()函数。

sprintf()最常见的应用之一莫过于把整数打印到字符串中,通过sprintf()函数,每一次保存图片都可以令图片名字不同,实现了保存多个图片的目的。

比如说,ad [] = “./%d.png” ./表示当前路径,%d这里的位置会被后边的int变量d替换,所以ad实际上是

./1.png ./2.png ./3.png

sprintf函数详解

四 任务

  1. 打开本机摄像头,学习并运行代码1,保存一段视频。写代码3,将视频中的每一帧拆为图片,要求格式为png,压缩级别为2,命名方式为1.png,2.png……
  2. 打开本机摄像头,学习并运行代码2,保存25张图片。写代码4,将这25张图片转化为一段视频,要求格式为.avi,帧率为5。

PS:任务的代码将在下一次更新时发布。

4.1 任务参考代码

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相关的计算机视觉知识。
公众号名称:三丰杂货铺
在这里插入图片描述

你可能感兴趣的:(RoboMaster视觉组笔记)