RoboMaster视觉教程OpenCV(三)利用文件处理图像

RoboMaster视觉教程OpenCV(三)利用文件处理图像

上一次的代码中,我们使用了一个字符串数组来达到写入多个图像而不覆盖的目的。但是仔细想想我们就可以知道每一次程序执行,整型变量还是会从零开始。那么也就是说,虽然第一次程序执行的期间能够不断迭代生成多个图像,但是当程序第二次启动第三次启动时,生成的图像会覆盖上一次程序执行期间生成的图像,因为文件名是一样的。

但是在Robomaster比赛中,我们要使用的嵌入式平台不断的自动开机和关机,如果轻易覆盖掉保存好的文件,那么我们想在热身赛获得的场地信息就可能丢失。如何达到每一次自启动都能够生成新的图片,而不覆盖上一次程序执行的图片呢?这里就需要用到文件。文件储存图片的数字,断电后也能够保存。程序一旦上电,读到的图片路径不再来自于局部变量,而是储存空间内的文件。

本节我们使用的标准C++中的文件操作。略微繁琐。之后我们还会学习Qt,利用QFile达到我们的目的,更加方便。我个人喜欢使用Qt提供的各种方便的类。

一 基本概念

阅读任意网课、教科书等资料中的文件读取写入内容。比如中国大学慕课的郭炜老师的讲授。

https://www.icourse163.org/course/PKU-1002029030

学习第七周的3.4节

二 操作文件保存图像

  1. 首先在程序所在当前目录下,创建一个video文件夹(必须手动,否则找不到路径,png、avi文件可以在程序写入时自动创建,文件夹不可以)

  2. 在工程目录下实现创建一个文件夹,名为video

  3. 将以下代码命名为file-video.cpp

  4. 利用该命令编译运行

g++ -std=c++11 -o file-video file-video.cpp `pkg-config --libs --cflags opencv`

这次使用g++编译时,加上-std=c++11,因为to_string等是11的新特性(我的笔记本不加-std=c++11就报错,有的人不报错,懵)

#include <iostream>
#include "opencv2/opencv.hpp"
#include <fstream>
using namespace std;
using namespace cv;

#define SAVEVIDEO 1//宏定义,控制

void saveVideos(const cv::Mat &frame);//函数以及对象如何声明
cv::VideoWriter initVideoWriter(const std::string &filename_prefix);

auto save_video = initVideoWriter("./video/");//auto声明必须初始化//需要事先创建一个文件夹

int main(int argc, char** argv)
{
	//打开第一个摄像头
	VideoCapture cap(0);//
	if(!cap.isOpened())
	{
	cerr << "Can not open a camera or file." << endl;//cerr not cout
	return -1;
	}

	Mat frame;
	

	for(;;)
	{
	//从 cap 中读一帧,存到 frame
	cap >> frame;
	if(frame.empty())
	break;
	imshow("pictures", frame);
	
	if (SAVEVIDEO) saveVideos(frame);
	
	//等待 30 秒,如果按键则推出循环
	if(waitKey(30) >= 0)
	break;
	}
	//退出时会自动释放 cap 中占用资源
	return 0;
}

void saveVideos(const cv::Mat &frame) {
    if (!frame.empty()) {
        save_video.write(frame);//video_writer << frame;
    } else return;
}

cv::VideoWriter initVideoWriter(const std::string &filename_prefix) {
    cv::VideoWriter video;
    std::ifstream in(filename_prefix + "cnt.txt");
    int cnt = 0;
    if (in.is_open()) {
        in >> cnt;
        in.close();
    }
    std::string file_name = filename_prefix + std::to_string(cnt) + ".avi";
    cnt++;
    std::ofstream out(filename_prefix + "cnt.txt");
    if (out.is_open()) {
        out << cnt << std::endl;
        out.close();
    }
    video.open(file_name, CV_FOURCC('P', 'I', 'M', '1'), 24, cv::Size(640, 480));//video(file_name, CV_FOURCC('P', 'I', 'M', '1'), 90, cv::Size(640, 480));
    
    return video;
}

三 代码解析

保存视频主要是有两个大知识点,一个是打开和关闭文件,写入或者读取文件中的整型数字。另一个是将文件中的整型数字和视频的路径(filename)结合起来。之后将filename作为参数传到VideoWriter类的对象initVideoWriter()。

3.1 main()函数

主函数的内容并不多,也不复杂。主要功能是打开摄像头,并读取一帧显示,然后将这个帧传给子函数saveVideos()中。

3.2 saveVideos()

saveVideos()函数中的代码很简单,就是将从摄像头捕获的一帧利用VideoWriter类的对象保存成为视频。那么,这个VideoWriter类的对象是哪里来的呢?是一个函数,initVideoWriter()传回来的。

3.3 initVideoWriter()

这个initVideoWriter()是一个函数,只不过这个函数的返回值是一个VideoWriter类的对象。在这个函数里,它读取了文件中的整型数字,将文件中的整型数字和视频的路径(filename)结合起来。之后初始化了一个VideoWriter类的对象。把这个类传给了saveVideos()函数。

此外,对于文件的输入及输出是相对于程序来说的。程序读取文件中的数字,对于程序来说是输入也就是in。5257行,读取文件数字,与6063行写法类似。

对于string类型的路径,学习一下to_string()以及互相之间的加减用法。

3.4 auto

代码中11行完成了对于这个VideoWriter类的对象到初始化。不过比较奇特的是,它使用了一个auto变量。这个变量是C++的新特性。他在这里的作用仅仅是为了能够自动获取到后面变量的类型,可以少些几个字。如果不用auto的话,可以将类型直接写为cv::VideoWriter。

auto拓展阅读:https://www.cnblogs.com/KunLunSu/p/7861330.html

四 今日任务

打开摄像头,从摄像头处读取帧,每一帧都显示,但是每200帧保存一帧为png图片,图片的名称从文件中查找。能够实现开机重启后不会覆盖图片的功能。

((为什么每200帧保存一帧?如果每一帧都保存的话,保存的文件会太多。所以读取200帧,虽然两百帧都显示,但是199帧显示完毕就不再管,只保存其中一帧。

可以在极短时间内运行一下程序,看看程序运行起来是多么快,类似manifold这样的平台,配上工业相机,帧率可达100帧。这时候这个保存间隔可以设置为20000帧)
PS:不建议不断打开关闭文件。这里只是一个自己学习玩乐的任务。另外,这里用的是一个很粗略的延时,利用计数,建议使用OpenCV自带的获取FPS的函数,再获取系统时间来实现精确延时。(比如每秒保存一张图)

微信公众号欢迎大家关注我的个人公众号,现阶段主要总结Robomaster相关的计算机视觉知识。
公众号名称:三丰杂货铺
在这里插入图片描述

©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页