转自:http://www.cppprog.com/2009/0424/106.html
CImg是一个跨平台的C++的图像处理库,提供了加载、处理、显示、保存等一系列功能,其中的图像处理功能尤其强大。
首先,建议先到这里欣赏一下使用CImg代码做的Demo,就是它使我这个没有图像处理经验的童鞋也心动得以致于研究了一星期^_^
主页地址:http://cimg.sourceforge.net/
下载地址:http://cimg.sourceforge.net/download.shtml
下载时注意应该下载源码包,里面附带的大量的例程。而实际上CImg库只是一个头文件CImg.h,这个头文件里包含了CImg库所有的代码。
另外不要错过下载列表中的一个部分完成的《CImg中文参考手册》。
CImg的Hello World
这段代码是从《CImg参考手册》里抄的,可以大致了解一下CImg的框架。
以VC为例:新建控制台程序,输入下面的代码。项目属性的链接器附加依赖项加入kernel.lib user32.lib gdi32.lib。最后,把CImg.h拷贝到项目路径下,即可成功编译运行
- #include"CImg.h"
- usingnamespacecimg_library;
- intmain()
- {
- //定义一个每个颜色8位(bit)的640x400的彩色图像
- CImg<unsignedchar>img(640,400,1,3);
- //将像素值设为0(黑色)
- img.fill(0);
- //定义一个紫色
- unsignedcharpurple[]={255,0,255};
- //在坐标(100,100)处画一个紫色的“Helloworld”
- img.draw_text(100,100,"HelloWorld",purple);
- //在一个标题为“MyfirstCImgcode”的窗口中显示这幅图像
- img.display("MyfirstCImgcode");
- return0;
- }
运行这段代码,显示结果:
在CImg体系中,图像有x,y,z,v四个轴,前三个当然是3维空间的三个方向(知道了吧?CImg可以处理3维图像),第四个v一般表示色彩通道数,比如RGB三色就是3。
上面的代码每行的注释已经写得很详细,从代码里可以看出CImg处于namespace cimg_library名空间之下;模板类CImg<>是主要的图像类,提供了大量的图像处理方法。
在CImg库里,还有一个重要的类是CImgDisplay,它提供了一个显示窗口,不仅可以显示CImg的内容,还可以接收键盘鼠标事件,我们可以暂时把它看成是一个CImg专用窗体类。
CImg类介绍
CImg类提供的方法非常多,为了便于查阅,我用Doxygen重新生成了一份CImg库的说明文档,并做成chm格式,可以本文后面找到下载地址。
CImg模板类提供了图像的载入、保存、处理功能,是整个库的核心组件。它的声明如下:
template<typename T> struct cimg_library::CImg;
模板参数T指明CImg中元素的类型。在自带的参考手册中称这些元素为像素(pixel),不过因为这里的像素和我们平时的像素概念稍微有点不同。因为前面说过CImg体系中有xyzv四个轴,CImg手册称它为4维图像,把4维图像里的单个元素称为像素。而事实上第四维的v通常就是我们的色彩通道,所以要在屏幕上显示出一个真正的像素往往要取同一xyz轴上所有v轴的点(所有色彩通道合成一个真正的彩色像素)。在本文中我决定把这个组成图像的最小单位称为元素,由所有色彩通道v组成一个像素。
CImg类中的6个成员变量:
- //分别对应x,y,z,v四个轴的大小,即宽度、高度、深度和通道数。
- unsignedintwidth,height,depth,dim;
- //指向内存中的图像数据
- T*data;
- //指明data是否是共用的,即是否有data的拥有权。
- boolis_shared;
CImg类里的成员变量都是public的,我们可以直接存取它们,不过为了防止破坏完整性,建议使用成员方法如dimx(), dimy(), dimz(), dimv() 和ptr()来操作。
CImg的构造函数,大部分构造函数都很直白,就不列出了,可以查看手册,下面列出的是一些特殊的构造函数。
- //以字符串指定的数据填充,其中的values字符串包含了一串十进制数字表示的数据。
- //比如参数values为字符串"20,30,40,50",repeat_pattern为true时
- //图像内的数据就以20,30,40,50,20,30,40,50,20...填充。
- CImg(
- constunsignedintdx,
- constunsignedintdy,
- constunsignedintdz,
- constunsignedintdv,
- constchar*constvalues,
- constboolrepeat_pattern)
- //由shared参数决定是否直接引用img中的data数据还是自己持有一份拷贝(是否共享)
- CImg(constCImg<T>&img,constboolshared)
- //参考img的大小构造一个新的CImg对象
- CImg(constCImg<t>&img,constchar*constdimensions)
这个构造函数的dimensions参数由一串数字或转义符组成,分别对应x,y,z,v的大小。比如:
参数为"20 20 1 3"时新CImg对象的x,y,z,v的大小分别是20 20 1 3。
转义符以%开头,后缀可以是:
x, dx, dimx, width 表示img.width y, dy, dimy, height 表示img.height z, dz, dimz, depth 表示img.depth v, dv, dimv, dim 表示img.dim
比如CImg(img, "%y %x 1 3");可以生成一个和img的宽高正好互换的CImg对象。
- //从文件里载入图像,文件类型由扩展名确定
- CImg(constchar*constfilename)
CImg库本身支持BMP,RAW,HDR,INR,PGM,PPM,PAN,DLM格式
安装了ImageMagick(Unix系)后可支持JPG,GIF,PNG,TIF等多种格式
CImg也能使用jpeg库,zlib/png库,tiff库等来支持多种图像格式,只需编译时加入这些库即可。可以到这里下载这些库文件。
- //从CImgDisplay对象的内容创建图像
- CImg(constCImgDisplay&disp)
CImg部分成员方法
CImg提供了丰富的成员方法,在这里全部列出是不可能的,这里只作一些简单介绍。更多方法请参考手册。
- //赋值,它有多个重载的版本,参数和构造函数一样,实际上构造函数最终都是调用它来实现的。
- CImg<T>&assign();
- //把data数据转交给img,自己不再有data数据的拥有权
- CImg<T>&transfer_to(CImg<T>&img);
- //清除数据
- CImg<T>&clear();
- //获得所有元素总数
- unsignedlongsize()const
- //获得某个轴向的大小
- intdimx()const;
- intdimy()const;
- intdimz()const;
- intdimv()const;
- //取得遍历元素的迭代器,这里的iterator其实就是T*。
- iteratorbegin();
- iteratorend();
- //取得首尾数据
- T&first();
- T&last();
- //取得图像内部数据(第二个版本取得数据并定位到指定位置)
- T*ptr();
- T*ptr(
- constunsignedintx,
- constunsignedinty=0,
- constunsignedintz=0,
- constunsignedintv=0);
- //快速存取指定位置上的数据
- T&operator()(
- constunsignedintx,
- constunsignedinty=0,
- constunsignedintz=0,
- constunsignedintv=0);
- //按索引直接存取data数组
- T&operator[](constunsignedlongoff)
- //得到指定位置的索引
- longoffset(
- constintx,
- constinty=0,
- constintz=0,
- constintv=0)const;
- //存取指定位置上的数据,当指定的轴的超出边界时返回最边上的值
- T&at(constintoff);
- T&atX(constintx,constinty,constintz,constintv);
- T&atXY(constintx,constinty,constintz,constintv);
- T&atXYZ(constintx,constinty,constintz,constintv);
- T&atXYZV(constintx,constinty,constintz,constintv);
- //存取指定位置上的数据,当指定的轴的超出边界时返回out_val
- T&at(constintoff,constTout_val);
- T&atX(constintx,constinty,constintz,constintv,constTout_val);
- T&atXY(constintx,constinty,constintz,constintv,constTout_val);
- T&atXYZ(constintx,constinty,constintz,constintv,constTout_val);
- T&atXYZV(constintx,constinty,constintz,constintv,constTout_val);
值得一提的是还有两种插值版本的at方法,分别是线性插值和三次插值。其中线性插值以linear_作为前缀,三次插值以cubic_作为前缀。参数和上面的类似,只是各轴位置的类型不是int而是float,这批方法会按插值法算出小数点位置上的数据。
到这里可以发现CImg类实际上提供了类似于vector容器的编程接口,这样我们的STL算法也能用于CImg的操作了,随后就可以看到库里有时也会把CImg类直接当作数据容器来使用。比如下面CImg中就有这个方法:
- //返回字符串的形式的图像中的数据,默认是逗号分隔的一长串数字。
- //这里返回的CImg<charT>不代表图像,而是一个一维的字符串数据,这时它只是一个容器而已,可以把它看作是vector<charT>。
- CImg<charT>value_string(constcharseparator=',',constunsignedintmax_size=0);
CImg类的画图方法
draw_point draw_line draw_polygon draw_spline draw_arrow draw_image draw_rectangle draw_triangle draw_ellipse draw_circle draw_text draw_quiver draw_graph draw_axis draw_grid draw_fill draw_plasma draw_mandelbrot draw_gaussian draw_object3d
CImg的画图方法真的是非常多,每种方法都有2D和3D的重载,具体可以参考手册。
CImg类的图像处理方法
CImg类的图像处理方法分两种版本:一种是直接在自己的数据上计算和保存;另一种是返回一个计算后的CImg对象,自己的数据不会改变,这个版本以get_作为前缀并且总是const方法。
- //填充图像,其中的valx参数用于顺序填充图像,最多可以有15个。
- CImg<T>&fill(constTval0,constTval1,...);
- //以values指定的字符串来填充图像,values格式参考上文的构造函数参数
- CImg<T>&fill(constchar*constvalues,constboolrepeat_pattern)
- //以values图像里的值填充。
- CImg<T>&fill(constCImg<t>&values,constboolrepeat_pattern=true)
相应的,有get_fill方法,返回填充后的新CImg对象。后面的方法同相也有get_方法,不再累述。
- //只填充指定的yzv位置上的x轴的数据
- CImg<T>&fillX(
- constunsignedinty,
- constunsignedintz,
- constunsignedintv,
- constinta0,...);
当然,也有fillY,fillZ,fillV。
- //线性规格化图像,即把图像中的所有元素数值线性放大或减小到a和b之间。
- CImg<T>&normalize(constTa,constTb)
- //把图像中的所有元素数值大小限制在a和b之间,若有超出,则直接限制在边界上。
- CImg<T>&cut(constTa,constTb);
- //把图像像素数据数字化到n级上,即图像元素数值从小到大被分成n个级别。(类似AD转换)
- CImg<T>&quantize(constunsignedintn,constboolkeep_range=true)
- //阈值化图像,小于value的为0,大于value的为1。
- //若soft=true,则小于value元素加上value,大于value的元素减去value。
- //strict决定比较时使用<操作还是<=操作。
- CImg<T>&threshold(constTvalue,constboolsoft=false,constboolstrict=false)
- //旋转图像,以cx,cy指定点为中心,旋转angle度,放大zoom倍。
- CImg<T>&rotate(
- constfloatangle,
- constfloatcx,
- constfloatcy,
- constfloatzoom,
- constunsignedintborder_conditions=3,
- constunsignedintinterpolation=1);
参数border_conditions指定怎样处理边界外的值,0-边界以外以0值填充,1-重复边界点的值,2-重复图像
参数interpolation指定采用何种插值方法,0-不插值,1-线性插值,2-三次插值
- //改变大小
- CImg<T>&resize(
- constintpdx,
- constintpdy=-100,
- constintpdz=-100,
- constintpdv=-100,
- constintinterpolation_type=1,
- constintborder_condition=-1,
- constboolcenter=false)
改变图像到指定大小,当pdx,pdy,pdz或pdv<0时,使用百分比。interpolation_type指定插值方法,分别是:
-1 = 不插值 : 图像直接按大小剪切 0 = 不插值 : 多余空间依据border_condition决定。 1 = 临近点插值。 2 = 移动平均数插值。 3 = 线性插值。 4 = 删格插值。 5 = 双三次插值。
另外,还有几个优化的resize版本:
- //优化的缩小至原来的1/2
- CImg<T>&resize_halfXY()
- //优化的放大至原来的2倍
- CImg<T>&resize_doubleXY()
- //优化的放大至原来的3倍
- CImg<T>&resize_tripleXY()
- //扭曲图像,由参数warp中相同位置的数据决定该位置元素的去向。
- CImg<T>&warp(
- constCImg<t>&warp,
- constboolrelative=false,
- constboolinterpolation=true,
- constunsignedintborder_conditions=0)
参数warp在这里只是一个容器(不要把它看成图像)的作用,其x,y,z点上的数据决定了当前图像x,y,z点上元素的新位置,这个新位置由warp的x,y,z点上的v轴数据给出。如果warp的v轴长度是1时,当前图像元素只会在x轴上移动。如果warp的v轴长度是2时,当前图像元素在x,y轴上移动...relative=ture时,表示wrap中的数据是相对位置。
- //按指定轴镜像,axis可以是'x','y','z','v'
- CImg<T>&mirror(constcharaxis);
- //偏移图像
- CImg<T>&translate(
- constintdeltax,
- constintdeltay=0,
- constintdeltaz=0,
- constintdeltav=0,
- constintborder_condition=0)
- //取原图像中的一块子图
- CImg<T>&crop(
- constintx0,constinty0,constintz0,constintv0,
- constintx1,constinty1,constintz1,constintv1,
- constboolborder_condition=false)
crop方法还有其它的版本,分别是省略v轴,z轴和y轴的版本。
- //把3D图像转成2D视图。
- CImg<T>&projections2d(
- constunsignedintx0,constunsignedinty0,constunsignedintz0,
- constintdx=-100,constintdy=-100,constintdz=-100);
最后,下面的这些方法提供类似Photoshop的滤镜功能,强啊~~:
noise 噪声 deriche Canny-Deriche算法 blur 模糊 blur_anisotropic 各向异性模糊 blur_bilateral 对称模糊 blur_patch 面片模糊 blur_median 中值模糊 sharpen 尖锐 haar 小波分析 ...还有好多
看了上面的介绍,我想大家肯定对CImg的强大能力有了初步的了解(要知道上面列出的方法只是CImg方法中的冰山一角,而且更强的是CImg库还能通过指定Plugin宏的方式插入新的方法,当CImg自带的方法不能满足我们的要求时,也许已经有人提供了对应的插件了),下次我们开始学习CImg的具体使用吧^_^
最后放上前面说的CImg的Doxygen手册,猛击这里下载。