gpt4 book ai didi

c++ - 如何为不同类型的图片设计加载器?

转载 作者:搜寻专家 更新时间:2023-10-31 02:03:03 26 4
gpt4 key购买 nike

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

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

另一个问题是我希望代码更快,使用策略和工厂似乎会大大降低速度,这就是为什么我想放弃它们并只使用模板。但是,这会带来其他问题,因为模板是运行时的,我不确定设计,因为它会要求大量模板特化。

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

糟糕的是我仍然需要使用一些已经在我使用的库中实现的预处理。我可以自己写,但是库已经存在很长时间了,我怀疑我自己写能否获得更好的性能。此外,预处理不是我的最终目标,这就是为什么如果没有必要,我宁愿不自己实现它。

有人对如何设计这个有什么建议吗?或者对我提出的想法有一些反馈?

非常感谢任何帮助!

最佳答案

出于类似的目的,我将数据存储为未压缩的位图,并使用指针数组将其映射为二维像素数组,以便直接访问像素。所以我认为你应该做类似的事情。在某些情况下,我还需要不同的类型。然后保存图像数据的类有一个类型的描述符,通常是 float*DWORD* 以及映射到表示图像的一维数组的二维数组。这会将图像文件编码与其表示分开。由此,您只需要在此表示和文件之间编码/解码的加载器/保存器。

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

//------------------------------------------------------------------------------
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;
}
//------------------------------------------------------------------------------

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

我还被迫在某些二维 vector 数据应用程序上使用不同的解码样式(更复杂的解码样式),这些应用程序从其标题中检测文件格式,因为许多用户通常使用 Windows 文件资源管理器并且经常错误地破坏扩展名并且因为今天的用户不知道文件名和文件扩展名是什么意思,而且他们也倾向于重命名他们设计不应该重命名的内容。

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 字节加载到内存中(大小取决于支持的文件格式),并测试每个支持的文件格式的文件格式签名。第一次成功时使用该文件格式解码文件...

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

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;
}

文件格式的顺序应该仔细设计,因为最慢的解码器或最不常见的解码器应该排在较快的解码器之后。还有一些文件格式没有签名,它们的检测是通过寻找格式错误来完成的。那些应该放在最后。

请注意,这两个加载程序都希望对每种支持的文件格式使用通用数据表示。

关于c++ - 如何为不同类型的图片设计加载器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56090191/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com