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

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

 

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

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

上测试代码先:

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

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

CImg类前面已有介绍。

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

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

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

  1. CImgList<T>& load_ffmpeg(const char *const filename,
  2.     const unsigned int first_frame=0,
  3.     const unsigned int last_frame=~0U,
  4.     const unsigned int step_frame=1,
  5.     const bool pixel_format=true,
  6.     const bool resume=false)
  7.  
  8. const CImgList<T>& save_ffmpeg(
  9.     const char *const filename,
  10.     const unsigned int first_frame=0,
  11.     const unsigned int last_frame=~0U,
  12.     const unsigned int fps=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<class T>
  2. void DrawToHDC(HDC dc, const CImg<T> &src)
  3. {
  4.     CImgDisplay disp;
  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<typename T>
  2. struct CImg {
  3.     ...
  4.     #ifdef cimg_plugin
  5.         #include cimg_plugin
  6.     #endif
  7.     #ifdef cimg_plugin1
  8.         #include cimg_plugin1
  9.     #endif
  10.     #ifdef cimg_plugin2
  11.         #include cimg_plugin2
  12.     #endif
  13.     #ifdef cimg_plugin3
  14.         #include cimg_plugin3
  15.     #endif
  16.     #ifdef cimg_plugin4
  17.         #include cimg_plugin4
  18.     #endif
  19.     #ifdef cimg_plugin5
  20.         #include cimg_plugin5
  21.     #endif
  22.     #ifdef cimg_plugin6
  23.         #include cimg_plugin6
  24.     #endif
  25.     #ifdef cimg_plugin7
  26.         #include cimg_plugin7
  27.     #endif
  28.     #ifdef cimg_plugin8
  29.         #include cimg_plugin8
  30.     #endif
  31. ...
  32. }

当我们如下定义时

#define cimg_plugin "cimg4hdc.h"

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

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

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

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

  1. #ifndef cimg_plugin_cimg4hdc
  2. #define cimg_plugin_cimg4hdc
  3.  
  4. HBITMAP to_bmp(HDC dc) const
  5. {
  6.     CImgDisplay disp;
  7.     disp.width = width;
  8.     disp.height = height;
  9.     disp.normalization = 3;
  10.     disp.is_closed = true;
  11.  
  12.     BITMAPINFO bmi;
  13.     BITMAPINFOHEADER &bh = bmi.bmiHeader;
  14.     bh.biSize = sizeof(BITMAPINFOHEADER);
  15.     bh.biWidth = disp.width;
  16.     bh.biHeight = -(int)disp.height;
  17.     bh.biPlanes = 1;
  18.     bh.biBitCount = 32;
  19.     bh.biCompression = BI_RGB;
  20.     bh.biSizeImage = 0;
  21.     bh.biXPelsPerMeter = 1;
  22.     bh.biYPelsPerMeter = 1;
  23.     bh.biClrUsed = 0;
  24.     bh.biClrImportant = 0;
  25.     void *pvBits;
  26.  
  27.     HBITMAP bmp = CreateDIBSection(dc,
  28.       &bmi,
  29.       DIB_RGB_COLORS,
  30.       &pvBits,
  31.       NULL,0
  32.     );   
  33.     if(bmp == NULL) return bmp;
  34.  
  35.     disp.bmi = bmi;   
  36.     disp.data = (unsigned int*)pvBits;
  37.     disp.render(*this);
  38.     disp.width = disp.height = 0;
  39.     disp.data = NULL;
  40.     return bmp;
  41. }
  42.  
  43. CImg<T>& display(HDC dc, RECT &rc)
  44. {
  45.     CImgDisplay disp;
  46.  
  47.     disp.width = rc.right - rc.left;
  48.     disp.height = rc.bottom - rc.top;
  49.     disp.normalization = 3;
  50.     disp.is_closed = true;
  51.    
  52.     BITMAPINFOHEADER &bh = disp.bmi.bmiHeader;
  53.     bh.biSize = sizeof(BITMAPINFOHEADER);
  54.     bh.biWidth = disp.width;
  55.     bh.biHeight = -(int)disp.height;
  56.     bh.biPlanes = 1;
  57.     bh.biBitCount = 32;
  58.     bh.biCompression = BI_RGB;
  59.     bh.biSizeImage = 0;
  60.     bh.biXPelsPerMeter = 1;
  61.     bh.biYPelsPerMeter = 1;
  62.     bh.biClrUsed = 0;
  63.     bh.biClrImportant = 0;
  64.     disp.data = new unsigned int[disp.width*disp.height];
  65.  
  66.     disp.render(*this);
  67.     ::SetDIBitsToDevice(dc,rc.left,rc.top,
  68.         disp.width,disp.height,0,0,
  69.         0,disp.height,disp.data,&disp.bmi,DIB_RGB_COLORS);
  70.  
  71.     disp.width = disp.height = 0;
  72.     delete []disp.data;
  73.  
  74.     return *this;
  75. }
  76.  
  77. CImg(HBITMAP bmp)
  78.     :width(0),height(0),depth(0),dim(0),is_shared(false),data(0)
  79. {
  80.     load_bmp(bmp);
  81. }
  82.  
  83. CImg<T>& assign(HBITMAP bmp)
  84. {
  85.     return load_bmp(bmp);
  86. }
  87.  
  88. CImg<T>& load_bmp(HBITMAP bmp)
  89. {
  90.     BITMAP bmpobj;
  91.     if(!::GetObject(bmp,sizeof(bmpobj),&bmpobj) || bmpobj.bmBitsPixel<16) return *this;
  92.  
  93.     LONG cbBuffer = bmpobj.bmWidthBytes*(bmpobj.bmHeight);
  94.     BYTE *lvbit = new BYTE[cbBuffer];
  95.  
  96.     if(!::GetBitmapBits(bmp, cbBuffer, lvbit))
  97.     {
  98.         delete []lvbit;
  99.         return  *this;
  100.     }
  101.  
  102.     unsigned char* ptrs = lvbit;
  103.     int align = (4 - bmpobj.bmWidthBytes%4)%4;
  104.  
  105.     // Read pixel data
  106.     assign(bmpobj.bmWidth,bmpobj.bmHeight,1,3);
  107.     switch (bmpobj.bmBitsPixel) {
  108.     case 16 : { // 16 bits colors
  109.     for (int y=height-1; y>=0; --y) { cimg_forX(*this,x) {
  110.       const unsigned char c1 = *(ptrs++), c2 = *(ptrs++);
  111.       const unsigned short col = (unsigned short)(c1|(c2<<8));
  112.       (*this)(x,y,2) = (T)(col&0x1F);
  113.       (*this)(x,y,1) = (T)((col>>5)&0x1F);
  114.       (*this)(x,y,0) = (T)((col>>10)&0x1F);
  115.     } ptrs+=align; }
  116.     } break;
  117.     case 24 : { // 24 bits colors
  118.     for (int y=height-1; y>=0; --y) { cimg_forX(*this,x) {
  119.       (*this)(x,y,2) = (T)*(ptrs++);
  120.       (*this)(x,y,1) = (T)*(ptrs++);
  121.       (*this)(x,y,0) = (T)*(ptrs++);
  122.     } ptrs+=align; }
  123.     } break;
  124.     case 32 : { // 32 bits colors
  125.     for (int y=height-1; y>=0; --y) { cimg_forX(*this,x) {
  126.       (*this)(x,y,2) = (T)*(ptrs++);
  127.       (*this)(x,y,1) = (T)*(ptrs++);
  128.       (*this)(x,y,0) = (T)*(ptrs++);
  129.       ++ptrs;
  130.     } ptrs+=align; }
  131.     } break;
  132.     }
  133.     mirror('y');
  134.    
  135.     delete []lvbit;
  136.     return *this;
  137. }
  138. #endif

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

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


效果如下:

你可能感兴趣的:(windows,测试,delete,library,图像处理,colors)