opencv自带例子学习-文件输入与输出

栏目: 编程工具 · 发布时间: 5年前

内容简介:输入输出(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;
}

程序详解

下面来一步一步地学习解读程序

自定义数据类

  1. 首先,程序声明里一个数据类来作为后面的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);
}
  1. 然后来看看重载了运算符<<

    先看看程序

    // 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一样内容显示到控制台屏幕上面。

文件读写

  1. 最后我们来看看本次主题,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;
    

程序运行的结果如下

控制台会显示如下内容

opencv自带例子学习-文件输入与输出

打开写入的文件,内容如下

opencv自带例子学习-文件输入与输出

重载运算符

  1. 最后,来学习一下如何给自定义类去重载<<、>>运算符

    重载>>

    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对象格式。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Dojo权威指南

Dojo权威指南

拉塞尔 / 李松峰、李丽 / 机械工业出版社 / 2009-4 / 79.00元

通过使用Dojo这个工业强度的JavaScript工具箱,我们可以比使用其他任何Ajax框架更高效、更容易地创建JavaScript或Ajax驱动的应用程序和站点。 《Dojo权威指南》向读者展示了如何充分利用Dojo工具箱中包含的大量实用特性,以前所未有的效率开发出功能丰富、响应敏捷的Web应用程序。读者通过《Dojo权威指南》能够学习到创建复杂布局和表单控件(常见于高级桌面应用程序)的技......一起来看看 《Dojo权威指南》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具