首页 > 其他 > 详细

图像处理程序的序列化和反序列化

时间:2019-05-05 20:28:37      阅读:121      评论:0      收藏:0      [点我收藏+]
      所谓序列化,就是讲内存数据保存为磁盘数据的过程,反序列化就是反过来理解。对于图像处理程序来说,最主要的变量是图片,然后还有相关的参数或运算结果。这里区分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, 100true);
        matwrite(fs, image2, 200true);
        matwrite(fs, image3, 300true);
        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

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!