博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
图像处理程序的序列化和反序列化
阅读量:5107 次
发布时间:2019-06-13

本文共 11199 字,大约阅读时间需要 37 分钟。

      所谓序列化,就是讲内存数据保存为磁盘数据的过程,反序列化就是反过来理解。对于图像处理程序来说,最主要的变量是图片,然后还有相关的参数或运算结果。这里区分4个部分、由简单到复杂,分享一下自己的研究成果,希望能够给需要的工程师提供一些帮助。
  
一、基本操作
        OpenCV本身提供了FileStorage的序列化保存方法,这对于保存参数来说非常适合;但是如果用来保存图片,会将原始图片的体积多倍增大,速度也比较慢。Mfc本身也提供了序列化的操作,但是使用起来的话,需要注意的地方比较多,比不上OpenCV来的直接。
        我们最终想要通过保存得到,并且能够被图像处理程序读取的,是一个单一的文件。这个文件不仅包含了图片数据,而且包括相关的参数和运算结果,同时这个文件不能太大。所以我想到采用zip压缩/解压的方式来打包原始图片和运算结果。实验证明,效果是能够符合要求的。
        在打包代码的选择上,找到了比较好的实现。zip.c++/unzip.c++中提供了稳定并且便于使用的压缩解压过程(具体使用参考对应的.h文件,压缩文件可以设定密码)。实际使用中,保存的时候参数保存为.xml文件,图片保存为.jpg图片,而后统一压缩成.go文件;读取的时候反过来操作。
        为了说明问题,编写例程。现在把使用说明一下,具体细节可以参考代码。
1、点击读取图片,可以读入jpg或bmp图片,同时手工设置参数一到三
2、点击保存,保存为.go文件
3、点击打开,打开相应的.go文件,同时解压缩后,图片和参数分别显示出来。
        本例程主要展现的是“图像处理程序的序列化和反序列化”,而后结合实际使用过程中发现的问题进行衍生。希望能够有类似需求的工程师提供一些帮助。
    
主要代码:
//保存序列化结果
void
CGOsaveView
:
:
OnButtonSave()
{
    CString str1;string s1;
    CString str2;string s2;
    CString str3;string s3;
    CString szFilters
=
_T(
"go(*.go)|*.go|*(*.*)|*.*||"
);
    CString FilePathName
=
""
;
    CFileDialog dlg(FALSE,NULL,NULL,
0
,szFilters,
this
);
   
if
(dlg.DoModal()
==
IDOK){
        FilePathName
=
dlg.GetPathName();
    }   
   
if
(m_fimage.rows
<
=
0
)
    {
        AfxMessageBox(
"m_fimage为空!"
);
       
return
;
    }
   
    GetDlgItemText(IDC_EDIT1,str1);
    GetDlgItemText(IDC_EDIT2,str2);
    GetDlgItemText(IDC_EDIT3,str3);
    s1
=
str1.GetBuffer(
0
);
    s2
=
str2.GetBuffer(
0
);
    s3
=
str3.GetBuffer(
0
);
    string filename
=
"params.xml"
;
    FileStorage fs(filename, FileStorage
:
:
WRITE);
    fs
<<
"str1"
<<
s1;
    fs
<<
"str2"
<<
s2;
    fs
<<
"str3"
<<
s3;
    fs.release();
    imwrite(
"m_fimage.jpg"
,m_fimage);
    AfxMessageBox(
"数据保存成功!"
);
    HZIP hz
=
CreateZip(FilePathName,
"GreenOpen"
);
//可以设定密码
    ZipAdd(hz,
"params.xml"
,
"params.xml"
);
    ZipAdd(hz,
"m_fimage.jpg"
,
"m_fimage.jpg"
);
    CloseZip(hz);
    AfxMessageBox(
"数据压缩成功!"
);
  
}
//打开序列化结果
void
CGOsaveView
:
:
OnButtonOpen()
{
    string s1;
    string s2;
    string s3;
    CString szFilters
=
_T(
"*(*.*)|*.*|go(*.go)|*.go||"
);
    CString FilePathName
=
""
;
    CFileDialog dlg(TRUE,NULL,NULL,
0
,szFilters,
this
);
   
if
(dlg.DoModal()
==
IDOK){
        FilePathName
=
dlg.GetPathName();
    }   
    HZIP hz
=
OpenZip(FilePathName,
"GreenOpen"
);
    ZIPENTRY ze; GetZipItem(hz,
-
1
,
&
ze);
int
numitems
=
ze.index;
   
if
(numitems
<
=
0
)
    {
        AfxMessageBox(
"文件读取错误!"
);
       
return
;
    }
   
for
(
int
i
=
0
; i
<
numitems; i
++
)
    { 
        GetZipItem(hz,i,
&
ze);
        UnzipItem(hz,i,ze.name);
    }
    CloseZip(hz);
    AfxMessageBox(
"数据解压缩成功"
);
    m_fimage
=
imread(
"m_fimage.jpg"
);
   
if
(m_fimage.rows
<
=
0
)
    {
        AfxMessageBox(
"文件读取错误!"
);
       
return
;
    }
    string filename
=
"params.xml"
;
    FileStorage fs(filename, FileStorage
:
:
READ);
    fs[
"str1"
]
>>
s1;
    fs[
"str2"
]
>>
s2;
    fs[
"str3"
]
>>
s3;
    SetDlgItemText(IDC_EDIT1,s1.c_str());
    SetDlgItemText(IDC_EDIT2,s2.c_str());
    SetDlgItemText(IDC_EDIT3,s3.c_str());
    AfxMessageBox(
"数据反序列成功"
);
    SOURSHOW;
}
 
    
    我们需要注意到的是这里的Mat是可以直接序列化的,这种方法对于存储OpenCV一类的变量来说,非常方便。但是如果是自己设定的结构体了?
二、存储自己的结构体
    
    
这里给出一个新的例子,值得参考:
//另存当前模板数据
BOOL CGOImageShopDoc
:
:OutPutElementItems(string filename)
{
    FileStorage fs(filename, FileStorage
:
:WRITE);
    
具体写下内容,注意OpenCV支持Rect等基础结构的序列化
    
int iElementStruct 
= m_rctTracker.size();
//数量
    fs 
<< 
"iElementStruct" 
<< iElementStruct;
    
//按照openCV推荐的方法来写入和读取数据。
    fs 
<< 
"ElementContent" 
<< 
"[";
    
for (
int i 
= 
0; i 
< iElementStruct; i
++)
    {
        string strName(CW2A(m_rctTracker[i].name.GetString()));
        string strTypeName(CW2A(m_rctTracker[i].typeName.GetString()));
        
int iLeft 
= m_rctTracker[i].AreaTracker.m_rect.left;
        
int iTop 
= m_rctTracker[i].AreaTracker.m_rect.top;
        
int iWidth 
= m_rctTracker[i].AreaTracker.m_rect.Width();
        
int iHeight 
= m_rctTracker[i].AreaTracker.m_rect.Height();
         fs
<<
"{:"
<<
"strName"
<<strName
<<
"strTypeName"
<<strTypeName
<<
"rectLeft"
<<iLeft
<<
"rectTop"
<<iTop
<<
"rectWidth"
<<iWidth
<<
"rectHeight"
<<iHeight
<<
"}";    
    }
    fs 
<< 
"]";
    
书写内容结束
    fs.release();
    
return TRUE;
}
 
//读取模板书
BOOL CGOImageShopDoc
:
:ReadElementsItems(string filename)
{
    
//读取数据
    FileStorage fs(filename, FileStorage
:
:READ);
    
if (fs.isOpened())
    {
        
//清空现有数据
        m_rctTracker.clear();
        
//具体业务
        
int iElementStruct 
= 
-
1;
        Rect rect;
        fs[
"iElementStruct"
>> iElementStruct;
        cv
:
:FileNode features 
= fs[
"ElementContent"];
        cv
:
:FileNodeIterator it 
= features.begin(), it_end 
= features.end();
        
int idx 
= 
0;
        
for (; it 
!= it_end; 
++it, idx
++)
        {
            string strName;string strTypeName;
            
int iLeft;
            
int iTop;
            
int iWidth;
            
int iHeight;
            strName 
= (string)(
*it)[
"strName"]; 
//获得strName
            strTypeName
=(string)(
*it)[
"strTypeName"];
            iLeft 
= (
int)(
*it)[
"rectLeft"];
            iTop 
= (
int)(
*it)[
"rectTop"];
            iWidth 
= (
int)(
*it)[
"rectWidth"];
            iHeight 
= (
int)(
*it)[
"rectHeight"];
            CRect rect 
= CRect(iLeft, iTop, iLeft
+iWidth, iTop
+iHeight);
//获得rect
            
//生成识别区域
            Mat matROI 
= m_imageRaw(Rect(iLeft,iTop,iWidth,iHeight));
            vector
<CRect
> vecFindRect ;
            
if (strTypeName 
== 
"定位")
            {
                vecFindRect 
= findRect(matROI);
            }
           ……
        }
    }
    fs.release();
    
    
return TRUE;
}
如果我们打开这里保存的文件,可以发现这种模式:
%YAML
:
1.
0
--
-
iElementStruct
:
15
ElementContent
:
   
- { strName
:
"定位", rectLeft
:
37, rectTop
:
73, rectWidth
:
241,
       rectHeight
:
120 }
   
- { strName
:
"定位", rectLeft
:
1556, rectTop
:
107, rectWidth
:
130,
       rectHeight
:
70 }
   
- { strName
:
"定位", rectLeft
:
3127, rectTop
:
99, rectWidth
:
93,
       rectHeight
:
70 }
   
- { strName
:
"定位", rectLeft
:
19, rectTop
:
2187, rectWidth
:
95,
       rectHeight
:
77 }
   
- { strName
:
"定位", rectLeft
:
1592, rectTop
:
2203, rectWidth
:
95,
       rectHeight
:
44 }
   
- { strName
:
"定位", rectLeft
:
3151, rectTop
:
2184, rectWidth
:
84,
       rectHeight
:
68 }
   
- { strName
:
"考号", rectLeft
:
1042, rectTop
:
419, rectWidth
:
300,
       rectHeight
:
121 }
   
- { strName
:
"主观分数", rectLeft
:
161, rectTop
:
678, rectWidth
:
929,
       rectHeight
:
63 }
   
- { strName
:
"主观分数", rectLeft
:
1789, rectTop
:
203, rectWidth
:
869,
       rectHeight
:
76 }
   
- { strName
:
"主观分数", rectLeft
:
1777, rectTop
:
717, rectWidth
:
868,
       rectHeight
:
64 }
   
- { strName
:
"主观分数", rectLeft
:
1785, rectTop
:
1713, rectWidth
:
388,
       rectHeight
:
66 }
   
- { strName
:
"主观题", rectLeft
:
76, rectTop
:
825, rectWidth
:
1450,
       rectHeight
:
1246 }
   
- { strName
:
"主观题", rectLeft
:
1692, rectTop
:
367, rectWidth
:
1524,
       rectHeight
:
323 }
   
- { strName
:
"主观题", rectLeft
:
1696, rectTop
:
864, rectWidth
:
1518,
       rectHeight
:
749 }
   
- { strName
:
"主观题", rectLeft
:
1696, rectTop
:
1787, rectWidth
:
1534,
       rectHeight
:
307 }
 
那么,这种方式是OpenCV支持的结构保存方式,每一个
- { strName
:
"主观题", rectLeft
:
1696, rectTop
:
1787, rectWidth
:
1534,
       rectHeight
:
307 }
是一个可以存储读取的结构。
 
三、FileNode支持哪些结构
    
    
在这个例子中,我们非常丑陋地使用了4个int值来定义一个Rect。为什么不能直接定义?
    
    比如编写代码
    
string filename 
=
 
"序列化.yml"
;
    FileStorage fs(filename, FileStorage
:
:WRITE);
    fs 
<< 
"str1" 
<<
1;
    cv
:
:Rect cvRect(
10,
10,
10,
10);
    fs
<<
"cvRect"
<<cvRect;
    fs.release();
    
return 
0;
    
    生成这样的结果:
%YAML
:
1.
0
--
-
str1
:
1
cvRect
: [
10,
10,
10,
10 ]
 
但是,如果我们读取这个Rect,并且编写这样的代码
则会报错:
    
    
为了进一步解析这个问题,翻看OpenCV的代码:
class CV_EXPORTS_W_SIMPLE FileNode
{
public
:
   
//! type of the file storage node
   
enum Type
    {
        NONE      
=
0,
//!< empty node
        INT      
=
1,
//!< an integer
        REAL      
=
2,
//!< floating-point number
        FLOAT    
= REAL,
//!< synonym or REAL
        STR      
=
3,
//!< text string in UTF-8 encoding
        STRING    
= STR,
//!< synonym for STR
        REF      
=
4,
//!< integer of size size_t. Typically used for storing complex dynamic structures where some elements reference the others
        SEQ      
=
5,
//!< sequence
        MAP      
=
6,
//!< mapping
        TYPE_MASK
=
7,
        FLOW      
=
8,  
//!< compact representation of a sequence or mapping. Used only by YAML writer
        USER      
=
16,
//!< a registered object (e.g. a matrix)
        EMPTY    
=
32,
//!< empty structure (sequence or mapping)
        NAMED    
=
64  
//!< the node has a name (i.e. it is element of a mapping)
    };
    
    那么的确是不可能直接转换为所有的OpenCV类型,这里只是保存为了其他节点的序列,通过代码测试也的确是这样。
 
    
    
    
在这种情况下,我们可以首先将序列读入vector中,非常有用。
而后再根据实际情况进行封装。
    
    
四、更进一步,进行类封装
    
    如果想更进一步,自然需要采用类的方法,这里是一个很好的例子。
#
include 
<opencv2\opencv.hpp
>
#
include 
"opencv2/imgproc/imgproc.hpp"
#
include 
"opencv2/highgui/highgui.hpp"
#
include 
"opencv2/core/core.hpp"
#
include 
<iostream
>
#
include 
<fstream
>
 
using 
namespace std;
using 
namespace cv;
 
class ColletorMat
{
private
:
    
int indexFrame;
    
bool found;
    Mat frame;
 
public
:
 
    ColletorMat(
int index, 
bool found, Mat frame)
    {
        
this
-
>indexFrame 
= index;
        
this
-
>found 
= found;
        
this
-
>frame 
= frame;
    }
 
    
~ColletorMat()
    {
 
    }
 
    
// settors
    
void set_indexFrame(
int index)
    {
        
this
-
>indexFrame 
= index;
    }
 
    
void set_found(
bool found)
    {
        
this
-
>found 
= found;
    }
 
    
void set_frame(Mat frame)
    {
        
this
-
>frame 
= frame;
    }
 
    
// accessors
    
int get_indexFrame()
    {
        
return 
this
-
>indexFrame;
    }
 
    
bool get_found()
    {
        
return 
this
-
>found;
    }
 
    Mat get_frame()
    {
        
return 
this
-
>frame;
    }
 
};
 
void matwrite(ofstream
& fs, 
const Mat
& mat, 
int index, 
bool checking)
{
    
// Data Object
    
int indexFrame 
= index;
    
bool found 
= checking;
    fs.write((
char
*)
&indexFrame, 
sizeof(
int));    
// indexFrame
    fs.write((
char
*)
&found, 
sizeof(
bool));    
// bool checking
 
    
// Header
    
int type 
= mat.type();
    
int channels 
= mat.channels();
    fs.write((
char
*)
&mat.rows, 
sizeof(
int));    
// rows
    fs.write((
char
*)
&mat.cols, 
sizeof(
int));    
// cols
    fs.write((
char
*)
&type, 
sizeof(
int));        
// type
    fs.write((
char
*)
&channels, 
sizeof(
int));    
// channels
 
    
// Data
    
if (mat.isContinuous())
    {
        fs.write(mat.ptr
<
char
>(
0), (mat.dataend 
- mat.datastart));
    }
    
else
    {
        
int rowsz 
= CV_ELEM_SIZE(type) 
* mat.cols;
        
for (
int r 
= 
0; r 
< mat.rows; 
++r)
        {
            fs.write(mat.ptr
<
char
>(r), rowsz);
        }
    }
}
 
ColletorMat matread(ifstream
& fs)
{
    
// Data Object
    
int indexFrame;
    
bool found;
    fs.read((
char
*)
&indexFrame, 
sizeof(
int));     
//
    fs.read((
char
*)
&found, 
sizeof(
bool));         
//
 
    
// Header
    
int rows, cols, type, channels;
    fs.read((
char
*)
&rows, 
sizeof(
int));         
// rows
    fs.read((
char
*)
&cols, 
sizeof(
int));         
// cols
    fs.read((
char
*)
&type, 
sizeof(
int));         
// type
    fs.read((
char
*)
&channels, 
sizeof(
int));     
// channels
 
    
// Data
    Mat mat(rows, cols, type);
    fs.read((
char
*)mat.data, CV_ELEM_SIZE(type) 
* rows 
* cols);
 
    ColletorMat ojbectMat(indexFrame, found, mat);
    
return ojbectMat;
}
 
int main()
{
    
// Save the random generated data
    {
        Mat image1, image2, image3;
        image1 
= imread(
"C:\\opencvVid\\data_seq\\Human3\\0001.jpg");
        image2 
= imread(
"C:\\opencvVid\\data_seq\\Human3\\0002.jpg");
        image3 
= imread(
"C:\\opencvVid\\data_seq\\Human3\\0003.jpg");
 
        
if (image1.empty() 
|| image2.empty() 
|| image3.empty()) {
            std
:
:cout 
<< 
"error: image not readed from file\n";
            
return(
0);
        }
 
        imshow(
"M1",image1);
        imshow(
"M2",image2);
        imshow(
"M3",image3);
 
        (
char)cvWaitKey(
0);
 
        ofstream fs(
"azdoudYoussef.bin", fstream
:
:binary);
        matwrite(fs, image1, 
100
true);
        matwrite(fs, image2, 
200
true);
        matwrite(fs, image3, 
300
true);
        fs.close();
 
        
double tic 
= 
double(getTickCount());
        ifstream loadFs(
"azdoudYoussef.bin", ios
:
:binary);
 
        
if(
!loadFs.is_open()){
            cout 
<< 
"error while opening the binary file" 
<< endl;
        }
 
        ColletorMat lcolletorMat1 
= matread(loadFs);
        ColletorMat lcolletorMat2 
= matread(loadFs);
        ColletorMat lcolletorMat3 
= matread(loadFs);
 
        cout 
<< 
"frames loaded up " 
<< endl;
 
        vector
<ColletorMat
> setFrames;
        setFrames.push_back(lcolletorMat1);
        setFrames.push_back(lcolletorMat2);
        setFrames.push_back(lcolletorMat3);
 
        imshow(
"1", lcolletorMat1.get_frame());
        imshow(
"2", lcolletorMat2.get_frame());
        imshow(
"3", lcolletorMat3.get_frame());
        (
char)cvWaitKey(
0);
 
        cout 
<< 
"indexFrame" 
<<lcolletorMat1.get_indexFrame() 
<< 
"found" 
<< lcolletorMat1.get_found();
        
double toc 
= (
double(getTickCount()) 
- tic) 
* 
1000
/ getTickFrequency();
        cout 
<< 
"Using Raw: " 
<< toc 
<< endl;
        loadFs.close();
 
    }
    
return 
0;
}

 

转载于:https://www.cnblogs.com/jsxyhelu/p/10815852.html

你可能感兴趣的文章
Android打包key密码丢失找回
查看>>
VC6.0调试技巧(一)(转)
查看>>
类库与框架,强类型与弱类型的闲聊
查看>>
webView添加头视图
查看>>
php match_model的简单使用
查看>>
在NT中直接访问物理内存
查看>>
Intel HEX 文件格式
查看>>
SIP服务器性能测试工具SIPp使用指导(转)
查看>>
php_扑克类
查看>>
回调没用,加上iframe提交表单
查看>>
(安卓)一般安卓开始界面 Loding 跳转 实例 ---亲测!
查看>>
Mysql 索引优化 - 1
查看>>
LeetCode(3) || Median of Two Sorted Arrays
查看>>
大话文本检测经典模型:EAST
查看>>
待整理
查看>>
一次动态sql查询订单数据的设计
查看>>
C# 类(10) 抽象类.
查看>>
Nginx+Keepalived 实现双击热备及负载均衡
查看>>
Vue_(组件通讯)子组件向父组件传值
查看>>
jvm参数
查看>>