在Visual C++中配合CImage使用Boost.GIL

在Boost中,有一个图像处理库,那就是GIL,该库是由鼎鼎大名的Adobe公司贡献的,它在图像处理方面的优势可想而知。使用GIL,可以非常方便地对图像数据进行处理而不用考虑太多的细节,比如很简单地定位图像的行、列或任意像素,可以任意定位图像中的某一个颜色通道,甚至提取其中一个通道构建另外一个灰度图像,可以方便地将RGB图像转换成RGBA、灰度图像或者反过来转换,可以方便地旋转、倒置图像......优点实在是太多,我也说不好。当然,最重要的优点是它的通用性和性能。

但是使用Boost.GIL有一个很大的障碍,那就是它的IO扩展需要编译第三方的图像解码库做支持,而且这个编译过程很麻烦。作为一个跨平台的库,这么做无可厚非,但是在Windows平台上,就没有必要使用第三方的图像解码库了,难道Windows提供的图像解码功能还不够强吗?

在Visual C++中,有一个类CImage可以读取任意格式的图像文件,无论bmp、gif、tiff、jpeg还是png,都行。还可以将图像数据保存到文件,还可以非常方便地将图像显示到屏幕上。下面,我将使用MFC实现一个简单的SDI程序,在该程序的基础上,可以非常方便第添加功能,使用GIL对图像数据进行操作。

第一步,使用MFC向导创建一个SDI程序。

第二步,在文档类中添加几个变量,用来保存图像数据,image_src保存源图像数据,image_dst保存修改后的图像数据,同时定义两个view,用到了GIL中的类型,可以看到,我都是使用的RGBA模式的图像数据。代码如下:
    CImage image_src;
    rgba8_view_t view_src;
    CImage image_dst;
    rgba8_view_t view_dst;

第三步,重写文档类的OnOpenDocument()方法。在这个方法中,使用CImage类读取图像文件,然后使用GIL将图像文件的数据复制到image_src和image_dst中。不管原始图像是RGB还是RGBA的,最后统一都变成了RGBA的。代码如下:
BOOL CGIL_StudyDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
    
if  ( ! CDocument::OnOpenDocument(lpszPathName))
        
return  FALSE;
    CImage loader;
    loader.Load(lpszPathName);
    
if (loader.IsNull())
        
return  FALSE;

    unsigned 
char *  buffer_loader  =  (unsigned  char * )loader.GetBits() + (loader.GetPitch() * (loader.GetHeight() - 1 ));
    
if ( ! image_src.IsNull())image_src.Destroy();
    image_src.Create(loader.GetWidth(),loader.GetHeight(),
32 );
    unsigned 
char *  buffer_src  =  (unsigned  char * )image_src.GetBits() + (image_src.GetPitch() * (image_src.GetHeight() - 1 ));
    view_src 
=  interleaved_view(image_src.GetWidth(),image_src.GetHeight(),(rgba8_pixel_t * )buffer_src,image_src.GetWidth() * 4 );

    
if ( ! image_dst.IsNull())image_dst.Destroy();
    image_dst.Create(loader.GetWidth(),loader.GetHeight(),
32 );
    unsigned 
char *  buffer_dst  =  (unsigned  char * )image_dst.GetBits() + (image_dst.GetPitch() * (image_dst.GetHeight() - 1 ));
    view_dst 
=  interleaved_view(image_dst.GetWidth(),image_dst.GetHeight(),(rgba8_pixel_t * )buffer_dst,image_dst.GetWidth() * 4 );

    
switch (loader.GetBPP()){
    
case   24 :
        {
            rgb8_view_t view_loader 
=  interleaved_view(
                loader.GetWidth(),loader.GetHeight(),
                (rgb8_pixel_t
* )buffer_loader,std::abs(loader.GetPitch()));
            copy_pixels(
                    color_converted_view
< rgba8_image_t::value_type > (view_loader),
                    view_src);
            copy_pixels(
                    color_converted_view
< rgba8_image_t::value_type > (view_loader),
                    view_dst);
            
break ;
        }
    
case   32 :
        {
            rgba8_view_t view_loader 
=  interleaved_view(
                  loader.GetWidth(),loader.GetHeight(),
                  (rgba8_pixel_t
* )buffer_loader,loader.GetWidth() * 4 );
            copy_pixels(
                    color_converted_view
< rgba8_image_t::value_type > (view_loader),
                    view_src);
            copy_pixels(
                color_converted_view
< rgba8_image_t::value_type > (view_loader),
                view_dst);
            
break ;
        }
    
default :
        assert(
false );
        
break ;
    }
    

    
return  TRUE;
}

第四步,重现MFC视图类中的OnDraw()方法,在窗口中显示image_dst,代码如下:
void  CGIL_StudyView::OnDraw(CDC *  pDC)
{
    CGIL_StudyDoc
*  pDoc  =  GetDocument();
    ASSERT_VALID(pDoc);
    
if  ( ! pDoc)
        
return ;
    
if (pDoc -> image_dst.IsNull()) return ;
    pDoc
-> image_dst.Draw( * pDC, 0 , 0 ,pDoc -> image_dst.GetWidth(),pDoc -> image_dst.GetHeight());

}

至此,简单的框架搭建完成。如果要对图像数据进行操作,只需要操作view_src,然后将结果写入view_dst即可。比如,要使用GIL自带的例子中的x_gradient算法,只需要以下两个步骤:
1、将GIL例子中的几个模板函数代码复制到我们的项目中,如下:
template  < typename SrcView, typename DstView >
void  x_gradient( const  SrcView &  src,  const  DstView &  dst) {
    
for  ( int  y = 0 ; y < src.height();  ++ y) {
        typename SrcView::x_iterator src_it 
=  src.row_begin(y);
        typename DstView::x_iterator dst_it 
=  dst.row_begin(y);

        
for  ( int  x = 1 ; x < src.width() - 1 ++ x) {
           
for  ( int  c = 0 ; c < num_channels < SrcView > ::value;  ++ c)
                dst_it[x][c] 
=  (src_it[x - 1 ][c] -  src_it[x + 1 ][c]) / 2 ;
        }
    }
}

2、为我们的SDI程序添加一个菜单,并在菜单的处理函数中调用上面的函数,如下:
void  CGIL_StudyView::OnXGradient()
{
    CGIL_StudyDoc
*  pDoc  =  GetDocument();
    ASSERT_VALID(pDoc);
    
if  ( ! pDoc)
        
return ;
    x_gradient(pDoc
-> view_src,pDoc -> view_dst);
    Invalidate();
}

运行程序,点击这个菜单,就可以看到效果了。

你可能感兴趣的:(在Visual C++中配合CImage使用Boost.GIL)