海边沫沫

相濡以沫,不如相忘于江湖
posts - 9, comments - 113, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理
在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();
}

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

Feedback

# re: 在Visual C++中配合CImage使用Boost.GIL  回复  更多评论   

2010-10-18 10:16 by xhk
不错

不过没看得出多少看点 呵呵。。。

# re: 在Visual C++中配合CImage使用Boost.GIL  回复  更多评论   

2013-02-05 10:16 by holyfire
CIMage需要MFC,这个对跨平台库有点障碍,你让用QT,BCB之类的人怎么办呢

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理