CImg库中CImg,CImgList,CImgDisplay三个类的介绍

本文介绍如何将CImg库的功能与Windows图形用户界面相结合,包括CImg库中的CImg、CImgList、CImgDisplay三个核心类的使用方法,并提供了一种将CImg图像显示到指定HDC设备上下文的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:http://www.cppprog.com/2009/0426/108.html

本文简单介绍了CImg库中的三个大类:CImg,CImgList,CImgDisplay。然后给出了让CImg在HDC上绘图以及与HBITMAP互换的方法,为部署CImg到WindowsGUI程序中提供了基本支持。

上回介绍了CImg模板类的一些函数,象我这种不在图像处理行业混的人来说很多术语实在是太专业了-_-,不理不理,看不懂就直接写测试代码看它们的作用是什么不就知道啦~~嘿嘿^_^。

上测试代码先:

  1. #include"CImg.h"
  2. usingnamespacecimg_library;
  3. intmain()
  4. {
  5. CImg<unsignedchar>src("test.bmp");
  6. //设置原图大小,貌似haar计算要求图像宽高是4的倍数
  7. src.resize(src.width-src.width%4,src.height-src.height%4);
  8. CImgList<unsignedchar>visu;
  9. visu
  10. <<src.get_crop(0,0,src.width/2,src.height/2)
  11. <<src.get_quantize(5)
  12. <<src.get_rotate(45,1)
  13. <<src.get_permute_axes("yxzv")
  14. <<src.get_erode(5)
  15. <<src.get_haar()
  16. <<src.get_dilate(3)
  17. <<src.get_blur(3)
  18. <<src.get_noise(3)
  19. <<src.get_deriche(3)
  20. <<src.get_blur_anisotropic(8)
  21. <<src.get_blur_bilateral(1,2)
  22. <<src.get_blur_patch(4,3)
  23. <<src.get_sharpen(3)
  24. <<src.get_blur_median(3)
  25. ;//如果愿意可以测试更多CImg的图像处理方法
  26. //用来显示效果
  27. CImgDisplaydisp(src.width*2,src.height);
  28. inti=0;
  29. unsignedchartextcolor[]={255,255,255};
  30. while(!disp.is_closed&&!disp.is_keyQ&&!disp.is_keyESC)
  31. {
  32. i=i%visu.size;
  33. charbuf[20];
  34. ::sprintf(buf,"img:%d",i);
  35. //显示效果,(CImg<<CImg)会生成一个新的CImgList
  36. //左边是原图,右边是处理图,外加写了个序号在上面以便区别
  37. disp.display(src<<(+visu[i]).draw_text(0,0,buf,textcolor)).wait();
  38. //按方向键下则显示下一个
  39. if(disp.is_keyARROWDOWN)i++;
  40. //方向键上则显示上一个
  41. if(disp.is_keyARROWUP)
  42. {
  43. i--;
  44. if(i<0)i=visu.size-1;
  45. }
  46. }
  47. return0;
  48. }

这个例子用到了CImgCImgListCImgDisplay三个类。

CImg类前面已有介绍。

CImgList是CImg的容器,用来保存一组CImg,主要方法有:

  1. CImgList<T>&remove(constunsignedintpos)//删除指定位置
  2. CImgList<T>&pop_back()//删除最后一个
  3. CImgList<T>&pop_front()//删除前端
  4. CImgList<T>&push_back(constCImg<t>&img)//从后面添加
  5. CImgList<T>&push_front(constCImg<t>&img)//从前面添加
  6. CImgList<T>&insert(constCImg<t>&img,constunsignedintpos)//插入到指定位置之前
  7. CImgList<T>&clear()//清空
  8. CImg<T>&operator[](constunsignedintpos)//取指定位置的图像

上面这些是它作为容器的基本功能,同时它也重载了一些操作符以便于使用,比如本例中的"<<"操作其实就是push_back方法。另外,它还有大量的运算功能用于给容器中的图像批量运算。最后,还有一些好玩的方法不可错过:从视频中载入或把图像保存到视频中:

  1. CImgList<T>&load_ffmpeg(constchar*constfilename,
  2. constunsignedintfirst_frame=0,
  3. constunsignedintlast_frame=~0U,
  4. constunsignedintstep_frame=1,
  5. constboolpixel_format=true,
  6. constboolresume=false)
  7. constCImgList<T>&save_ffmpeg(
  8. constchar*constfilename,
  9. constunsignedintfirst_frame=0,
  10. constunsignedintlast_frame=~0U,
  11. constunsignedintfps=25)

这两个方法要求链接ffmpeg库,如果没有这个库文件,还可以使用load_ffmpeg_externalsave_ffmpeg_external方法调用已外部安装的ffmpeg程序编解码。

CImgDisplay类是一个窗口类,它主要用来显示CImg和CImgList。一般使用它的流程是:

  1. 新建CImgDisplay对象
  2. 设置它的大小,除直接输入宽高外也能用直接用CImg、CImgList或另一个CImgDisplay对象作为调整大小的依据。这时,CImgDisplay对象内部已经建立了一个窗口了。
  3. 使用display方法显示图像
  4. 使用wait方法等待事件发生(键盘、鼠标、超时等)
  5. 检查is_keyXXXX、is_closed、button、wheel等成员变量确定是什么事件,再决定我们该做什么操作。
  6. 如果需要,循环回第三步
  7. 析构时窗口收回。

在本例中,如果窗体关闭或按了Q键或按了ESC键则退出循环,程序结束。或者显示由原图和处理后的图组成的CImgList图像,如果按了上下方向键,则改变当前显示的处理图。

这是本例运行时的截图:
/
水墨画风格的《清明上河图》

CImg的图像处理方法绝不止上面例子中写的那么一点点,如果都写上去的话光运算就得等很长的时间,内存资源就更不用说了。所以建议大家一批批地写上去试验。或者可以修改一下代码,用逐次运算来试验效果。当然,如果你是图像处理领域的大牛,看方法名应试就知道是干啥的了,上面的代码就当熟悉一下CImg库吧。

在Windows里使用CImg

现在,我们已经可以使用CImg的现成方法做一些图像处理了。不过,有一个大问题需要解决,怎样把CImg的强大功能和我们的GUI程序相结合呢?我们总不能只使用CImgDisplay作为最终产品的显示界面吧?我们急需一个把CImg显示到指定HDC上的方法!

好在CImg库就一个头文件,所有的源代码都在这个头文件里。只要看一下它是怎么把CImg对象显示到CImgDisplay上的,我们就能依样画葫芦地把它显示到指定HDC上。

经过一番摸索,终于发现可以这样把一个CImg对象显示到HDC上:

  1. template<classT>
  2. voidDrawToHDC(HDCdc,constCImg<T>&src)
  3. {
  4. CImgDisplaydisp;
  5. disp.assign(src,0,3,false,true);//最后一个true指明m_disp不要显示
  6. disp.render(m_src);//把src的内容解析到disp的data中
  7. SetDIBitsToDevice(dc,0,0,disp.width,disp.height,0,0,
  8. 0,disp.height,disp.data,&disp.bmi,DIB_RGB_COLORS);
  9. }

CImgDisplay在显示过程中会填充bmi和data成员变量,而这两个变量正好是DIB数据,这段代码正好利用了这点。
但是它的运行效率实在是不怎样,经调试发现disp.assign方法会生成窗体、互斥量等东东,析构时又要删除它们,我们在这里根本用不着这些东西,浪费啊~~,于是不得不写一个新的画图方法了。

在说新版本的画图方法之前,得先说一下CImg库的另一个好功能:插件支持。只要在#include "CImg.h"之前

#define cimg_plugin "插件文件"
#define cimg_plugin1 "插件文件1"
#define cimg_plugin2 "插件文件2"
...
#define cimg_plugin8 "插件文件8"

CImg库就会把插件文件中的方法加入到CImg类中,看来起很酷,其实工作原理很简单,头文件中CImg类是这样定义的:

  1. template<typenameT>
  2. structCImg{
  3. ...
  4. #ifdefcimg_plugin
  5. #includecimg_plugin
  6. #endif
  7. #ifdefcimg_plugin1
  8. #includecimg_plugin1
  9. #endif
  10. #ifdefcimg_plugin2
  11. #includecimg_plugin2
  12. #endif
  13. #ifdefcimg_plugin3
  14. #includecimg_plugin3
  15. #endif
  16. #ifdefcimg_plugin4
  17. #includecimg_plugin4
  18. #endif
  19. #ifdefcimg_plugin5
  20. #includecimg_plugin5
  21. #endif
  22. #ifdefcimg_plugin6
  23. #includecimg_plugin6
  24. #endif
  25. #ifdefcimg_plugin7
  26. #includecimg_plugin7
  27. #endif
  28. #ifdefcimg_plugin8
  29. #includecimg_plugin8
  30. #endif
  31. ...
  32. }

当我们如下定义时

#define cimg_plugin "cimg4hdc.h"

cimg4hdc.h文件的内容就插入到了CImg类的定义中间。

我们现在就可以写一个插件文件为CImg类加入与Windows GUI交互的方法了,下面的代码是我写的插件文件,文件名为“cimg4hdc.h”,新加了五个方法:

  1. HBITMAPto_bmp(HDCdc=0)const//从CImg产生一个HBITMAP,使用完要记得DeleteObject
  2. CImg<T>&display(HDCdc,RECT&rc)//把CImg显示到HDC上,rc指定显示位置和大小
  3. CImg(HBITMAPbmp)//从HBITMAP直接生成一个CImg,HBITMAP必须是16位色以上
  4. CImg<T>&assign(HBITMAPbmp)//同上
  5. CImg<T>&load_bmp(HBITMAPbmp)//同上

插件文件代码(点击下载,标准做法是右击,链接另存为...):

  1. #ifndefcimg_plugin_cimg4hdc
  2. #definecimg_plugin_cimg4hdc
  3. HBITMAPto_bmp(HDCdc)const
  4. {
  5. CImgDisplaydisp;
  6. disp.width=width;
  7. disp.height=height;
  8. disp.normalization=3;
  9. disp.is_closed=true;
  10. BITMAPINFObmi;
  11. BITMAPINFOHEADER&bh=bmi.bmiHeader;
  12. bh.biSize=sizeof(BITMAPINFOHEADER);
  13. bh.biWidth=disp.width;
  14. bh.biHeight=-(int)disp.height;
  15. bh.biPlanes=1;
  16. bh.biBitCount=32;
  17. bh.biCompression=BI_RGB;
  18. bh.biSizeImage=0;
  19. bh.biXPelsPerMeter=1;
  20. bh.biYPelsPerMeter=1;
  21. bh.biClrUsed=0;
  22. bh.biClrImportant=0;
  23. void*pvBits;
  24. HBITMAPbmp=CreateDIBSection(dc,
  25. &bmi,
  26. DIB_RGB_COLORS,
  27. &pvBits,
  28. NULL,0
  29. );
  30. if(bmp==NULL)returnbmp;
  31. disp.bmi=bmi;
  32. disp.data=(unsignedint*)pvBits;
  33. disp.render(*this);
  34. disp.width=disp.height=0;
  35. disp.data=NULL;
  36. returnbmp;
  37. }
  38. CImg<T>&display(HDCdc,RECT&rc)
  39. {
  40. CImgDisplaydisp;
  41. disp.width=rc.right-rc.left;
  42. disp.height=rc.bottom-rc.top;
  43. disp.normalization=3;
  44. disp.is_closed=true;
  45. BITMAPINFOHEADER&bh=disp.bmi.bmiHeader;
  46. bh.biSize=sizeof(BITMAPINFOHEADER);
  47. bh.biWidth=disp.width;
  48. bh.biHeight=-(int)disp.height;
  49. bh.biPlanes=1;
  50. bh.biBitCount=32;
  51. bh.biCompression=BI_RGB;
  52. bh.biSizeImage=0;
  53. bh.biXPelsPerMeter=1;
  54. bh.biYPelsPerMeter=1;
  55. bh.biClrUsed=0;
  56. bh.biClrImportant=0;
  57. disp.data=newunsignedint[disp.width*disp.height];
  58. disp.render(*this);
  59. ::SetDIBitsToDevice(dc,rc.left,rc.top,
  60. disp.width,disp.height,0,0,
  61. 0,disp.height,disp.data,&disp.bmi,DIB_RGB_COLORS);
  62. disp.width=disp.height=0;
  63. delete[]disp.data;
  64. return*this;
  65. }
  66. CImg(HBITMAPbmp)
  67. :width(0),height(0),depth(0),dim(0),is_shared(false),data(0)
  68. {
  69. load_bmp(bmp);
  70. }
  71. CImg<T>&assign(HBITMAPbmp)
  72. {
  73. returnload_bmp(bmp);
  74. }
  75. CImg<T>&load_bmp(HBITMAPbmp)
  76. {
  77. BITMAPbmpobj;
  78. if(!::GetObject(bmp,sizeof(bmpobj),&bmpobj)||bmpobj.bmBitsPixel<16)return*this;
  79. LONGcbBuffer=bmpobj.bmWidthBytes*(bmpobj.bmHeight);
  80. BYTE*lvbit=newBYTE[cbBuffer];
  81. if(!::GetBitmapBits(bmp,cbBuffer,lvbit))
  82. {
  83. delete[]lvbit;
  84. return*this;
  85. }
  86. unsignedchar*ptrs=lvbit;
  87. intalign= (4 - bmpobj.bmWidthBytes%4)%4;
  88. //Readpixeldata
  89. assign(bmpobj.bmWidth,bmpobj.bmHeight,1,3);
  90. switch(bmpobj.bmBitsPixel){
  91. case16:{//16bitscolors
  92. for(inty=height-1;y>=0;--y){cimg_forX(*this,x){
  93. constunsignedcharc1=*(ptrs++),c2=*(ptrs++);
  94. constunsignedshortcol=(unsignedshort)(c1|(c2<<8));
  95. (*this)(x,y,2)=(T)(col&0x1F);
  96. (*this)(x,y,1)=(T)((col>>5)&0x1F);
  97. (*this)(x,y,0)=(T)((col>>10)&0x1F);
  98. }ptrs+=align;}
  99. }break;
  100. case24:{//24bitscolors
  101. for(inty=height-1;y>=0;--y){cimg_forX(*this,x){
  102. (*this)(x,y,2)=(T)*(ptrs++);
  103. (*this)(x,y,1)=(T)*(ptrs++);
  104. (*this)(x,y,0)=(T)*(ptrs++);
  105. }ptrs+=align;}
  106. }break;
  107. case32:{//32bitscolors
  108. for(inty=height-1;y>=0;--y){cimg_forX(*this,x){
  109. (*this)(x,y,2)=(T)*(ptrs++);
  110. (*this)(x,y,1)=(T)*(ptrs++);
  111. (*this)(x,y,0)=(T)*(ptrs++);
  112. ++ptrs;
  113. }ptrs+=align;}
  114. }break;
  115. }
  116. mirror('y');
  117. delete[]lvbit;
  118. return*this;
  119. }
  120. #endif

测试代码(在WTL里测试):

  1. #definecimg_plugin"cimg4hdc.h"
  2. #include"CImg.h"
  3. usingnamespacecimg_library;
  4. CImg<unsignedchar>m_show;
  5. //生成部分
  6. {
  7. //生成一个100*100的HBITMAP
  8. HDCdc=::CreateCompatibleDC(0);
  9. HBITMAPbmp=::CreateCompatibleBitmap(::GetDC(0),100,100);
  10. ::SelectObject(dc,bmp);
  11. ::TextOut(dc,10,10,_T("HelloWorld"),11);
  12. //从HBITMAP生成一个CImg
  13. CImg<unsignedchar>src(bmp);
  14. //模糊计算,结果与原图并排送给m_show
  15. m_show=(src<<src.get_blur(2)).get_append('x');
  16. //删除HBITMAP和内存DC
  17. ::DeleteObject(bmp);
  18. ::DeleteDC(dc);
  19. }
  20. //显示部分
  21. LRESULTOnPaint(UINT/*uMsg*/,WPARAM/*wParam*/,LPARAM/*lParam*/,BOOL&/*bHandled*/)
  22. {
  23. CPaintDCdc(m_hWnd);
  24. RECTrc={
  25. 10,10,10+m_show.width,10+m_show.height
  26. };
  27. //在HDC上显示m_show
  28. m_show.display(dc,rc);
  29. return0;
  30. }


效果如下:
/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值