本文介绍了如何为不同类型的图像设计加载程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在做一个副项目,该项目对大量图像进行矩阵计算.为此,我使用了几种不同的库(包括libpng,libjpg,libtiff和easybmp)进行加载,或者进行预处理(即灰度或调整大小)并存储图像.这些库中的每一个都以不同的方式存储图像,这就是问题所在.我想使用策略来拥有一个名为Image的基类和多个派生类(即ImagePNG,ImageBMP等),并使用工厂根据我要加载的图像类型实例化一个对象.

I'm working on a side project that does matrix calculations over a large number of images. To achieve this, I'm using several different libraries (including libpng, libjpg, libtiff and easybmp) to load, maybe preprocess (i.e. grayscale or resize), and store the images. Each of these libraries stores the images differently, and this is where the issue occurs. I want to use strategy to have a base class named Image and multiple derived classes (i.e. ImagePNG, ImageBMP, etc.), and factory to instantiate an object depending of the type of the image I want to load.

我想解决这个问题的一种方法是在基类中使用void *或std :: any并将对象存储在该类中.但是,我希望基类内部没有任何对象,而只有纯虚函数,并且我不喜欢使用C ++进行转换.

One way I thought of solving this was by using void* or std::any inside the base class and store the object there. However, I'd prefer if the base class wouldn't have any objects inside it, only pure virtual functions and I'm not a fan of casting in C++.

另一个问题是我希望代码更快,并且使用strategy和factory似乎会大大降低它的速度,这就是为什么我考虑删除它们并仅使用模板的原因.但是,这将带来其他问题,因为模板是运行时,并且我不确定该设计,因为它会要求大量模板专门化.

Another issue is that I want the code to be faster, and using strategy and factory seems like it's going to slow it down drastically, which is why I thought of dropping them and use templates only. However, that would provide other issues, since templates are runtime and I'm not sure about the design since it would ask for a lot of template specialization.

好处是,我需要返回所有图像作为指向uint8_t数组的指针,以便能够处理它们,这意味着每种图像类型只有加载部分可能有所不同.

The good thing is that I need to return all images as a pointer to uint8_t array to be able to process them, which means that only the loading part could be different for each type of the image.

不好的是,我仍然需要使用在使用的库中已经实现的一些预处理.我可以自己编写,但是库已经存在很长时间了,我怀疑自己编写可以提高性能.另外,预处理不是我的最终目标,这就是为什么我宁愿在不需要的情况下自己实现.

The bad thing is that I still need to use some preprocessing that is already implemented in the libraries that I use. I could write it myself, but the libraries are out there for a long time, and I doubt that I could achieve a better performance by writing it myself. Also, the preprocessing isn't my final goal, which is why I'd rather not implement it myself if I don't have to.

有人对此设计有任何建议吗?还是对我提出的想法有一些反馈?

Does anyone have any advice on how to design this? Or some feedback on the ideas that I presented?

非常感谢您的帮助!

推荐答案

出于类似的目的,我将数据存储为未压缩的位图,并带有指针数组,该指针数组将其映射为2D像素数组以进行直接像素访问.所以我认为您应该做类似的事情.在某些情况下,我还需要不同的类型.然后,保存图像数据的类具有通常为 float * DWORD * 类型的描述符,以及映射到表示图像的1D数组的2D数组.这会将图像文件编码与其表示形式分开.由此,您只需要在此表示形式和文件之间进行编码/解码的加载程序/保存程序即可.

For similar purposes I am storing the data as uncompressed bitmap with array of pointers that maps it as 2D pixel array for direct pixel access. So I think you should do something similar. In some cases I need also different types. Then the class holding image data has a descriptor of the type usually float* and DWORD* and 2D array mapped into 1D array that represents the image. This will separate image file encoding from its representation. From this you just need loader/saver that encodes/decodes between this representation and file.

以下是我使用的示例(基于 C ++/VCL ):

Here is example what I use (C++/VCL based):

//------------------------------------------------------------------------------
int picture_load(Graphics::TBitmap *bmp,AnsiString name,int *_alpha)
    {
    if (bmp==NULL)        { _errorlog+="picture_load bmp is NULL\n"; return 0; }
    if (!FileExists(name)){ _errorlog+="picture_load file \""+name+"\" dont exist\n"; return 0; }
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    AnsiString ext=ExtractFileExt(name).LowerCase();
    for(;;)
        {
        if (ext==".bmp")
            {
            bmp->LoadFromFile(name);
            break;
            }
        if (ext==".jpg")
            {
            TJPEGImage *jpg=new TJPEGImage;
            #ifdef _mmap_h
            if (jpg) mmap_new('GL',jpg,sizeof(TJPEGImage));
            #endif
            if (jpg==NULL) { _errorlog+="picture_load not enough memory\n"; return 0; }
            jpg->LoadFromFile(name);
            bmp->Assign(jpg);
            #ifdef _mmap_h
            mmap_del('GL',jpg);
            #endif
            delete jpg;
            break;
            }
        if (ext==".png")
            {
            TPNGObject *png=new TPNGObject;
            #ifdef _mmap_h
            if (png) mmap_new('GL',png,sizeof(TJPEGImage));
            #endif
            if (png==NULL) { _errorlog+="picture_load not enough memory\n"; return 0; }
            png->LoadFromFile(name);
            bmp->Assign(png);
            #ifdef _mmap_h
            mmap_del('GL',png);
            #endif
            delete png;
            break;
            }
        if ((ext==".sgi")||(ext==".rgb"))
            {
            sgi sss;
            sss.load(name);
            bmp->Width=sss.rgba->Width;
            bmp->Height=sss.rgba->Height;
            bmp->Canvas->Draw(0,0,sss.rgba);
            break;
            }
        if (ext==".pcx")
            {
            unsigned int *p,c;
            int     x,y,adr;
            int hnd,siz,l,xs,ys;
            unsigned int pal[256],r,g,b;
            Byte *dat;
            for(;;)
                {
                hnd=FileOpen(name,fmOpenRead);
                if (hnd<0) { _errorlog+="picture_load file \""+name+"\" dont exist\n"; return 0; }
                siz=FileSeek(hnd,0,2);
                FileSeek(hnd,0,0);
                dat=new Byte[siz];
                #ifdef _mmap_h
                if (dat) mmap_new('GL',dat,siz*sizeof(BYTE));
                #endif
                if (dat==NULL) { FileClose(hnd); _errorlog+="picture_load not enough memory\n"; return 0; }
                FileRead(hnd,dat,siz);
                FileClose(hnd);
                adr=siz-3*256;
                for (l=0;l<256;l++)
                    {
                    r=dat[adr]; adr++; r&=255;
                    g=dat[adr]; adr++; g&=255;
                    b=dat[adr]; adr++; b&=255;
                    c=(r<<16)|(g<<8)|(b);
                    c&=0x00FFFFFF;
                    pal[l]=c;
                    }
                xs=int(dat[ 8])-int(dat[4])+((int(dat[ 9])-int(dat[5]))<<8)+1;
                ys=int(dat[10])-int(dat[6])+((int(dat[11])-int(dat[7]))<<8)+1;

                bmp->HandleType=bmDIB;
                bmp->PixelFormat=pf32bit;
                bmp->Width=xs;
                bmp->Height=ys;
                xs=bmp->Width;
                ys=bmp->Height;

                adr=128;
                for (y=0;y<ys;y++)
                    {
                    p=(unsigned int*)bmp->ScanLine[y];
                    for (x=0;x<xs;)
                        {
                        c=dat[adr];
                        if (c<192) l=1;
                        else{
                            l=c&63;
                            adr++;
                            c=dat[adr];
                            }
                        adr++;
                        for (;l>0;l--)
                            {
                            if (x>=xs) break;
                            p[x]=pal[c];
                            x++;
                            }
                        }
                    }
                #ifdef _mmap_h
                mmap_del('GL',dat);
                #endif
                delete[] dat;
                break;
                }
            break;
            }
        if (ext==".dds")
            {
            DDS::load(bmp,name);
            _errorlog+=DDS::_errorlog;
            DDS::_errorlog="";
            break;
            }
        _errorlog+="picture_load unsuported file extension \""+ext+"\"\n";
        return 0;
        }
    bmp->HandleType=bmDIB;
    if (_alpha) _alpha[0]=(bmp->PixelFormat==pf32bit);
    bmp->PixelFormat=pf32bit;
    return 1;
    }
//------------------------------------------------------------------------------
int  picture_save(Graphics::TBitmap *bmp,AnsiString name)
    {
    if (bmp==NULL)        { _errorlog+="picture_load bmp is NULL\n"; return 0; }
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    AnsiString ext=ExtractFileExt(name).LowerCase();
    for(;;)
        {
        if (ext==".bmp")
            {
            bmp->SaveToFile(name);
            break;
            }
        if (ext==".jpg")
            {
            TJPEGImage *jpg=new TJPEGImage;
            #ifdef _mmap_h
            if (jpg) mmap_new('GL',jpg,sizeof(TJPEGImage));
            #endif
            if (jpg==NULL) { _errorlog+="picture_load not enough memory\n"; return 0; }
            jpg->Assign(bmp);
            jpg->SaveToFile(name);
            #ifdef _mmap_h
            mmap_del('GL',jpg);
            #endif
            delete jpg;
            break;
            }
        if (ext==".png")
            {
            TPNGObject *png=new TPNGObject;
            #ifdef _mmap_h
            if (png) mmap_new('GL',png,sizeof(TJPEGImage));
            #endif
            if (png==NULL) { _errorlog+="picture_load not enough memory\n"; return 0; }
            png->Assign(bmp);
            png->SaveToFile(name);
            #ifdef _mmap_h
            mmap_del('GL',png);
            #endif
            delete png;
            break;
            }
        _errorlog+="picture_load unsuported file extension \""+ext+"\"\n";
        return 0;
        }
    return 1;
    }
//------------------------------------------------------------------------------

所以我根据文件名扩展名决定格式,并使用适当的object/lib/code进行编码/解码...

So I decide the format based on the filename extention and use appropriate object/lib/code for encoding/decoding ...

在某些应用程序上,我还被迫对2D矢量数据使用不同的解码方式(更复杂的一种),以便从其标头中检测文件格式,因为许多用户通常使用Windows File Explorer,并且经常错误地破坏扩展名,并且因为今天的用户不知道文件名和文件扩展名的含义,而且他们还倾向于重命名他们本来不应该设计的名称.

I was also forced to use different decoding style (more complicated one) on some applications for 2D vector data that detects the file format from its headers because many users are using usually Windows File Explorer and very often corrupt the extensions by mistake and as todays users do not know what filename and file extentions means and they also tend to rename what they should not by design.

bool decode_interface_class::load(AnsiString name)
    {
    int hnd=-1;
    int siz=0,siz0=0;
    BYTE *dat=NULL;
    reset();
    #ifdef decode_interface_log
    decode_id.num=0;
    decode_log="";
    #endif
    decode_cfg =true;
    decode_col =true;
    decode_tool=true;
    decode_ext=ExtractFileExt(name).LowerCase();
    decoded_ext=".";
    decoded_info="";

    decode_emf emf;
    decode_wmf wmf;
    decode_dkr dkr;
    decode_dk3 dk3;
    decode_box box;
    decode_bxl bxl;
    decode_dxf dxf;
    decode_svg svg;
    decode_v2x v2x;
    decode_v2d v2d;

    const int _size=4096;
    BYTE head[_size];
    #ifdef decode_interface_log
    siz=0;  // find minimal size
    if (siz<_decode_emf_hdr) siz=_decode_emf_hdr;
    if (siz<_decode_wmf_hdr) siz=_decode_wmf_hdr;
    if (siz<_decode_dkr_hdr) siz=_decode_dkr_hdr;
    if (siz<_decode_dk3_hdr) siz=_decode_dk3_hdr;
    if (siz<_decode_box_hdr) siz=_decode_box_hdr;
    if (siz<_decode_bxl_hdr) siz=_decode_bxl_hdr;
    if (siz<_decode_dxf_hdr) siz=_decode_dxf_hdr;
    if (siz<_decode_svg_hdr) siz=_decode_svg_hdr;
    if (siz<_decode_v2x_hdr) siz=_decode_v2x_hdr;
    if (siz<_decode_v2d_hdr) siz=_decode_v2d_hdr;
    if (siz>_size)
        {
        decode_log+="Decoding header size too small needed to be "+AnsiString(siz)+" Bytes.\r\n";
        }
    #endif


    hnd=FileOpen(name,fmOpenRead);
    if (hnd<0)
        {
        #ifdef decode_interface_log
        decode_log+="File "+name+" not found.\r\n";
        #endif
        return false;
        }
    siz=FileSeek(hnd,0,2);
        FileSeek(hnd,0,0);
    dat=new BYTE[siz];
    if (dat==NULL)
        {
        #ifdef decode_interface_log
        decode_log+="Not enough memory need: "+AnsiString(siz)+" Bytes.\r\n";
        #endif
        FileClose(hnd);
        return false;
        }
    siz0=siz;
    siz=FileRead(hnd,dat,siz);
    FileClose(hnd);
    if (siz!=siz0)
        {
        #ifdef decode_interface_log
        decode_log+="Disc drive or file system error.\r\n";
        #endif
        }

    // file signature detection
    for (int i=0;i<_size;i++) if (i<siz) head[i]=dat[i]; else head[i]=0;
         if (emf.is_header(head,_size,siz)) { decoded_ext=_decode_emf_ext; emf.load(this[0],dat,siz); }
    else if (wmf.is_header(head,_size,siz)) { decoded_ext=_decode_wmf_ext; wmf.load(this[0],dat,siz); }
    else if (dkr.is_header(head,_size,siz)) { decoded_ext=_decode_dkr_ext; dkr.load(this[0],dat,siz); }
    else if (dk3.is_header(head,_size,siz)) { decoded_ext=_decode_dk3_ext; dk3.load(this[0],dat,siz); }
    else if (box.is_header(head,_size,siz)) { decoded_ext=_decode_box_ext; box.load(this[0],dat,siz); }
    else if (bxl.is_header(head,_size,siz)) { decoded_ext=_decode_bxl_ext; bxl.load(this[0],dat,siz); }
    else if (dxf.is_header(head,_size,siz)) { decoded_ext=_decode_dxf_ext; dxf.load(this[0],dat,siz); }     // toto koli rychlost ku koncu (hlada string)
    else if (svg.is_header(head,_size,siz)) { decoded_ext=_decode_svg_ext; svg.load(this[0],dat,siz); }     // toto koli rychlost ku koncu (hlada string)
    else if (v2x.is_header(head,_size,siz)) { decoded_ext=_decode_v2x_ext; v2x.load(this[0],dat,siz); }     // toto az na konci pre bezpecnost (nema signaturu)
    else if (v2d.is_header(head,_size,siz)) { decoded_ext=_decode_v2d_ext; v2d.load(this[0],dat,siz); }     // toto az na konci pre bezpecnost (nema signaturu)
    // if fail use file extension
    else if (decode_ext==_decode_emf_ext)   { decoded_ext=_decode_emf_ext; emf.load(this[0],dat,siz); decoded_info="*"+decoded_info; }
    else if (decode_ext==_decode_wmf_ext)   { decoded_ext=_decode_wmf_ext; wmf.load(this[0],dat,siz); decoded_info="*"+decoded_info; }
    else if (decode_ext==_decode_dkr_ext)   { decoded_ext=_decode_dkr_ext; dkr.load(this[0],dat,siz); decoded_info="*"+decoded_info; }
    else if (decode_ext==_decode_dk3_ext)   { decoded_ext=_decode_dk3_ext; dk3.load(this[0],dat,siz); decoded_info="*"+decoded_info; }
    else if (decode_ext==_decode_box_ext)   { decoded_ext=_decode_box_ext; box.load(this[0],dat,siz); decoded_info="*"+decoded_info; }
    else if (decode_ext==_decode_bxl_ext)   { decoded_ext=_decode_bxl_ext; bxl.load(this[0],dat,siz); decoded_info="*"+decoded_info; }
    else if (decode_ext==_decode_dxf_ext)   { decoded_ext=_decode_dxf_ext; dxf.load(this[0],dat,siz); decoded_info="*"+decoded_info; }
    else if (decode_ext==_decode_svg_ext)   { decoded_ext=_decode_svg_ext; svg.load(this[0],dat,siz); decoded_info="*"+decoded_info; }
    else if (decode_ext==_decode_v2x_ext)   { decoded_ext=_decode_v2x_ext; v2x.load(this[0],dat,siz); decoded_info="*"+decoded_info; }
    else if (decode_ext==_decode_v2d_ext)   { decoded_ext=_decode_v2d_ext; v2d.load(this[0],dat,siz); decoded_info="*"+decoded_info; }
    // if fail then error
    else{
        #ifdef decode_interface_log
        decode_log+="File "+name+" not recognized.\r\n";
        #endif
        }
    if (decode_cfg)
        {
        if (!decode_col )
            {
            if (decode_tool) set_cfgs  (dk3_charaktool ,33);
                             set_colors(dk3_charakcolor,33);
            }
        if (!decode_tool)    set_tools (dk3_charaktool ,33);
        }
    #ifdef decode_interface_log
    if (decode_ext!=decoded_ext)
        decode_log+="Wrong file extension in "+name+" should be \""+decoded_ext+"\"\r\n";
    hnd=FileCreate(ExtractFilePath(Application->ExeName)+"svg_decode.log");
    FileWrite(hnd,decode_log.c_str(),decode_log.Length());
    FileClose(hnd);
    #endif
    compute();
    compute_objsize();
    if (dat) delete[] dat;
    return true;
    }

因此,我首先将最多4096字节加载到内存中(大小取决于支持的文件格式),并测试每种支持的文件格式的文件格式签名.首次成功使用该文件格式对文件进行解码...

So I load first up to 4096 Bytes into memory (the size depends on supported fileformats), and test for fileformat signature of each supported fileformat. On first success use that fileformat to decode the file...

每种受支持的文件类型都需要具有在此处 WMF 示例检测其签名的功能:

Each supported filetype need to have function that detects its signature here WMF example:

bool decode_wmf::is_header(BYTE *head,DWORD size,DWORD filesize)
    {
    dst=NULL;
    if (size<_decode_wmf_hdr) return 0;
    if (((DWORD*)(head+0))[0]==0x9AC6CDD7) return 1;    // placeable wmf
    WORD *dw=(WORD*)head,a;
    a=dw[0];    // type mem/file
    if ((a!=0)&&(a!=1)) return 0;
    a=dw[1];    // header size
    if (a!=9) return 0;
//  a=dw[2];    // version
//  if (a!=) return 0;
    return 1;
    }

文件格式的顺序应谨慎设计,因为最慢的解码器或最不常用的解码器应追随较快的解码器.另外,某些文件格式没有签名,它们的检测是通过查找格式错误来完成的.那些应该走到最后.

The order of the fileformats should be carefully designed as the slowest decoders or the least common ones should go after the faster ones. Also some fileformats does not have signature and their detection is done by looking for format bug. Those should go last.

请注意,两个加载器都希望每种支持的文件格式具有通用的数据表示形式.

Beware both of the loaders expects common data representation for every supported fileformat.

这篇关于如何为不同类型的图像设计加载程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-27 15:50