内容简介:输入输出(input out),也称io操作。在c++编程里有学过io的知识点,主要是两种,一个是控制台io,即std::cout与std::cin等标准输入输出流的操作。这一种io的操作作用是实现数据在内存与控制台显示的传输。另一个是文件io,即iftream与ofstream对象文件输入输出流的操作,这一种io操作的作用是实现数据在内存与硬盘文件的传输。那么在opencv里面也是一样的,一样拥有这控制台与文件的两种io操作方式。只不过有所不同的是,opencv里面的数据结构有所不同,io操作的输入输出
输入输出(input out),也称io操作。在c++编程里有学过io的知识点,主要是两种,一个是控制台io,即std::cout与std::cin等标准输入输出流的操作。这一种io的操作作用是实现数据在内存与控制台显示的传输。另一个是文件io,即iftream与ofstream对象文件输入输出流的操作,这一种io操作的作用是实现数据在内存与硬盘文件的传输。
那么在opencv里面也是一样的,一样拥有这控制台与文件的两种io操作方式。只不过有所不同的是,opencv里面的数据结构有所不同,io操作的输入输出流对象、文件流对象有所不一样,即被定制化了而已。原理与使用方式在本质上是一只的。
源代码程序
下面先附上代码解释
//包含头文件
#include <opencv2/core.hpp>
#include <iostream>
#include <string>
//使用命名空间,不建议这么做,建议显示使用,如cv::imshow
using namespace cv;
using namespace std;
//显示帮助信息
static void help(char** av)
{
cout << endl
<< av[0] << " shows the usage of the OpenCV serialization functionality." << endl
<< "usage: " << endl
<< av[0] << " outputfile.yml.gz" << endl
<< "The output file may be either XML (xml) or YAML (yml/yaml). You can even compress it by "
<< "specifying this in its extension like xml.gz yaml.gz etc... " << endl
<< "With FileStorage you can serialize objects in OpenCV by using the << and >> operators" << endl
<< "For example: - create a class and have it serialized" << endl
<< " - use it to read and write matrices." << endl;
}
//数据类
class MyData
{
public:
MyData() : A(0), X(0), id()//构造函数
{}
explicit MyData(int) : A(97), X(CV_PI), id("mydata1234") // 显示构造函数,防止隐式调用
{}
void write(FileStorage& fs) const//顺序写类的数据函数,为常成员函数,不能修改数据。
{
fs << "{" << "A" << A << "X" << X << "id" << id << "}";
}
void read(const FileNode& node)//读取数据函数
{
A = (int)node["A"];
X = (double)node["X"];
id = (string)node["id"];
}
public: // Data Members
//数据成员
int A;
double X;
string id;
};
//写数据函数,为静态函数,函数内调用类内成员函数
static void write(FileStorage& fs, const std::string&, const MyData& x)
{
x.write(fs);
}
//读取数据函数,为静态函数,函数内调用类内成员函数
static void read(const FileNode& node, MyData& x, const MyData& default_value = MyData()){
if(node.empty())
x = default_value;
else
x.read(node);
}
// This function will print our custom class to the console
//重载<<运算符
static ostream& operator<<(ostream& out, const MyData& m)
{
out << "{ id = " << m.id << ", ";
out << "X = " << m.X << ", ";
out << "A = " << m.A << "}";
return out;
}
int main(int ac, char** av)
{
//检查输入参数
if (ac != 2)
{
help(av);
return 1;
}
string filename = av[1];//文件路径名
//把MyData类对象的数据写到文件里面
{
Mat R = Mat_<uchar>::eye(3, 3),
T = Mat_<double>::zeros(3, 1);
MyData m(1);
FileStorage fs(filename, FileStorage::WRITE);//以写的方式打开文件
//按照一对一的键值对格式输出到文件
fs << "iterationNr" << 100;
//使用中括号输出序列到文件
fs << "strings" << "["; // text - string sequence
fs << "image1.jpg" << "Awesomeness" << "../data/baboon.jpg";
fs << "]"; // close sequence
//以花括号的格式输出数据到文件
fs << "Mapping"; // text - mapping
fs << "{" << "One" << 1;
fs << "Two" << 2 << "}";
//输出矩阵
fs << "R" << R; // cv::Mat
fs << "T" << T;
fs << "MyData" << m; // your own data structures
fs.release(); // explicit close
cout << "Write Done." << endl;
}
{//read
//打开文件读取数据到MyData类对象
cout << endl << "Reading: " << endl;
FileStorage fs;
fs.open(filename, FileStorage::READ);//以读的方式打开文件
int itNr;
//fs["iterationNr"] >> itNr;
//读取一对一的键值对数据
itNr = (int) fs["iterationNr"];
cout << itNr;
//检查文件是否正常打开
if (!fs.isOpened())
{
cerr << "Failed to open " << filename << endl;
help(av);
return 1;
}
//读取一对多的中括号序列格式数据
FileNode n = fs["strings"]; // Read string sequence - Get node
if (n.type() != FileNode::SEQ)
{
cerr << "strings is not a sequence! FAIL" << endl;
return 1;
}
//使用迭代器
FileNodeIterator it = n.begin(), it_end = n.end(); // Go through the node
for (; it != it_end; ++it)
cout << (string)*it << endl;
//读取花括号格式数据
n = fs["Mapping"]; // Read mappings from a sequence
cout << "Two " << (int)(n["Two"]) << "; ";
cout << "One " << (int)(n["One"]) << endl << endl;
MyData m;
Mat R, T;
//读取矩阵数据
fs["R"] >> R; // Read cv::Mat
fs["T"] >> T;
//读取MyData类数据
fs["MyData"] >> m; // Read your own structure_
cout << endl
<< "R = " << R << endl;
cout << "T = " << T << endl << endl;
cout << "MyData = " << endl << m << endl << endl;//重载的运算符这里被使用
//Show default behavior for non existing nodes
cout << "Attempt to read NonExisting (should initialize the data structure with its default).";
fs["NonExisting"] >> m;
cout << endl << "NonExisting = " << endl << m << endl;
}
cout << endl
<< "Tip: Open up " << filename << " with a text editor to see the serialized data." << endl;
return 0;
}
程序详解
下面来一步一步地学习解读程序
自定义数据类
-
首先,程序声明里一个数据类来作为后面的io操作提供数据封装的实体。类的声明如下
//数据类 class MyData { public: MyData() : A(0), X(0), id()//构造函数 {} explicit MyData(int) : A(97), X(CV_PI), id("mydata1234") // 显示构造函数,防止隐式调用 {} void write(FileStorage& fs) const//顺序写类的数据函数,为常成员函数,不能修改数据。 { fs << "{" << "A" << A << "X" << X << "id" << id << "}"; } void read(const FileNode& node)//读取数据函数 { A = (int)node["A"]; X = (double)node["X"]; id = (string)node["id"]; } public: // Data Members //数据成员 int A; double X; string id; };类包含了三个不同类型的数据成员,默认构造函数与显示构造函数,以及write和read两个成员函数,分别承担写入与读取功能。需要明白的是,write写入函数是将MyData的数据成员写入到FileStorage对象fs中,read读取函数是从FileNode对象node读取数据存到MyData之中。Filestorage与FileNode即为opencv中所“定制化的io操作类”。附上解释
FileStorage&FileNode
//FileStorage XML/YAML/JSON file storage class that encapsulates all the information necessary for writing or reading data to/from a file. //FileNode File Storage Node class. The node is used to store each and every element of the file storage opened for reading. When XML/YAML file is read, it is first parsed and stored in the memory as a hierarchical collection of nodes. Each node can be a "leaf" that is contain a single number or a string, or be a collection of other nodes. There can be named collections (mappings) where each element has a name and it is accessed by a name, and ordered collections (sequences) where elements do not have names but rather accessed by index. Type of the file node can be determined using FileNode::type method. Note that file nodes are only used for navigating file storages opened for reading. When a file storage is opened for writing, no data is stored in memory after it is written.
而且,还在类外对读写两个函数再次进行了封装
//写数据函数,为静态函数,函数内调用类内成员函数
static void write(FileStorage& fs, const std::string&, const MyData& x)
{
x.write(fs);
}
//读取数据函数,为静态函数,函数内调用类内成员函数
static void read(const FileNode& node, MyData& x, const MyData& default_value = MyData()){
if(node.empty())
x = default_value;
else
x.read(node);
}
-
然后来看看重载了运算符<<
先看看程序
// This function will print our custom class to the console //重载<<运算符 static ostream& operator<<(ostream& out, const MyData& m) { out << "{ id = " << m.id << ", "; out << "X = " << m.X << ", "; out << "A = " << m.A << "}"; return out; }
这个重载的作用英文注释已经解释了,作用即将MyData类对象m里面的数据打印显示到控制台屏幕上面,从函数的原型也可以看得出,参数包含ostream对象与MyData对象,从而把MyData对象数据传到输出流ostream对象上面,结果自然是与std::cout一样内容显示到控制台屏幕上面。
文件读写
-
最后我们来看看本次主题,opencv怎么进行文件的读写操作。
程序中读文件和写文件所用到的类是FileStorage
//FileStorage XML/YAML/JSON file storage class that encapsulates all the information necessary for writing or reading data to/from a file.
这里可以看到,FileStorage读写文件的格式是XML/YAML/JSON类型的数据格式,这些数据格式有着比较明显的特点
//XML <!-- Copyright w3school.com.cn --> <note> <to>George</to> <from>John</from> <heading>Reminder</heading> <body>Don't forget the meeting!</body> </note> //YAML --- # 一位职工记录 name: Example Developer job: Developer skill: Elite employed: True foods: - Apple - Orange - Strawberry - Mango languages: ruby: Elite python: Elite dotnet: Lame //JSON "firstName" : "John" { "firstName":"John" , "lastName":"Doe" } { "employees": [ { "firstName":"John" , "lastName":"Doe" }, { "firstName":"Anna" , "lastName":"Smith" }, { "firstName":"Peter" , "lastName":"Jones" } ] }程序中读写操作的是json格式的文件。来学习一下具体的文件读写操作
首先是文件的打开方式
FileStorage fs(filename, FileStorage::WRITE);//以写的方式打开文件 FileStorage fs; fs.open(filename, FileStorage::READ);//以读的方式打开文件
接着是读写json格式文件
//一对一键值对读写 fs << "iterationNr" << 100;//写到文件 itNr = (int) fs["iterationNr"];//从文件读取出来 cout << itNr; //中括号数组形式的读写 //写到文件 fs << "strings" << "["; // text - string sequence fs << "image1.jpg" << "Awesomeness" << "../data/baboon.jpg"; fs << "]"; // close sequence //从文件读取出来 FileNode n = fs["strings"]; // Read string sequence - Get node if (n.type() != FileNode::SEQ) { cerr << "strings is not a sequence! FAIL" << endl; return 1; } //使用迭代器 FileNodeIterator it = n.begin(), it_end = n.end(); // Go through the node for (; it != it_end; ++it) cout << (string)*it << endl; //花括号json对象的读写 //写文件 fs << "Mapping"; // text - mapping fs << "{" << "One" << 1; fs << "Two" << 2 << "}"; //读文件 n = fs["Mapping"]; // Read mappings from a sequence cout << "Two " << (int)(n["Two"]) << "; "; cout << "One " << (int)(n["One"]) << endl << endl; //矩阵以及MyData类数据的读写 fs << "R" << R; // cv::Mat fs << "T" << T; fs << "MyData" << m; fs["R"] >> R; // Read cv::Mat fs["T"] >> T; //读取MyData类数据 fs["MyData"] >> m;
程序运行的结果如下
控制台会显示如下内容
打开写入的文件,内容如下
重载运算符
-
最后,来学习一下如何给自定义类去重载<<、>>运算符
重载>>
class Date { int mo, da, yr; public: Date(int m, int d, int y) { mo = m; da = d; yr = y; } friend ostream& operator<<(ostream& os, const Date& dt); }; istream& operator>> ( istream& is, Date& dt ) { is >> dt.mo >> dt.da >> dt.yr; return is; }重载<<
ostream& operator<<(ostream& os, const Date& dt) { os << dt.mo << '/' << dt.da << '/' << dt.yr; return os; }
总结回顾
io操作一般有内存变量与控制台和内存变量与硬盘文件两类,opencv中实现操作的类为FileStorage和FileNode等。FileStorage支持读写的文件格式为XML/YAML/JSON,json数据格式有单一键值对、中括号数组格式和花括号json对象格式。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- laravel自带用户认证
- Mac自带apache配置
- opencv自带例子学习-图像混合
- Golang中自带的强大命令工具
- Android调用系统自带的分享功能
- 使用oracle自带的命令进行导入导出
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
人人都是产品经理2.0
苏杰 / 电子工业出版社 / 2017-5 / 66.6
《人人都是产品经理2.0——写给泛产品经理》继续定位在-1~3 岁的产品经理。这里特别要强调,“-1 岁”指的是“泛产品经理”群体,比如自认为是“产品新人”的“职场老人”,需要自己做产品的早期创业者,对产品感兴趣并且工作中可能要承担部分职责的技术、设计、运营等人员,其他行业对互联网产品感兴趣的从业者等,《人人都是产品经理2.0——写给泛产品经理》可以说是为他们量身定做的。 内容方面,《人人都......一起来看看 《人人都是产品经理2.0》 这本书的介绍吧!