FileStorage是CV开发经常使用的文件IO工具,可以极其简便地读写C++及OpenCV数据类型,且支持XML/YAML/JSON等纵多盛行的文件格式。
1.支持的数据类型
(1)C++静态类型:所有。
(2)C++容器类型:典型地string、vector<type>、vector<vector<type>>,这里的type是FileStorage,最多支持两重vector。
(3)CV静态类型:截至OpenCV4.5,支持除TermCriteria、Matx和Affine等外的所有类型。
(4)CV容器类型:Mat
(5)自定义类型:可重载运算符<<,支持自定义类型。
2.常用的成员函数
(1)type()=int:返回NONE/INT/REAL=FLOAT/STR=STRING/SEQ/MAP、TYPE_MASK/FLOW=UNIFORM/EMPTY/NAMED。
(2)isNone()、isInt()、isReal()、isString()、isSeq()、isMap()、isNamed()=bl:判断类型及是否有名。
(3)real()=db、string()=str、mat()=mat、()=int/flt/db/str、name()=str:返回结点值及结点名。
(4)size()=int:返回序列或集合的子结点数。
(5)keys()=strs:返回地图所有子结点名。
(6)begin()、end()=iter:返回首末迭代器。
3.遍历方法
(1)方式一:begin()和end()结合既能遍历向量也能遍历地图
(2)方式二:size()=int和FileNode[index]结合遍历向量,keys()和FileNode[name]结合遍历地图
注意点:截至OpenCV4.5还无OpenCV类型识别接口,但截至OpenCV4.5中数据结构也只有Mat是MAP类型,也就是说如果只读写C++和OpenCV数据类型,若读到MAP类型,要么是root结点要么是cv::Mat。
4.使用样例
以下提供FileStorage的使用样例以供参数,封装在类AboutFileStorage,其功能要点如下:
(1)如何读写非序列类型:以读写C++::int、C++::string、cv::Rect、cv::Mat为例
(2)如何写读序列类型:以vector<…>、vector<vector<…>>为例。
(3)如何使用begin()和end()遍历FileStorage。
(4)如何使用size()、keys()及FileNode[id/name]遍历FileStorage。
(5)如何复制如下特点的FileStorage:结点深度为1(即只有root和first level)、每个结点只能是静态类型或Mat或它们vector<…>或vector<vector<…>>。
以下是详细代码,依赖于C++14、OpenCV4.x和Spdlog。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <opencv2/opencv.hpp> 2 #include <opencv2/core/utils/filesystem.hpp> 3 #include <spdlog/spdlog.h> 4 using namespace std; 5 using namespace cv; 6 7 #ifndef StrPairKey 8 #define StrPairKey(key) make_pair(#key, key) 9 #define StrPairVal(val) make_pair(val, #val) 10 #endif 11 12 class AboutFileStorage 13 { 14 public: 15 map<int, string> fnTypes = //for (map<key, val>::iterator it = map.begin(); it != map.end(); ++it) it->first; it->second; 16 { 17 StrPairVal(FileNode::NONE), 18 StrPairVal(FileNode::INT), 19 StrPairVal(FileNode::REAL),//=FLOAT 20 StrPairVal(FileNode::STR),//=STRING 21 StrPairVal(FileNode::SEQ), 22 StrPairVal(FileNode::MAP), 23 StrPairVal(FileNode::NAMED), 24 StrPairVal(FileNode::TYPE_MASK), 25 StrPairVal(FileNode::FLOW),//=UNIFORM 26 StrPairVal(FileNode::EMPTY)//for SEQ and MAP 27 }; 28 29 string traverseFS(FileNode fn, int level, bool useIndex = true) 30 { 31 string str; for (int k = 0; k < level; ++k) str += " "; 32 str += fmt::format("<L{}>-<{}>-<{}>: ", level, fnTypes[fn.type()], (fn.isNamed() ? fn.name() : "noName")); 33 34 if (fn.isInt()) str += std::to_string(int(fn)); 35 if (fn.isReal()) str += std::to_string(fn.real()); 36 if (fn.isString()) str += fn.string(); 37 if (useIndex) 38 { 39 if (fn.isSeq()) for (size_t k = 0; k < fn.size(); ++k) str += " " + traverseFS(fn[k], level + 1); 40 if (fn.isMap()) for (size_t k = 0; k < fn.keys().size(); ++k) str += " " + traverseFS(fn[fn.keys()[k]], level + 1); 41 } 42 else if (fn.isSeq() || fn.isMap()) for (FileNodeIterator it = fn.begin(); it != fn.end(); ++it) str += " " + traverseFS(*it, level + 1); 43 return str; 44 }; 45 46 void copyFS(FileStorage srcFS, FileStorage dstFS) 47 { 48 //(1)Node support: to the first level 49 //(2)Type support: C++ static, OpenCV static, std::string, std::vector, cv::Mat 50 for (FileNodeIterator it = srcFS.root(0).begin(); it != srcFS.root(0).end(); ++it) 51 { 52 //Scalar 53 FileNode fn0 = *it; 54 if (fn0.isInt()) dstFS << fn0.name() << int(fn0); 55 if (fn0.isReal()) dstFS << fn0.name() << fn0.real(); 56 if (fn0.isString()) dstFS << fn0.name() << fn0.string(); 57 if (fn0.isMap()) dstFS << fn0.name() << fn0.mat(); 58 59 //Vector 60 FileNode fn1 = *(fn0.begin()); 61 if (fn0.isSeq() && fn1.isInt()) { vector<int> int1D; srcFS[fn0.name()] >> int1D; dstFS << fn0.name() << int1D; } 62 if (fn0.isSeq() && fn1.isReal()) { vector<double> flt1D; srcFS[fn0.name()] >> flt1D; dstFS << fn0.name() << flt1D; } 63 if (fn0.isSeq() && fn1.isString()) { vector<string> str1D; srcFS[fn0.name()] >> str1D; dstFS << fn0.name() << str1D; } 64 if (fn0.isSeq() && fn1.isMap()) { vector<Mat> mat1D; srcFS[fn0.name()] >> mat1D; dstFS << fn0.name() << mat1D; } 65 66 //VectorVector 67 FileNode fn2 = *(fn1.begin()); 68 if (fn0.isSeq() && fn1.isSeq() && fn2.isInt()) { vector<vector<int>> int2D; srcFS[fn0.name()] >> int2D; dstFS << fn0.name() << int2D; } 69 if (fn0.isSeq() && fn1.isSeq() && fn2.isReal()) { vector<vector<double>> flt2D; srcFS[fn0.name()] >> flt2D; dstFS << fn0.name() << flt2D; } 70 if (fn0.isSeq() && fn1.isSeq() && fn2.isString()) { vector<vector<string>> str2D; srcFS[fn0.name()] >> str2D; dstFS << fn0.name() << str2D; } 71 if (fn0.isSeq() && fn1.isSeq() && fn2.isMap()) { vector<vector<Mat>> mat2D; srcFS[fn0.name()] >> mat2D; dstFS << fn0.name() << mat2D; } 72 } 73 }; 74 75 public: 76 void testFS(int argc = 0, char** argv = 0) 77 { 78 string fsPath0 = "./fs0.yml"; 79 string fsPath1 = "./fs1.yml"; 80 string fsPath2 = "./fs2.yml"; 81 string fsAllPath1 = "./fsAllData1.txt"; 82 string fsAllPath2 = "./fsAllData2.txt"; 83 string fsRootPath = "./fsRootInfo.txt"; 84 85 //1.GetSourceFS 86 { 87 FileStorage fs(fsPath0, FileStorage::WRITE); 88 89 //1.1 C++int 90 int int0D = 111; 91 vector<int> int1D(2, int0D); for (size_t k = 0; k < int1D.size(); ++k) int1D[k] += int(k + 1); 92 vector<vector<int>> int2D(3, int1D); for (size_t i = 0; i < int2D.size(); ++i) for (size_t j = 0; j < int2D[i].size(); ++j) int2D[i][j] += int(i + j); 93 fs << "int0D" << int0D << "int1D" << int1D << "int2D" << int2D; 94 95 //1.2 C++string 96 string str0D("abc"); 97 vector<string> str1D(2, str0D); for (size_t k = 0; k < str1D.size(); ++k) str1D[k] += std::to_string(k); 98 vector<vector<string>> str2D(3, str1D); for (size_t i = 0; i < str2D.size(); ++i) for (size_t j = 0; j < str2D[i].size(); ++j) str2D[i][j] += fmt::format("{}{}", i, j); 99 fs << "str0D" << str0D << "str1D" << str1D << "str2D" << str2D; 100 101 //1.3 OpenCVRect 102 Rect rect0D(111, 222, 333, 444); 103 vector<Rect> rect1D(2); cv::randu(rect1D, 111, 999); 104 vector<vector<Rect>> rect2D(3, vector<Rect>(2)); for (size_t k = 0; k < rect2D.size(); ++k) cv::randu(rect2D[k], 111, 999); 105 fs << "rect0D" << rect0D << "rect1D" << rect1D << "rect2D" << rect2D; 106 107 //1.4 OpenCVMat 108 Mat_<Vec3i> mat0D(3, 3, Vec3i(111, 222, 333)); 109 vector<Mat_<Vec3i>> mat1D(2); for (int k = 0; k < mat1D.size(); ++k) { mat1D[k].create(3, 3); cv::randu(mat1D[k], 111, 999); } 110 vector<vector<Mat_<Vec3i>>> mat2D(3, vector<Mat_<Vec3i>>(2)); for (size_t i = 0; i < mat2D.size(); ++i) for (size_t j = 0; j < mat2D[i].size(); ++j) { mat2D[i][j].create(3, 3); cv::randu(mat2D[i][j], 111, 999); } 111 fs << "mat0D" << mat0D << "mat1D" << mat1D << "mat2D" << mat2D; 112 } 113 114 //2.ReadFS0AndWriteFS1 115 { 116 FileStorage fs0(fsPath0, FileStorage::READ); 117 FileStorage fs1(fsPath1, FileStorage::WRITE); 118 119 //2.1 C++int 120 int int0D; fs0["int0D"] >> int0D; 121 vector<int> int1D; fs0["int1D"] >> int1D; 122 vector<vector<int>> int2D; fs0["int2D"] >> int2D; 123 fs1 << "int0D" << int0D << "int1D" << int1D << "int2D" << int2D; 124 125 //2.2 C++string 126 string str0D; fs0["str0D"] >> str0D; 127 vector<string> str1D; fs0["str1D"] >> str1D; 128 vector<vector<string>> str2D; fs0["str2D"] >> str2D; 129 fs1 << "str0D" << str0D << "str1D" << str1D << "str2D" << str2D; 130 131 //2.3 OpenCVRect 132 Rect rect0D; fs0["rect0D"] >> rect0D; 133 vector<Rect> rect1D; fs0["rect1D"] >> rect1D; 134 vector<vector<Rect>> rect2D; fs0["rect2D"] >> rect2D; 135 fs1 << "rect0D" << rect0D << "rect1D" << rect1D << "rect2D" << rect2D; 136 137 //2.4 OpenCVMat 138 Mat_<Vec3b> mat0D; fs0["mat0D"] >> mat0D; 139 vector<Mat_<Vec3b>> mat1D; fs0["mat1D"] >> mat1D; 140 vector<vector<Mat_<Vec3i>>> mat2D; fs0["mat2D"] >> mat2D; 141 fs1 << "mat0D" << mat0D << "mat1D" << mat1D << "mat2D" << mat2D; 142 } 143 144 //3.CopyFS0ToFS2 145 { 146 FileStorage fs0(fsPath0, FileStorage::READ); 147 FileStorage fs2(fsPath2, FileStorage::WRITE); 148 copyFS(fs0, fs2); 149 } 150 151 //4.TranverseFS 152 string rets; 153 { 154 auto SaveTraverseFS = [this](string fsPath, string fsTraPath, bool useIndex)->string 155 { 156 FileStorage fs(fsPath, FileStorage::READ); 157 string str = traverseFS(fs.root(), 0, useIndex); 158 fs.release(); 159 FILE* file = fopen(fsTraPath.c_str(), "w"); 160 fprintf(file, "%s", str.c_str()); 161 fclose(file); 162 return str; 163 }; 164 rets += " " + SaveTraverseFS(fsPath0, fsAllPath1, true); 165 rets += " " + SaveTraverseFS(fsPath0, fsAllPath2, false); 166 } 167 168 //5.CheckRootNode 169 { 170 auto PrintNode = [this](FileNode fn)->string 171 { 172 string str = fmt::format(" fn.name(): {} fn.type(): {} fn.size(): {} ", (fn.isNamed() ? fn.name() : "noName"), fnTypes[fn.type()], fn.size()); 173 if (fn.isMap()) { str += fmt::format("fn.keys(): "); for (size_t k = 0; k < fn.keys().size(); ++k) str += fmt::format("{} ", fn.keys()[k]); } 174 return str; 175 }; 176 177 FileStorage fs(fsPath0, FileStorage::READ); 178 string str = fmt::format("fs.root(0): the zero level has only one node {} ", PrintNode(fs.root(0))); 179 str + fmt::format("fs.getFirstTopLevelNode(): the first level: the first node {} ", PrintNode(fs.getFirstTopLevelNode())); 180 str += "fs.root(0).begin()...fs.root(0).end(): the first level: the first node...the final node"; for (FileNodeIterator it = fs.root(0).begin(); it != fs.root(0).end(); ++it) str += PrintNode(*it); str += " "; 181 str += fmt::format("fs[rect0D]: the first level: the specific node {} ", PrintNode(fs["rect0D"])); 182 rets += " " + str; 183 184 FILE* file = fopen(fsRootPath.c_str(), "w"); 185 fprintf(file, "%s", str.c_str()); 186 fclose(file); 187 } 188 spdlog::info(rets); 189 } 190 }; 191 192 int main(int argc, char** argv) { AboutFileStorage afs; afs.testFS(argc, argv); return 0; }