core: FileStorage - add support for writing vector<String> with bindings (#11883)
[platform/upstream/opencv.git] / modules / core / src / persistence_cpp.cpp
1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html
4
5
6 #include "precomp.hpp"
7 #include "persistence.hpp"
8
9 ///////////////////////// new C++ interface for CvFileStorage ///////////////////////////
10
11 namespace cv
12 {
13
14 static void getElemSize( const String& fmt, size_t& elemSize, size_t& cn )
15 {
16     const char* dt = fmt.c_str();
17     cn = 1;
18     if( cv_isdigit(dt[0]) )
19     {
20         cn = dt[0] - '0';
21         dt++;
22     }
23     char c = dt[0];
24     elemSize = cn*(c == 'u' || c == 'c' ? sizeof(uchar) : c == 'w' || c == 's' ? sizeof(ushort) :
25         c == 'i' ? sizeof(int) : c == 'f' ? sizeof(float) : c == 'd' ? sizeof(double) :
26         c == 'r' ? sizeof(void*) : (size_t)0);
27 }
28
29 FileStorage::FileStorage()
30 {
31     state = UNDEFINED;
32 }
33
34 FileStorage::FileStorage(const String& filename, int flags, const String& encoding)
35 {
36     state = UNDEFINED;
37     open( filename, flags, encoding );
38 }
39
40 FileStorage::FileStorage(CvFileStorage* _fs, bool owning)
41 {
42     if (owning) fs.reset(_fs);
43     else fs = Ptr<CvFileStorage>(Ptr<CvFileStorage>(), _fs);
44
45     state = _fs ? NAME_EXPECTED + INSIDE_MAP : UNDEFINED;
46 }
47
48 FileStorage::~FileStorage()
49 {
50     while( structs.size() > 0 )
51     {
52         cvEndWriteStruct(fs);
53         structs.pop_back();
54     }
55 }
56
57 bool FileStorage::open(const String& filename, int flags, const String& encoding)
58 {
59     CV_INSTRUMENT_REGION()
60
61     release();
62     fs.reset(cvOpenFileStorage( filename.c_str(), 0, flags,
63                                 !encoding.empty() ? encoding.c_str() : 0));
64     bool ok = isOpened();
65     state = ok ? NAME_EXPECTED + INSIDE_MAP : UNDEFINED;
66     return ok;
67 }
68
69 bool FileStorage::isOpened() const
70 {
71     return fs && fs->is_opened;
72 }
73
74 void FileStorage::release()
75 {
76     fs.release();
77     structs.clear();
78     state = UNDEFINED;
79 }
80
81 String FileStorage::releaseAndGetString()
82 {
83     String buf;
84     if( fs && fs->outbuf )
85         icvClose(fs, &buf);
86
87     release();
88     return buf;
89 }
90
91 FileNode FileStorage::root(int streamidx) const
92 {
93     return isOpened() ? FileNode(fs, cvGetRootFileNode(fs, streamidx)) : FileNode();
94 }
95
96 int FileStorage::getFormat() const
97 {
98     CV_Assert(!fs.empty());
99     return fs->fmt & FORMAT_MASK;
100 }
101
102 FileStorage& operator << (FileStorage& fs, const String& str)
103 {
104     CV_TRACE_REGION_VERBOSE();
105
106     enum { NAME_EXPECTED = FileStorage::NAME_EXPECTED,
107         VALUE_EXPECTED = FileStorage::VALUE_EXPECTED,
108         INSIDE_MAP = FileStorage::INSIDE_MAP };
109     const char* _str = str.c_str();
110     if( !fs.isOpened() || !_str )
111         return fs;
112     if( *_str == '}' || *_str == ']' )
113     {
114         if( fs.structs.empty() )
115             CV_Error_( CV_StsError, ("Extra closing '%c'", *_str) );
116         if( (*_str == ']' ? '[' : '{') != fs.structs.back() )
117             CV_Error_( CV_StsError,
118             ("The closing '%c' does not match the opening '%c'", *_str, fs.structs.back()));
119         fs.structs.pop_back();
120         fs.state = fs.structs.empty() || fs.structs.back() == '{' ?
121             INSIDE_MAP + NAME_EXPECTED : VALUE_EXPECTED;
122         cvEndWriteStruct( *fs );
123         fs.elname = String();
124     }
125     else if( fs.state == NAME_EXPECTED + INSIDE_MAP )
126     {
127         if (!cv_isalpha(*_str) && *_str != '_')
128             CV_Error_( CV_StsError, ("Incorrect element name %s", _str) );
129         fs.elname = str;
130         fs.state = VALUE_EXPECTED + INSIDE_MAP;
131     }
132     else if( (fs.state & 3) == VALUE_EXPECTED )
133     {
134         if( *_str == '{' || *_str == '[' )
135         {
136             fs.structs.push_back(*_str);
137             int flags = *_str++ == '{' ? CV_NODE_MAP : CV_NODE_SEQ;
138             fs.state = flags == CV_NODE_MAP ? INSIDE_MAP +
139                 NAME_EXPECTED : VALUE_EXPECTED;
140             if( *_str == ':' )
141             {
142                 flags |= CV_NODE_FLOW;
143                 _str++;
144             }
145             cvStartWriteStruct( *fs, fs.elname.size() > 0 ? fs.elname.c_str() : 0,
146                 flags, *_str ? _str : 0 );
147             fs.elname = String();
148         }
149         else
150         {
151             write( fs, fs.elname, (_str[0] == '\\' && (_str[1] == '{' || _str[1] == '}' ||
152                 _str[1] == '[' || _str[1] == ']')) ? String(_str+1) : str );
153             if( fs.state == INSIDE_MAP + VALUE_EXPECTED )
154                 fs.state = INSIDE_MAP + NAME_EXPECTED;
155         }
156     }
157     else
158         CV_Error( CV_StsError, "Invalid fs.state" );
159     return fs;
160 }
161
162
163 void FileStorage::writeRaw( const String& fmt, const uchar* vec, size_t len )
164 {
165     if( !isOpened() )
166         return;
167     size_t elemSize, cn;
168     getElemSize( fmt, elemSize, cn );
169     CV_Assert( len % elemSize == 0 );
170     cvWriteRawData( fs, vec, (int)(len/elemSize), fmt.c_str());
171 }
172
173
174 void FileStorage::writeObj( const String& name, const void* obj )
175 {
176     if( !isOpened() )
177         return;
178     cvWrite( fs, name.size() > 0 ? name.c_str() : 0, obj );
179 }
180
181
182 void FileStorage::write( const String& name, int val )
183 {
184     *this << name << val;
185 }
186
187 void FileStorage::write( const String& name, double val )
188 {
189     *this << name << val;
190 }
191
192 void FileStorage::write( const String& name, const String& val )
193 {
194     *this << name << val;
195 }
196
197 void FileStorage::write( const String& name, const Mat& val )
198 {
199     *this << name << val;
200 }
201
202 void FileStorage::write( const String& name, const std::vector<String>& val )
203 {
204     *this << name << val;
205 }
206
207 void FileStorage::writeComment( const String& comment, bool append )
208 {
209     cvWriteComment(fs, comment.c_str(), append ? 1 : 0);
210 }
211
212 String FileStorage::getDefaultObjectName(const String& _filename)
213 {
214     static const char* stubname = "unnamed";
215     const char* filename = _filename.c_str();
216     const char* ptr2 = filename + _filename.size();
217     const char* ptr = ptr2 - 1;
218     cv::AutoBuffer<char> name_buf(_filename.size()+1);
219
220     while( ptr >= filename && *ptr != '\\' && *ptr != '/' && *ptr != ':' )
221     {
222         if( *ptr == '.' && (!*ptr2 || strncmp(ptr2, ".gz", 3) == 0) )
223             ptr2 = ptr;
224         ptr--;
225     }
226     ptr++;
227     if( ptr == ptr2 )
228         CV_Error( CV_StsBadArg, "Invalid filename" );
229
230     char* name = name_buf.data();
231
232     // name must start with letter or '_'
233     if( !cv_isalpha(*ptr) && *ptr!= '_' ){
234         *name++ = '_';
235     }
236
237     while( ptr < ptr2 )
238     {
239         char c = *ptr++;
240         if( !cv_isalnum(c) && c != '-' && c != '_' )
241             c = '_';
242         *name++ = c;
243     }
244     *name = '\0';
245     name = name_buf.data();
246     if( strcmp( name, "_" ) == 0 )
247         strcpy( name, stubname );
248     return String(name);
249 }
250
251 FileNode FileStorage::operator[](const String& nodename) const
252 {
253     return FileNode(fs, cvGetFileNodeByName(fs, 0, nodename.c_str()));
254 }
255
256 FileNode FileStorage::operator[](const char* nodename) const
257 {
258     return FileNode(fs, cvGetFileNodeByName(fs, 0, nodename));
259 }
260
261 FileNode FileNode::operator[](const String& nodename) const
262 {
263     return FileNode(fs, cvGetFileNodeByName(fs, node, nodename.c_str()));
264 }
265
266 FileNode FileNode::operator[](const char* nodename) const
267 {
268     return FileNode(fs, cvGetFileNodeByName(fs, node, nodename));
269 }
270
271 FileNode FileNode::operator[](int i) const
272 {
273     return isSeq() ? FileNode(fs, (CvFileNode*)cvGetSeqElem(node->data.seq, i)) :
274         i == 0 ? *this : FileNode();
275 }
276
277 String FileNode::name() const
278 {
279     const char* str;
280     return !node || (str = cvGetFileNodeName(node)) == 0 ? String() : String(str);
281 }
282
283 void* FileNode::readObj() const
284 {
285     if( !fs || !node )
286         return 0;
287     return cvRead( (CvFileStorage*)fs, (CvFileNode*)node );
288 }
289
290 static const FileNodeIterator::SeqReader emptyReader = {0, 0, 0, 0, 0, 0, 0, 0};
291
292 FileNodeIterator::FileNodeIterator()
293 {
294     fs = 0;
295     container = 0;
296     reader = emptyReader;
297     remaining = 0;
298 }
299
300 FileNodeIterator::FileNodeIterator(const CvFileStorage* _fs,
301                                    const CvFileNode* _node, size_t _ofs)
302 {
303     reader = emptyReader;
304     if( _fs && _node && CV_NODE_TYPE(_node->tag) != CV_NODE_NONE )
305     {
306         int node_type = _node->tag & FileNode::TYPE_MASK;
307         fs = _fs;
308         container = _node;
309         if( !(_node->tag & FileNode::USER) && (node_type == FileNode::SEQ || node_type == FileNode::MAP) )
310         {
311             cvStartReadSeq( _node->data.seq, (CvSeqReader*)&reader );
312             remaining = FileNode(_fs, _node).size();
313         }
314         else
315         {
316             reader.ptr = (schar*)_node;
317             reader.seq = 0;
318             remaining = 1;
319         }
320         (*this) += (int)_ofs;
321     }
322     else
323     {
324         fs = 0;
325         container = 0;
326         remaining = 0;
327     }
328 }
329
330 FileNodeIterator::FileNodeIterator(const FileNodeIterator& it)
331 {
332     fs = it.fs;
333     container = it.container;
334     reader = it.reader;
335     remaining = it.remaining;
336 }
337
338 FileNodeIterator& FileNodeIterator::operator ++()
339 {
340     if( remaining > 0 )
341     {
342         if( reader.seq )
343         {
344             if( ((reader).ptr += (((CvSeq*)reader.seq)->elem_size)) >= (reader).block_max )
345             {
346                 cvChangeSeqBlock( (CvSeqReader*)&(reader), 1 );
347             }
348         }
349         remaining--;
350     }
351     return *this;
352 }
353
354 FileNodeIterator FileNodeIterator::operator ++(int)
355 {
356     FileNodeIterator it = *this;
357     ++(*this);
358     return it;
359 }
360
361 FileNodeIterator& FileNodeIterator::operator --()
362 {
363     if( remaining < FileNode(fs, container).size() )
364     {
365         if( reader.seq )
366         {
367             if( ((reader).ptr -= (((CvSeq*)reader.seq)->elem_size)) < (reader).block_min )
368             {
369                 cvChangeSeqBlock( (CvSeqReader*)&(reader), -1 );
370             }
371         }
372         remaining++;
373     }
374     return *this;
375 }
376
377 FileNodeIterator FileNodeIterator::operator --(int)
378 {
379     FileNodeIterator it = *this;
380     --(*this);
381     return it;
382 }
383
384 FileNodeIterator& FileNodeIterator::operator += (int ofs)
385 {
386     if( ofs == 0 )
387         return *this;
388     if( ofs > 0 )
389         ofs = std::min(ofs, (int)remaining);
390     else
391     {
392         size_t count = FileNode(fs, container).size();
393         ofs = (int)(remaining - std::min(remaining - ofs, count));
394     }
395     remaining -= ofs;
396     if( reader.seq )
397         cvSetSeqReaderPos( (CvSeqReader*)&reader, ofs, 1 );
398     return *this;
399 }
400
401 FileNodeIterator& FileNodeIterator::operator -= (int ofs)
402 {
403     return operator += (-ofs);
404 }
405
406
407 FileNodeIterator& FileNodeIterator::readRaw( const String& fmt, uchar* vec, size_t maxCount )
408 {
409     if( fs && container && remaining > 0 )
410     {
411         size_t elem_size, cn;
412         getElemSize( fmt, elem_size, cn );
413         CV_Assert( elem_size > 0 );
414         size_t count = std::min(remaining, maxCount);
415
416         if( reader.seq )
417         {
418             cvReadRawDataSlice( fs, (CvSeqReader*)&reader, (int)count, vec, fmt.c_str() );
419             remaining -= count*cn;
420         }
421         else
422         {
423             cvReadRawData( fs, container, vec, fmt.c_str() );
424             remaining = 0;
425         }
426     }
427     return *this;
428 }
429
430
431 void write( FileStorage& fs, const String& name, int value )
432 { cvWriteInt( *fs, name.size() ? name.c_str() : 0, value ); }
433
434 void write( FileStorage& fs, const String& name, float value )
435 { cvWriteReal( *fs, name.size() ? name.c_str() : 0, value ); }
436
437 void write( FileStorage& fs, const String& name, double value )
438 { cvWriteReal( *fs, name.size() ? name.c_str() : 0, value ); }
439
440 void write( FileStorage& fs, const String& name, const String& value )
441 { cvWriteString( *fs, name.size() ? name.c_str() : 0, value.c_str() ); }
442
443 void writeScalar(FileStorage& fs, int value )
444 { cvWriteInt( *fs, 0, value ); }
445
446 void writeScalar(FileStorage& fs, float value )
447 { cvWriteReal( *fs, 0, value ); }
448
449 void writeScalar(FileStorage& fs, double value )
450 { cvWriteReal( *fs, 0, value ); }
451
452 void writeScalar(FileStorage& fs, const String& value )
453 { cvWriteString( *fs, 0, value.c_str() ); }
454
455
456 void write( FileStorage& fs, const String& name, const Mat& value )
457 {
458     if( value.dims <= 2 )
459     {
460         CvMat mat = value;
461         cvWrite( *fs, name.size() ? name.c_str() : 0, &mat );
462     }
463     else
464     {
465         CvMatND mat = value;
466         cvWrite( *fs, name.size() ? name.c_str() : 0, &mat );
467     }
468 }
469
470 // TODO: the 4 functions below need to be implemented more efficiently
471 void write( FileStorage& fs, const String& name, const SparseMat& value )
472 {
473     Ptr<CvSparseMat> mat(cvCreateSparseMat(value));
474     cvWrite( *fs, name.size() ? name.c_str() : 0, mat );
475 }
476
477
478 internal::WriteStructContext::WriteStructContext(FileStorage& _fs,
479     const String& name, int flags, const String& typeName) : fs(&_fs)
480 {
481     cvStartWriteStruct(**fs, !name.empty() ? name.c_str() : 0, flags,
482                        !typeName.empty() ? typeName.c_str() : 0);
483     fs->elname = String();
484     if ((flags & FileNode::TYPE_MASK) == FileNode::SEQ)
485     {
486         fs->state = FileStorage::VALUE_EXPECTED;
487         fs->structs.push_back('[');
488     }
489     else
490     {
491         fs->state = FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP;
492         fs->structs.push_back('{');
493     }
494 }
495
496 internal::WriteStructContext::~WriteStructContext()
497 {
498     cvEndWriteStruct(**fs);
499     fs->structs.pop_back();
500     fs->state = fs->structs.empty() || fs->structs.back() == '{' ?
501         FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP :
502         FileStorage::VALUE_EXPECTED;
503     fs->elname = String();
504 }
505
506
507 void read( const FileNode& node, Mat& mat, const Mat& default_mat )
508 {
509     if( node.empty() )
510     {
511         default_mat.copyTo(mat);
512         return;
513     }
514     void* obj = cvRead((CvFileStorage*)node.fs, (CvFileNode*)*node);
515     if(CV_IS_MAT_HDR_Z(obj))
516     {
517         cvarrToMat(obj).copyTo(mat);
518         cvReleaseMat((CvMat**)&obj);
519     }
520     else if(CV_IS_MATND_HDR(obj))
521     {
522         cvarrToMat(obj).copyTo(mat);
523         cvReleaseMatND((CvMatND**)&obj);
524     }
525     else
526     {
527         cvRelease(&obj);
528         CV_Error(CV_StsBadArg, "Unknown array type");
529     }
530 }
531
532 void read( const FileNode& node, SparseMat& mat, const SparseMat& default_mat )
533 {
534     if( node.empty() )
535     {
536         default_mat.copyTo(mat);
537         return;
538     }
539     Ptr<CvSparseMat> m((CvSparseMat*)cvRead((CvFileStorage*)node.fs, (CvFileNode*)*node));
540     CV_Assert(CV_IS_SPARSE_MAT(m));
541     m->copyToSparseMat(mat);
542 }
543
544 CV_EXPORTS void read(const FileNode& node, KeyPoint& value, const KeyPoint& default_value)
545 {
546     if( node.empty() )
547     {
548         value = default_value;
549         return;
550     }
551     node >> value;
552 }
553
554 CV_EXPORTS void read(const FileNode& node, DMatch& value, const DMatch& default_value)
555 {
556     if( node.empty() )
557     {
558         value = default_value;
559         return;
560     }
561     node >> value;
562 }
563
564 #ifdef CV__LEGACY_PERSISTENCE
565 void write( FileStorage& fs, const String& name, const std::vector<KeyPoint>& vec)
566 {
567     // from template implementation
568     cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ);
569     write(fs, vec);
570 }
571
572 void read(const FileNode& node, std::vector<KeyPoint>& keypoints)
573 {
574     FileNode first_node = *(node.begin());
575     if (first_node.isSeq())
576     {
577         // modern scheme
578 #ifdef OPENCV_TRAITS_ENABLE_DEPRECATED
579         FileNodeIterator it = node.begin();
580         size_t total = (size_t)it.remaining;
581         keypoints.resize(total);
582         for (size_t i = 0; i < total; ++i, ++it)
583         {
584             (*it) >> keypoints[i];
585         }
586 #else
587         FileNodeIterator it = node.begin();
588         it >> keypoints;
589 #endif
590         return;
591     }
592     keypoints.clear();
593     FileNodeIterator it = node.begin(), it_end = node.end();
594     for( ; it != it_end; )
595     {
596         KeyPoint kpt;
597         it >> kpt.pt.x >> kpt.pt.y >> kpt.size >> kpt.angle >> kpt.response >> kpt.octave >> kpt.class_id;
598         keypoints.push_back(kpt);
599     }
600 }
601
602 void write( FileStorage& fs, const String& name, const std::vector<DMatch>& vec)
603 {
604     // from template implementation
605     cv::internal::WriteStructContext ws(fs, name, FileNode::SEQ);
606     write(fs, vec);
607 }
608
609 void read(const FileNode& node, std::vector<DMatch>& matches)
610 {
611     FileNode first_node = *(node.begin());
612     if (first_node.isSeq())
613     {
614         // modern scheme
615 #ifdef OPENCV_TRAITS_ENABLE_DEPRECATED
616         FileNodeIterator it = node.begin();
617         size_t total = (size_t)it.remaining;
618         matches.resize(total);
619         for (size_t i = 0; i < total; ++i, ++it)
620         {
621             (*it) >> matches[i];
622         }
623 #else
624         FileNodeIterator it = node.begin();
625         it >> matches;
626 #endif
627         return;
628     }
629     matches.clear();
630     FileNodeIterator it = node.begin(), it_end = node.end();
631     for( ; it != it_end; )
632     {
633         DMatch m;
634         it >> m.queryIdx >> m.trainIdx >> m.imgIdx >> m.distance;
635         matches.push_back(m);
636     }
637 }
638 #endif
639
640 int FileNode::type() const { return !node ? NONE : (node->tag & TYPE_MASK); }
641 bool FileNode::isNamed() const { return !node ? false : (node->tag & NAMED) != 0; }
642
643 size_t FileNode::size() const
644 {
645     int t = type();
646     return t == MAP ? (size_t)((CvSet*)node->data.map)->active_count :
647         t == SEQ ? (size_t)node->data.seq->total : (size_t)!isNone();
648 }
649
650 void read(const FileNode& node, int& value, int default_value)
651 {
652     value = !node.node ? default_value :
653     CV_NODE_IS_INT(node.node->tag) ? node.node->data.i : std::numeric_limits<int>::max();
654 }
655
656 void read(const FileNode& node, float& value, float default_value)
657 {
658     value = !node.node ? default_value :
659         CV_NODE_IS_INT(node.node->tag) ? (float)node.node->data.i :
660         CV_NODE_IS_REAL(node.node->tag) ? saturate_cast<float>(node.node->data.f) : std::numeric_limits<float>::max();
661 }
662
663 void read(const FileNode& node, double& value, double default_value)
664 {
665     value = !node.node ? default_value :
666         CV_NODE_IS_INT(node.node->tag) ? (double)node.node->data.i :
667         CV_NODE_IS_REAL(node.node->tag) ? node.node->data.f : std::numeric_limits<double>::max();
668 }
669
670 void read(const FileNode& node, String& value, const String& default_value)
671 {
672     value = !node.node ? default_value : CV_NODE_IS_STRING(node.node->tag) ? String(node.node->data.str.ptr) : String();
673 }
674
675 void read(const FileNode& node, std::string& value, const std::string& default_value)
676 {
677     value = !node.node ? default_value : CV_NODE_IS_STRING(node.node->tag) ? std::string(node.node->data.str.ptr) : default_value;
678 }
679
680 } // cv::