文章目录
- 一、概述
- 二、文件操作
- 三、打开文件
- 四、写入
- 五、读写个人源码
一、概述
除了图像数据之外,有时程序中的尺寸较小的Mat类矩阵、字符串、数组等
数据也需要进行保存,这些数据通常保存成XML文件或者YAML文件。本小节中将介绍如何利用OpenCV
4中的函数将数据保存成XML文件或者YAML文件以及如何读取这两种文件中的数据。
XML是一种元标记语言,所谓元标记就是使用者可以根据自身需求定义自己的标记,例如可以用、等标记来定义数据的含义,例如用24来表示age数据的数值为24。XML是一种结构化的语言,通过XML语言可以知道数据之间的隶属关系,例如100150表示在color数据中含有两个名为red和blue的数据,两者的数值分别是100和150。通过标记的方式,无论以任何形式保存数据,只要文件满足XML格式,那么读取出来的数据就不会出现混淆和歧义。XML文件的扩展名是“.xml”。
YMAL是一种以数据为中心的语言,通过“变量:数值”的形式来表示每个数据的数值,通过不同的缩进来表示不同数据之间的结构和隶属关系。YMAL可读性高,常用来表达资料序列的格式,它参考了多种语言,包括XML、C语言、Python、Perl等。YMAL文件的扩展名是“.ymal”或者“.yml”。
OpenCV
4中提供了用于生成和读取XML文件和YMAL文件的FileStorage类,类中定义了初始化类、写入数据和读取数据等方法。我们在使用该FileStorage类时首先需要对其进行初始化,初始化可以理解为声明需要操作的文件和操作类型。OpenCV
4提供了两种初始化的方法,分别是不输入任何参数的初始化(可以理解为只定义,并未初始化)和输入文件名称和操作类型的初始化。后者初始化构造函数的函数原型在代码清单2-35中给出。
二、文件操作
cv::FileStorage::FileStorage(const String & filename,
int flags,
const String & encoding = String()
)
- filename:打开的文件名称。
- flags:对文件进行的操作类型标志,常用参数及含义在表2-8给出。
- encodin:编码格式,目前不支持UTF-16 XML编码,需要使用UTF-8 XML编码
该函数是FileStorage类的构造函数,用于声明打开的文件名称和操作的类型。函数第一个参数是打开的文件名称,参数是字符串类型,文件的扩展名是“.xml”、“.ymal”或者“.yml”。打开得文件可以已经存在或者未存在,但是当对文件进行读取操作时需要是已经存在的文件。第二个参数是对文件进行的操作类型标志,例如对文件进行读取操作、写入操作等,常用参数及含义在表2-8给出,由于该标志量在FileStorage类中,因此在使用时需要加上类名作为前缀,例如“FileStorage::WRITE”。最后一个参数是文件的编码格式,目前不支持UTF-16XML编码,需要使用UTF-8 XML编码,通常情况下使用该参数的默认值即可。
三、打开文件
打开文件后,可以通过FileStorage类中的isOpened()函数判断是否成功打开文件,如果成功打开文件,该函数返回true,如果打开文件失败,则该函数返回false。
FileStorage类中默认构造函数没有任何参数,因此没有声明打开的文件和操作的类型,此时需要通过FileStorage类中的open()函数单独进行声明。
virtual bool cv::FileStorage::open(const String & filename,
int flags,
const String & encoding = String()
)
- filename:打开的文件名称。
- flags:对文件进行的操作类型标志,常用参数及含义在表2-8给出。
- encodin:编码格式,目前不支持UTF-16 XML编码,需要使用UTF-8 XML编码。
该函数补充了默认构造函数没有声明打开文件的缺点,函数可以指定FileStorage类打开的文件,如果成功打开文件,则返回值为true,否则为false。函数中所有的参数及含义都与代码清单2-35中的相同,因此这里不再进行赘述。同样,通过该函数打开文件后仍然可以通过FileStorage类中的isOpened()函数判断是否成功打开文件。
四、写入
打开文件后,类似C++中创建的数据流,可以通过“<<”操作符将数据写入文件中,或者通过“>>”操作符从文件中读取数据。除此之外,还可以通过FileStorage类中的write()函数将数据写入文件中,该函数的函数原型在代码清单2-37中给出
void cv::FileStorage::write(const String & name,
int val
)
- name:写入文件中的变量名称。
- val:变量值。
该函数能够将不同数据类型的变量名称和变量值写入到文件中。该函数的第一个参数是写入文件中的变量名称。第二个参数是变量值,代码清单2-37中的变量值是int类型,但是在FileStorage类中提供了write()函数的多个重载函数,分别用于实现将double、String、Mat、vector类型的变量值写入到文件中。
使用操作符向文件中写入数据时与write()函数类似,都需要声明变量名和变量值,例如变量名为“age”,变量值为“24”,可以通过“file<<”age”<<24”来实现。如果某个变量的数据是一个数组,可以用“[]”将属于同一个变量的变量值标记出来,例如“file<<”age”<<“[”<<24<<25<<”]””。如果某些变量隶属于某个变量,可以用“{}”表示变量之间的隶属关系,例如“file<<”age”<<“{”<<”Xiaoming”<<24<<”Wanghua”<<25<<”}””。
读取文件中的数据时,只需要通过变量名就可以读取变量值。例如“file [“x”] >> xRead”是读取变量名为x的变量值。但是,当某个变量中含有多个数据或者含有子变量时,就需要通过FileNode节点类型和迭代器FileNodeIterator进行读取,例如某个变量的变量值是一个数组,首先需要定义一个file [“age”]的FileNode节点类型变量,之后通过迭代器遍历其中的数据。另外一种方法可以不使用迭代器,通过在变量后边添加“[]”地址的形式读取数据,例如FileNode[0]表示数组变量中的第一个数据,FileNode[“Xiaoming”]表示“age”变量中的“Xiaoming”变量的数据,依次向后添加“[]”地址实现多节点数据的读取。
为了了解如何生成和读取XML文件和YMAL文件,在代码清单2-38中给出了实现文件写入和读取的示例程序。程序中使用write()函数和“<<”操作符两种方式向文件中写入数据,使用迭代器和“[]”地址两种方式从文件中读取数据。数据的写入和读取方法在前面已经介绍,在代码清单2-38中需要重点了解如何通过程序实现写入与读取。程序生成的XML文件和YMAL文件中的数据在图2-10给出,读取文件数据的结果在图2-9给出。
#include<iostream>
#include<vector>
#include<string>
//#include <stdio.h>
#include <opencv2/opencv.hpp>
#include "opencv/highgui.h"
using namespace std;
using namespace cv;
int main(int argc,char** argv) {
cout<<"OpenCv Version: "<<CV_VERSION<<endl;
string filename="datas.yaml";//文件的名称
FileStorage fwriter(filename,FileStorage::WRITE);
//存入矩阵Mat类型的数据
Mat mat=Mat::eye(3,3,CV_8U);
fwriter.write("mat",mat);//使用write()函数写入数据
//存入浮点型数据,节点名称为x
float x=100;
fwriter<<"x"<<x;
//存入字符串类型数据,节点名称为str
string str="Learn OpenCV 4";
fwriter<<"str"<<str;
//存入数组,节点名称为number_array
fwriter<<"number_array"<<"["<<4<<5<<6<<"]";
//存入多node节点数据,主名为multi_nodes
fwriter<<"multi_nodes"<<"{"<<"month"<<8<<"day"<<28<<"year"
<<2020<<"time"<<"["<<0<<1<<2<<3<<"]"<<"}";
//关闭文件
fwriter.release();
//以读取的模式打开文件
FileStorage fread(filename,FileStorage::READ);
//判断是否打开成功
if(!fread.isOpened()){
cout<<"打开文件失败,请确认文件名称是否正确!"<<endl;
return -1;
}
//读取文件中的数据
float xRead;
fread["x"]>>xRead;//读取浮点型数据
cout<<"x = "<<xRead<<endl;
//读取字符串数据
string strRead;
fread["str"]>>strRead;
cout<<"str = "<<strRead<<endl;
//读取含多个number_array节点
FileNode fileNode=fread["number_array"];
cout<<"number_array = [";
for(FileNodeIterator i=fileNode.begin();i!=fileNode.end();++i){
float a;
*i>>a;
cout<<a<<" ";
}
cout<<"]"<<endl;
//读取Mat类型数据
Mat matRead;
fread["mat="]>>matRead;
cout<<"mat = "<<mat<<endl;
//读取含有多个子节点的节点数据,不使用fileNode和迭代器进行读取
FileNode fileNode1=fread["multi_nodes"];
int month=(int)fileNode1["month"];
int day=(int)fileNode1["day"];
int year=(int)fileNode1["year"];
cout<<"multi_nodes:"<<endl;
cout<<" month = "<<month<<" day = "<<day<<" year = "<<year<<" time=[";
for(int i=0;i<4;++i){
int a=(int)fileNode1["time"][i];
cout<<a<<" ";
}
cout<<"]"<<endl;
//关闭文件
fread.release();
return 0;
}
五、读写个人源码
bool Percipio_mindCalibrationThread::SaveCalibOfflinexml(QVector<QPair< QStringList,TY_CAMERA_CALIB_INFO>> Datainfo )
{
show_message("------------Start Save offline Calibfile-----------------");
FolderFile_Getfiles->CreateFolder_F(Datainfo[0].first[2]);
QString path=Datainfo[0].first[2]+"/CalibrationOffline.xml";
FolderFile_Getfiles->CreateFile_F(path);
cv::FileStorage fwrite(path.toStdString(),cv::FileStorage::WRITE);
// getALlXYZtSetting.QVerctorQstringList.push_back({{XYZSN,"depth",Path2D+"/3D"},percipioCamera->Get_DepthCalibData()});
show_message("Save Sn and fodlerpath");
std::string sntype="";
show_message(Datainfo[0].first[2]+"/3D/");
qDebug()<<Datainfo[0].first[2]+"/3D/"<<endl;
std::vector<std::string> XYZimagefiles=FolderFile_Getfiles->GetFloderAllFile(Datainfo[0].first[2]+"/3D/");
if(XYZimagefiles.size()>3)
{
std::pair<std::string,std::string> ImageReslotion=FolderFile_Getfiles->GetImageResolution(Datainfo[0].first[2]+"/3D/");
sntype="DepthCamera";
fwrite<<sntype<<"{:";
fwrite<<"SN"<<(Datainfo[0].first[0]).toStdString();
fwrite<<"Width"<< atoi(ImageReslotion.first.c_str());
fwrite<<"Height"<<atoi(ImageReslotion.second.c_str());
cv::Mat Depthintrinsic=cv::Mat(3, 3, CV_32F,Datainfo[0].second.intrinsic.data);
cv::Mat Depthextrinsic=cv::Mat(4, 4, CV_32F,Datainfo[0].second.extrinsic.data);
cv::Mat Depthdistortion=cv::Mat(12, 1, CV_32F,Datainfo[0].second.distortion.data);
fwrite<<"IntrinsicWidth"<<atoi(std::to_string(Datainfo[0].second.intrinsicWidth).c_str());
fwrite<<"IntrinsicHeight"<<atoi(std::to_string(Datainfo[0].second.intrinsicHeight).c_str());;
fwrite<<"Intrinsic"<<Depthintrinsic;
fwrite<<"Extrinsic"<<Depthextrinsic;
fwrite<<"Distortion"<<Depthdistortion;
fwrite<<"}";
fwrite<<"FilePath"<<(Datainfo[0].first[2]).toStdString();
// int Patternwidth;
// int Patternheight;
// float Sqyaresize;
fwrite<<"Patternwidth"<<Patternwidth;
fwrite<<"Patternheight"<<Patternheight;
fwrite<<"Sqyaresize"<<Sqyaresize;
fwrite.release();
show_message("offline xml save OK");
return true;
}
else
{
show_message("Please checke the folder files path setting and mustbe three images at least,offline xml save NG");
return false;
}
}
std::vector<cameraInfo> Percipio_mindCalibrationThread::ReadCalibOfflinexml(QString Path )
{
std::vector<cameraInfo> offlineVector;
show_message("----------------Read offline configxml--------------------");
cv::FileStorage fs(Path.toStdString()+"/CalibrationOffline.xml",cv::FileStorage::READ);
if(!fs.isOpened())
{
show_message("Cannot open the CalibrationOffline.xml,NG");
return offlineVector;
}
cameraInfo cameraDepth;
cameraInfo cameraColor;
std::string Filepath;
fs["FilePath"]>>Filepath;
cv::FileNode node=fs["DepthCamera"];
std::vector<std::string> filepath=FolderFile_Getfiles->GetFloderAllFile(Path+"/3D/");
TY_CAMERA_CALIB_INFO calibDepth;
calibDepth.intrinsicWidth=(int)node["IntrinsicWidth"];
calibDepth.intrinsicHeight=(int)node["IntrinsicHeight"];
show_message("Get Patter info");
fs["Patternwidth"]>> Patternwidth;
fs["Patternheight"]>> Patternheight;
fs["Sqyaresize"]>> Sqyaresize;
show_message("Patternwidth: "+QString::number(Patternwidth));
show_message("Patternheight: "+QString::number(Patternheight));
show_message("Sqyaresize: "+QString::number(Sqyaresize));
cv::Mat Depthintrinsic(3, 3, CV_32F,cv::Scalar(0));
cv::Mat Depthextrinsic(4, 4, CV_32F,cv::Scalar(0));
cv::Mat Depthdistortion(12, 1, CV_32F,cv::Scalar(0));
show_message("Read Depth info");
node["Intrinsic"]>>Depthintrinsic;
node["Extrinsic"]>>Depthextrinsic;
node["Distortion"]>>Depthdistortion;
//calibDepth.intrinsic.data
memcpy((uchar*)calibDepth.intrinsic.data,Depthintrinsic.data,sizeof (float) * 9);
memcpy((uchar*)calibDepth.extrinsic.data,Depthextrinsic.data,sizeof (float) * 16);
memcpy((uchar*)calibDepth.distortion.data,Depthdistortion.data,sizeof (float) * 12);
/*
int Intrinsicindex=0;
int Extrinsicindex=0;
int Distortionindex=0;
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
{
calibDepth.intrinsic.data[Intrinsicindex] = Depthintrinsic.at<float>(i,j);
show_message(QString::number(calibDepth.intrinsic.data[Intrinsicindex] ));
Intrinsicindex++;
}
}
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
calibDepth.extrinsic.data[Extrinsicindex] = Depthextrinsic.at<float>(i,j);
show_message(QString::number(calibDepth.extrinsic.data[Extrinsicindex]));
Extrinsicindex++;
}
}
for(int i=0;i<12;i++)
{
for(int j=0;j<1;j++)
{
calibDepth.distortion.data[Distortionindex] = Depthintrinsic.at<float>(i,j);
show_message(QString::number(calibDepth.distortion.data[Distortionindex]));
Distortionindex++;
}
}
*/
cameraDepth.sn=(std::string)node["SN"];
show_message("Read Depth sn:"+QString::fromStdString(cameraDepth.sn));
cameraDepth.files=filepath;
cameraDepth.compont="depth";
cameraDepth.cali=calibDepth;
offlineVector.push_back(cameraDepth);
std::vector<std::string> colorfilepath=FolderFile_Getfiles->GetFloderAllFile(Path+"/2D/");
TY_CAMERA_CALIB_INFO calibcolor;
cameraColor.sn=FolderFile_Getfiles->GetCameraSn(Path+"/2D/");
show_message("Read Color sn:"+QString::fromStdString(cameraColor.sn));
cameraColor.files=colorfilepath;
cameraColor.compont="color";
cameraColor.cali=calibcolor;
offlineVector.push_back(cameraColor);
show_message("Read config.xml complete");
return offlineVector;
}